Recover a purchase
The full procedure to read and reconcile a purchase after a lost reply, a timeout or a reconnect, without paying twice.
After a lost reply, a timeout or a reconnect, read get_purchase_status and reconcile before you make any further payment. If you cannot tell whether checkout completed, report failed with failure_reason: other. Recover the same operation with its original key and references. A supported linked retry_of is a separate step; follow the returned recovery instruction.
Support revision: envoy@2026-10. Last updated: 1 November 2026. All values are synthetic.
Find your symptom
| Symptom | First step | Then |
|---|---|---|
The start_purchase reply was lost or timed out | Retry start_purchase with the same idempotency_key and input | The reply is the same purchase with replayed: true. |
| You lost the purchase reference | Call get_account (it returns active_purchase) or get_purchase_status with no purchase | Follow that purchase's next. Do not start a new one. |
| You paid but the report reply failed or was lost | Read get_purchase_status for that purchase | Repeat the same report with its original attempt ID, when supplied. |
| Checkout timed out, a page went blank, or a bank step did not finish | Check the merchant's order page, then read get_purchase_status | If you cannot tell whether payment completed, report failed with failure_reason: other. Do not pay again. |
The state is verifying | Keep reading get_purchase_status as next directs | Never pay again or restart. If the read stops, tell the user the purchase is not confirmed yet. |
Situation confirming or needs_support (HTTP: reason ORDER_OUTCOME_UNKNOWN) | Stop | Tell the user to contact Envoy support in the app, with support_ref if they ask. Do not pay again or restart. |
Another purchase in progress (REST: ACTIVE_PURCHASE_EXISTS) | Show the message | The user finishes or cancels the other purchase in the Envoy app. |
Sign-in failure (HTTP: UNAUTHENTICATED) | Renew once with your refresh token if you hold one; if renewal fails or you have none, ask the user to reconnect Envoy | Then read get_purchase_status with your saved reference. |
retry same_call (HTTP: RATE_LIMITED or retryable UNAVAILABLE) | Wait next.wait_seconds (HTTP: next.after_seconds) | Repeat the same call unchanged. Never pay elsewhere meanwhile. |
| The merchant total differs from the purchase | Do not pay | Call cancel_purchase and tell the user. |
Every error code is explained in Errors and reason codes. For a connection problem, see Reconnect and resume your task.
Over MCP the agent reads a briefing and a slim JSON line, not the HTTP envelope. Read situation where this page says purchase.state, money for purchase.money.status, and the What to do line and next for next. confirming is verifying, and stopped is a failed or cancelled purchase that is over. Never show the JSON line, codes or ids to the user; give support_ref only if they ask for help. See Replies and next steps.
The rule: never pay twice
This outranks every retry rule.
- A lost reply or timeout is not proof the payment failed. Read the purchase first.
- A report is not settlement.
report_checkout_resultrecords what you saw; completion follows money evidence. - Money and the merchant order are separate facts. Read
purchase.money.statuson its own. - While the state is
verifying, money is unsettled. Never pay again and never restart the purchase. - Never generate a new key to recover a lost operation. A server-supported linked
retry_ofexplicitly requires a new key and preserves its parent reference. - Keep each operation's
idempotency_keyandpurchase_id. If Envoy creates a linked replacement, retain both references. - An error or
isErrorresult usually means nothing changed. Whenretryischeck_status_first, or an HTTPnextreads a purchase, the write may have happened. Readget_purchase_statusfirst, then follownext. - Never pay for an unknown outcome. Read the purchase until the state is known.
Read the purchase before anything else
Call get_purchase_status with the purchase you have. Waiting states follow the returned cadence. A ready read can release credentials or start Envoy's gift-card checkout. Do not poll or refresh ready status continuously.
{"purchase": "synthetic-intent-0001"}purchase accepts the purchase_id or the original idempotency_key. Read the reply in this order:
purchase.stateandpurchase.status_text. The text is safe to show the user.purchase.money.status:not_started,unknown,debited,refund_pending,refundedormerchant_collects. The last value means the merchant collects payment on delivery.purchase.reason_code, if set.next. Follow it.
| What you see | What it means | What you do |
|---|---|---|
ready, and you have not attempted checkout | The card or paid gift-card voucher is available. | Check the payment-detail kind. Use it once as instructed, then report. |
ready, but you may already have paid | You cannot tell whether the merchant took payment. | Check the merchant's own order page first. If any order or charge exists, or you cannot tell, report failed with failure_reason: other and do not pay again. |
waiting_for_user or processing | The user or Envoy is still working. | Keep following next. Pay nothing. |
needs_answer | Envoy asks the user something in purchase.question. | Show the user the prompt word for word. Send the reply with submit_answer and next.args. Pay nothing. |
verifying | Money is unsettled. | Keep reading status. Never pay again or restart. |
completed | The purchase is complete. Read the money status to distinguish a charge from payment collected on delivery. | Tell the user what the reply confirms. |
failed or cancelled, money.status not_started or refunded | It ended and no money is outstanding. | Tell the user from status_text. A new attempt needs a new key and new approval. |
failed or verifying with reason ORDER_OUTCOME_UNKNOWN (MCP: confirming or needs_support) | The payment result is not known. | Tell the user to contact Envoy support in the app, with support_ref if they ask. Do not pay again or restart. |
superseded | A linked purchase replaced this one. | Read the replacement in chain.superseded_by, as next directs. It needs its own approval. |
Start a new purchase only when the state is terminal, no money is unsettled and the user wants to try again. Even then, check get_account for an active_purchase first.
Recover a lost start reply
Retry start_purchase with the same idempotency_key and the same input. Envoy returns the same purchase with replayed: true. If you cannot resend the same input, read the purchase with get_purchase_status and the key as purchase.
IDEMPOTENCY_CONFLICT(REST only): the key was used with a different merchant URL or amount. Resend the original input unchanged, or read the existing purchase. Use a new key only for a different purchase.ACTIVE_PURCHASE_EXISTS(REST only; a plain sentence over MCP): another purchase is in progress. Showuser_action.messageand stop this attempt. Find that purchase with the steps below; do not start another.
Recover a lost purchase reference
If you lost both the purchase_id and the key, find the purchase from the account. Do not start a new purchase to recover. Either call works:
get_accountwithinclude: ["active_purchase"]. It returns theactive_purchase, ornullwhen nothing is in progress. It points to one open purchase and is not a complete list when several are open. If it leavesactive_purchaseout and lists it inunavailable, Envoy could not tell: don't start a new purchase until a later read shows it.get_purchase_statuswith nopurchase. It returns the caller's active or latest purchase.
{"include": ["active_purchase"]}NOT_FOUND (REST only) means no purchase matches that reference for this user; it also covers a purchase owned by someone else. Check the reference from this task. Do not start a new purchase.
Recover after a lost report
You paid, then the report_checkout_result call failed, timed out or its reply was lost. Envoy may already have saved the report.
When the error directs get_purchase_status, follow that call with the supplied purchase reference. Do not repeat merchant payment.
- Read
get_purchase_status. If the report was recorded, the purchase readsverifying. If it still readsready, the report was not saved; it is not a request to pay. - Send the same report again if you cannot tell whether it was recorded. Keep the original
checkout_attempt_idwhen supplied. Do not replace it with a later handout's ID. - If you cannot tell what happened at the merchant, report
failedwithfailure_reason: other. - Never pay again to retry a report.
Recover after a timeout or unclear checkout
A timeout, a declined page, an OTP error or a missing bank event does not show that another payment is safe.
- Look at the merchant's session or order page.
- Read
get_purchase_status. - If you cannot tell whether payment completed, call
report_checkout_resultwithoutcome: failedandfailure_reason: other. - Keep reading status. Do not pay again.
Bank verification is conditional; not every payment asks for it. Never send a bank or payment OTP to any Envoy tool. On the agent route, if the bank page asks for a code or OTP, enter the card's checkout_code there once; the bank sends the user no code for this card. On the Envoy route, the user enters it in the Envoy app. Never ask the user for a bank OTP in chat.
Recover after a reconnect
A reconnect proves who you are. It does not change the purchase.
- If a call fails to authenticate (
UNAUTHENTICATEDon HTTP), renew once with your refresh token, if you hold one. If renewal fails or you have no token, ask the user to reconnect Envoy through the host. - After reconnecting, read
get_purchase_statuswith your saved reference, or useget_accountfor theactive_purchase. - Follow
nextfrom that reply.
No card is released after a disconnect. Only the connection that started a purchase receives its card; another connection of the same agent can read or report it. Cancellation is available before a checkout report, and again when Envoy reopens the card after a failed report. Removing a connection does not end its purchases. Read the purchase from the new connection and follow next.
Retry on the Envoy route after a failed report
A failed report stops use of the old handout. If the card was not charged, Envoy can reopen that same card with a new checkout code. Pay again only when the latest reply explicitly permits it and supplies a new ready handout. Do not reuse the old code. This is separate from starting a linked replacement.
After a failed report, unless the reason is price_changed or out_of_stock, the connection that started the purchase may request a retry on Envoy's browser checkout. Call start_purchase with retry_of set to the reported purchase_id or original idempotency key only when Envoy's status authorizes it. Envoy stops the original card and starts a linked replacement, which pays only after that card is closed and refunded. Envoy refuses unsafe or unknown facts. A failed report does not itself close that card. Follow the returned instruction and never pay with an old handout. See Retry a failed card checkout.
For gift-card cancellation and refund limits, follow the gift-card guide. Do not apply a one-time-card retry option to a gift card.
Stop waiting without paying
Stop waiting and tell the user when next.by passes, when four held reads come back unchanged, or when next.action is stop. The Envoy app shows the user the result. Do not pay and do not restart the purchase; read get_purchase_status again later.
Respect the wait time before any retry: next.wait_seconds over MCP, error.retry_after_seconds and next.after_seconds on HTTP.
Cancel safely
Before any checkout report, call cancel_purchase when the user changes their mind or the merchant total differs. After a report, Envoy refuses cancellation and reconciles the purchase, unless it reopens the card (the purchase reads ready again); then cancel_purchase works again.
- Cancelling is not proof of a refund, and not proof the card is blocked. Envoy releases no further card details after a confirmed cancellation; this does not prove the issuer blocked the card.
cancelledmeans the purchase stopped.refunding(refund_pending) means a refund has started but has not arrived.refundedmeans Envoy recorded the refund. Readpurchase.money.statusto tell them apart.- A replacement purchase (
superseded) needs its own approval in the Envoy app. - A card already used at the merchant stays used. Reconcile with
get_purchase_statusand the merchant's order page. - After a cancellation request, do not pay with card details you saw earlier.
- A failed payment does not prove that money returned. Read the returned money status and contact support if the outcome stays unclear. Merchant refunds follow the merchant's policy. Do not promise a refund date;
purchase.money.statussays what Envoy knows. While money is unknown, never pay again.
Practice recovery before real money
First inspect the synthetic examples offline. Identify the saved key, status call, stop instruction and money state. These examples move no money.
An optional production trial moves real money; there is no test mode. Obtain the account owner's explicit authorization for the merchant and amount first.
- Start only that authorized purchase. Save the key.
- Pretend the reply was lost: call
start_purchaseagain with the same key and input. Confirm the samepurchase_idandreplayed: true. - Pretend you lost the key: call
get_accountwithinclude: ["active_purchase"]and find it. - Call
cancel_purchase, then confirm the state withget_purchase_status.
Related
- Make a purchase, start to finish
- Errors and reason codes
- Replies and next steps
- Tool reference
- Credentials and secrets
Before you contact support
Keep the purchase reference (purchase_id or idempotency_key), the support_ref (HTTP: the request_id and trace_id of the reply), the time, and what you saw at the merchant. Support: envoy.money/support or support@envoy.money. Never send card details, checkout codes or OTPs to support or put them in feedback.