# API Reference

## DetourService

High-level orchestration layer for routing-safe integration. Extends `ChangeNotifier` so it can be used with Flutter's `ListenableBuilder` or provider-based state management.

### Constructor

```dart
DetourService({
  Duration duplicateSuppressionWindow = const Duration(seconds: 2),
})
```

<PropertyList>
  <Property name="duplicateSuppressionWindow" type={"Duration"} default={"Duration(seconds: 2)"}>
    Time window used to suppress duplicate emissions for the same link (matched by `type|url`, and the URL comparison ignores case). Prevents double-navigation when the OS delivers the same link to both the initial and runtime paths.
  </Property>
</PropertyList>

### Properties

<PropertyList>
  <Property name="isStarted" type={"bool"}>
    `true` once `start()` has configured the native SDK, until `stop()` is called. While it is `true`, `start()` calls are no-ops.
  </Property>
  <Property name="isInitialLinkProcessed" type={"bool"}>
    `true` once the initial link resolve flow has completed, regardless of whether a link was found. Use this to conditionally show a splash screen.
  </Property>
  <Property name="pendingIntent" type={"DetourIntent?"} typeHref="#detourintent">
    Last navigation intent waiting to be consumed. Set when a link is resolved and cleared by `consumePendingIntent()`. Notifies listeners on change.
  </Property>
</PropertyList>

### Methods

#### `start`

```dart
Future<void> start(DetourConfig config)
```

Starts the Detour flow: configures the native SDK, subscribes to the runtime
link stream, and resolves the initial link. Safe to call multiple times. Only
the first call starts the service. After `stop()`, the next call starts it
again.

#### `processLink`

```dart
Future<DetourResult> processLink(String url, {bool emitIntent = true})
```

Processes an arbitrary URL through the native SDK. When `emitIntent` is `true` (default) and the result contains a link, a new `DetourIntent` with source `manual` is emitted and `pendingIntent` is updated.

#### `logEvent`

```dart
Future<void> logEvent(DetourEventName eventName, {Map<String, dynamic>? data})
```

Records a user action using a predefined [`DetourEventName`](#detoureventname). The optional `data` parameter accepts a map of arbitrary metadata.

#### `logRetention`

```dart
Future<void> logRetention(String eventName)
```

Records a retention/lifecycle event with a free-form event name string.

#### `consumePendingIntent`

```dart
void consumePendingIntent()
```

Marks the current `pendingIntent` as consumed by setting it to `null` and notifying listeners. Call this after your navigation layer has handled the intent to prevent repeated navigation.

#### `stop`

```dart
Future<void> stop()
```

Cancels the runtime link stream subscription and resets the service.
`pendingIntent` becomes `null`, `isStarted` and `isInitialLinkProcessed` become
`false`, and the recent links held for duplicate suppression are cleared.
Listeners are notified. A later `start()` call runs the full start flow again,
including [`resolveInitialLink`](#resolveinitiallink).

On Android, the native SDK keeps the configuration from the first `start()`.
The repeated [`resolveInitialLink`](#resolveinitiallink) follows the rules for
repeated calls.

## DetourFlutterPlugin

Low-level method/event-channel bridge to native SDKs. Use this directly only when you need fine-grained control over the link lifecycle. For most integrations, prefer [`DetourService`](#detourservice).

### Methods

#### `configure`

```dart
Future<void> configure(DetourConfig config)
```

Configures the native SDK with credentials and behavioral settings. Must be called before other methods.

#### `resolveInitialLink`

```dart
Future<DetourResult> resolveInitialLink()
```

Resolves the link that launched the app (cold start). On iOS, a repeated call
returns a result with no link. On Android, a repeated call processes the launch
intent again, so it returns the same App Link or custom scheme link as the first
call. On both platforms, only the first call can return a deferred link.

#### `processLink`

```dart
Future<DetourResult> processLink(String url)
```

Processes a single URL through the native SDK and returns the result.

#### `linkStream`

```dart
Stream<DetourResult> get linkStream
```

Stream of results for links the app receives while it is running. It carries
Universal Links (iOS) and App Links (Android) unless `linkProcessingMode` is
`LinkProcessingMode.deferredOnly`, and custom scheme links only when it is
`LinkProcessingMode.all`.

#### `logEvent`

```dart
Future<void> logEvent(DetourEventName eventName, {Map<String, dynamic>? data})
```

Forwards an analytics event to the native SDK.

#### `logRetention`

```dart
Future<void> logRetention(String eventName)
```

Forwards a retention event to the native SDK.

## Types

### DetourConfig

```dart
class DetourConfig {
  final String apiKey;
  final String appID;
  final bool shouldUseClipboard;
  final LinkProcessingMode linkProcessingMode;
}
```

<PropertyList>
  <Property name="apiKey" type={"String"} required>
    Publishable API key from the Detour Dashboard. Used as a Bearer token for all API calls (deferred link matching, short-link resolution, analytics)
  </Property>
  <Property name="appID" type={"String"} required>
    Unique app identifier from the Detour Dashboard. Used to route API requests to the correct app.
  </Property>
  <Property name="shouldUseClipboard" type={"bool"} default={"true"}>
    iOS only. When `true`, the SDK reads the device clipboard as part of probabilistic fingerprinting for deferred link matching. May trigger the iOS system paste permission prompt.
  </Property>
  <Property name="linkProcessingMode" type={"LinkProcessingMode"} typeHref="#linkprocessingmode" default={"LinkProcessingMode.all"}>
    Controls which link sources the SDK processes. See [LinkProcessingMode](#linkprocessingmode) for details.
  </Property>
</PropertyList>

### DetourResult

```dart
class DetourResult {
  final bool processed;
  final DetourLink? link;
}
```

Result returned by link processing methods.

<PropertyList>
  <Property name="processed" type={"bool"}>
    `true` when the native SDK has finished processing (regardless of whether a link was found)
  </Property>
  <Property name="link" type={"DetourLink?"} typeHref="#detourlink">
    Parsed link data, or `null` if no link was matched.
  </Property>
</PropertyList>

### DetourLink

```dart
class DetourLink {
  final String url;
  final String route;
  final String pathname;
  final Map<String, String> params;
  final LinkType type;
}
```

Parsed link data returned inside a `DetourResult`.

<PropertyList>
  <Property name="url" type={"String"}>
    Full URL that was matched or opened.
  </Property>
  <Property name="route" type={"String"}>
    In-app route for navigation, as a path with query string, first path segment (app hash) stripped (e.g. `/promo/123?utm=1`)
  </Property>
  <Property name="pathname" type={"String"}>
    Route path without query string (e.g. `/promo/123`)
  </Property>
  <Property name="params" type={"Map<String, String>"}>
    Parsed query parameters as key-value pairs.
  </Property>
  <Property name="type" type={"LinkType"} typeHref="#linktype">
    How the link arrived in the app.
  </Property>
</PropertyList>

### DetourIntent

```dart
class DetourIntent {
  final DetourLink link;
  final DetourIntentSource source;
  final DateTime receivedAt;
}
```

A single navigation intent produced by `DetourService`. Represents a resolved link that is ready to be consumed by the app's navigation layer.

<PropertyList>
  <Property name="link" type={"DetourLink"} typeHref="#detourlink">
    The resolved link data.
  </Property>
  <Property name="source" type={"DetourIntentSource"} typeHref="#detourintentsource">
    Origin of the link, which says how it was resolved.
  </Property>
  <Property name="receivedAt" type={"DateTime"}>
    Timestamp when the intent was created.
  </Property>
</PropertyList>

### DetourIntentSource

```dart
enum DetourIntentSource {
  initial,
  runtime,
  manual,
}
```

Origin of a resolved Detour link emitted by `DetourService`:

| Value | Meaning |
| --- | --- |
| `initial` | Link resolved from cold-start flow via `resolveInitialLink()` |
| `runtime` | Link received while the app is running via the native link stream |
| `manual` | Link resolved from an explicit `processLink(url)` call |

### LinkType

```dart
enum LinkType {
  deferred,
  verified,
  scheme,
}
```

Describes how the link arrived in the app:

| Value | Meaning |
| --- | --- |
| `deferred` | The link was obtained from the Detour deferred-link API. This happens when a user who does not have the app installed clicks a Detour link, installs the app, and opens it. The SDK retrieves the original link from the backend on first launch |
| `verified` | The link arrived as a Universal Link (iOS) or App Link (Android), an HTTP/HTTPS URL that the OS verified belongs to your app and delivered directly to it |
| `scheme` | The link arrived via a custom URL scheme (e.g. `myapp://path`). These are non-HTTP URLs that the OS routes to your app based on the registered scheme |

### LinkProcessingMode

```dart
enum LinkProcessingMode {
  all,
  webOnly,
  deferredOnly,
}
```

Controls which link sources the native SDK processes:

| Value | Behavior |
| --- | --- |
| `all` (default) | Processes all link sources: deferred links from the Detour API, Universal Links (iOS) / App Links (Android), and custom scheme deep links |
| `webOnly` | Same as `all` but filters out custom scheme links. Only deferred links and HTTP/HTTPS links are processed |
| `deferredOnly` | Only processes deferred links from the Detour API. Skips runtime link listener and initial URL check. Recommended when your navigation framework already handles runtime deep links |

### DetourEventName

Standard event names for use with `logEvent()`.

| Enum value | String value |
| :--- | :--- |
| **General** | |
| `DetourEventName.login` | `login` |
| `DetourEventName.search` | `search` |
| `DetourEventName.share` | `share` |
| `DetourEventName.signUp` | `sign_up` |
| `DetourEventName.tutorialBegin` | `tutorial_begin` |
| `DetourEventName.tutorialComplete` | `tutorial_complete` |
| `DetourEventName.reEngage` | `re_engage` |
| `DetourEventName.invite` | `invite` |
| `DetourEventName.openedFromPushNotification` | `opened_from_push_notification` |
| **Sales** | |
| `DetourEventName.addPaymentInfo` | `add_payment_info` |
| `DetourEventName.addShippingInfo` | `add_shipping_info` |
| `DetourEventName.addToCart` | `add_to_cart` |
| `DetourEventName.removeFromCart` | `remove_from_cart` |
| `DetourEventName.refund` | `refund` |
| `DetourEventName.viewItem` | `view_item` |
| `DetourEventName.beginCheckout` | `begin_checkout` |
| `DetourEventName.purchase` | `purchase` |
| `DetourEventName.adImpression` | `ad_impression` |

## Notes

- `logEvent` accepts only predefined `DetourEventName` enum values.
- The Flutter bridge does not expose `mountAnalytics`, `unmountAnalytics`, or `resetSession`. Analytics are managed automatically by the native SDKs.

## Related pages

- [Usage](https://detour.swmansion.com/docs/sdk/flutter/sdk-usage)
- [Examples](https://detour.swmansion.com/docs/sdk/flutter/examples)
- [Testing & Troubleshooting](https://detour.swmansion.com/docs/sdk/flutter/testing)