> ## Documentation Index
> Fetch the complete documentation index at: https://docs.privy.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Mobile SDK breadcrumbs

> Register a breadcrumb handler to receive lightweight diagnostic events from the Privy iOS and Android SDKs and route them to your own analytics or observability tooling

**Breadcrumbs are lightweight diagnostic events emitted as the Privy SDK progresses through key
flows**, such as session restoration and wallet creation. Register a breadcrumb handler to receive
these events and forward them to your own analytics, logging, or observability tooling (e.g.
Sentry, Datadog, Firebase).

<Info>Breadcrumbs are currently available on the Privy iOS and Android native SDKs only.</Info>

<Tip>
  Breadcrumbs are for lightweight operational visibility, not full error reporting. Payloads never
  contain PII, and handlers must not throw or block, since they're called synchronously on the
  emitting thread.
</Tip>

### 1. Register a breadcrumb handler

Implement the platform's breadcrumb handler interface and pass it to your `PrivyConfig` when
initializing the SDK.

<View title="iOS" icon="swift">
  ```swift theme={"system"}
  import PrivySDK

  struct LoggingBreadcrumbHandler: PrivyBreadcrumbHandler {
      func receive(_ breadcrumb: PrivyBreadcrumb) {
          print("PrivyBreadcrumb: \(breadcrumb.event) timestamp=\(breadcrumb.timestamp) data=\(breadcrumb.data)")
          // Forward to your analytics or observability tool, e.g.:
          // Analytics.track(breadcrumb.event, properties: breadcrumb.data)
      }
  }

  let config = PrivyConfig(
      appId: privyAppId,
      appClientId: privyAppClientId,
      breadcrumbHandler: LoggingBreadcrumbHandler()
  )
  ```
</View>

<View title="Android" icon="android">
  ```kotlin theme={"system"}
  import io.privy.breadcrumbs.PrivyBreadcrumbHandler
  import io.privy.sdk.PrivyConfig

  val config = PrivyConfig(
      appId = privyAppId,
      appClientId = privyAppClientId,
      breadcrumbHandler = PrivyBreadcrumbHandler { breadcrumb ->
          Log.d("PrivyBreadcrumb", "${breadcrumb.event} timestamp=${breadcrumb.timestamp} data=${breadcrumb.data}")
          // Forward to your analytics or observability tool, e.g.:
          // analytics.track(breadcrumb.event, breadcrumb.data)
      },
  )
  ```

  `PrivyBreadcrumbHandler` is a functional interface, so a lambda works directly wherever a
  `PrivyConfig` is constructed.
</View>

`breadcrumbHandler` is optional. If it's omitted, the SDK emits no breadcrumbs and there's no
performance cost.

### 2. Breadcrumb payload shape

Every breadcrumb exposes the same three fields, regardless of event type:

| Field       | Type                                                       | Description                                               |
| ----------- | ---------------------------------------------------------- | --------------------------------------------------------- |
| `event`     | `String`                                                   | Dotted event name, e.g. `auth.session_restore.started`    |
| `timestamp` | `Date` (iOS) / `Long` epoch millis (Android)               | The time the event occurred                               |
| `data`      | `[String: String]` (iOS) / `Map<String, String>` (Android) | Additional context for the event. Values are strings only |

`receive(_:)` is called synchronously on the thread that triggered the event. Keep handler
implementations fast and non-blocking, and dispatch any expensive work (network calls, disk
writes) to a background queue.

### 3. Available breadcrumb events

<Expandable title="Session restore events">
  Emitted when the SDK attempts to restore a cached session on startup.

  | Event                            | When it fires                                          |
  | -------------------------------- | ------------------------------------------------------ |
  | `auth.session_restore.started`   | A prior session was found and restoration has begun    |
  | `auth.session_restore.completed` | The session was successfully refreshed and restored    |
  | `auth.session_restore.failed`    | Restoration failed; `data.error` describes the failure |
</Expandable>

<Expandable title="Wallet events">
  Emitted for embedded wallet creation, migration, and bridge operations.

  | Event                        | When it fires                                                                                                                                    |
  | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
  | `wallet.create.started`      | Embedded wallet creation began; `data.chain-type` is `ethereum` or `solana`                                                                      |
  | `wallet.create.completed`    | Embedded wallet creation succeeded                                                                                                               |
  | `wallet.create.failed`       | Embedded wallet creation failed; `data.error` describes the failure                                                                              |
  | `wallet.migration.started`   | Migration of legacy embedded wallets to the TEE stack began                                                                                      |
  | `wallet.migration.completed` | Migration completed successfully                                                                                                                 |
  | `wallet.migration.failed`    | Migration failed; `data.error` describes the failure                                                                                             |
  | `wallet.bridge.failed`       | The webview/iframe bridge failed to complete an operation (timeout or transport error), as opposed to the operation being rejected by the server |
</Expandable>

<Warning>
  On iOS, `data.error` is a stable, non-sensitive error code (the `PrivyError` code hierarchy, or
  just the Swift type name for non-`PrivyError` types). On Android, `data.error` is currently the
  underlying exception's message. Avoid logging Android `data.error` values to systems where raw
  exception messages shouldn't be stored, and treat both as best-effort diagnostic strings rather
  than a stable enum.
</Warning>

### 4. Example: forwarding to Sentry

<View title="iOS" icon="swift">
  ```swift theme={"system"}
  import Sentry

  struct SentryBreadcrumbHandler: PrivyBreadcrumbHandler {
      func receive(_ breadcrumb: PrivyBreadcrumb) {
          let crumb = Breadcrumb(level: .info, category: "privy")
          crumb.message = breadcrumb.event
          crumb.data = breadcrumb.data
          SentrySDK.addBreadcrumb(crumb)
      }
  }
  ```
</View>

<View title="Android" icon="android">
  ```kotlin theme={"system"}
  import io.sentry.Breadcrumb
  import io.sentry.Sentry
  import io.privy.breadcrumbs.PrivyBreadcrumbHandler

  val sentryBreadcrumbHandler = PrivyBreadcrumbHandler { breadcrumb ->
      val crumb = Breadcrumb().apply {
          message = breadcrumb.event
          category = "privy"
          breadcrumb.data.forEach { (key, value) -> setData(key, value) }
      }
      Sentry.addBreadcrumb(crumb)
  }
  ```
</View>

<Tip>
  Because breadcrumbs carry no PII and are cheap to emit, it's safe to register a handler in every
  build configuration, including production. Gate any verbose console logging (like the examples in
  step 1) behind a debug flag.
</Tip>
