Browse the docs

List purchase intents

GET /v2/api-gateway/purchase-intents

Your organization's purchase intents, newest first. Paginated with current_page / per_page (default 10, maximum 100). Filter by card_id or by status — for example everything still AWAITING_APPROVAL. status is matched against the effective status, so AWAITING_APPROVAL and APPROVED never return a row that is already past its expires_at, and EXPIRED returns every row that is — whether or not anything had written that down yet.

Request

Authentication: your API key in the x-api-key header.

Query parameters

FieldTypeDescription
card_idstring, uuidOnly intents for this card.
statusstringOnly intents in this status.One of: AWAITING_APPROVAL, APPROVED, CONSUMED, DENIED, EXPIRED, CANCELLED
recurringbooleantrue for standing approvals only, false for one-off ones only. A standing approval that is still in force reads CONSUMED, because the first charge spent it and it goes on authorizing the same purchase.
current_pageintegerPage number, starting at 1. Default 1. Maximum 10000 — past that, narrow with card_id or status rather than paging deeper.
per_pageintegerIntents per page. Minimum 1, maximum 100. Default 10. A larger value is rejected with a 400 — it is not silently reduced.

Example request

cURL

curl --request GET \
     --url 'https://api.themooncard.com/v2/api-gateway/purchase-intents' \
     --header 'x-api-key: YOUR_API_KEY'

Responses

200 Success
FieldTypeDescription
purchase_intents requiredarray of object
purchase_intents[].id requiredstringPurchase intent id.
purchase_intents[].card_id requiredstringThe ask-first card this approval is for.
purchase_intents[].cardholder_id requiredstring, nullableThe cardholder who was asked, or null when the card has no cardholder.
purchase_intents[].approver_type requiredstringWho 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
purchase_intents[].status requiredstringAWAITING_APPROVAL 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_at reports EXPIRED here whether or not anything had written it down yet.One of: AWAITING_APPROVAL, APPROVED, CONSUMED, DENIED, EXPIRED, CANCELLED
purchase_intents[].kind requiredstringsingle — 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_total in total at that merchant, over any number of authorizations, for valid_for_days 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
purchase_intents[].merchant requiredobjectThe merchant the approval covers.
purchase_intents[].merchant.name requiredstring, nullableMerchant name, or null when only an MCC was given.
purchase_intents[].merchant.mcc requiredstring, nullableMerchant category code, or null when only a name was given.
purchase_intents[].merchant_names requiredarray of stringEVERY 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.
purchase_intents[].merchant_domain requiredstring, nullableThe 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.
purchase_intents[].amount requirednumberThe approved amount, in the card's own currency, in major units.
purchase_intents[].currency requiredstringISO code of the card's ledger account currency.
purchase_intents[].merchant_amount requirednumber, nullableThe price as the merchant stated it, in merchant_currency, when the request gave it that way (merchant_amount + merchant_currency). 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.
purchase_intents[].merchant_currency requiredstring, nullableThe merchant currency (uppercase ISO 4217) of merchant_amount, or null.
purchase_intents[].request_fx_rate requirednumber, nullableThe rate (merchant_currency 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.
purchase_intents[].tolerance requiredobjectHow far an authorization may sit from amount and still match. The wider of the two applies, symmetrically in both directions.
purchase_intents[].tolerance.percent requirednumber, nullablePercentage slack on amount, or null when none applies.
purchase_intents[].tolerance.absolute requirednumber, nullableSlack in the card's own currency, in major units like amount, or null when none applies.
purchase_intents[].reason requiredstringThe sentence the cardholder read before deciding.
purchase_intents[].recurring requiredbooleanA 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.
purchase_intents[].monthly_limit requirednumber, nullableHow 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.
purchase_intents[].recurring_spent_this_month requirednumberTaken 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.
purchase_intents[].recurring_period_start requiredstring, nullableFirst day of the month recurring_spent_this_month is counting, as YYYY-MM-DD. Null until the first charge.
purchase_intents[].recurring_mcc requiredstring, nullableThe 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.
purchase_intents[].valid_for_days requirednumber, nullableHow 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_within_minutes (60 by default) always bounds ANSWERING the request; this is how long the answer is worth anything, and expires_at is rewritten to it on approval.
purchase_intents[].max_total requirednumber, nullableAn 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.
purchase_intents[].spent requirednumber, nullableHow much of that budget has been taken. Null on anything that is not an order. Refunds and reversals do not give budget back.
purchase_intents[].remaining requirednumber, nullablemax_total 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.
purchase_intents[].expires_at requiredstringAfter this instant the approval is worthless and an authorization on the card is declined with INTENT_REQUIRED again. Until the approver answers it is the deadline to ANSWER (answer_within_minutes after the request, 60 by default). On approval it is rewritten to how long the approval may be SPENT: valid_for_days 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".
purchase_intents[].spendable_until requiredstring, nullableUntil when an APPROVED approval may be spent: the same instant as expires_at once the intent has been approved, and null before that — while it is awaiting an answer expires_at 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".
purchase_intents[].created_at requiredstringWhen the request was made. ISO-8601 UTC with milliseconds, e.g. "2026-09-16T17:05:00.000Z".
purchase_intents[].approved_at requiredstring, nullableWhen the cardholder approved it, or null. ISO-8601 UTC with milliseconds, e.g. "2026-09-16T17:05:00.000Z".
purchase_intents[].decided_at requiredstring, nullableWhen it was approved, denied or cancelled, or null. ISO-8601 UTC with milliseconds, e.g. "2026-09-16T17:05:00.000Z".
purchase_intents[].approved_via requiredstring, nullableHow 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.
purchase_intents[].autonomy_grant_id requiredstring, nullableReserved for a feature that is not generally available yet. Always null today; ignore it.
purchase_intents[].consumed_transaction_id requiredstring, nullableThe authorization that spent this approval, or null.
purchase_intents[].agent_key_id requiredstring, nullableThe key_id of the agent key that signed the request — the RFC 7638 JWK thumbprint register_agent_key returned. Null when the request was not signed, which is allowed.
purchase_intents[].agent_label requiredstring, nullableThat key’s current label. Read from the key on every request, so renaming a key renames it on intents that already exist.
purchase_intents[].profile_fields requiredarray of stringReserved for a feature that is not generally available yet. Always null today; ignore it.
purchase_intents[].profile_shipping_address requiredobject, nullableReserved for a feature that is not generally available yet. Always null today; ignore it.
purchase_intents[].profile_releases_remaining requiredinteger, nullableReserved for a feature that is not generally available yet. Always null today; ignore it.
pagination requiredobject
pagination.total requiredintegerTotal number of items across all pages.
pagination.last_page requiredintegerNumber of the last page (0 when there are no items).
pagination.prev_page requiredinteger, nullablePrevious page number, or null on the first page.
pagination.next_page requiredinteger, nullableNext page number, or null on the last page.
pagination.per_page requiredintegerPage size that was applied.
pagination.current_page requiredinteger1-based current page.
pagination.from requiredinteger1-based index of the first item on this page (0 when empty).
pagination.to requiredinteger1-based index of the last item on this page (0 when empty).
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.

500 Error

Unexpected server error. Body is { message: "Internal server error", code: "INTERNAL_SERVER_ERROR" }.

The body is the standard error object: see Errors.

Last updated September 25, 2026