# API reference

> Every endpoint, request and response schema, and error code in the On Me API, with code samples for each request.

Base URL: `https://api.onme.com`

OpenAPI 3.1 spec: [openapi.json](https://business.onme.com/docs/openapi.json)

## Introduction

Buy and send On Me gifts programmatically. New here? Start with the [quickstart](https://business.onme.com/docs/quickstart).

Authenticate with a secret key as a bearer token: `Authorization: Bearer omk-...`. See [Authentication](https://business.onme.com/docs/authentication).

There is one base URL for both environments, and your key decides which one answers: `omk-` reaches live, `omk-sandbox-` reaches the sandbox. Everything below works the same in each, so moving an integration to production is a key swap and nothing else.

The sandbox additionally answers the **Sandbox** endpoints below, and lets you choose what becomes of a gift by the recipient address you send to — delivered, bounced, opened, claimed, spent, or a failed order. Operations badged _Mockable_ are the ones that address steers. See [Testing in the sandbox](https://business.onme.com/docs/sandbox).

Successful responses return the resource itself, or `{ "object": "list", "data": [...], "has_more": false }` for lists. Errors return a single `error` object; see [Errors](https://business.onme.com/docs/reference#errors).

`POST /v1/orders` requires an `Idempotency-Key` header. Retry a timed-out request with the same key and you get the original order, never a second charge. See [Idempotency and retries](https://business.onme.com/docs/idempotency).

## Errors

### Handling errors

Every error response has the same shape:

```json
{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_invalid",
    "message": "Invalid email address",
    "param": "gifts[0].delivery.email",
    "doc_url": "https://business.onme.com/docs/reference#errors/parameter_invalid",
    "request_id": "req-3kTq9x..."
  }
}
```

- Branch on `type` and `code`. Never parse `message`; it is for humans and may change.
- `param` names the offending field when there is one, as a path such as `gifts[0].amount`.
- Log `request_id` (also sent as the `Request-Id` header on every response) and quote it when contacting support.

An error in this shape means **nothing happened**: no order, no charge, no gifts. A timeout, a dropped connection or a gateway error without an `error` object means the outcome is unknown: retry with the same `Idempotency-Key`.

### Retrying

Every code below says whether to retry it:

| Retry | Meaning |
| --- | --- |
| `yes` | Send the same request again, unchanged, after a short backoff. We send `Retry-After` with these. |
| `after_fix` | Fix what the error names (the request, your key, your balance or your account setup), then retry. For `POST /v1/orders`, keep the same `Idempotency-Key`. |
| `no` | Sending it again cannot succeed. |

Back off exponentially with jitter (for example 0.5s, 1s, 2s, capped at 10s) and honour `Retry-After` when present.

### Error codes

#### api_key_missing

**401** · `authentication_error` · Retry: after fix

No API key was provided. Send it as a bearer token in the Authorization header.

Send `Authorization: Bearer omk-...` on every request.

#### api_key_invalid

**401** · `authentication_error` · Retry: after fix

The API key is not recognised.

Check for a truncated or mistyped key. An org admin can create a new one under Account → Developers in the business portal.

#### api_key_revoked

**401** · `authentication_error` · Retry: after fix

The API key was valid but has since been revoked.

Switch to a current key. An org admin can create one under Account → Developers in the business portal.

#### api_key_environment_mismatch

**401** · `authentication_error` · Retry: after fix

The request reached the wrong environment for its key. You should not see this: there is one base URL, and your key decides which deployment serves the request. It means the request bypassed that routing, or reached a deployment host directly.

Call the documented base URL rather than a deployment host directly. If you already are, this is a fault on our side — quote the request_id.

#### organization_suspended

**403** · `authentication_error` · Retry: no

The organization owning this API key is suspended. Contact support.

#### resource_missing

**404** · `invalid_request_error` · Retry: no

No such resource. Also returned for a resource owned by another organization, so that IDs are not enumerable.

#### parameter_invalid

**422** · `invalid_request_error` · Retry: after fix

A parameter failed validation, or a required one such as the Idempotency-Key header is missing. `param` identifies it.

Fix the parameter named in `param` (for example `gifts[0].delivery.phone`) and send the request again. For `POST /v1/orders`, keep the same Idempotency-Key.

#### cursor_invalid

**422** · `invalid_request_error` · Retry: after fix

The cursor in starting_after or ending_before does not refer to a resource you can access.

#### request_body_invalid

**400** · `invalid_request_error` · Retry: after fix

The request body could not be parsed as JSON.

#### request_body_too_large

**413** · `invalid_request_error` · Retry: after fix

The request body exceeds the 1MB limit.

#### gift_already_redeemed

**422** · `invalid_request_error` · Retry: no

The gift has already been redeemed by its recipient.

#### gift_already_delivered

**422** · `invalid_request_error` · Retry: no

The gift has already been delivered.

#### gift_resend_limit_reached

**422** · `invalid_request_error` · Retry: no

The gift has reached its delivery-retry limit.

#### gift_delivery_not_updatable

**422** · `invalid_request_error` · Retry: no

Delivery can only be redirected for a gift whose delivery has failed and that is not yet redeemed.

#### idempotency_key_reused

**409** · `invalid_request_error` · Retry: no

This Idempotency-Key was already used for a request with a different body. Use a new key for a new order.

Generate a new Idempotency-Key for a new order. Reuse a key only to retry the exact same request.

#### organization_not_configured

**422** · `invalid_request_error` · Retry: after fix

The organization has no active member for orders to be attributed to. Add an active admin, or set a billing contact who is one, in the On Me business portal.

In the business portal, make sure your organization has an active admin, or set a billing contact who is one.

#### insufficient_balance

**402** · `insufficient_balance_error` · Retry: after fix

The organization balance cannot cover this order. Top up and retry; no order was created and nothing was charged.

Add funds in the On Me business portal under Billing, then retry with the same Idempotency-Key.

#### rate_limit_exceeded

**429** · `rate_limit_error` · Retry: yes

Too many requests. Retry after the interval given in the Retry-After header.

Back off for the number of seconds in `Retry-After`, then retry.

#### sandbox_missing

**404** · `invalid_request_error` · Retry: after fix

No sandbox environment exists for this organization, or the requested sandbox belongs to another organization.

#### gift_not_claimed

**422** · `invalid_request_error` · Retry: after fix

The gift has not been claimed yet, and the requested operation requires a claimed gift.

#### sandbox_endpoint_only

**403** · `invalid_request_error` · Retry: no

A `/v1/sandbox` endpoint was called with a live key. These endpoints fabricate delivery and redemption state, so they answer only in the sandbox. They are documented here because they share a base URL with everything else, not because they exist in production.

Send the request with a sandbox key (`omk-sandbox-`). There is no live equivalent — in production these transitions are caused by the recipient.

#### gift_delivery_not_simulatable

**422** · `invalid_request_error` · Retry: after fix

Sandbox only. Delivery can only be simulated for a gift On Me sends itself (email or phone); a link gift is delivered by you.

#### resource_locked

**409** · `api_error` · Retry: yes

Another operation holds this resource. Nothing happened; retry after a short wait.

#### idempotency_in_progress

**409** · `api_error` · Retry: yes

A request with this Idempotency-Key is still in progress. Retry after a short wait to get its result.

Wait a second and retry with the same key; you will get the original result.

#### internal_error

**500** · `api_error` · Retry: yes

An unexpected error occurred on our side. It is safe to retry after a short wait; quote the request_id if it persists.

Retry after a short wait, with the same Idempotency-Key for `POST /v1/orders`. If it keeps happening, contact support with the `request_id`.

## Categories

The gift catalog.

### `GET /v1/categories`: List categories

Returns the full catalog in one response. Uses the same list envelope as the other collections, but is not paginated — the catalog is small and bounded, so `has_more` is always false.

**Responses**

- `200`: The full category catalog. Returns `CategoryList` (see Objects).
- `401`: The API key is missing, invalid, or revoked. Returns `Error` (see Objects).
- `403`: The organization is suspended. Returns `Error` (see Objects).
- `429`: Rate limit exceeded. Retry after the interval in Retry-After. Returns `Error` (see Objects).
- `default`: An unexpected error. Returns `Error` (see Objects).

## Orders

Purchases of one or more gifts.

### `GET /v1/orders`: List orders

Orders belonging to the calling organization, most recent first. Includes orders still `processing` as well as `processed` ones; orders that were never paid for are not returned.

**Query parameters**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `limit` | integer | No | Page size for the order list. (min 1, max 100, default: 10) |
| `starting_after` | string | No | Return results after this resource ID. Mutually exclusive with ending_before. (non-empty, pattern: ^[A-Za-z0-9_-]+$) |
| `ending_before` | string | No | Return results before this resource ID. Mutually exclusive with starting_after. (non-empty, pattern: ^[A-Za-z0-9_-]+$) |

**Responses**

- `200`: A page of orders. Returns `OrderList` (see Objects).
- `401`: The API key is missing, invalid, or revoked. Returns `Error` (see Objects).
- `403`: The organization is suspended. Returns `Error` (see Objects).
- `422`: A query parameter or cursor is invalid. Returns `Error` (see Objects).
- `429`: Rate limit exceeded. Retry after the interval in Retry-After. Returns `Error` (see Objects).
- `default`: An unexpected error. Returns `Error` (see Objects).

### `POST /v1/orders`: Create an order

**Mockable**

Creates and funds an order of 1–10 gifts, charged to your organization balance in a single debit.

Responds once every gift exists: 201 for a new order, and 200 when an `Idempotency-Key` replay returns the order that key already created. If we hit a problem after charging you, the order comes back `processing` instead; it is paid for and we will finish it.

Every request must carry an `Idempotency-Key` header. See [Idempotency and retries](https://business.onme.com/docs/idempotency) and [Errors](https://business.onme.com/docs/reference#errors).

**Mockable in the sandbox.** The recipient address you send to decides what happens: a gift that is delivered, bounces, is opened, claimed or spent, an order that stays `processing` for twenty seconds, or a whole-order 402 or 500. See [Testing in the sandbox](https://business.onme.com/docs/sandbox).

**Headers**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `Idempotency-Key` | string | Yes | Required. A unique key for this order, up to 255 printable ASCII characters. A retry with the same key and body returns the original order instead of charging again. (max 255 chars) |

**Request body** (JSON)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `gifts` | array of objects | Yes | The gifts to create, 1–10. The whole order is funded from your organization balance in one debit. (1–10 items) |
| `gifts[].amount` | integer | Yes | Gift value in cents, between 1000 and 50000. (min 1000, max 50000) |
| `gifts[].category` | string | Yes | ID of the category, as returned by GET /v1/categories. (non-empty) |
| `gifts[].delivery` | one of: email \| phone \| link | Yes |  |
| `gifts[].delivery.method` | string | Yes | (enum: email \| phone \| link) |
| `gifts[].delivery.email` | string | No | Recipient address. Required when method is "email". (format: email) |
| `gifts[].delivery.phone` | string | No | Recipient number in international format, such as +14155551234. Required when method is "phone". Spaces, dashes and parentheses are accepted; the number is stored in E.164. |
| `gifts[].personalization` | object | No | Optional names to show the recipient. `sender_name` defaults to your organization name; `recipient_name` is shown only when you supply it. |
| `gifts[].personalization.recipient_name` | string | No | (1–200 chars) |
| `gifts[].personalization.sender_name` | string | No | (1–200 chars) |

**Responses**

- `200`: An Idempotency-Key replay: the order this key already created. Returns `Order` (see Objects).
  - Header `Idempotent-Replayed`: Present on a replay.
- `201`: The new order: `processed`, with its gifts, or `processing` if we hit a problem after charging you. Returns `Order` (see Objects).
- `400`: The request body could not be parsed. Returns `Error` (see Objects).
- `401`: The API key is missing, invalid, or revoked. Returns `Error` (see Objects).
- `402`: The organization balance cannot cover this order. Nothing was created or charged. Returns `Error` (see Objects).
- `403`: The organization is suspended. Returns `Error` (see Objects).
- `409`: A request with this key is still in progress (`idempotency_in_progress`), this key was used with a different body (`idempotency_key_reused`), or another order for your organization was being charged at the same moment (`resource_locked`). Nothing was created or charged. Returns `Error` (see Objects).
- `413`: The request body exceeds the 1MB limit. Returns `Error` (see Objects).
- `422`: The Idempotency-Key header is missing or invalid, the request body failed validation, or the organization has no active member for orders to be attributed to. Returns `Error` (see Objects).
- `429`: Rate limit exceeded. Retry after the interval in Retry-After. Returns `Error` (see Objects).
- `500`: An unexpected error. Nothing was created or charged, so it is safe to retry. Returns `Error` (see Objects).
- `default`: An unexpected error. Returns `Error` (see Objects).

### `GET /v1/orders/{order_id}`: Retrieve an order

**Path parameters**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `order_id` | string | Yes |  |

**Responses**

- `200`: The order. Returns `Order` (see Objects).
- `401`: The API key is missing, invalid, or revoked. Returns `Error` (see Objects).
- `403`: The organization is suspended. Returns `Error` (see Objects).
- `404`: No such resource, or it belongs to another organization. Returns `Error` (see Objects).
- `429`: Rate limit exceeded. Retry after the interval in Retry-After. Returns `Error` (see Objects).
- `default`: An unexpected error. Returns `Error` (see Objects).

## Gifts

Individual gifts and their delivery.

### `GET /v1/gifts`: List gifts

Gifts belonging to the calling organization, most recent purchase first. Redemption links in a list response are not refreshed — retrieve a gift individually to be sure of a working link.

**Query parameters**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `limit` | integer | No | Page size for the gift list. (min 1, max 100, default: 10) |
| `starting_after` | string | No | Return results after this resource ID. Mutually exclusive with ending_before. (non-empty, pattern: ^[A-Za-z0-9_-]+$) |
| `ending_before` | string | No | Return results before this resource ID. Mutually exclusive with starting_after. (non-empty, pattern: ^[A-Za-z0-9_-]+$) |

**Responses**

- `200`: A page of gifts. Returns `GiftList` (see Objects).
- `401`: The API key is missing, invalid, or revoked. Returns `Error` (see Objects).
- `403`: The organization is suspended. Returns `Error` (see Objects).
- `422`: A query parameter or cursor is invalid. Returns `Error` (see Objects).
- `429`: Rate limit exceeded. Retry after the interval in Retry-After. Returns `Error` (see Objects).
- `default`: An unexpected error. Returns `Error` (see Objects).

### `GET /v1/gifts/{gift_id}`: Retrieve a gift

Retrieving a link-delivered gift whose redemption token has expired mints a fresh one, so the returned link always works.

**Path parameters**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `gift_id` | string | Yes |  |

**Responses**

- `200`: The gift. Returns `Gift` (see Objects).
- `401`: The API key is missing, invalid, or revoked. Returns `Error` (see Objects).
- `403`: The organization is suspended. Returns `Error` (see Objects).
- `404`: No such resource, or it belongs to another organization. Returns `Error` (see Objects).
- `429`: Rate limit exceeded. Retry after the interval in Retry-After. Returns `Error` (see Objects).
- `default`: An unexpected error. Returns `Error` (see Objects).

### `PUT /v1/gifts/{gift_id}/delivery`: Redirect a failed delivery

**Mockable**

Re-sends a gift to a different address. Restricted to gifts whose delivery has FAILED and that are not yet redeemed; any other state returns 422. Redirecting a successfully delivered gift would let a caller reroute it, so this is a security constraint rather than a convenience.

**Mockable in the sandbox.** The failed delivery this needs is otherwise unreachable on demand — order to `delivery-failed@sandbox.onme.com`, or call `POST /v1/sandbox/gifts/{gift_id}/deliver` with `{"outcome":"failed"}`, to get a gift in the one state it accepts. See [Testing in the sandbox](https://business.onme.com/docs/sandbox).

**Path parameters**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `gift_id` | string | Yes |  |

**Request body** (JSON)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `method` | string | Yes | (enum: email \| phone) |
| `email` | string | No | When method is email. Address to re-send the gift to. (format: email) |
| `phone` | string | No | When method is phone. Number to re-send the gift to, in international format. Stored in E.164. |

**Responses**

- `200`: The updated gift. Returns `Gift` (see Objects).
- `400`: The request body could not be parsed. Returns `Error` (see Objects).
- `401`: The API key is missing, invalid, or revoked. Returns `Error` (see Objects).
- `403`: The organization is suspended. Returns `Error` (see Objects).
- `404`: No such resource, or it belongs to another organization. Returns `Error` (see Objects).
- `413`: The request body exceeds the 1MB limit. Returns `Error` (see Objects).
- `422`: The gift is not in a state whose delivery can be redirected, or the body failed validation. Returns `Error` (see Objects).
- `429`: Rate limit exceeded. Retry after the interval in Retry-After. Returns `Error` (see Objects).
- `default`: An unexpected error. Returns `Error` (see Objects).

## Sandbox

Simulation endpoints, answered only for a sandbox key. They drive state that in production only a recipient can cause — delivery settling, opening, claiming, spending — plus a reset. See [Testing in the sandbox](https://business.onme.com/docs/sandbox).

### `POST /v1/sandbox/gifts/{gift_id}/deliver`: Simulate delivery

**Sandbox only**

Settles a pending email or phone delivery as `delivered` or `failed`. Use this on a gift that already exists. A link-delivered gift has no delivery for On Me to settle and returns 422 `gift_delivery_not_simulatable`. A `failed` outcome is what unlocks `PUT /v1/gifts/{gift_id}/delivery`.

Sandbox only: a live key gets 403 `sandbox_endpoint_only`. In production this transition is caused by the recipient, and there is no way to ask for it.

**Path parameters**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `gift_id` | string | Yes |  |

**Request body** (JSON)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `outcome` | string | No | What to settle the pending delivery as. Defaults to `delivered`, so an empty body is the happy path. (enum: delivered \| failed, default: delivered) |

**Responses**

- `200`: The gift, after the transition. Returns `Gift` (see Objects).
- `400`: The request body could not be parsed. Returns `Error` (see Objects).
- `401`: The API key is missing, invalid, or revoked. Returns `Error` (see Objects).
- `403`: Called with a live key (`sandbox_endpoint_only`) — these endpoints exist only in the sandbox — or the organization is suspended. Returns `Error` (see Objects).
- `404`: No such resource, or it belongs to another organization. Returns `Error` (see Objects).
- `422`: Delivery cannot be simulated for this gift (`gift_delivery_not_simulatable` — the gift is delivered by link, so there is nothing for us to send), or the body failed validation. Returns `Error` (see Objects).
- `429`: Rate limit exceeded. Retry after the interval in Retry-After. Returns `Error` (see Objects).
- `default`: An unexpected error. Returns `Error` (see Objects).

### `POST /v1/sandbox/gifts/{gift_id}/open`: Simulate the recipient opening a gift

**Sandbox only**

Stamps the gift opened, as a recipient following their link would.

Sandbox only: a live key gets 403 `sandbox_endpoint_only`. In production this transition is caused by the recipient, and there is no way to ask for it.

**Path parameters**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `gift_id` | string | Yes |  |

**Responses**

- `200`: The gift, after the transition. Returns `Gift` (see Objects).
- `401`: The API key is missing, invalid, or revoked. Returns `Error` (see Objects).
- `403`: Called with a live key (`sandbox_endpoint_only`) — these endpoints exist only in the sandbox — or the organization is suspended. Returns `Error` (see Objects).
- `404`: No such resource, or it belongs to another organization. Returns `Error` (see Objects).
- `429`: Rate limit exceeded. Retry after the interval in Retry-After. Returns `Error` (see Objects).
- `default`: An unexpected error. Returns `Error` (see Objects).

### `POST /v1/sandbox/gifts/{gift_id}/claim`: Simulate the recipient claiming a gift

**Sandbox only**

Redeems the gift onto a card. Back-fills the opened timestamp if the gift was never opened, because a claimed-but-never-opened gift is a state production cannot produce and your integration should not have to handle.

Sandbox only: a live key gets 403 `sandbox_endpoint_only`. In production this transition is caused by the recipient, and there is no way to ask for it.

**Path parameters**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `gift_id` | string | Yes |  |

**Responses**

- `200`: The gift, after the transition. Returns `Gift` (see Objects).
- `401`: The API key is missing, invalid, or revoked. Returns `Error` (see Objects).
- `403`: Called with a live key (`sandbox_endpoint_only`) — these endpoints exist only in the sandbox — or the organization is suspended. Returns `Error` (see Objects).
- `404`: No such resource, or it belongs to another organization. Returns `Error` (see Objects).
- `422`: The gift is already redeemed (`gift_already_redeemed`). Returns `Error` (see Objects).
- `429`: Rate limit exceeded. Retry after the interval in Retry-After. Returns `Error` (see Objects).
- `default`: An unexpected error. Returns `Error` (see Objects).

### `POST /v1/sandbox/gifts/{gift_id}/spend`: Simulate a first spend

**Sandbox only**

Records spend against the claimed gift’s card. Ordering is enforced as it is in production: spending before claiming returns 422 `gift_not_claimed`.

Sandbox only: a live key gets 403 `sandbox_endpoint_only`. In production this transition is caused by the recipient, and there is no way to ask for it.

**Path parameters**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `gift_id` | string | Yes |  |

**Responses**

- `200`: The gift, after the transition. Returns `Gift` (see Objects).
- `401`: The API key is missing, invalid, or revoked. Returns `Error` (see Objects).
- `403`: Called with a live key (`sandbox_endpoint_only`) — these endpoints exist only in the sandbox — or the organization is suspended. Returns `Error` (see Objects).
- `404`: No such resource, or it belongs to another organization. Returns `Error` (see Objects).
- `422`: The gift has not been claimed yet (`gift_not_claimed`). Returns `Error` (see Objects).
- `429`: Rate limit exceeded. Retry after the interval in Retry-After. Returns `Error` (see Objects).
- `default`: An unexpected error. Returns `Error` (see Objects).

### `POST /v1/sandbox/reset`: Delete all sandbox data

**Sandbox only**

Deletes every order, gift, balance and balance ledger entry belonging to your organization in the sandbox. Your API keys survive; you will need to add funds again before you can order.

Not reversible and not idempotency-protected. It is scoped to your own sandbox data, which is the only safety property it needs. The counts come back separately rather than summed so an automated setup step can assert on what actually went.

Sandbox only: a live key gets 403 `sandbox_endpoint_only`.

**Responses**

- `200`: What was deleted. Returns `SandboxResetResult` (see Objects).
- `401`: The API key is missing, invalid, or revoked. Returns `Error` (see Objects).
- `403`: Called with a live key (`sandbox_endpoint_only`) — this endpoint exists only in the sandbox — or the organization is suspended. Returns `Error` (see Objects).
- `429`: Rate limit exceeded. Retry after the interval in Retry-After. Returns `Error` (see Objects).
- `default`: An unexpected error. Returns `Error` (see Objects).

## Meta

Unauthenticated service endpoints.

### `GET /v1/health`: Health check

Liveness probe. Requires no authentication.

**Responses**

- `200`: The API is reachable.
- `429`: Rate limit exceeded. Retry after the interval in Retry-After. Returns `Error` (see Objects).
- `default`: An unexpected error. Returns `Error` (see Objects).

### `GET /v1/openapi.json`: Retrieve this specification

Returns this document. Requires no authentication, so a client can read the contract before it holds a key.

**Responses**

- `200`: The OpenAPI document.
- `429`: Rate limit exceeded. Retry after the interval in Retry-After. Returns `Error` (see Objects).
- `default`: An unexpected error. Returns `Error` (see Objects).

## Objects

### GiftDelivery

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `method` | string | Yes | (enum: email \| phone \| link) |
| `status` | string | Yes | (enum: sent \| failed \| opened \| claimed) |
| `email` | string | No | Present only when method is "email". |
| `phone` | string | No | Present only when method is "phone". E.164 format. |
| `link` | string | No | Redemption URL. Present only when method is "link". Retrieving a single gift refreshes this if it has expired. |

### GiftPersonalization

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `recipient_name` | string | No |  |
| `sender_name` | string | No |  |

### Gift

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `gift_id` | string | Yes |  |
| `amount` | integer | Yes | Gift value in cents. |
| `category` | string | Yes | ID of the category this gift is for. |
| `delivery` | object | Yes |  |
| `delivery.method` | string | Yes | (enum: email \| phone \| link) |
| `delivery.status` | string | Yes | (enum: sent \| failed \| opened \| claimed) |
| `delivery.email` | string | No | Present only when method is "email". |
| `delivery.phone` | string | No | Present only when method is "phone". E.164 format. |
| `delivery.link` | string | No | Redemption URL. Present only when method is "link". Retrieving a single gift refreshes this if it has expired. |
| `personalization` | object | No | What the recipient sees. `sender_name` is always present, falling back to your organization name when you do not supply one; `recipient_name` appears only when you do. |
| `personalization.recipient_name` | string | No |  |
| `personalization.sender_name` | string | No |  |

### GiftList

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `object` | string | Yes |  |
| `data` | array of objects | Yes |  |
| `data[].gift_id` | string | Yes |  |
| `data[].amount` | integer | Yes | Gift value in cents. |
| `data[].category` | string | Yes | ID of the category this gift is for. |
| `data[].delivery` | object | Yes |  |
| `data[].delivery.method` | string | Yes | (enum: email \| phone \| link) |
| `data[].delivery.status` | string | Yes | (enum: sent \| failed \| opened \| claimed) |
| `data[].delivery.email` | string | No | Present only when method is "email". |
| `data[].delivery.phone` | string | No | Present only when method is "phone". E.164 format. |
| `data[].delivery.link` | string | No | Redemption URL. Present only when method is "link". Retrieving a single gift refreshes this if it has expired. |
| `data[].personalization` | object | No | What the recipient sees. `sender_name` is always present, falling back to your organization name when you do not supply one; `recipient_name` appears only when you do. |
| `data[].personalization.recipient_name` | string | No |  |
| `data[].personalization.sender_name` | string | No |  |
| `has_more` | boolean | Yes | Whether more results exist after this page in the direction of travel. |

### Order

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `order_id` | string | Yes |  |
| `status` | string | Yes | Whether every gift has been created. `processed` means they all exist. `processing` means the order is paid for and some gifts are still being created; it happens only if we hit a problem after charging you, and the order finishes on its own. Retrying the original request with the same Idempotency-Key returns it. (enum: processing \| processed) |
| `gifts` | array of objects | Yes | The order's gifts, in the order you submitted them, so the Nth gift here is the Nth gift you sent. |
| `gifts[].gift_id` | string | Yes |  |
| `gifts[].amount` | integer | Yes | Gift value in cents. |
| `gifts[].category` | string | Yes | ID of the category this gift is for. |
| `gifts[].delivery` | object | Yes |  |
| `gifts[].delivery.method` | string | Yes | (enum: email \| phone \| link) |
| `gifts[].delivery.status` | string | Yes | (enum: sent \| failed \| opened \| claimed) |
| `gifts[].delivery.email` | string | No | Present only when method is "email". |
| `gifts[].delivery.phone` | string | No | Present only when method is "phone". E.164 format. |
| `gifts[].delivery.link` | string | No | Redemption URL. Present only when method is "link". Retrieving a single gift refreshes this if it has expired. |
| `gifts[].personalization` | object | No | What the recipient sees. `sender_name` is always present, falling back to your organization name when you do not supply one; `recipient_name` appears only when you do. |
| `gifts[].personalization.recipient_name` | string | No |  |
| `gifts[].personalization.sender_name` | string | No |  |

### OrderList

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `object` | string | Yes |  |
| `data` | array of objects | Yes |  |
| `data[].order_id` | string | Yes |  |
| `data[].status` | string | Yes | Whether every gift has been created. `processed` means they all exist. `processing` means the order is paid for and some gifts are still being created; it happens only if we hit a problem after charging you, and the order finishes on its own. Retrying the original request with the same Idempotency-Key returns it. (enum: processing \| processed) |
| `data[].gifts` | array of objects | Yes | The order's gifts, in the order you submitted them, so the Nth gift here is the Nth gift you sent. |
| `data[].gifts[].gift_id` | string | Yes |  |
| `data[].gifts[].amount` | integer | Yes | Gift value in cents. |
| `data[].gifts[].category` | string | Yes | ID of the category this gift is for. |
| `data[].gifts[].delivery` | object | Yes |  |
| `data[].gifts[].delivery.method` | string | Yes | (enum: email \| phone \| link) |
| `data[].gifts[].delivery.status` | string | Yes | (enum: sent \| failed \| opened \| claimed) |
| `data[].gifts[].delivery.email` | string | No | Present only when method is "email". |
| `data[].gifts[].delivery.phone` | string | No | Present only when method is "phone". E.164 format. |
| `data[].gifts[].delivery.link` | string | No | Redemption URL. Present only when method is "link". Retrieving a single gift refreshes this if it has expired. |
| `data[].gifts[].personalization` | object | No | What the recipient sees. `sender_name` is always present, falling back to your organization name when you do not supply one; `recipient_name` appears only when you do. |
| `data[].gifts[].personalization.recipient_name` | string | No |  |
| `data[].gifts[].personalization.sender_name` | string | No |  |
| `has_more` | boolean | Yes | Whether more results exist after this page in the direction of travel. |

### Category

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | Yes | Stable identifier for the category. Pass this as `category` when creating a gift. |
| `name` | string | Yes |  |
| `description` | string | Yes | Short description of who the category suits. |
| `artwork` | string \| null | Yes | URL of the card artwork, or null if none is configured. |
| `product_details_url` | string | Yes | Public category details page on onme.com. |

### CategoryList

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `object` | string | Yes |  |
| `data` | array of objects | Yes |  |
| `data[].id` | string | Yes | Stable identifier for the category. Pass this as `category` when creating a gift. |
| `data[].name` | string | Yes |  |
| `data[].description` | string | Yes | Short description of who the category suits. |
| `data[].artwork` | string \| null | Yes | URL of the card artwork, or null if none is configured. |
| `data[].product_details_url` | string | Yes | Public category details page on onme.com. |
| `has_more` | boolean | Yes | Whether more results exist after this page in the direction of travel. |

### SandboxResetResult

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `object` | string | Yes |  |
| `orders_deleted` | integer | Yes |  |
| `gifts_deleted` | integer | Yes |  |
| `balances_deleted` | integer | Yes |  |
| `ledger_entries_deleted` | integer | Yes | Balance ledger entries removed alongside the balances. Counted separately because the balance and its ledger are separate records, and a reset that cleared one but not the other would leave a sandbox that reconciles wrong. |

### Error

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | object | Yes |  |
| `error.type` | string | Yes | Broad class of error. Clients should branch on this rather than on the HTTP status. (enum: invalid_request_error \| authentication_error \| insufficient_balance_error \| rate_limit_error \| api_error) |
| `error.code` | string | Yes | Stable, machine-readable identifier for the specific error. Safe to branch on. (enum: api_key_missing \| api_key_invalid \| api_key_revoked \| api_key_environment_mismatch \| organization_suspended \| resource_missing \| parameter_invalid \| cursor_invalid \| request_body_invalid \| request_body_too_large \| gift_already_redeemed \| gift_already_delivered \| gift_resend_limit_reached \| gift_delivery_not_updatable \| idempotency_key_reused \| organization_not_configured \| insufficient_balance \| rate_limit_exceeded \| sandbox_missing \| gift_not_claimed \| sandbox_endpoint_only \| gift_delivery_not_simulatable \| resource_locked \| idempotency_in_progress \| internal_error) |
| `error.message` | string | Yes | Human-readable explanation. Not stable — never branch on this. |
| `error.param` | string | No | Path of the offending field, when the error concerns one. |
| `error.doc_url` | string | No | Documentation for this error code. |
| `error.request_id` | string | Yes | Identifier for this request. Present on every error. Quote it when contacting support. |
