Skip to main content
POST
Shop and pay with the holder's saved card

Authorizations

Authorization
string
header
required

Firebase ID token from /auth or any paid route, sent as a Bearer token: Authorization: Bearer <id_token> (the Bearer prefix is required).

Headers

Idempotency-Key
string

Optional client-generated unique key (a UUID works) that makes this write safe to retry. A retry with the same key within 24 hours returns the recorded response of the original request, marked with an Idempotency-Replayed: true header, instead of performing the write again. Reusing a key with a different body or route is rejected with 422 (code: idempotency_key_reused); a retry that arrives while the original is still running gets 409 (code: idempotency_key_in_progress), so wait a moment and retry with the same key. Keys are scoped to the authenticated account.

Maximum string length: 255

Body

application/json
ask
string

What the holder wants, in plain language. Required unless confirming.

Maximum string length: 2000
conversation_id
string

The conversation to continue. Required with confirm.

confirm
string

A cart's hash from an earlier turn, to place exactly that cart.

Pattern: ^[0-9a-f]{16}$
delivery_address
object

Where to ship. Send it on the call that asks for the cart (a cart is bound to the address it was shown for) and on the confirm.

Response

The turn, complete or still running.

turn_id
string

This turn's id. Poll GET /get-purchase-turn with it while state is running.

state
enum<string>

running means the turn is still talking to the merchant; the fields below appear once it is done.

Available options:
running,
done,
failed
created_at
integer

Milliseconds since the epoch.

conversation_id
string

Send it back on every follow-up and on the confirm.

status
enum<string>

What to do next. needs_input: a question or a cart waiting on a confirm. awaiting_approval: send approval_url to the holder, then repeat the same confirm. in_progress: the order is already being placed; read GET /get-purchase-conversation instead of confirming again. order_placed. declined: see decline_code. conflict: nothing ran, see error_code and confirm the fresh cart's hash. Never branch on reply.

Available options:
needs_input,
awaiting_approval,
in_progress,
order_placed,
declined,
conflict
reply
string

The shopping assistant's turn as prose, ready to show the holder.

cart
object | null

The most recently shown cart.

carts
object[]

Every open cart in the conversation, oldest first.

catalog
object[]

The last product search. Empty on a turn that did not search again, so keep the previous one.

unmatched
object[]

Things asked for that did not make it into a cart, with a reason. Show these rather than guessing from reply.

order_id
string | null

Set when this turn placed an order.

decline_code
string | null

The machine reason behind declined, e.g. items_unavailable.

approval_url
string | null

Send to the holder when status is awaiting_approval. Never open it yourself.

charge_status
enum<string> | null

Whether money moved: none, confirming, settled, or unknown (do not retry; read the conversation).

Available options:
none,
confirming,
settled,
unknown,
null
card_last4
string | null

The saved card that paid, for naming it to the holder.

error_code
string | null

On conflict: cart_changed, turn_in_progress, delivery_address_changed, or delivery_address_bound_after_cart.

error
string

Present when state is failed.

note
string

The next step.