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

# Measurement ingest

> Receive MMP server callbacks (Adjust first) into the FloKit analytics warehouse.

<Note>
  This endpoint is live in production today, served by the FloKit measurement gateway. It is separate from the v1 REST API, which is still in design-partner preview.
</Note>

The measurement gateway receives server-to-server callbacks from MMPs, normalizes them into canonical FloKit events, deduplicates, and writes them to the analytics warehouse. **Adjust** is the first supported vendor; the endpoint is vendor-pluggable.

**Base URL:** `https://ingest.flokitai.com`

***

## GET / POST /ingest/:vendor

Accepts a single MMP callback per request. Both `GET` (query parameters — Adjust's default callback style) and `POST` are supported.

### Authentication

* **`secret` query parameter** — per-vendor shared secret, compared in constant time. Requests with a missing or wrong secret get `401`.
* **`app_token` query parameter** — the vendor app token; FloKit maps it to your workspace server-side.
* An optional per-vendor IP allowlist can additionally restrict callers.

### Adjust callback URL

Configure this as the callback URL in your Adjust dashboard (placeholders in `{}` are filled by Adjust):

```
https://ingest.flokitai.com/ingest/adjust?secret=<ADJUST_CALLBACK_SECRET>
  &app_token=<token>
  &event_token={event_token}&activity_kind={activity_kind}&created_at={created_at}
  &os_name={os_name}&country={country}&adid={adid}&idfa={idfa}&gps_adid={gps_adid}
  &revenue={revenue}&currency={currency}&user_id={FloKit user id partner param}
```

Mapping notes (Adjust adapter):

* Vendor event tokens are translated to canonical FloKit event types via your workspace's mapping configuration. **Unmapped event tokens are dropped**, counted in the `dropped` field.
* `created_at` becomes the event's `occurred_at` (Unix seconds or ISO 8601 accepted); events with a missing or invalid `created_at` are dropped.
* `os_name` is normalized to `ios` / `android`; `country` is uppercased.
* `revenue`, `currency`, `event_token`, and `activity_kind` are preserved in the event's `properties`.
* Device identifiers (`adid`, `idfa`, `gps_adid`) and the FloKit `user_id` partner parameter are carried for identity joining.

### Example

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://ingest.flokitai.com/ingest/adjust?secret=$ADJUST_CALLBACK_SECRET&app_token=abc123token&event_token=g3mz3t&activity_kind=event&created_at=1750500000&os_name=ios&country=us&adid=1a2b3c&revenue=49.99&currency=USD&user_id=usr_abc123"
  ```

  ```typescript TypeScript theme={null}
  // Typically Adjust calls this endpoint directly. For testing:
  const params = new URLSearchParams({
    secret: process.env.ADJUST_CALLBACK_SECRET!,
    app_token: 'abc123token',
    event_token: 'g3mz3t',
    activity_kind: 'event',
    created_at: '1750500000',
    os_name: 'ios',
    country: 'us',
    user_id: 'usr_abc123',
  });

  const res = await fetch(
    `https://ingest.flokitai.com/ingest/adjust?${params}`,
  );

  const outcome = await res.json();
  // { received: 1, written: 1, duplicate: 0, dropped: 0 }
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "received": 1,
  "written": 1,
  "duplicate": 0,
  "dropped": 0
}
```

| Field       | Type   | Description                                              |
| ----------- | ------ | -------------------------------------------------------- |
| `received`  | number | Events received in this request                          |
| `written`   | number | Events written to the warehouse pipeline                 |
| `duplicate` | number | Events skipped because they were already ingested        |
| `dropped`   | number | Events dropped (unmapped event token, invalid timestamp) |

### Deduplication

Events are deduplicated on a stable event ID with a 30-day window. Adjust callbacks without a stable ID get a derived ID hashed from the vendor, app, user, event type, timestamp, and raw event ID — so redelivered callbacks count as `duplicate` instead of being double-written. If a write fails, the dedup claim is released and the request returns `500`, so the vendor's retry re-processes the event safely.

### Errors

| Status | Body                                      | Condition                                    |
| ------ | ----------------------------------------- | -------------------------------------------- |
| `401`  | `{ "error": "Unverified callback." }`     | Bad secret or IP not allowlisted             |
| `404`  | `{ "error": "Unknown vendor: <vendor>" }` | Vendor has no registered adapter             |
| `500`  | `{ "error": "Internal Server Error" }`    | Write failure — safe for the vendor to retry |

***

## GET /healthz

Health check.

```json theme={null}
{ "status": "ok" }
```
