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

SymptomFirst stepThen
The start_purchase reply was lost or timed outRetry start_purchase with the same idempotency_key and inputThe reply is the same purchase with replayed: true.
You lost the purchase referenceCall get_account (it returns active_purchase) or get_purchase_status with no purchaseFollow that purchase's next. Do not start a new one.
You paid but the report reply failed or was lostRead get_purchase_status for that purchaseRepeat the same report with its original attempt ID, when supplied.
Checkout timed out, a page went blank, or a bank step did not finishCheck the merchant's order page, then read get_purchase_statusIf you cannot tell whether payment completed, report failed with failure_reason: other. Do not pay again.
The state is verifyingKeep reading get_purchase_status as next directsNever 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)StopTell 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 messageThe 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 EnvoyThen 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 purchaseDo not payCall 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_result records what you saw; completion follows money evidence.
  • Money and the merchant order are separate facts. Read purchase.money.status on 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_of explicitly requires a new key and preserves its parent reference.
  • Keep each operation's idempotency_key and purchase_id. If Envoy creates a linked replacement, retain both references.
  • An error or isError result usually means nothing changed. When retry is check_status_first, or an HTTP next reads a purchase, the write may have happened. Read get_purchase_status first, then follow next.
  • 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.

recover-purchase.json
{"purchase": "synthetic-intent-0001"}

purchase accepts the purchase_id or the original idempotency_key. Read the reply in this order:

  1. purchase.state and purchase.status_text. The text is safe to show the user.
  2. purchase.money.status: not_started, unknown, debited, refund_pending, refunded or merchant_collects. The last value means the merchant collects payment on delivery.
  3. purchase.reason_code, if set.
  4. next. Follow it.
What you seeWhat it meansWhat you do
ready, and you have not attempted checkoutThe 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 paidYou 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 processingThe user or Envoy is still working.Keep following next. Pay nothing.
needs_answerEnvoy 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.
verifyingMoney is unsettled.Keep reading status. Never pay again or restart.
completedThe 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 refundedIt 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.
supersededA 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. Show user_action.message and 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_account with include: ["active_purchase"]. It returns the active_purchase, or null when nothing is in progress. It points to one open purchase and is not a complete list when several are open. If it leaves active_purchase out and lists it in unavailable, Envoy could not tell: don't start a new purchase until a later read shows it.
  • get_purchase_status with no purchase. It returns the caller's active or latest purchase.
find-active-purchase.json
{"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.

  1. Read get_purchase_status. If the report was recorded, the purchase reads verifying. If it still reads ready, the report was not saved; it is not a request to pay.
  2. Send the same report again if you cannot tell whether it was recorded. Keep the original checkout_attempt_id when supplied. Do not replace it with a later handout's ID.
  3. If you cannot tell what happened at the merchant, report failed with failure_reason: other.
  4. 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.

  1. Look at the merchant's session or order page.
  2. Read get_purchase_status.
  3. If you cannot tell whether payment completed, call report_checkout_result with outcome: failed and failure_reason: other.
  4. 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.

  1. If a call fails to authenticate (UNAUTHENTICATED on 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.
  2. After reconnecting, read get_purchase_status with your saved reference, or use get_account for the active_purchase.
  3. Follow next from 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.
  • cancelled means the purchase stopped. refunding (refund_pending) means a refund has started but has not arrived. refunded means Envoy recorded the refund. Read purchase.money.status to 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_status and 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.status says 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.

  1. Start only that authorized purchase. Save the key.
  2. Pretend the reply was lost: call start_purchase again with the same key and input. Confirm the same purchase_id and replayed: true.
  3. Pretend you lost the key: call get_account with include: ["active_purchase"] and find it.
  4. Call cancel_purchase, then confirm the state with get_purchase_status.

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.

On this page