Errors and reason codes

What each error code and reason code means, and the next step the reply gives your agent.

HTTP tool results. This page describes the full reply that POST /tools/:tool returns: error.code, reason codes, purchase.state and next.action. An MCP agent does not receive these tool-result fields. If you build an MCP agent, read Replies and next steps instead. It lists each situation, money and retry value and what to do.

Over MCP, a tool-result failure is a plain sentence for the user, a What to do line, retry and support_ref. Envoy keeps the codes in the result's _meta["money.envoy/support"] for support and hosts. Give the user the support_ref and support can find the code.

Before an MCP tool call starts, an authentication refusal is an HTTP 401 transport response, not a tool result. Read WWW-Authenticate to find Envoy's sign-in metadata. Its body has AUTH_REQUIRED for a missing or invalid bearer, or INSTALLATION_PROOF_REQUIRED when a key-bound connection lacks a valid proof. The body request_id matches the X-Request-Id header. Renew a rejected bearer once if the host has a refresh token; otherwise reconnect. For a proof refusal, use a fresh proof from the original installation. If that key is lost, reconnect as a new connection. See Request protection.

A failed call usually changed nothing. When its next reads a purchase (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. The reply always says what to do next.

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

How an error reaches you over HTTP

FieldMeaning
error.codeOne of the 12 codes below. Branch on this.
error.messageSafe to show the user. For NO_RAIL it explains the rail reason.
error.fieldsUp to 10 rejected fields, each with path, problem and expected.
error.reason_codeOptional. Why a purchase or rail decision went this way (see below).
error.retryableWhether repeating the same call is allowed.
error.retry_after_secondsSeconds to wait before a retry, when set.
nextWhat to do now: action, tool, args, instruction, after_seconds, by. Follow it.

The HTTP reply also has the HTTP status below. next.instruction is for the agent. error.message and purchase.status_text are safe to show the user.

Reconcile before you pay again

Never pay twice. This outranks every retry rule on this page.

  • After a lost reply or timeout, read get_purchase_status before any further payment. If you cannot tell whether checkout completed, report failed with failure_reason: other. See Recover a purchase.
  • When the reason is ORDER_OUTCOME_UNKNOWN, the purchase result is not known. Tell the user to contact Envoy support in the app. Do not pay again or restart.
  • While the state is verifying, money is unsettled: keep reading status as next directs.
  • Retrying start_purchase with the same idempotency_key continues the same purchase. A new key is a new purchase.

Error codes

When an error concerns an existing purchase, the reply includes it and next reads that purchase with get_purchase_status. Otherwise next.action is the one shown. The full table with the agent's instruction for each code is in the reference. Each code has its own heading in Each error code.

CodeHTTPWhat happenednext.actionNext steps
INVALID_INPUT400An input field is wrong; error.fields names each one.fix_inputFix every listed field. Call the same tool once more.
UNAUTHENTICATED401The access token is missing, expired or revoked.reconnectRenew once with your refresh token. If renewal fails or you have none, ask the user to reconnect Envoy. Then retry.
NOT_ALLOWED403This connection may not do that.stopTell the user. Do not retry, because the answer will not change.
SERVICE_NOT_ENABLED403The purchase service is not enabled for this user or host.stopTell the user. Do not retry.
NOT_FOUND404No purchase matches that reference for this user.stopCheck the reference from this task. Do not start a new purchase to recover.
IDEMPOTENCY_CONFLICT409The key was already used with a different merchant URL or amount.fix_inputResend the original input unchanged. Use a new key only for a genuinely new purchase.
ACTIVE_PURCHASE_EXISTS409Another purchase is in progress.stopShow user_action.message to the user. Stop this attempt.
STATE_CONFLICT409The call does not fit the purchase's current state.stopDo not retry the call. Read the purchase with get_purchase_status.
NO_RAIL422Envoy cannot pay this purchase; nothing was created.stopTell the user why, using error.message. Do not retry or split the purchase.
RATE_LIMITED429Too many requests for this access token.retryWait next.after_seconds. Repeat the same call unchanged. See Retry after a 429.
UNAVAILABLE503Envoy or an upstream service is unavailable.retry when error.retryable is true; otherwise stop or a read of the purchaseFollow next. Never pay another way meanwhile.
CLIENT_UPDATE_REQUIRED426This client is too old.update_clientTell the user to update the client.

Each error code

Each heading below is a link target for one code.

INVALID_INPUT

An input field broke a rule. error.fields lists up to 10 fields with path, problem and expected. Fix all of them, then call the same tool once more. Over MCP this reads retry after_fix and a fix list.

submit_answer also refuses an answer that looks like a one-time code or holds card details, unless the open question asks for the user's merchant login. Envoy never echoes the refused value. If Envoy cannot tell which question the answer is for, error.fields names the open question's id. Ask the user again, then send the answer with that question.

UNAUTHENTICATED

The access token is missing, expired or revoked. Renew once with your refresh token if you hold one. If you cannot renew, the user reconnects Envoy through the host. A reconnect proves who you are and changes no purchase. See Reconnect and resume your task.

NOT_ALLOWED

This connection may not do that. Tell the user. Do not retry.

SERVICE_NOT_ENABLED

The purchase service is not enabled for this user or host. Tell the user. Do not retry.

NOT_FOUND

The reference does not match a purchase for this user. The reference is wrong for this user. The purchase is not gone. It also covers a purchase owned by someone else. Check the purchase_id or idempotency_key from this task. Do not start a new purchase.

IDEMPOTENCY_CONFLICT

The idempotency_key was used before with different input. Resend the original input unchanged, or read the existing purchase. Use a new key only for a different purchase. For submit_answer, the same key with a different answer returns this code.

ACTIVE_PURCHASE_EXISTS

The reply carries a user_action with task resolve_active_purchase and the message "You already have a purchase in progress. Finish or cancel it in the Envoy app first." Show it and stop. Do not start another purchase. Find the active purchase with get_account; see Recover a lost purchase reference.

STATE_CONFLICT

The call does not fit where the purchase is now. Common cases:

  • report_checkout_result before the purchase is ready.
  • cancel_purchase on a purchase that already completed or failed.
  • submit_answer with a question that is no longer the open one. Envoy has moved on. Call get_purchase_status and read the new question. Never send the old answer to the new question.
  • submit_answer with type details when no question is open.
  • submit_answer with type feedback while the purchase is still in progress. Feedback is optional and never blocks checkout.

Do not retry the call. Read the purchase and follow its next.

NO_RAIL

Nothing exists to cancel or recover. error.reason_code is the rail reason, for example RAIL_AMOUNT_OUTSIDE_CARD_LIMITS. Tell the user using error.message. Do not retry or split the purchase into smaller ones. See Rail reasons.

RATE_LIMITED

You sent too many requests for this access token. Wait next.after_seconds, then repeat the same call unchanged. Add jitter and back off if you see it again. See Retry after a 429.

UNAVAILABLE

Envoy or an upstream service is unavailable. If next calls get_purchase_status with your original reference, the write may or may not have happened. Read the purchase before anything else. If error.retryable is true, wait next.after_seconds and repeat the same call. If it is false, tell the user to try again later. Never pay another way meanwhile, because the first purchase may still complete.

CLIENT_UPDATE_REQUIRED

This client is too old (HTTP 426). Tell the user to update it.

Reason codes

purchase.reason_code says why a purchase is in its state. A reason code never arrives alone. It arrives in one of three places:

  • On purchase.reason_code, next to a purchase.state. Most reasons in the first two tables below.
  • On error.reason_code, next to the error code NO_RAIL. Only rail reasons.
  • On purchase.rail_reason, which says which payment path Envoy chose. Not an error.

Over MCP the reason shapes the briefing's wording and appears only in _meta. The agent never sees the code. The meaning below is the sentence Envoy shows as purchase.status_text, safe to show the user.

The Arrives with column comes from Envoy's state mapping and the service code. "Reserved, not sent today" means the contract lists the code but no service path produces it yet. The contract only adds codes and never removes them, so a reserved code stays listed. Handle it by its meaning, in case it starts to appear.

Policy and user decisions

Reason codeMeaningArrives withNext steps
POLICY_DENIEDYour Envoy spending settings do not allow this purchase.State failedStop. Tell the user. Do not retry. If the user already approved it, Envoy refused it itself; give support the support_ref.
POLICY_AMOUNT_LIMITThis purchase is above your Envoy spending limit.State failed, when Envoy refuses the amount at the startStop. A new purchase for the same amount is refused the same way.
POLICY_ACTIVE_PURCHASE_EXISTSAnother purchase is still in progress or waiting for approval, so this one could not start.State failed, when the start is refused because another purchase is openStop. Tell the user to finish or cancel the other purchase first; a new purchase may work after that.
POLICY_ADMISSION_UNAVAILABLEEnvoy could not check this purchase against your spending settings, so it did not go ahead.Reserved, not sent todayStop. Tell the user.
POLICY_FRAUD_DECLINEDThis purchase was declined by a security check.Reserved, not sent todayStop. Do not start a new purchase.
POLICY_AGENT_LIMITThis purchase is above the limit set for this connection.Reserved, not sent todayStop. Do not start a new purchase for the same amount.
USER_DECLINEDYou declined this purchase in the Envoy app.State failedStop. The user chose this. Start a new purchase only if the user asks.
USER_APPROVAL_EXPIREDThe approval expired before Envoy could prepare the card. Nothing was charged.State failedStop. A new attempt needs a new key and a new approval.
USER_OTP_EXPIREDThe one-time code from your bank expired before it was entered.Reserved, not sent todayStop. A new attempt needs a new key.
USER_CANCELLEDYou cancelled this purchase in the Envoy app.State cancelledDone. Carry on with the user's task.
USER_FUNDS_NOT_ADDEDThe gift-card funding task expired unpaid.State failedStop this purchase. Follow the returned recovery instruction; do not pay through an expired funding task.

Order outcomes

Reason codeMeaningArrives withNext steps
ORDER_CANCELLED_BY_AGENTThis purchase was cancelled on your behalf.State cancelled, after you called cancel_purchase and nothing was chargedDone.
ORDER_CARD_NOT_ACCEPTEDThe merchant did not accept the Envoy card.State failed, when you reported failed with failure_reason card_not_accepted and Envoy then returned the card's moneyStop. Tell the user. Another card purchase at this merchant is refused the same way.
ORDER_CARD_DECLINEDThe card payment was declined.State failed, when you reported failed with failure_reason card_declined and Envoy then returned the card's moneyStop. Do not pay again. Tell the user.
ORDER_OTP_FAILEDThe bank one-time code check failed, so the payment did not go through.State failed, when you reported failed with failure_reason otp_failed and Envoy then returned the card's moneyStop. Do not retry the code. Tell the user.
ORDER_PRICE_CHANGEDThe merchant's price changed. Nothing was charged.State failed, when you reported failed with failure_reason price_changed and Envoy then returned the card's moneyStop. Tell the user the new price.
ORDER_OUT_OF_STOCKThe item is out of stock at the merchant.State failed, when you reported failed with failure_reason out_of_stock and Envoy then returned the card's moneyStop. Tell the user. A new purchase hits the same stock.
ORDER_CHECKOUT_FAILEDCheckout at the merchant did not go through.State failed: when Envoy checks out itself (checkout_by envoy) and its checkout fails, or when you reported failed with failure_reason site_error or other and Envoy then returned the card's moneyStop. Tell the user. A new purchase may be tried when nothing was charged.
ORDER_CHECKOUT_BLOCKEDThe merchant blocked this checkout.State failed: when Envoy checks out itself (checkout_by envoy) and the site blocks it, or when you reported failed with failure_reason site_blocked and Envoy then returned the card's moneyStop. Another card purchase at this merchant is refused the same way.
ORDER_CARD_EXPIRED_UNUSEDThe one-time card expired before it was used. Nothing was charged.State failedStop. A new purchase needs a new approval.
ORDER_ISSUER_UNAVAILABLEThe card issuer was unavailable, so the purchase could not be paid.State failedStop. Tell the user. A new purchase may work later.
ORDER_CARD_REQUIREDThis merchant is not available without an Envoy card. list_gift_card_merchants lists the merchants that are. Nothing was charged.State failedStop. Tell the user. Offer a merchant from list_gift_card_merchants.
ORDER_INTEGRATION_ERROREnvoy hit an internal problem with this purchase and is checking it.State failed or verifyingFollow next. In verifying, never pay again. Give support the support_ref.
ORDER_OUTCOME_UNKNOWNThe purchase result is not known yet. Do not pay again until it is resolved.State verifyingSee below.
ORDER_CONNECTION_REVOKEDThe connection that made this purchase was removed from Envoy.Reserved, not sent todayStop. The user connects again to start a new purchase.

Over MCP, a purchase that Envoy refused after the user approved it reads "Envoy couldn't complete this payment after you approved it". It does not say "your spending settings do not allow this purchase", because the user's settings are not the cause.

ORDER_OUTCOME_UNKNOWN is the recovery reason. It arrives with state verifying. Keep reading status as next directs. Never pay again. If next.action becomes stop, tell the user to contact Envoy support in the app.

A failure_reason in report_checkout_result (card_not_accepted, card_declined, otp_failed, price_changed, out_of_stock, site_error, site_blocked, other) records what you saw. Like every report, it moves a ready purchase to verifying until Envoy has money evidence. If you cannot tell whether checkout completed, report failed with failure_reason: other. Envoy refuses agent cancellation after a report unless it reopens the card with a new ready handout.

Rail reasons

purchase.rail_reason says which payment path Envoy chose. With NO_RAIL, error.reason_code says why Envoy could not pay, and error.message repeats the meaning. Envoy chooses the path for each purchase. The agent can use a one-time card or paid gift-card voucher, or Envoy checks out itself. purchase.checkout_by carries the result; see Read checkout_by to know who pays. Do not predict the path from a rail reason.

The generated reason-code reference lists the exact current values and reserved codes. It includes host restrictions, merchant gateways, gift cards and available marketplace offers. A declared value does not mean that its route is enabled for this account.

For a refusal, use error.message and follow next. For a selected route, read the latest checkout_by. Never infer permission to pay from a reason code alone.

States and next.action

Every purchase reply has a purchase.state and a next.action. This table shows the pair for all ten states. Over MCP the agent reads situation instead; see Replies and next steps.

purchase.statenext.actionWhat it means for you
needs_detailscall_tool (submit_answer)Envoy needs more details. Show the user purchase.question.prompt word for word. Then call submit_answer with next.args and the user's words as answer.
needs_answercall_tool (submit_answer)Envoy asks the user a question in purchase.question. Show the prompt word for word and wait. Then call submit_answer with next.args and the user's exact words as answer.
waiting_for_usercall_tool (get_purchase_status, held)Show the returned user task once. App tasks stay in the app. For pay_link, give the link and amount to the user; never open or pay it yourself. Follow status as directed.
processingcall_tool (get_purchase_status, held)Envoy is preparing payment or checking out. Keep reading status. Start nothing else.
readycheckout (report_checkout_result)Authorized card or gift-card details are in data.payment_details on get_purchase_status. Check kind and follow the handout. Pay once, then report. Any other tool sends you to get_purchase_status first with call_tool.
verifyingcall_tool (get_purchase_status, held)Money is unsettled. Keep reading status. Never pay again or restart.
completeddoneThe purchase is complete. Read money.status separately; the merchant may collect on delivery. Tell the user what the reply confirms.
failedstopThe purchase is over. Tell the user from status_text. With ORDER_OUTCOME_UNKNOWN, tell the user to contact support.
cancelleddoneThe purchase was cancelled. Carry on with the user's task.
supersededcall_tool (get_purchase_status on the replacement)Another purchase replaced this one. It needs its own approval.

A held state changes to stop after four unchanged held reads or when next.by passes. Stop waiting then, and do not pay. The Envoy app shows the user the result.

The other next.action values come with errors and reads: fix_input, retry, reconnect, update_client, stop (see the error table) and continue (a successful read such as get_account).

On this page