> For the complete documentation index, see [llms.txt](https://docs.amply.tools/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.amply.tools/reference/api-reference.md).

# API reference

The Amply SDK reaches the backend over a small, signed HTTPS API. Mobile apps normally never call these endpoints directly — the SDK handles the request shape, authentication, and retry behavior. This page is here for integration audits, server-to-server relay setups, and debugging.

## At a glance

| Method | Path                  | Purpose                                                                 |
| ------ | --------------------- | ----------------------------------------------------------------------- |
| POST   | `/api/v1/session`     | Report sessions, events, and user/device state. The main data channel.  |
| POST   | `/api/v1/attribution` | Report install-attribution data from third-party attribution providers. |
| POST   | `/api/v1/tm/apple`    | Report Apple App Store in-app purchase transactions.                    |

**Base URL:** `https://api.amply.tools`

All endpoints accept and return JSON (`Content-Type: application/json`).

***

## Authentication

All three endpoints identify your application via `X-Api-Key`. The session endpoint additionally requires a request signature and a unique request ID. The SDK computes these for you; if you relay requests through your own backend, reproduce the same headers.

### Required headers

| Header         | `/api/v1/session` | `/api/v1/attribution` | `/api/v1/tm/apple` |
| -------------- | ----------------- | --------------------- | ------------------ |
| `X-Api-Key`    | required          | required              | required           |
| `X-Signature`  | required          | —                     | —                  |
| `X-Timestamp`  | required          | —                     | —                  |
| `X-Request-Id` | required          | —                     | —                  |

* `X-Api-Key` — your application's public API key. Identifies the application for all three endpoints.
* `X-Signature` — request signature (see below).
* `X-Timestamp` — Unix timestamp (seconds) at the moment the request is built. Included in the signed payload.
* `X-Request-Id` — freshly generated UUID v4 per request. Used to reject retries of the same request (see [Idempotency](#idempotency)).

Session requests missing any of these headers are rejected with `403 Forbidden` (missing signature parts) or `409 Conflict` (missing/invalid request ID).

### Signature

The signature is an HMAC-SHA256 of the concatenation of the HTTP method, the request URI, the raw request body, and the timestamp, keyed with your secret API key. The SDK takes care of this. Only re-implement it if you are forwarding requests from your own server.

Both the public and secret API keys are issued in your Amply dashboard under the application's settings. Treat the secret key like a password — ship it only to trusted servers, not to the mobile app unless you are using the SDK's built-in signing.

### Application lookup

The backend locates your application from `X-Api-Key`. If the key is unknown or the application has been deleted, every endpoint returns `404 Not Found` with the body `{"detail": "Application not found"}`.

***

## POST /api/v1/session

Reports one or more sessions along with their events. This is the endpoint the SDK calls when it flushes its queue.

### Request

```
POST /api/v1/session HTTP/1.1
Host: api.amply.tools
Content-Type: application/json
X-Api-Key: <public key>
X-Signature: <hmac>
X-Timestamp: <seconds>
X-Request-Id: <uuid v4>

{ ... }
```

### Request body

```json
{
  "user": {
    "id": "u_abc123",
    "lastSyncTS": 1714000000,
    "properties": [
      { "name": "plan", "value": "pro" }
    ]
  },
  "device": {
    "id": "d_xyz789",
    "properties": {
      "sdkVersionNormalized": 10203,
      "osVersionNormalized": 170400,
      "appVersionNormalized": 20100,
      "appInstallVersionNormalized": 20000,
      "platform": "iOS",
      "deviceModel": "iPhone15,3",
      "vendorIdentifier": "B1D…",
      "installDatetime": 1713900000,
      "adId": "D4B…",
      "country": "US"
    }
  },
  "sessions": [
    {
      "id": "s_001",
      "number": { "global": 42, "version": 3 },
      "startDate": "2026-04-24T10:00:00+00:00",
      "finishDate": "2026-04-24T10:05:12+00:00",
      "events": [
        {
          "id": "e_001",
          "name": "PurchaseCompleted",
          "type": "custom",
          "timestamp": 1714000200,
          "date": "2026-04-24T10:03:20+00:00",
          "params": [
            { "name": "sku",   "value": "premium_yearly" },
            { "name": "price", "value": 59.99 }
          ]
        }
      ]
    }
  ]
}
```

#### Top-level fields

| Field      | Type   | Required | Description                                                   |
| ---------- | ------ | -------- | ------------------------------------------------------------- |
| `user`     | object | yes      | User identity and custom properties.                          |
| `device`   | object | yes      | Device identity and collected device properties.              |
| `sessions` | array  | yes      | One or more session records. Must contain at least one entry. |

#### `user`

| Field        | Type                     | Required | Description                                                              |
| ------------ | ------------------------ | -------- | ------------------------------------------------------------------------ |
| `id`         | string \| null           | no       | Your application's user identifier, if set via `setUserId(...)`.         |
| `lastSyncTS` | number \| null           | no       | Unix seconds of the last successful sync, used for delta reconciliation. |
| `properties` | array of `{name, value}` | no       | Custom user properties. `value` is `string`, `number`, or `boolean`.     |

#### `device`

| Field        | Type   | Required | Description                                   |
| ------------ | ------ | -------- | --------------------------------------------- |
| `id`         | string | yes      | Stable device identifier assigned by the SDK. |
| `properties` | object | yes      | Device metadata.                              |

`device.properties`:

| Field                         | Type           | Required | Description                                                           |
| ----------------------------- | -------------- | -------- | --------------------------------------------------------------------- |
| `sdkVersionNormalized`        | number         | yes      | SDK version encoded as a comparable integer.                          |
| `osVersionNormalized`         | number         | yes      | OS version encoded as a comparable integer.                           |
| `appVersionNormalized`        | number         | yes      | Current app version encoded as a comparable integer.                  |
| `appInstallVersionNormalized` | number         | yes      | App version at install time, encoded as a comparable integer.         |
| `platform`                    | string         | yes      | `"iOS"` or `"Android"`.                                               |
| `deviceModel`                 | string         | yes      | Device model identifier (for example, `iPhone15,3`).                  |
| `vendorIdentifier`            | string         | yes      | Vendor-scoped device identifier (IDFV on iOS, equivalent on Android). |
| `installDatetime`             | number         | yes      | Unix seconds of the first install.                                    |
| `adId`                        | string \| null | no       | Advertising identifier, if available and permitted.                   |
| `country`                     | string \| null | no       | ISO country code, if resolved.                                        |

#### `sessions[*]`

| Field        | Type                      | Required | Description                                                                                                                                   |
| ------------ | ------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`         | string                    | yes      | Unique session identifier generated by the SDK.                                                                                               |
| `number`     | object                    | yes      | `{ "global": <int>, "version": <int> }` — `global` counts sessions for this device, `version` counts sessions within the current app version. |
| `startDate`  | string (ISO 8601)         | yes      | Session start, with timezone offset.                                                                                                          |
| `finishDate` | string (ISO 8601) \| null | no       | Session end. Null if the session is still open at flush time.                                                                                 |
| `events`     | array                     | no       | Events captured during the session. May be empty.                                                                                             |

#### `sessions[*].events[*]`

| Field       | Type                     | Required | Description                                                    |
| ----------- | ------------------------ | -------- | -------------------------------------------------------------- |
| `id`        | string                   | yes      | Event identifier generated by the SDK.                         |
| `name`      | string                   | yes      | Event name.                                                    |
| `type`      | string                   | yes      | `"custom"` or `"system"`.                                      |
| `timestamp` | number                   | yes      | Unix seconds when the event was recorded.                      |
| `date`      | string (ISO 8601)        | yes      | Same instant as `timestamp`, rendered as an ISO string.        |
| `params`    | array of `{name, value}` | yes      | Event properties. `value` is `string`, `number`, or `boolean`. |

### Successful response

```
HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 24 Apr 2026 10:05:13 GMT

{}
```

The SDK reads the server time from the response `Date` header and uses it to correct clock skew on the device. If the header is missing or unparseable, the SDK logs a warning but treats the request as failed for clock-sync purposes.

### Error responses

| Status                     | Meaning                                                                                       |
| -------------------------- | --------------------------------------------------------------------------------------------- |
| `403 Forbidden`            | Missing/invalid `X-Api-Key`, `X-Signature`, or `X-Timestamp`, or the signature did not match. |
| `404 Not Found`            | The application for this API key does not exist.                                              |
| `409 Conflict`             | The `X-Request-Id` has already been seen. Use a new UUID and retry.                           |
| `422 Unprocessable Entity` | Request body failed validation. The response body lists the offending fields.                 |
| `5xx`                      | Server-side error. Safe to retry with a fresh `X-Request-Id` and exponential backoff.         |

Example validation error:

```json
{
  "error":   "validation_failed",
  "status":  422,
  "message": "Request body failed validation.",
  "fields": [
    {
      "path":    "sessions[0].events[0].name",
      "message": "Event name must not be blank."
    }
  ]
}
```

***

## POST /api/v1/attribution

Reports install-attribution data from third-party attribution providers.

### Request body

```json
{
  "device": {
    "id": "d_xyz789",
    "external_id": "ext-abc"
  },
  "system": {
    "os_version": "17.4"
  },
  "attribution": {
    "adjust": {},
    "apple_search_ads": {
      "orgId": 123456,
      "campaignId": 789,
      "conversionType": "Download",
      "clickDate": "2026-04-23",
      "adGroupId": 456,
      "countryOrRegion": "US",
      "keywordId": 42,
      "adId": 1001
    },
    "apps_flyer": {}
  },
  "custom": {}
}
```

| Field                          | Type           | Required | Description                                                                                       |
| ------------------------------ | -------------- | -------- | ------------------------------------------------------------------------------------------------- |
| `device`                       | object         | yes      | Device identity. `id` is required; `external_id` is optional.                                     |
| `system`                       | object         | yes      | Device system information. Must include `os_version`.                                             |
| `attribution`                  | object         | yes      | Container for per-provider payloads. Unused providers may be omitted or sent as `{}`.             |
| `attribution.adjust`           | object \| null | no       | Adjust attribution payload. Pass-through; schema follows Adjust's attribution response.           |
| `attribution.apple_search_ads` | object \| null | no       | Apple Search Ads attribution. See table below.                                                    |
| `attribution.apps_flyer`       | object \| null | no       | AppsFlyer attribution payload. Pass-through; schema follows AppsFlyer's conversion-data response. |
| `custom`                       | object \| null | no       | Arbitrary custom-attribution object. Stored as-is.                                                |

#### `attribution.apple_search_ads`

All fields are optional. Copy the values verbatim from the Apple Search Ads attribution API response.

| Field             | Type                        |
| ----------------- | --------------------------- |
| `orgId`           | number                      |
| `campaignId`      | number                      |
| `conversionType`  | string                      |
| `clickDate`       | string (date, `YYYY-MM-DD`) |
| `adGroupId`       | number                      |
| `countryOrRegion` | string                      |
| `keywordId`       | number                      |
| `adId`            | number                      |

### Successful response

```
HTTP/1.1 200 OK
Content-Type: application/json

{}
```

### Error responses

| Status                     | Meaning                                              |
| -------------------------- | ---------------------------------------------------- |
| `404 Not Found`            | Missing/invalid `X-Api-Key`, or unknown application. |
| `422 Unprocessable Entity` | Validation failed.                                   |
| `5xx`                      | Server-side error; safe to retry.                    |

Note: this endpoint does not require request signing or replay protection at present.

***

## POST /api/v1/tm/apple

Reports Apple App Store in-app purchase transactions for a device. Send one request per batch of transactions. Each request must include at least one transaction.

### Request body

```json
{
  "device": {
    "id": "d_xyz789",
    "external_id": "ext-abc"
  },
  "transactions": [
    { "id": "2000000123456789" },
    { "id": "2000000123456790" }
  ]
}
```

| Field                | Type   | Required | Description                                                                           |
| -------------------- | ------ | -------- | ------------------------------------------------------------------------------------- |
| `device`             | object | yes      | Device identity. `id` required; `external_id` optional.                               |
| `transactions`       | array  | yes      | One entry per transaction. Must contain at least one item.                            |
| `transactions[*].id` | string | yes      | The transaction ID returned by StoreKit (`originalTransactionId` or `transactionId`). |

The server calls Apple's App Store Server API to fetch the signed transaction and entitlement details, so only the ID is needed in the payload.

### Successful response

```
HTTP/1.1 200 OK
Content-Type: application/json

{}
```

### Error responses

| Status                     | Meaning                                                             |
| -------------------------- | ------------------------------------------------------------------- |
| `404 Not Found`            | Missing/invalid `X-Api-Key`, or unknown application.                |
| `422 Unprocessable Entity` | `transactions` was missing or empty, or a transaction ID was blank. |
| `5xx`                      | Server-side error; safe to retry.                                   |

***

## Idempotency

`POST /api/v1/session` rejects requests whose `X-Request-Id` has already been processed with `409 Conflict`. This protects against duplicate flushes after flaky network conditions.

* Generate a new UUID v4 per request.
* If you receive `409`, generate a fresh UUID before retrying.
* `/api/v1/attribution` and `/api/v1/tm/apple` do not currently enforce replay rejection at the request level.

## Rate limits

No per-endpoint rate limit is enforced today. Apps are expected to batch events — the SDK does this automatically by buffering events and flushing on a schedule rather than per call.

## Retries

* Retry `5xx` and network-level failures with exponential backoff.
* Do not retry `4xx` without fixing the cause — they indicate malformed payloads, expired keys, or a conflicting request ID.
* The SDK keeps unsent events on disk until a successful `2xx` response, so a failed flush does not lose data.

***

## Related

* [Events reference](/reference/events.md) — event names, types, and property conventions.
* [SDK reference — iOS](/reference/sdk-ios.md) — the client-side entry points that call these endpoints.
* [SDK reference — Android](/reference/sdk-android.md) — same for Android.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.amply.tools/reference/api-reference.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
