Make a purchase, start to finish

Start one purchase, follow its user task and checkout route, then read the purchase outcome and money status.

Call start_purchase for the user's buying intent. Follow the returned user task: approval or payment in the Envoy app, or a gift-card payment link the user pays themselves. Read checkout_by in each reply. On agent checkout, use the authorized card or paid voucher once and report the result. On Envoy checkout, follow status while Envoy handles the merchant.

Read the purchase outcome and money status separately. An uncertain outcome must be reconciled before another payment.

How one purchase reaches an outcome

start_purchaseThe agent requests one purchase.
User completes the taskApp approval, or a gift-card link paid by the user.
Envoy sets checkout_byRead it in every reply.
agent: you check outUse this purchase's authorized card or paid gift-card voucher once, then report.
envoy: Envoy checks outNo card is released. Do not open the payment step or report.
Outcome from evidenceEnvoy confirms money movement. A report is only an observation.
Follow the returned user task and checkout_by. Approval, gift-card funding, merchant checkout and money outcome are separate facts.

Read this page if you build or run an agent that buys for an Envoy user, over MCP or the HTTP API. Envoy supplies the user task, payment details and money evidence. The agent owns the merchant checkout only when checkout_by is agent.

Support revision: envoy@2026-10. Last updated: 1 November 2026.

All values below are synthetic. They are offline examples, not a sandbox. Field lists and generated examples are in the tool reference. No live checkout has been recorded for this flow on this site, so no merchant or payment outcome here is a verified claim.

Read results over MCP or HTTP

This page names the HTTP envelope fields (purchase.state, next, data.payment_details). Over MCP the agent reads a briefing instead, and the same steps apply:

HTTP envelopeMCP result
purchase.statesituation: waiting_for_user, working, ready, confirming, done, stopped, needs_support, needs_details or needs_answer
purchase.money.statusmoney
nextThe What to do line and next {tool, args, wait_seconds}
user_actionThe user task in the briefing; gift-card payment-link funding includes its link; an in-app task has no link
data.payment_detailscard, only while ready
error.codeA plain sentence, retry and support_ref

Tell the user the first part of the briefing in your own words. Never show the JSON line, codes, ids or keys. See Replies and next steps. Never copy an MCP parser into HTTP code, or the reverse, because the two reply shapes differ.

Before you start

  • Get explicit authorization from the account owner for this merchant and this amount. A connection lets the agent read the account and request purchases; it is not approval.
  • Every purchase moves real money. There is no test mode and no sandbox.
  • An Envoy connection with the purchase service enabled. Service and sign-in failures are covered in Errors and reason codes.
  • A merchant URL and a total in rupees, up to ₹1,00,000.
  • Enough balance for a balance-funded purchase. For a gift-card payment link, the user pays the link themselves.
  • No matching purchase in progress. Envoy allows up to five open purchases per user, and refuses a second open purchase from the same agent at the same merchant for the same amount. Envoy also refuses a second Envoy-checkout or at-checkout purchase on a site where the user already has an open purchase, at any amount, and allows at most three approvals waiting on the user at once. Any of these refusals fails start_purchase (ACTIVE_PURCHASE_EXISTS on the HTTP API), and the briefing tells the agent to stop.

The path in one view

Call start_purchase. It returns at once with a purchase and a next.

Follow next. Call get_purchase_status as it directs.

Wait while the user completes the returned approval or payment-link task.

Read checkout_by. Envoy sets it for this purchase.

On agent checkout, pay once when the state is ready, then call report_checkout_result. On Envoy checkout, keep following next.

Read the final state.

Purchase states over HTTP and MCP

  1. waiting_for_userMCP waiting_for_userThe user completes the app task or pays the returned gift-card link.
  2. processingMCP workingEnvoy prepares payment, obtains a gift-card quote or checks out.
  3. readyMCP readyAgent route only. Pay once, then report.
  4. verifyingMCP confirmingMoney is unsettled. Never pay again.
  5. completedMCP doneThe purchase is complete. Read money separately, including payment collected on delivery.
needs_answer, needs_detailsEnvoy needs something only the user can give. Show the prompt word for word.
failed, cancelledMCP stopped when money is not_charged or refunded; otherwise needs_support.
supersededMCP working. A linked purchase replaced this one and needs its own approval.
Each row shows the HTTP purchase.state, then the matching MCP situation. Read money separately: the same state can carry different money values.

Every state is in the state table.

Start the purchase

Call start_purchase once. It returns promptly and does not wait for the card.

start-purchase.json
{
  "idempotency_key": "synthetic-intent-0001",
  "merchant_url": "https://shop.example.test",
  "total": "2499.00",
  "items": [{"name": "Headphones, black", "quantity": 1}],
  "user_request": "Buy the black headphones from this shop."
}
  • idempotency_key: one key per intent, 16 to 64 characters, not starting with pur_. Retrying with the same key continues the same purchase; a new key is a new purchase.
  • merchant_url: an https URL. Envoy takes the merchant name from its host.
  • total: rupees with up to two decimals, as a number or a string such as "1299" or "₹1,299.00".
  • items: one to 20 entries of {name, quantity, url, image}; only name is required. Put the variant in the name.
  • user_request: the user's own words, up to 300 characters.
  • max_total (optional): the card limit. It defaults to total and can be at most total plus the larger of 10% or ₹100. It does not authorize an amount the user has not accepted.
  • merchant_logo and items[].image (optional): authentic https image URLs from the merchant. They help the user recognize the request on the approval screen. Send them when the merchant provides them. Never invent them or use unrelated images. Envoy drops an invalid value silently, and the call does not fail. The approval still works without them.
  • address or address_id (never both): required when Envoy checks out, because Envoy needs the delivery details in start_purchase itself. address is one line with the recipient's name and phone. address_id is a saved address from get_account with include: ["addresses"]; saved addresses may be unavailable, so a typed address is the practical choice. Agent checkout does not need them, because you enter delivery details at the merchant.
start-purchase-tool-call.json
{
  "name": "start_purchase",
  "arguments": {
    "idempotency_key": "synthetic-intent-0001",
    "merchant_url": "https://shop.example.test",
    "merchant_logo": "https://shop.example.test/logo.png",
    "total": "2499.00",
    "items": [{"name": "Headphones, black", "quantity": 1, "image": "https://shop.example.test/headphones.jpg"}],
    "user_request": "Buy the black headphones from this shop."
  }
}

A synthetic briefing:

Approve this purchase in the Envoy app. (₹2,499.00 at "Example Store")
What to do: Show the user this message; they can't see tool results. Then call get_purchase_status with the next arguments to check progress. Follow each returned next step until Envoy says to stop. If you need to wait for the user, ask them to reply when they finish in the Envoy app.

{"situation":"waiting_for_user","money":"not_charged","purchase_id":"pur_5b1e9c7a3f2d5a8e9c0b1d2e3f4a5b6c","next":{"tool":"get_purchase_status","args":{"purchase":"pur_5b1e9c7a3f2d5a8e9c0b1d2e3f4a5b6c","after_revision":1,"wait_seconds":45}}}

This synthetic card example returns waiting_for_user with user task approve. Other routes can return a question, processing or a payment-link task. Follow the actual next. The start_purchase reference shows the full synthetic reply.

If a write is ambiguous, recover before you do anything else:

  • Keep the same idempotency_key and the returned purchase_id.
  • After a lost reply, a timeout or retry check_status_first, call get_purchase_status before anything else.
  • Never issue a new payment for an unknown outcome.
  • Follow retry and next.
  • Cancelling is not a refund, and it does not prove the card is blocked.

See Recover a purchase.

Discover what Envoy can buy

Call list_gift_card_merchants before promising a gift-card merchant. The reply lists merchant names, domains, available amounts or a whole-rupee range, and redeem_at when known. Use only the returned amounts or range. A discount_percent is a maximum saving, so say “up to”. The funding price is confirmed later. An empty list means no gift-card merchants are available here.

When the user names no merchant, or asks for Elixir or Envoy products, call search_products with the user's query. Show the returned names and prices. An empty result means no products are available for that query; do not invent an offer.

To buy returned offers, call start_purchase once for the cart. Copy each selected offer's product_id into its item, together with name and quantity. Every item must have a product ID, or none may have one. Use the returned merchant_url and the sum of the selected totals. If Envoy returns a changed total, ask the user before resending it. Offer expiry and delivery availability can change the result.

Search does not move money. An offer does not prove that purchasing is enabled for this account or deployment. Follow the returned user task and next. See the search reference and start reference for current inputs.

Envoy can also return an offer question before starting the original merchant purchase. Show that exact question and wait for the user's choice. Send their words with the supplied submit_answer args. Never choose an alternative merchant or product for the user.

Follow a gift-card purchase

Call list_gift_card_merchants before offering a gift card. To request one explicitly, call start_purchase with rail: "gift_card". Without that field, Envoy may choose another route. A ChatGPT connection has no gift cards and buys only from Elixir's marketplace. On a Claude connector, Envoy drops rail and checks out itself at the merchant total, with no voucher or payment link; do not offer a gift card or a discount there. See the gift-card guide.

Follow the gift-card guide for merchant discovery, funding, money status, voucher delivery, cancellation and deadlines. The user completes the returned approval or payment task. Do not infer a balance debit from the app task, or a charge from a voucher's face value.

Read checkout_by again after funding. Envoy may complete merchant checkout itself. An authorized agent handout carries kind: "gift_card" and the voucher's redemption instructions. A paid gift card does not prove that merchant checkout completed.

Card limits

Envoy chooses how to pay for each purchase. The agent can use a one-time RuPay card or a paid gift-card voucher. Envoy can also check out itself. checkout_by carries the decision; see Read checkout_by to know who pays.

A card pays a card limit (max_total, or total) from ₹10 to ₹10,000, in whole rupees. On the one-time card route, a total below ₹10 is refused, and nothing is created. On the HTTP API the code is NO_RAIL with reason RAIL_AMOUNT_OUTSIDE_CARD_LIMITS. The card minimum does not apply when Envoy checks out itself. A total above ₹10,000, up to ₹1,00,000, requires Envoy checkout, which may be unavailable. Above ₹1,00,000, start_purchase fails with INVALID_INPUT, and nothing is created. When neither a card nor Envoy can pay it, start_purchase fails with NO_RAIL, and nothing is created. Do not split a larger total across purchases.

The card pays at most the card limit. When expires_at is present it says when spending access closes; when it is absent there is no known deadline, so never read its absence as an expired card. Funding limits the card's value. Envoy does not guarantee merchant locking, single use, or that the issuer blocks the card the moment you pay.

Follow next

Pass next.args unchanged to next.tool. When the reply specifies a delay, wait before calling: next.wait_seconds over MCP or next.after_seconds over HTTP.

A get_purchase_status call with after_revision may hold while the purchase waits and return early when it changes. next.args.wait_seconds bounds that wait. Follow the reply's cadence; do not add extra waits.

Call next.tool with next.args exactly as the briefing gives them. If your host sends _meta.progressToken on this call, the reply can arrive as an event stream: progress messages with plain sentences, then the final result. Without a progress token, the reply is one JSON body.

Stop waiting and tell the user when any of these happens:

  • next.by has passed (REST only; over MCP, next has no tool).
  • Four held reads come back unchanged.
  • next.action is stop (REST only; over MCP, next has no tool).

At a stop, do not pay and do not start again. The Envoy app shows the user the result. To read it later, call get_purchase_status.

Wait while the user approves

State waiting_for_user means the user has a task; purchase.user_task says which. For task approve, the user completes the returned approval or payment step in the Envoy app. On a gift card, that task alone does not identify the funding source. A gift-card task pay_link instead asks the user to pay the returned link. No agent can complete either task for the user. Show the app message once (user_action on HTTP, the briefing over MCP); Envoy sends no approval link. Then follow next.tool with next.args to check progress. A held get_purchase_status can wait up to 45 seconds. If you need to wait for the user, ask them to reply when they finish in the Envoy app. For a passkey approval, expiry at approval.decide_by can fail with USER_APPROVAL_EXPIRED. A gift-card funding task can instead enter verification while Envoy checks whether payment arrived. Follow the returned state; expiry alone does not prove failure or permit another payment.

The user completes approve, add_money and enter_otp in the Envoy app. For pay_link, give the user the returned payment link once. The user opens and pays it themselves; the agent never opens or pays it. No Envoy tool accepts a payment or bank OTP.

Read checkout_by to know who pays

Envoy decides who completes the merchant checkout, and purchase.checkout_by carries the decision. Read it in each reply and act on the latest one. Do not predict it.

  • agent: you pay. See Agent checkout.
  • envoy: Envoy pays. See Envoy checkout.
  • null: not decided yet. Keep following next, and do not infer a route.

The latest reply decides who checks out. A supported retry_of creates a linked replacement that Envoy checks out. See Retry a failed card checkout. Never ask the user for card details instead.

State processing (working over MCP) means Envoy is preparing payment, obtaining a gift-card quote, or placing the order itself. Keep reading status. Do not start another payment.

Agent checkout

When checkout_by is agent, wait for ready, pay once, report what happened and confirm the result. Follow complete checkout after approval.

Envoy checkout

When checkout_by is envoy, Envoy handles the merchant page and releases no card. Follow complete checkout after approval.

Answer Envoy's question

If Envoy needs the user's answer, show its prompt and send the reply once. See answer Envoy's question.

Pay once when the state is ready

The authorized ready card or gift-card voucher belongs to this purchase. Use it once as instructed at the merchant. See pay once when ready.

Report the result

After agent checkout, report paid only on merchant confirmation. Otherwise report failed with a reason. See report the result.

Retry a failed card checkout on Envoy's browser

A reported failed card checkout may offer a browser retry through retry_of. Envoy stops the original card, and the linked replacement waits for its closure and refund. Follow retry a failed card checkout.

Confirm the outcome

A report records what the agent saw. Envoy confirms the outcome from money evidence. See confirm the outcome.

Stop a purchase

A cancel is not proof of a refund or a blocked card. See stop a purchase.

Run an authorized production trial

Production moves real money, and there is no test mode or sandbox. Run a trial only with the account owner's explicit authorization for that merchant and that amount.

  • Use a small amount you are authorized to spend, from ₹10.
  • Practice the stop path: start a purchase, call cancel_purchase, then read the state with get_purchase_status.
  • Practice a lost reply: retry start_purchase with the same key and confirm you get the same purchase_id with replayed: true.

When something goes wrong

  • A call fails (isError over MCP, ok: false on HTTP). It usually changed nothing. Over MCP, follow the briefing and retry. On HTTP, read error.code and follow next.
  • retry is check_status_first over MCP, or next reads a purchase over HTTP (for example UNAVAILABLE after a write). The write may have happened. Read that purchase first. Never start a new one, because Envoy may already hold the first. See Errors and reason codes.
  • A reply is lost or a call times out: read get_purchase_status before any further payment. If checkout outcome is unclear, report failed with failure_reason: other. See Recover a purchase.
  • Another purchase is in progress (ACTIVE_PURCHASE_EXISTS on HTTP): show the message and stop this attempt. Do not start another.
  • For help, give the user the support_ref (for example EV-XXXXXX) and point them to envoy.money/support.

On this page