> ## 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.

# Analytics & Export

> Read dashboard analytics and export raw SDK events

## Overview

Two families of endpoints are available:

* **Dashboard analytics** (`/api/v1/analytics/*`) — the aggregated KPI, timeseries, funnel, retention, and activity data that backs the console dashboard. These use **Customer JWT** authentication (`Authorization: Bearer <token>`) and are scoped to the authenticated user's `tenant_id` and `app_id`. They power the console UI, so response shapes track the dashboard.
* **Event export** (`/api/v1/sdk/events/export*`) — raw SDK event access for your own pipelines. These authenticate with an **API key that has the `export_data` permission** (`x-api-key` header), the stable programmatic surface.

<Info>
  All endpoints are scoped to a single `tenant_id` + `app_id`. There is no way to query across tenants or applications.
</Info>

***

## Dashboard Analytics

<Note>
  Analytics data has a processing delay of up to \~5 minutes from event ingestion.
</Note>

### KPI Dashboard

```
GET /api/v1/analytics/kpis
```

**Authentication:** Customer JWT

Returns the KPI tiles configured for the dashboard, each with current/previous values and a sparkline.

**Query parameters** (all optional):

| Parameter    | Type                  | Description                                                                 |
| ------------ | --------------------- | --------------------------------------------------------------------------- |
| `start_date` | `string (YYYY-MM-DD)` | Start of the range. Defaults to 29 days ago.                                |
| `end_date`   | `string (YYYY-MM-DD)` | End of the range. Defaults to today.                                        |
| `compare`    | `string`              | Comparison window: `previous_period`, `previous_month`, or `previous_year`. |
| `platform`   | `string`              | Optional platform dimension filter (e.g. `ios`, `android`).                 |

**Response:**

```json theme={null}
{
  "kpis": [
    {
      "metric": "dau",
      "current": 12450,
      "previous": 11500,
      "change_pct": 8.3,
      "trend": "up",
      "sparkline": [11200, 11500, 11800, 12100, 12450]
    }
  ],
  "period": { "start": "2026-02-01", "end": "2026-02-19" },
  "compared_to": { "start": "2026-01-13", "end": "2026-01-31" }
}
```

`metric` is one of the supported metric identifiers (see [Metric identifiers](#metric-identifiers)). `trend` is `up`, `down`, or `flat`.

### KPI Timeseries

```
GET /api/v1/analytics/kpis/timeseries
```

**Authentication:** Customer JWT

Returns one series per requested metric at the requested granularity.

**Query parameters:**

| Parameter     | Type                  | Required | Description                                                                                       |
| ------------- | --------------------- | -------- | ------------------------------------------------------------------------------------------------- |
| `metrics`     | `string`              | Yes      | One or more metric identifiers, comma-separated (e.g. `dau,revenue_daily`).                       |
| `period`      | `string`              | No       | Convenience range: `7d`, `30d`, `90d`, or `12m`. Ignored if `start_date` is set.                  |
| `start_date`  | `string (YYYY-MM-DD)` | No       | Explicit start of the range.                                                                      |
| `end_date`    | `string (YYYY-MM-DD)` | No       | Explicit end of the range. Defaults to today.                                                     |
| `granularity` | `string`              | No       | `daily` (default), `weekly`, or `monthly`.                                                        |
| `segment`     | `string`              | No       | Break each series out by `overall` (default), `platform`, or `country`.                           |
| `compare`     | `string`              | No       | Adds a `compare_data` series per metric: `previous_period`, `previous_month`, or `previous_year`. |

**Response:**

```json theme={null}
{
  "metrics": [
    {
      "metric": "dau",
      "data": [
        { "date": "2026-02-01", "value": 11200 },
        { "date": "2026-02-02", "value": 11500 }
      ]
    }
  ],
  "period": { "start": "2026-02-01", "end": "2026-02-19" },
  "granularity": "daily",
  "compare": null,
  "meta": { "as_of": "2026-02-19" }
}
```

When `compare` is set, each metric object also carries a `compare_data` array (same shape as `data`) and a `compare_label`. When `segment` is not `overall`, each point additionally carries a `breakdown_value`.

### Funnel Analysis

```
GET /api/v1/analytics/funnel/{funnelName}
```

**Authentication:** Customer JWT

Returns the step-by-step conversion for a named funnel, for the latest available date.

**Path parameter:**

| Parameter    | Type     | Description                                                                                                                   |
| ------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `funnelName` | `string` | One of: `onboarding`, `monetization`, `retention`, `icp_usp`, `aso`, `paid_ua`, `organic_social`, `web2app`, `feedback_loop`. |

**Response:**

```json theme={null}
{
  "funnel_name": "onboarding",
  "steps": [
    { "step_name": "flow_started", "step_order": 1, "users": 5000, "conversion_rate": 100.0, "cumulative_rate": 100.0, "drop_off_rate": 0.0 },
    { "step_name": "step_1_completed", "step_order": 2, "users": 4200, "conversion_rate": 84.0, "cumulative_rate": 84.0, "drop_off_rate": 16.0 },
    { "step_name": "flow_completed", "step_order": 3, "users": 2800, "conversion_rate": 66.7, "cumulative_rate": 56.0, "drop_off_rate": 33.3 }
  ],
  "date": "2026-02-19"
}
```

### Retention Cohorts

```
GET /api/v1/analytics/retention
```

**Authentication:** Customer JWT

Returns retention curves per acquisition cohort.

**Query parameters** (all optional):

| Parameter    | Type                  | Description                                    |
| ------------ | --------------------- | ---------------------------------------------- |
| `start_date` | `string (YYYY-MM-DD)` | Start of the range. Defaults to 89 days ago.   |
| `end_date`   | `string (YYYY-MM-DD)` | End of the range. Defaults to today.           |
| `segment`    | `string`              | `overall` (default), `platform`, or `country`. |

**Response:**

```json theme={null}
{
  "cohorts": [
    {
      "cohort_date": "2026-02-03",
      "cohort_size": 1200,
      "retention": { "d1": 62.5, "d3": 48.3, "d7": 41.0, "d14": 33.2, "d30": 25.1, "d60": 18.4, "d90": 12.0 },
      "segment": "overall"
    }
  ],
  "period": { "start": "2026-02-01", "end": "2026-02-19" }
}
```

`retention` values are the percentage of the cohort still active on day N.

### Activity Feed

```
GET /api/v1/analytics/activity
```

**Authentication:** Customer JWT

Returns the 20 most recent workspace activity entries (configuration changes, publishes, key rotations, and similar) — the feed shown on the dashboard. This endpoint takes no query parameters.

**Response:**

```json theme={null}
{
  "items": [
    {
      "id": "a1b2c3d4",
      "icon": "beaker",
      "description": "Created experiment: \"Winter Paywall\"",
      "timestamp": "2026-02-19T10:30:00Z",
      "relative_time": "2 hours ago",
      "entity_type": "experiment",
      "action": "create"
    }
  ]
}
```

### Metric identifiers

`kpis` and `kpis/timeseries` accept these metric identifiers:

```
mau, wau, dau,
mrr, arr, revenue_daily, net_revenue, arpu, arppu,
onboarding_start_rate, onboarding_completion_rate,
paywall_view_rate, trial_start_rate, trial_to_paid_d7, trial_to_paid_d30,
retention_d1, retention_d7, retention_d30,
avg_session_duration, avg_sessions_per_user, stickiness,
new_users, refund_rate, crash_free_rate
```

***

## Event Export

Export raw SDK events for your own warehouse or analysis. All export endpoints authenticate with an **API key that has the `export_data` permission**, sent in the `x-api-key` header. Grant the permission when you create the key in the dashboard.

<Note>
  Export request dates use full ISO 8601 timestamps (e.g. `2026-02-01T00:00:00Z`), not date-only strings.
</Note>

### Paginated export (synchronous)

```
GET /api/v1/sdk/events/export
```

**Query parameters:**

| Parameter     | Type                | Required | Description                                                  |
| ------------- | ------------------- | -------- | ------------------------------------------------------------ |
| `start_date`  | `string (ISO 8601)` | Yes      | Start of the export window.                                  |
| `end_date`    | `string (ISO 8601)` | Yes      | End of the export window.                                    |
| `event_names` | `string`            | No       | Comma-separated event names to include. Omit for all events. |
| `user_id`     | `string`            | No       | Restrict to a single user.                                   |
| `limit`       | `number`            | No       | Page size, 1–1000. Defaults to 100.                          |
| `offset`      | `number`            | No       | Row offset for paging. Defaults to 0.                        |

**Response:**

```json theme={null}
{
  "data": [ { "event_name": "session_start", "user_id": "usr_123", "timestamp": "2026-02-01T10:30:00Z" } ],
  "meta": { "total": 15000, "hasMore": true }
}
```

Page through the result set with `limit` + `offset` until `meta.hasMore` is `false`.

### Direct download

```
GET /api/v1/sdk/events/export/download
```

Streams the matching events as a single file. Same date/event filters as the paginated endpoint, plus a `format` parameter (`json` — default — or `csv`).

### Async bulk export

For large windows, start an asynchronous export and be notified when it is ready.

**1. Start the export**

```
POST /api/v1/sdk/events/export/bulk
```

Request body:

```json theme={null}
{
  "start_date": "2026-01-01T00:00:00Z",
  "end_date": "2026-02-01T00:00:00Z",
  "event_names": ["purchase_completed", "subscription_started"],
  "callback_url": "https://example.com/hooks/appdna-export"
}
```

`event_names` and `callback_url` are optional. The response is `202 Accepted` with the export record:

```json theme={null}
{
  "data": {
    "id": "exp-abc123",
    "status": "processing",
    "params": { "start_date": "2026-01-01T00:00:00Z", "end_date": "2026-02-01T00:00:00Z", "event_names": [] }
  }
}
```

**2. Poll for status**

```
GET /api/v1/sdk/events/export/bulk/{id}
```

Returns the same record; when `status` becomes `completed`, `download_url` and the summary fields below are populated.

**Export record fields:**

| Field                | Type                | Description                                        |
| -------------------- | ------------------- | -------------------------------------------------- |
| `status`             | `string`            | `processing`, `completed`, or `failed`.            |
| `params.start_date`  | `string (ISO 8601)` | Export window start.                               |
| `params.end_date`    | `string (ISO 8601)` | Export window end.                                 |
| `params.event_names` | `string[]`          | Event filter (empty = all events).                 |
| `download_url`       | `string`            | Path to download the completed export (see below). |
| `event_count`        | `number`            | Total events in the export.                        |
| `file_size_bytes`    | `number`            | Size of the export file.                           |
| `completed_at`       | `string (datetime)` | When processing finished.                          |
| `expires_at`         | `string (datetime)` | When the download expires.                         |

**3. Download the export**

Once `status` is `completed`, fetch the file from the `download_url`:

```
GET /api/v1/sdk/events/export/bulk/{id}/download
```

Streams the exported events as a JSON attachment (`Content-Disposition: attachment; filename="export-{id}.json"`). Returns `409` if the export is not yet completed or has passed its `expires_at`, and `404` for an unknown id. Uses the same `x-api-key` + `export_data` auth.

**4. Optional callback**

If you supplied a `callback_url`, AppDNA sends a POST to it when the export completes or fails:

```json theme={null}
{
  "export_id": "exp-abc123",
  "status": "completed",
  "download_url": "https://...",
  "event_count": 150000,
  "file_size_bytes": 48230400
}
```

<Warning>
  Download URLs are time-limited. Fetch the file before `expires_at`; after it expires, start a new export.
</Warning>

### Event schema

```
GET /api/v1/sdk/events/export/schema
```

Returns the event schema (event names and their known properties) for the app, so you can build a stable ingestion mapping before exporting.
