# Intake API

What every Apparent SDK sends. Use it to write an SDK for another platform, or to send events from a server.

## Send a batch

```http
POST https://in.apparent.fyi/i
content-type: application/json
```

No auth header: the app id and key go in the body. One request is one batch of up to 100 events from one app on one phone. You can gzip the body with `content-encoding: gzip`; 1 MB is the limit both as sent and unpacked.

```sh
curl https://in.apparent.fyi/i -H 'content-type: application/json' -d '{
  "a": "<app id>", "k": "<write key>", "v": "my-sdk-0.1.0", "sa": 1792761605123,
  "d": { "platform": "ios", "os": "18.0", "app": "1.0.6", "model": "iPhone15,2", "locale": "de-DE",
         "anon": "9f2c4b7e1a3d4c5b8e6f7a9b0c1d2e3f", "user": null },
  "e": [{ "n": "app_open", "t": 1792761600010, "s": "5d1e0a9b7c3f4e2a8b6d0c1f2e3a4b5c",
          "i": "0a1b2c3d4e5f60718293a4b5c6d7e8f9", "p": { "cold": true } }]
}'
```

## The batch

| Field | What it is |
|---|---|
| `a` | The app's id. |
| `k` | The app's write key. It only adds events. |
| `v` | Your SDK's name and version. |
| `sa` | The phone's clock when this request is sent, in milliseconds. Read again on every retry. |
| `d` | The device, below. |
| `e` | 1 to 100 events, oldest first. |

## The device: `d`

| Field | What to send |
|---|---|
| `platform` | `"ios"`, `"android"` or `"macos"` (a Mac app, also an iPhone app running on a Mac). iPadOS is `"ios"`. |
| `os` | iOS: `"18.0"`. Android: the API level, `"30"`. |
| `app` | The store version only, exactly as in the store: `"1.0.6"`. |
| `build` | The build number, `"42"`. Optional. |
| `model` | The raw hardware id: `"iPhone15,2"`, `"Pixel 8"`, `"Mac14,3"`. In the iOS Simulator, `"arm64"`. |
| `simulator` | `true` in the iOS Simulator or an Android emulator. Leave it out on a real phone, never `false`. |
| `locale` | The first preferred language, `"de-DE"`. |
| `region` | The region setting, `"DE"`. Optional. |
| `tz` | The time zone, `"Europe/Berlin"`. Optional. |
| `anon` | The install id of the first event: 32 random hex characters, saved on the phone. |
| `user` | The first event's user id, or `null`. |

Send raw values: Apparent spots simulators, emulators and test phones from them.

## An event: `e[]`

| Field | What it is |
|---|---|
| `n` | Name, `lowercase_snake`, up to 80 characters. |
| `t` | When it happened, phone clock, milliseconds. Never changed later. |
| `s` | Session id: new at launch and after 30 minutes away. |
| `i` | A random id for this event (32 hex), the same on every retry. |
| `p` | Properties, flat: text, number, true/false, `null`. Optional. |
| `a`, `u` | This event's install or user id, only when it differs from `d`. |

## Answers

| Answer | Means | Do |
|---|---|---|
| `200 {"ok":true,"accepted":4,"duplicates":0,"skipped":0,"dropped":0}` | Stored. `dropped`: events past the 500th, not stored (never happens at 100 a batch). | Remove the batch. |
| `413` | Over 1 MB. | Halve the batch, send again. |
| `408`, `429`, `5xx`, no answer | Busy or offline. | Keep it, wait, retry: 2 s doubling up to 5 min, or `Retry-After`. |
| `401` | Wrong app id or key. | Drop it. Retrying won't help. In a debug build, print once: `Apparent: app id or key is wrong — events are not being saved`. |
| other `4xx` | Bad JSON or gzip, a body that isn't one object, `e` that isn't a list, or an encoding other than gzip (`415`). | Drop it. |

Sending a batch twice is safe: the server keeps one copy per event id. When unsure, send again.

## Limits

500 events per batch (stay at 100), 60 properties per event, 64 characters per property name, 512 per value, ids up to 128. Ids must be text (or numbers): any other type counts as missing, so an event falls back to the batch's install and user id. Events older than 60 days are skipped. Phones with a wrong clock are corrected with `sa`.

## Check your SDK

Every Apparent SDK passes the same 25-rule check against a local intake. Writing your own? Write to hello@apparent.fyi and we'll run it with you.
