# Flutter SDK API

> Functions, options, parameters, futures, and native behavior for the Anectico Flutter SDK.

Canonical page: https://anectico.com/docs/reference/flutter-sdk/


`anectico_flutter` bridges Dart to the native iOS and Android SDKs. Native code owns identity, sessions,
persistence, transport, and native crashes; Dart adds Flutter framework and uncaught async error
capture. Configuration failures, including a missing native plugin, fail the returned future;
subsequent telemetry calls remain best-effort and swallow channel failures.

Use a project key with `ingest:write` and `analytics:write`. Flutter replay is not supported.

## Configure and shut down

```dart
await Anectico.configure(
  const AnecticoOptions(
    apiKey: 'an_...',
    environment: 'production',
    serviceVersion: '1.4.0',
  ),
);
```

| API | Parameters | Returns | Behavior |
| --- | --- | --- | --- |
| `Anectico.configure(options)` | `AnecticoOptions` | `Future<void>` | Configures native state and installs Dart handlers unless disabled. The future fails for invalid options or a missing native plugin, and `isConfigured` remains false. |
| `Anectico.flush()` | — | `Future<bool>` | Flushes the native event queue and resolves whether every batch it attempted was accepted. See [Knowing whether your events arrived](#knowing-whether-your-events-arrived). |
| `Anectico.shutdown()` | — | `Future<bool>` | Removes Dart/native hooks, flushes, and releases resources. Resolves the final flush's outcome, like `flush`. |
| `Anectico.configure(options, resumeAnalyticsCollection: true)` | Explicit new consent after awaited shutdown | `Future<void>` | Purges all old analytics queue data before reopening collection; failures reject configuration. |
| `Anectico.disableAnalytics()` | — | `Future<bool>` | Persist analytics withdrawal and purge queued data; await the result before relying on the native gate. |
| `Anectico.stats()` | — | `Future<AnalyticsStats>` | Delivery counters — `recorded`, `delivered`, `dropped`, `queued`. Zeros before `configure`. |
| `Anectico.deliveryErrors` | property | `Stream<DeliveryError>` | Emits once per permanently discarded batch with `status`, `dropped`, and `reason`. |
| `Anectico.isConfigured` | property | `bool` | Dart-side view of whether configure completed. Diagnostic use only. |
| `AnecticoFlutter.installErrorCapture()` | — | `void` | Installs and chains `FlutterError.onError` and `PlatformDispatcher.onError`. |
| `AnecticoFlutter.uninstallErrorCapture()` | — | `void` | Restores prior handlers when they are still owned by Anectico. |
| `AnecticoFlutter.isErrorCaptureInstalled` | property | `bool` | Whether Dart hooks are currently installed. |

## Withdrawing analytics consent

Await `Anectico.disableAnalytics()` when analytics consent is withdrawn. The native SDK closes analytics admission,
including capture, identify, group, reset, and lifecycle events, and discards pending events without
flushing them. Errors, crashes, agent traces, and OTLP logs have separate controls and continue.
An already-admitted request can still reach the server: validated receipt outcomes remain credited;
uncertain retries and unsent remainder are discarded with `collection_disabled` and status `0`.
This does not erase previously accepted data. `recorded = delivered + dropped + queued` remains true.

A `true` result confirms both the persistent withdrawal marker and deletion of the analytics spool.
On `false`, this configured client stays disabled, but persistence/purge needs retry; do not assume
restart safety until a retry succeeds. Await the future before relying on the native admission fence. A missing plugin or channel failure returns `false`.
Ordinary configuration preserves the marker across restart. Only after obtaining new consent,
await `Anectico.shutdown()`, then call `await Anectico.configure(options, resumeAnalyticsCollection: true)`.
Explicit renewal purges all old analytics queue data even when the marker is absent; a purge failure
fails configuration. Repeated configuration of an already configured SDK does not renew consent.
Wait for shutdown completion before reconfiguring; configuration during retirement fails.

## Knowing whether your events arrived

`flush` returns `true` only when every attempted product-event position has a validated
`queued` acknowledgement. An empty queue succeeds. This is evidence of queue acceptance;
it does not prove query visibility, unique stored rows, or identity application.
Errors and crash reports use a separate pipeline.

A complete version-one receipt classifies each original position before the SDK updates
its queue or counters:

| Receipt outcome | Your events | `flush` | Queue and callback |
| --- | --- | --- | --- |
| `queued` | removed from the pending queue | succeeds if all positions were queued | increments `delivered` |
| `rejected` | refused positions are discarded | failure | increments `dropped`; fires a drop callback |
| `unknown`, malformed, truncated, oversized, or absent receipt | uncertain positions remain pending | failure | retains original message IDs; no drop callback |

Typed permanent request failures also discard the attempted batch. Network failures,
rate limits, and dependency failures retain it. Repeated uncertainty backs off from one
second to 60 seconds; a fully settled receipt resets that delay. Server retry hints are
clamped to that range.

`Anectico.stats()` reports `recorded`, `delivered`, `dropped`, and `queued`; `recorded` equals
the other three added together. `delivered` counts validated queue acknowledgements,
not unique stored events.

The `Anectico.deliveryErrors` stream reports the number of discarded events once per affected chunk. A batch
receipt can refuse events with HTTP `200`: `invalid_event`, `quota_exceeded`, or
`mixed_rejection`. Typed request failures use `bad_request`, `unauthorized`, `forbidden`,
`not_found`, `payload_too_large`, `unprocessable`, or `rejected`. Local `queue_overflow`
and `not_serializable` drops carry status `0`. Unknown outcomes remain queued without a callback unless explicitly discarded on withdrawal with `collection_disabled`. An unrecognized reason maps to `DeliveryErrorReason.unknown`, preserving the original code in `rawReason`.

Capturing stays non-blocking: `capture`, `identify`, `group`, and `screen` queue the event and return
immediately, so the delivery outcome reaches you through the three signals above and never through
their own return value.

```dart
Anectico.deliveryErrors.listen((DeliveryError error) {
  debugPrint('lost ${error.dropped} events: ${error.rawReason} (HTTP ${error.status})');
});

if (!await Anectico.flush()) {
  final AnalyticsStats stats = await Anectico.stats();
  // stats.queued > 0 means a retry is pending; otherwise the events are gone.
}
```

When the plugin is not installed in the running app, `flush()` and `shutdown()` resolve `false` and
`stats()` reports zeros — nothing was delivered, so nothing is reported as accepted.

## `AnecticoOptions`

Only `apiKey` is required. Null fields are omitted across the channel so native defaults apply.
Invalid endpoints, timers, capacities, frame limits, and non-finite or out-of-range sampling values
fail configuration instead of being clamped.

| Option | Type/default | Purpose |
| --- | --- | --- |
| `apiKey` | `String`; required | Error/event API key. |
| `endpoint` | `String?`; native `https://api.anectico.com` | API base URL. |
| `environment` | `String?`; native `development` | Deployment environment. |
| `release`, `dist` | `String?` | Release and artifact identifiers. |
| `serviceName`, `serviceVersion` | `String?` | Native resource identity; defaults from the app. |
| `flushAt` | `int?`; native `20` | Event-count flush threshold. |
| `flushIntervalMs` | `int?`; native `5000` | Foreground-only native periodic flush; zero disables it. |
| `maxStackTraceFrames` | `int?`; native `50` | Dart/native frame cap. |
| `breadcrumbCapacity` | `int?`; native `100` | Native breadcrumb capacity. |
| `maxQueueSize` | `int?`; native `10000` | Native event queue cap. |
| `errorSampleRate` | `double?`; native `1` | Non-fatal error sampling from 0–1. |
| `enableCrashReporting` | `bool?`; native `true` | Install native crash handlers. |
| `debug` | `bool?`; native `false` | Native diagnostic logging. |
| `enableDartErrorCapture` | `bool`; `true` | Dart-only automatic error handlers. |

## Identity, accounts, and events

| Method | Parameters | Returns | Behavior |
| --- | --- | --- | --- |
| `Anectico.identify(distinctId, {set})` | stable ID; person properties | `Future<void>` | Links anonymous and known identity. |
| `Anectico.group(type, key, {set})` | account type/key; properties | `Future<void>` | Records membership and `$groupidentify`. |
| `Anectico.capture(event, {properties})` | event; properties | `Future<void>` | Queues a durable diagnostic event, natively stamped with the reserved `$release`/`$app_version` properties (see [Reserved event properties](/docs/investigate/event-schema#reserved-properties)) when `configure()` set `release`/`serviceVersion`. |
| `Anectico.screen(name, {properties})` | screen; properties | `Future<void>` | Queues `$screen` and `$screen_name`. |
| `Anectico.reset()` | — | `Future<void>` | Logout: rotates native identity/session and clears groups, global error-user context, and breadcrumbs. |
| `Anectico.getDistinctId()` | — | `Future<String>` | Current identity or empty string. |
| `Anectico.getSessionId()` | — | `Future<String>` | Current session without extending idle lifetime, or empty string. |

## Agent runs

`Anectico.startAgentRun(AgentRunOptions options)` synchronously returns a one-shot `AgentRun`.

```dart
final run = Anectico.startAgentRun(const AgentRunOptions(
  agent: 'checkout-helper',
  agentId: 'checkout',
  agentVersion: '1.4.0',
  conversationId: 'chat-42',
));
// Perform the agent's work, then declare its actual outcome.
await run.end(AgentRunTerminalStatus.completed);
// On failure instead:
// await run.end(AgentRunTerminalStatus.failed, terminalReason: 'tool_failed');
```

`AgentRunOptions.agent` is required. `agentId`, `agentVersion`, and `conversationId` are optional strings mapping to
`gen_ai.agent.id`, `gen_ai.agent.version`, and `gen_ai.conversation.id`. The agent name maps to
`gen_ai.agent.name`. Configure and identify the customer before starting a run. A terminal record
is an `invoke_agent` span sent with `ingest:write`; it does not require an agent-read or agent-write
management scope. It is not automatic instrumentation of nested model/tool calls.

`end(AgentRunTerminalStatus status, {String? terminalReason})` returns `Future<void>`, not a
delivery boolean. Status is required: `completed`, `failed`, `timedOut`, `cancelled`, or
`maxSteps`; multiword wire values are `timed_out` and `max_steps`.

Use a stable, non-sensitive reason code, not an exception message, prompt, or customer value.
Reasons are trimmed, lowercased, and must match `^[a-z][a-z0-9_]{0,63}$`. Omitted reasons default to
`timeout` for a timeout, `user_cancelled` for cancellation, and the wire status for other outcomes.
These are source-declared outcomes; Anectico does not infer success from the absence of an error.

An invalid reason fails the future with `ArgumentError` and leaves the handle open. A native
`false` result completes the future but leaves the handle open for a later retry; channel failures
are handled by the native bridge wrapper. Concurrent end calls share the in-flight future.
After native acceptance, later valid calls do nothing. Completion is not network-delivery
confirmation. Native identity is attached at completion, so end the run before resetting or
switching customers.

## Errors, messages, users, and breadcrumbs

| Method | Parameters | Returns | Behavior |
| --- | --- | --- | --- |
| `Anectico.captureError(error, stackTrace, {options})` | object; optional stack; options | `Future<String?>` | Parses Dart frames and forwards a caught error to native. Only `options.tags` applies to this bridge path. |
| `Anectico.captureMessage(message, {level, options})` | text; level default `info`; options | `Future<String?>` | Native message capture with full options fidelity. |
| `Anectico.addBreadcrumb(category, message, {level, data})` | fields | `Future<void>` | Adds native trail context for the next error. |
| `Anectico.setUser(user)` | `AnecticoUser` | `Future<void>` | Sets global error user context. |
| `Anectico.clearUser()` | — | `Future<void>` | Clears error user context. |

`CaptureOptions` fields are `tags`, `extra`, `user`, `level`, and `fingerprint`. Only `tags` is used
by `captureError`; all fields can be used by `captureMessage`. `AnecticoUser` fields are `id`, `email`,
`username`, `ipAddress`, and `segment`. Breadcrumb constants live in `AnecticoBreadcrumbLevel`.

Automatic handlers use mechanisms `AnecticoMechanism.frameworkError` and
`AnecticoMechanism.platformDispatcher`. They forward to the prior handler afterward, preserving normal
Flutter behavior. Native fatal crashes are handled by the underlying SDK.

The native transport adds SDK-owned app/build, OS, and privacy-safe hardware-model context. It does
not collect stable device identifiers or user-assigned device names.

## Backend propagation

`Anectico.propagationHeaders(): Future<Map<String,String>>` returns a fresh W3C traceparent and current
identity baggage. Send it only to a trusted application backend. The backend must authenticate the
request independently.

## Stack utility exports

The package exports `AnecticoFrame`, `parseStackTrace`, `parseStackString`, `framesToJson`, and
`maxDartFrames` for custom error integrations. Normal application code should call
`Anectico.captureError`, which uses those helpers automatically.

Anectico does not currently symbolicate Dart frames obfuscated with `--obfuscate`. Retain Flutter debug
information and use `flutter symbolize` outside Anectico for those builds.

- [Install and use Flutter](/docs/instrument/flutter)
- [Mobile SDK behavior](/docs/instrument/mobile)
- [Capture errors and releases](/docs/instrument/errors)

## Native capture receipt migration

Both bundled native sources now validate complete version-one indexed capture acknowledgements.
Mixed receipts retire queued and refused positions while retaining uncertain original events in
their durable spools. Malformed or incomplete responses remain uncertain. Ledger refusals can carry
HTTP 200 with `invalid_event`, `quota_exceeded`, or `mixed_rejection`; those codes cross this bridge.
Without a hint, native retry delay doubles from one second to a 60-second cap until a receipt
fully settles. Retry-After is clamped to 1–60 seconds. Published-package, device and backend qualification remains open; this is not a completed release.
