Get a purchase intent
GET /v2/api-gateway/purchase-intents/{id}
One purchase intent: what was asked for, whether the cardholder has answered, and whether an authorization has already spent it. status is evaluated against expires_ on every read, so a request nobody answered in time reports EXPIRED rather than an AWAITING_ that can never be satisfied.
Request
Authentication: your API key in the x-api-key header.
Path parameters
| Field | Type | Description |
|---|---|---|
id required | string, uuid | Purchase intent id. |
Example request
cURL
curl --request GET \
--url 'https://api.themooncard.com/v2/api-gateway/purchase-intents/8d2cd518-5ef2-4280-91e8-63b1a0e672e7' \
--header 'x-api-key: YOUR_API_KEY'Responses
200 Success
| Field | Type | Description |
|---|---|---|
id required | string | Purchase intent id. |
card_ required | string | The ask-first card this approval is for. |
cardholder_ required | string, nullable | The cardholder who was asked, or null when the card has no cardholder. |
approver_ required | string | Who was asked to approve. cardholder when the card has one — the approval is that person’s and is never forwarded to the organization. account when the card has no cardholder, in which case the request goes to the email address of the account that owns the card, and when the card is on the organization’s own cardholder (the one a card issued without naming a cardholder lands on), which is the account holder. Always one of the two.One of: cardholder, account |
status required | string | AWAITING_ until the cardholder answers; APPROVED once they have and until an authorization spends it; CONSUMED afterwards. DENIED, EXPIRED and CANCELLED are terminal and mean nothing will be charged. Expiry is evaluated when the intent is read, so a row past expires_ reports EXPIRED here whether or not anything had written it down yet.One of: AWAITING_APPROVAL, APPROVED, CONSUMED, DENIED, EXPIRED, CANCELLED |
kind required | string | single — one merchant, one amount inside the symmetric tolerance band, and exactly one authorization, spendable for 15 minutes once approved. order — a budget: up to max_ in total at that merchant, over any number of authorizations, for valid_ days from the moment it was approved. An approval created before this field existed reads single, which is what it was.One of: single, order |
merchant required | object | The merchant the approval covers. |
merchant. required | string, nullable | Merchant name, or null when only an MCC was given. |
merchant. required | string, nullable | Merchant category code, or null when only a name was given. |
merchant_ required | array of string | EVERY merchant name this approval matches: merchant.name first, then the other spellings the request gave and the ones Moon expanded from a brand it recognises. Any of them matching the acquirer descriptor is enough, and the approver was shown the whole list before deciding. Empty when the approval named only an MCC. |
merchant_ required | string, nullable | The site the purchase is being made on, as a bare hostname, or null when the request did not name one. Advisory: a card network carries no domain, so the authorization gate neither checks this nor could. It is what the approver was shown next to the merchant and where the agent was told to use the credential. |
amount required | number | The approved amount, in the card's own currency, in major units. |
currency required | string | ISO code of the card's ledger account currency. |
merchant_ required | number, nullable | The price as the merchant stated it, in merchant_, when the request gave it that way (merchant_ + merchant_). amount is then Moon's conversion of it into the card's currency, which is what the authorization is compared with. Null when the request gave amount. |
merchant_ required | string, nullable | The merchant currency (uppercase ISO 4217) of merchant_, or null. |
request_ required | number, nullable | The rate (merchant_ to the card's currency) amount was converted with when the request was made: a market rate with no spread. The network converts the real charge at its own rate, which the tolerance absorbs. Null when the request gave amount. |
tolerance required | object | How far an authorization may sit from amount and still match. The wider of the two applies, symmetrically in both directions. |
tolerance. required | number, nullable | Percentage slack on amount, or null when none applies. |
tolerance. required | number, nullable | Slack in the card's own currency, in major units like amount, or null when none applies. |
reason required | string | The sentence the cardholder read before deciding. |
recurring required | boolean | A standing approval. Once the first authorization spends it, any later authorization from the same merchant for the same amount (within the same tolerance) is let through without a new request — with no expiry, until it is cancelled. Such an approval reads CONSUMED while it is still in force, because the first charge spent it; DELETE /purchase-intents/{id} is what ends it. |
monthly_ required | number, nullable | How much this merchant may charge per UTC calendar month, in the card's own currency. Null on a one-off approval. This is what bounds a standing approval: the per-charge tolerance decides the first charge only. |
recurring_ required | number | Taken from the current calendar month's budget so far. 0 before the first charge, and 0 again on the first read of a new month. |
recurring_ required | string, nullable | First day of the month recurring_ is counting, as YYYY-MM-DD. Null until the first charge. |
recurring_ required | string, nullable | The merchant category the standing approval was first used at, pinned at that first charge. A later authorization carrying a different category is refused. Null when the network sent none, in which case nothing is pinned. |
valid_ required | number, nullable | How many days the approval stays spendable once it has been approved — an order's window, or a recurring approval's grace before its first charge. Null on a plain single approval, which may be spent for 15 minutes once approved. answer_ (60 by default) always bounds ANSWERING the request; this is how long the answer is worth anything, and expires_ is rewritten to it on approval. |
max_ required | number, nullable | An order's budget: amount plus the tolerance band, in the card's own currency. Every authorization at that merchant is accepted while the running total stays inside it, with no lower bound per charge — which is what lets a split shipment or a small verification authorization through. Null on anything that is not an order. |
spent required | number, nullable | How much of that budget has been taken. Null on anything that is not an order. Refunds and reversals do not give budget back. |
remaining required | number, nullable | max_ minus spent, floored at zero. Null on anything that is not an order. An order that reaches its total closes itself and reads CONSUMED; unlike a standing recurring approval, it then authorizes nothing further. |
expires_ required | string | After this instant the approval is worthless and an authorization on the card is declined with INTENT_ again. Until the approver answers it is the deadline to ANSWER (answer_ after the request, 60 by default). On approval it is rewritten to how long the approval may be SPENT: valid_ days out for an order and a recurring approval, 15 minutes out for a single one (see spendable_until). ISO-8601 UTC with milliseconds, e.g. "2026-09-16T17:05:00.000Z". |
spendable_ required | string, nullable | Until when an APPROVED approval may be spent: the same instant as expires_ once the intent has been approved, and null before that — while it is awaiting an answer expires_ is the deadline to ANSWER, not to spend. Null on a recurring approval too, which has no spend deadline once it is in force. ISO-8601 UTC with milliseconds, e.g. "2026-09-16T17:05:00.000Z". |
created_ required | string | When the request was made. ISO-8601 UTC with milliseconds, e.g. "2026-09-16T17:05:00.000Z". |
approved_ required | string, nullable | When the cardholder approved it, or null. ISO-8601 UTC with milliseconds, e.g. "2026-09-16T17:05:00.000Z". |
decided_ required | string, nullable | When it was approved, denied or cancelled, or null. ISO-8601 UTC with milliseconds, e.g. "2026-09-16T17:05:00.000Z". |
approved_ required | string, nullable | How the person answered: email (the one-time link or the page it opens), mcp (an agent connection recorded the answer the person gave it) or web (the account owner answered in the Moon app, signed in). Null until answered. Treat this as an open set: branch on the values you know and fall through on the rest. |
autonomy_ required | string, nullable | Reserved for a feature that is not generally available yet. Always null today; ignore it. |
consumed_ required | string, nullable | The authorization that spent this approval, or null. |
agent_ required | string, nullable | The key_ of the agent key that signed the request — the RFC 7638 JWK thumbprint register_ returned. Null when the request was not signed, which is allowed. |
agent_ required | string, nullable | That key’s current label. Read from the key on every request, so renaming a key renames it on intents that already exist. |
profile_ required | array of string | Reserved for a feature that is not generally available yet. Always null today; ignore it. |
profile_ required | object, nullable | Reserved for a feature that is not generally available yet. Always null today; ignore it. |
profile_ required | integer, nullable | Reserved for a feature that is not generally available yet. Always null today; ignore it. |
400 Error
Validation failed (errors[] lists each offending field) or a domain rule rejected the request ({ message, code }).
The body is the standard error object: see Errors.
401 Error
Missing or invalid credentials, e.g. { "message": "Invalid API Key", "code": "UNAUTHORIZED" }.
The body is the standard error object: see Errors.
404 Error
No such purchase intent for this organization (PURCHASE_).
The body is the standard error object: see Errors.
500 Error
Unexpected server error. Body is { message: "Internal server error", code: "INTERNAL_.
The body is the standard error object: see Errors.
Last updated September 25, 2026