Overview
What the On Me API does, how a gift moves from order to recipient, and the core concepts every integration uses.
For AI agents: this page is available as Markdown, and every guide plus the API reference is in llms-full.txt.
The On Me API lets your platform buy and send On Me gifts from your own code. You pick a category, choose an amount and a way to deliver it, and we take care of the rest: the gift reaches your recipient, they claim it, and they get a virtual card they can spend at that category's merchants.
Every gift is paid for from your organization's On Me balance, so there are no cards to charge and no checkout to build.
What you can do
- Browse the catalog. List every gift category with
GET /v1/categories. - Send gifts. Create an order of 1 to 10 gifts with
POST /v1/orders. The whole order is paid for in one debit from your balance. - Deliver them your way. We can email or text each gift for you, or hand you a link to share however you like.
- Track what happens next. Read orders and gifts back to see whether each one was sent, opened, claimed or failed.
- Fix a failed delivery. Point an undelivered email or text gift at a new address with
PUT /v1/gifts/{gift_id}/delivery.
Core concepts
- Category: what the gift is for, such as Coffee or Dining. The category decides where the recipient's card can be spent. You pass its
idwhen you create a gift. Some categories, like Choice, let the recipient pick a category when they claim. - Order: one purchase of 1 to 10 gifts, created with a single request and paid for with a single debit from your balance. Order IDs look like
o-…. - Gift: one gift for one recipient, with an amount, a category, a delivery method and, optionally, the names the recipient sees. Gift IDs look like
g-…. Every gift belongs to exactly one order. - Recipient: the person who receives the gift. They don't need an On Me account to receive it; they sign in or create one when they claim it.
- Balance: your organization's prepaid funds. You top it up in the business portal under Billing, and each order draws it down by the order's total.
How a gift moves
Fund your balance. Add funds in the business portal. Your balance must cover an order's full total before we accept it.
Create an order. POST /v1/orders debits your balance and creates every gift in the order before it responds. Either the whole order goes through, or nothing happens. In rare cases we hit a problem after the debit: the order then comes back as processing, possibly with gifts: [], and its gifts are finished shortly after. See when the status is processing.
We deliver each gift. Email and SMS gifts go out straight away. Link gifts come back with a delivery.link URL for you to share.
The recipient opens and claims it. They open the gift, see who it's from, and claim it to their On Me account.
We issue the card. Claiming issues a virtual card that works at the category's merchants, or adds the funds to a card they already hold for that category.
You can follow each gift through delivery.status:
delivery.status | What it means |
|---|---|
sent | We sent the email or text, or created the link. This is where every gift starts. |
opened | The recipient opened the gift. |
claimed | The recipient claimed the gift and has a card to spend it on. |
failed | We couldn't deliver the email or text. You can send it somewhere else. |
The status always shows the furthest point a gift has reached. A gift that failed, was re-sent and then claimed reads claimed.
Requests and responses
Every request goes to https://api.onme.com, over HTTPS, and every path starts with /v1. Send request bodies as JSON with Content-Type: application/json; bodies are limited to 1 MB. Responses are always JSON.
Responses come in three shapes. There is no wrapper around them: what you see below is the entire body.
A single resource comes back as the object itself:
{
"order_id": "o-bc1KeWwb90nw_0Jfspeu_oNs0EM7XcAKLQKS64QdQIo",
"status": "processed",
"gifts": [
{
"gift_id": "g-Q5-Vef_a-vnAqGgTVfBArnb8sjHOBCjz7Z7nm4Udlww",
"amount": 2500,
"category": "coffee",
"delivery": { "method": "email", "status": "sent", "email": "sam@example.com" },
"personalization": { "recipient_name": "Sam", "sender_name": "Acme" }
}
]
}A list comes back in a list object, with the items in data:
{
"object": "list",
"data": [
{
"gift_id": "g-Q5-Vef_a-vnAqGgTVfBArnb8sjHOBCjz7Z7nm4Udlww",
"amount": 2500,
"category": "coffee",
"delivery": { "method": "email", "status": "opened", "email": "sam@example.com" },
"personalization": { "recipient_name": "Sam", "sender_name": "Acme" }
}
],
"has_more": true
}An error comes back as a single error object, with a non-2xx status:
{
"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-C4451Fpu_emD-fIdoqAQ8dKd9HqnoBiJUf-EvRdz0qM"
}
}Branch on type and code, never on message: the message is written for people and can change. When the error is about one field, param names it. Every code has its own entry in the error reference, which doc_url links to directly.
Conventions
Amounts
Amounts are integers in US cents. 2500 is $25.00. A gift can be worth between 1000 ($10) and 50000 ($500).
IDs
IDs are opaque strings with a prefix that tells you what they are:
| Prefix | Identifies |
|---|---|
o- | An order |
g- | A gift |
req- | A single API request |
Treat everything after the prefix as opaque: don't parse it or rely on its length. Category IDs are strings such as coffee.
Pagination
GET /v1/orders and GET /v1/gifts return results newest first, one page at a time. They take three query parameters:
| Parameter | Description |
|---|---|
limit | Page size, from 1 to 100. Defaults to 10. |
starting_after | An ID from the current page. Returns the page after it. |
ending_before | An ID from the current page. Returns the page before it. |
Send starting_after or ending_before, not both. When has_more is true, pass the last item's ID as starting_after to get the next page:
curl "https://api.onme.com/v1/orders?limit=25&starting_after=o-bc1KeWwb90nw_0Jfspeu_oNs0EM7XcAKLQKS64QdQIo" \
-H "Authorization: Bearer $ONME_API_KEY"A cursor that doesn't match one of your orders or gifts returns cursor_invalid. GET /v1/categories returns the whole catalog in one response, so its has_more is always false.
Request IDs
Every response, success or error, carries a Request-Id header, such as req-C4451Fpu_emD-fIdoqAQ8dKd9HqnoBiJUf-EvRdz0qM. Error bodies repeat it as request_id. Log it alongside your own records, and include it when you contact support@onme.com so we can find the exact request.
Rate limits
Limits apply per API key, over a 60-second window:
| Scope | Limit |
|---|---|
| All endpoints | 120 requests per minute |
POST /v1/orders | 30 requests per minute, which also count toward the 120 |
A separate limit of 300 requests per minute per IP address covers all traffic, including requests without a valid key.
Every response tells you where you stand, using the IETF RateLimit headers:
RateLimit-Policy: 120;w=60
RateLimit: limit=120, remaining=117, reset=42remaining is how many requests you have left in the window, and reset is the number of seconds until it resets. Responses from POST /v1/orders report its tighter limit. Go over a limit and you get a 429 with the rate_limit_exceeded code and a Retry-After header. Wait that many seconds, then retry.
Compatibility
While the API is in beta, any part of it can change, including in ways that break working integrations. We also add things without notice: new fields on responses, new endpoints, new optional parameters and new error codes. Build your integration so it ignores fields it doesn't recognize and handles a value it hasn't seen before, such as a new delivery.status, without crashing. If a change breaks your integration, email support@onme.com.
Next steps
- Authentication: get your API key and learn how to keep it safe.
- Quickstart: send your first gift in about five minutes.
- Creating orders: everything a
POST /v1/ordersrequest can do. - API reference: every endpoint, field and error code.