# In-app messages API


Management routes require `X-OP-API-Key`. Device fetch, compiled HTML, and IAM
receipts require `X-OP-SDK-Key` (plus `X-OP-Device-Key` on the shared sample app).

## Management

| Method | Path | Purpose |
|---|---|---|
| `POST` | `/v1/apps/{app}/in-app-messages` | Create a draft definition |
| `GET` | `/v1/apps/{app}/in-app-messages` | List definitions; filter by lifecycle/search |
| `GET` | `/v1/apps/{app}/in-app-messages/{iam}` | Definition and report summary |
| `PATCH` | `/v1/apps/{app}/in-app-messages/{iam}` | Update a draft |
| `POST` | `/v1/apps/{app}/in-app-messages/{iam}/{action}` | `set-live`, `pause`, `resume`, `end`, `archive`, or `restore` |
| `DELETE` | `/v1/apps/{app}/in-app-messages/{iam}` | Delete a draft only |

The action route accepts exactly those six values and nothing else.
**Duplicating a definition and sending a test are console-only actions** — they
exist in the product but are not wired to this route, so `POST …/duplicate` and
`POST …/test` return `404 unknown in-app action`.

The structured definition contains `layout`, `blocks`, `triggers`, audience,
dismissal, scheduling, frequency, and optional Liquid fields. Only drafts are
editable; lifecycle transitions are explicit actions.

## Device fetch

```
GET /v1/apps/{app}/subscriptions/{subscription}/iams
```

Returns `{ "in_app_messages": [...] }`, capped at 25 active definitions. Each
definition contains stable ids, placement, triggers, duration, redisplay rules,
and either resolved structured blocks or an `html_path`.

```
GET /v1/apps/{app}/iams/{iam}/html?subscription_id={subscription}
```

Returns a self-contained document with Liquid already resolved, a narrow native
bridge, safe-area-aware placement, and carousel page-change events.

## Receipts

Use the ordinary `POST /v1/ingest` route with one of:

```json
{"app_id":"runner-club","subscription_id":"sub_…","type":"iam_impression","iam_id":"iam_…"}
```

```json
{"app_id":"runner-club","subscription_id":"sub_…","type":"iam_click","iam_id":"iam_…","click_id":"iamclk_…","block_key":"iamblk_…"}
```

A third type, `iam_page_impression`, is accepted by the ingest route and
additionally carries the stable carousel page id/index. **No shipping SDK emits
it.** iOS, Android and Unity send only `iam_impression` and `iam_click`, so
carousel page rows exist as a server-side capability with no client behind it
yet — do not build a report on them.

Duplicate click ids from the same device are ignored by the SDK before
transmission.
