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

# Event Ingestion

> SDK event tracking endpoint and event schema

## Overview

The Event Ingestion API accepts structured events from the AppDNA SDKs. Events are batched on the client, sent to the ingestion endpoint, and processed through the analytics pipeline for querying and dashboards.

***

## Endpoints

### Ingest Events

```
POST /api/v1/ingest/events
```

**Authentication:** SDK Key (`x-api-key` header)

Accepts a batch of events from the SDK. Each event follows the envelope schema defined below.

**Request body:**

A batch of 1 to 500 events under the `batch` key:

```json theme={null}
{
  "batch": [
    { ... },
    { ... }
  ]
}
```

**Response:** `HTTP 202 Accepted`

```json theme={null}
{
  "accepted": 18,
  "rejected": 0,
  "sampled": 2,
  "consent_declined": 0
}
```

An `errors` array is included when one or more events are rejected.

### Identify User

```
POST /api/v1/ingest/identify
```

**Authentication:** SDK Key (`x-api-key` header)

Links an anonymous device to a known user ID. Call this when the user logs in or creates an account.

**Request body:**

```json theme={null}
{
  "anon_id": "550e8400-e29b-41d4-a716-446655440000",
  "user_id": "user-12345",
  "ts_ms": 1708344000000,
  "traits": {
    "plan": "premium"
  }
}
```

`anon_id`, `user_id`, and `ts_ms` (Unix timestamp in milliseconds) are required. `traits` is an optional object of user attributes.

### Health Check

```
GET /api/v1/ingest/health
```

**Authentication:** None (public)

Returns the health status of the ingestion service.

***

## Event Envelope Schema

Every event sent to the ingestion API must conform to the `EventSchema` envelope:

```json theme={null}
{
  "schema_version": 1,
  "event_id": "550e8400-e29b-41d4-a716-446655440000",
  "event_name": "purchase_completed",
  "ts_ms": 1708344000000,
  "user": {
    "anon_id": "660e8400-e29b-41d4-a716-446655440001",
    "user_id": "user-12345"
  },
  "device": {
    "platform": "ios",
    "os": "17.0",
    "app_version": "1.0.0",
    "sdk_version": "1.0.0",
    "framework": "react_native",
    "framework_version": "1.0.7",
    "locale": "en_US",
    "country": "US"
  },
  "context": {
    "session_id": "770e8400-e29b-41d4-a716-446655440002",
    "screen": "home",
    "experiment_exposures": [
      { "exp": "paywall_price_test", "variant": "control" }
    ],
    "client_seq": 42
  },
  "properties": {
    "product_id": "premium_monthly",
    "price": 9.99,
    "currency": "USD"
  },
  "privacy": {
    "consent": {
      "analytics": true
    }
  }
}
```

### Field Reference

| Field                          | Type            | Required | Description                                                                                                                                                                                                                                            |
| ------------------------------ | --------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `schema_version`               | `number`        | Yes      | Schema version, currently `1`                                                                                                                                                                                                                          |
| `event_id`                     | `string (uuid)` | Yes      | Unique event identifier, generated by the SDK                                                                                                                                                                                                          |
| `event_name`                   | `string`        | Yes      | Name of the event                                                                                                                                                                                                                                      |
| `ts_ms`                        | `number`        | Yes      | Unix timestamp in milliseconds                                                                                                                                                                                                                         |
| `user.anon_id`                 | `string (uuid)` | Yes      | Anonymous device identifier                                                                                                                                                                                                                            |
| `user.user_id`                 | `string`        | No       | Known user identifier (set after identify)                                                                                                                                                                                                             |
| `device.platform`              | `string`        | Yes      | `ios` or `android`                                                                                                                                                                                                                                     |
| `device.os`                    | `string`        | Yes      | OS version                                                                                                                                                                                                                                             |
| `device.app_version`           | `string`        | Yes      | Host app version                                                                                                                                                                                                                                       |
| `device.sdk_version`           | `string`        | Yes      | Version of the **native** AppDNA SDK (iOS/Android) that emitted the event — including when a cross-platform wrapper is in use                                                                                                                          |
| `device.framework`             | `string`        | No       | Which SDK produced the event: `native`, `flutter`, or `react_native`. Injected by the wrapper; defaults to `native`                                                                                                                                    |
| `device.framework_version`     | `string`        | No       | Version of the **wrapper** package (the React Native npm package, the Flutter pub package). Omitted for native hosts. `sdk_version` names the native core, so this is the only field that identifies the artifact a cross-platform developer installed |
| `device.locale`                | `string`        | Yes      | Device locale (e.g., `en_US`)                                                                                                                                                                                                                          |
| `device.country`               | `string`        | Yes      | ISO country code                                                                                                                                                                                                                                       |
| `context.session_id`           | `string (uuid)` | Yes      | Current session identifier                                                                                                                                                                                                                             |
| `context.screen`               | `string`        | No       | Current screen name at the time of the event                                                                                                                                                                                                           |
| `context.experiment_exposures` | `array`         | No       | Active experiment exposures, each `{ exp, variant }`                                                                                                                                                                                                   |
| `context.client_seq`           | `number`        | No       | Per-device monotonic sequence number (survives restarts)                                                                                                                                                                                               |
| `properties`                   | `object`        | No       | Arbitrary key-value pairs for the event                                                                                                                                                                                                                |
| `privacy.consent.analytics`    | `boolean`       | Yes      | Whether the user has consented to analytics                                                                                                                                                                                                            |

<Warning>
  Events where `privacy.consent.analytics` is `false` are accepted but not processed into analytics pipelines. They are retained only for audit purposes.
</Warning>

***

## Auto-Tracked Events

The AppDNA SDKs automatically track the following events without any additional code. These events are fired across all four SDKs (iOS, Android, Flutter, React Native):

### Session Events

| Event Name      | Trigger                                     |
| --------------- | ------------------------------------------- |
| `session_start` | App enters foreground or new session begins |
| `session_end`   | App enters background or session times out  |
| `app_open`      | App launched                                |
| `app_close`     | App terminated                              |

### Push Notification Events

| Event Name                | Trigger                               |
| ------------------------- | ------------------------------------- |
| `push_token_registered`   | Device push token obtained            |
| `push_permission_granted` | User grants push permission           |
| `push_permission_denied`  | User denies push permission           |
| `push_delivered`          | Push notification delivered to device |
| `push_tapped`             | User taps a push notification         |

### Purchase Events

| Event Name           | Trigger                                |
| -------------------- | -------------------------------------- |
| `purchase_started`   | User initiates a purchase flow         |
| `purchase_completed` | Purchase transaction succeeds          |
| `purchase_failed`    | Purchase transaction fails             |
| `purchase_canceled`  | User cancels during purchase           |
| `purchase_pending`   | Purchase is pending (e.g., Ask to Buy) |
| `purchase_restored`  | A previous purchase is restored        |

### Onboarding Events

| Event Name                  | Trigger                            |
| --------------------------- | ---------------------------------- |
| `onboarding_flow_started`   | User enters an onboarding flow     |
| `onboarding_step_viewed`    | Onboarding step rendered on screen |
| `onboarding_step_completed` | User completes an onboarding step  |
| `onboarding_flow_completed` | User finishes all onboarding steps |
| `onboarding_flow_dismissed` | User skips or dismisses onboarding |

### Paywall and Experiment Events

| Event Name            | Trigger                               |
| --------------------- | ------------------------------------- |
| `paywall_view`        | Paywall displayed to user             |
| `paywall_close`       | User dismisses paywall                |
| `experiment_exposure` | User exposed to an experiment variant |

### Other Events

| Event Name                    | Trigger                                     |
| ----------------------------- | ------------------------------------------- |
| `survey_shown`                | Survey presented to user                    |
| `survey_completed`            | User submits a survey                       |
| `survey_dismissed`            | User dismisses a survey                     |
| `web_entitlement_activated`   | Web-based entitlement activated             |
| `web_entitlement_expired`     | Web-based entitlement expired               |
| `deferred_deep_link_resolved` | Deferred deep link resolved on first launch |

***

## Batching Behavior

The SDK batches events on the client to minimize network overhead:

| Setting        | Default Value | Description                                                  |
| -------------- | ------------- | ------------------------------------------------------------ |
| Batch size     | 20 events     | Events are flushed when the batch reaches this size          |
| Flush interval | 30 seconds    | Events are flushed on this interval regardless of batch size |

<Info>
  Events are also flushed immediately when the app enters the background, ensuring no data loss during session transitions.
</Info>

***

## Webhook Triggers

Certain ingestion events automatically trigger webhooks if configured on your account:

| Webhook Event          | Triggered By                               |
| ---------------------- | ------------------------------------------ |
| `onboarding.started`   | `onboarding_flow_started` event ingested   |
| `onboarding.completed` | `onboarding_flow_completed` event ingested |
| `experiment.exposure`  | `experiment_exposure` event ingested       |
| `user.identified`      | `/api/v1/ingest/identify` called           |

<Note>
  Webhook delivery is asynchronous and does not affect ingestion latency. See the [Webhooks](/api-reference/webhooks) documentation for payload format and retry policy.
</Note>
