Skip to content
API Reference

Error Reference

Complete error catalog for the Trusteed API v1 and MCP Gateway. Use this to write precise retry logic and handle failures gracefully.

Error Response Format

REST and MCP fail differently, and the difference matters when you write retry logic. REST returns an HTTP status with a JSON envelope; an MCP tool returns HTTP 200 with isError on the result.

// REST — GET/POST /api/v1/...
{
  "success": false,
  "error": "Human-readable message",
  "code": "MACHINE_CODE",   // present on some endpoints, not all
  "details": { ... }        // optional
}

// MCP tool result — transport status is 200
{
  "isError": true,
  "content": [{ "type": "text", "text": "Human-readable message" }],
  "structuredContent": {
    "error": {
      "code": "CART_NOT_MUTABLE",
      "remedy": "refresh_state",
      "retryable": false,
      "scope": "call",
      "details": { ... },
      "reference": "https://trusteed.xyz/en/developers/errors#CART_NOT_MUTABLE"
    }
  }
}

On REST, error carries the message and code is not yet uniform across every endpoint — branch on the HTTP status. On MCP, structuredContent.error.code is the stable discriminator and the table below is generated from the same source the server emits.

Authentication Errors

Returned when the API key is missing, invalid, or lacks the required permissions to perform the requested action.

HTTP CodeErrorMeaningRecommended Action
401unauthorizedMissing or invalid API keyVerify the X-Agent-Api-Key header is present and correctly formatted (agnt_xxx)
401token_expiredOAuth token has expiredRefresh the token via POST /api/v1/oauth/token
403forbiddenKey lacks the required scope for this actionCheck key scopes in the Dashboard under Agent Keys
403store_suspendedMerchant is not currently availableChoose a different merchant from the directory

Rate Limit Errors

Rate limits are enforced per key and per tool. Read the X-RateLimit-Reset header to know when the window resets.

HTTP CodeErrorMeaningRecommended Action
429rate_limit_exceededPer-key request limit hitPause and retry after the Unix timestamp in X-RateLimit-Reset

Rate limit response headers

  • X-RateLimit-LimitMaximum requests allowed in the current window
  • X-RateLimit-RemainingRequests remaining before the limit is hit
  • X-RateLimit-ResetUnix timestamp (seconds) when the window resets

Business Logic Errors

Returned when a request is structurally valid but fails due to application state (e.g. cart expired, store not found).

HTTP CodeErrorMeaningRecommended ActionRetryable
404store_not_foundStore does not exist or is inactiveVerify the slug from the merchant directoryNo
500internal_errorUnexpected server errorRetry with exponential backoff (see Retry Guidance below)Yes
503service_unavailableTemporary overload or maintenanceRetry after 30 secondsYes

MCP Tool Errors

When an MCP tool fails, the transport still returns 200 — the failure is on the result. Branch on structuredContent.error.remedy: it is a closed set, so you can handle it exhaustively. Codes marked funds captured mean a charge may already exist; retrying one of those can charge the buyer twice.

CodeWhat it meansRemedyRetry?Holds for
HUMAN_CONFIRMATION_DECLINEDA human was asked and declined. Nothing was changed. Re-asking without new information is not acceptable behaviour.Stop — do not retryNoThis call only
IDEMPOTENCY_KEY_CONFLICTThis session is already bound to a different idempotency_key. Reusing the session under a new key is refused because it cannot be distinguished from a double-submit.Stop — do not retryNoThis call only
ORDER_OWNERSHIP_FAILEDThe order is not associated with the authenticated customer. Retrying under the same identity is refused, and repeated attempts are recorded.Stop — do not retryNoThis call only
PAYMENT_AMOUNT_MISMATCHFunds may already be capturedThe captured amount does not match the amount the session authorised. Funds have moved. Do not retry; this needs reconciliation.Stop — do not retryNoThis call only
PAYMENT_CAPTURED_FINALIZATION_FAILEDFunds may already be capturedThe capture SUCCEEDED and order finalization then failed. The buyer has been charged. Do not retry — a retry risks a second charge. Escalate with `details.capture_reference`.Stop — do not retryNoThis call only
HUMAN_CONFIRMATION_REQUIREDThe action needs a human to approve it and this path cannot ask one — the client does not support elicitation, or the approver is the merchant rather than the buyer. `details.approver` says who. Nothing was changed.A human must decideNoThis call only
NATIVE_CHECKOUT_REQUIREDThis merchant requires the purchase to complete on the platform's own hosted checkout. `details.checkout_url` carries where to send the buyer.A human must decideNoThis merchant (cacheable)
ORDER_NOT_CANCELLABLEThe order has advanced past the point where an agent may cancel it. `details.status` carries the current status.A human must decideNoThis call only
RETURN_WINDOW_EXPIREDThe merchant's return window has closed for this order. Only a human on the merchant side can override it.A human must decideNoThis call only
AGENT_TOKEN_INVALIDThe supplied agent token failed verification. `details.reason` carries the verifier's reason. Re-mint the token; do not retry the same one.Fix the argumentsNoThis call only
CURRENCY_MISMATCHThe requested items do not share a single currency. Split them into one cart per currency.Fix the argumentsNoThis call only
IDENTITY_TOKEN_REJECTEDThe supplied identity token failed verification. `details.reason` carries the reason.Fix the argumentsNoThis call only
IDENTITY_TOKEN_STORE_MISMATCHThe identity token is valid but was issued for a different store. Tokens are not portable across merchants.Fix the argumentsNoThis call only
INSUFFICIENT_INPUTThe call is well-formed but does not carry enough material to act on — e.g. a comparison with fewer than two resolvable products.Fix the argumentsNoThis call only
INVALID_ARGUMENTAn argument is malformed — a non-UUID id, an unparseable product id, or a value outside the accepted range. `details.field` names it when known.Fix the argumentsNoThis call only
MANDATE_LIMIT_EXCEEDEDThe order exceeds a boundary of the payment mandate the caller itself presented — amount, currency, audience or expiry. `details` carries the mandate's own limit and the cart total, because both are already known to the caller. Correct the cart (or present a mandate that covers it) and call again; the identical call cannot succeed.Fix the argumentsNoThis call only
MISSING_REQUIRED_ARGUMENTA required argument was omitted, or a conditionally-required one was omitted for the chosen mode. `details.field` names it.Fix the argumentsNoThis call only
ORDER_NOT_FOUNDNo order matches the given id for this store.Fix the argumentsNoThis call only
PRODUCT_NOT_FOUNDNo product matches the given id in this store. Re-run discovery rather than retrying the id.Fix the argumentsNoThis call only
TOOL_NOT_FOUNDNo tool is registered under that name for this store. The set of tools a store exposes is fixed for the session — re-run discovery rather than guessing another name.Fix the argumentsNoThis merchant (cacheable)
CART_NOT_FOUNDNo cart session exists for that id, or it has expired. Create a new cart; do not retry with the same id.Re-read state, then call againNoThis call only
CART_NOT_MUTABLEThe cart was modified, completed or expired since it was read. Re-read the cart state and reapply the change against the current version.Re-read state, then call againNoThis call only
CHECKOUT_SESSION_NOT_FOUNDNo checkout session exists for that id, or it has expired. Start from cart creation.Re-read state, then call againNoThis call only
CHECKOUT_STATUS_INVALIDThe operation is not legal from the session's current status. `details.status` carries that status; advance the session through the documented transition first.Re-read state, then call againNoThis call only
PRECONDITION_NOT_META prerequisite step has not been completed — no shipping method selected, no shipping options loaded yet. `details.required_step` names the tool to call first; retrying this one without it repeats the same refusal.Re-read state, then call againNoThis call only
SESSION_INACTIVEThe session is no longer active and accepts no further changes.Re-read state, then call againNoThis call only
STATE_RECONFIRMATION_REQUIREDThe merchant's own state moved between the approved preview and this execution, by more than the tolerance but not past the blocking cut. `details.reconfirm_state_hash` is the hash of the state now in force; call again passing it as `reconfirmed_state_hash` to execute against THAT state. A retry without it, or with a stale hash, is refused again.Re-read state, then call againNoThis call only
VERIFICATION_NOT_FOUNDNo attribute verification exists for that reference, or it has expired before being polled. Start a new verification.Re-read state, then call againNoThis call only
CHECKOUT_BLOCKED_BY_POLICYA merchant enforcement rule blocked this checkout. `details.rule_code` names the rule. The block follows from THIS order — a different amount, item set or destination may pass — so retrying the identical checkout will not.Try a different pathNoThis call only
FEATURE_NOT_ENABLEDThe merchant has not enabled this capability. It is a configuration state, not a fault — retrying never flips it.Try a different pathNoThis merchant (cacheable)
NOT_ELIGIBLEThe request is valid and the capability is enabled, but this subject does not qualify under the merchant's rules.Try a different pathNoThis call only
PAYMENT_DECLINEDThe payment instrument was declined by the processor. Nothing was captured. Retrying the same instrument reproduces the decline.Try a different pathNoThis call only
PAYMENT_METHOD_NOT_CONFIGUREDThe requested payment rail is not configured for this store. Call get_payment_methods and pick one that is.Try a different pathNoThis merchant (cacheable)
PLAN_UPGRADE_REQUIREDThe merchant's plan does not include this path. Only the merchant can change that; pick another payment method or tool.Try a different pathNoThis merchant (cacheable)
STATE_EXECUTION_BLOCKEDThe merchant's own state diverged from the approved preview past the point where reconfirming is acceptable — insufficient stock, a price move too large, or a policy version change under a prior approval. `details.reasons` says which. No reconfirmation is offered: a different item set or quantity may pass.Try a different pathNoThis call only
STORE_NOT_CONFIGUREDThis store has not completed the configuration this tool depends on. Retrying will not change that; another store or another tool may work.Try a different pathNoThis merchant (cacheable)
UPSTREAM_RESPONSE_INVALIDA dependency answered, but its response failed schema validation. Retrying reproduces it — the upstream contract is broken, not the connection.Try a different pathNoThis call only
CHECKOUT_ALREADY_IN_PROGRESSA checkout for this session is already running or has completed. Retrying with the SAME idempotency_key is safe and returns the existing order.Retry the same callYesThis call only
PAYMENT_FAILED_ROLLED_BACKThe payment attempt failed and the session was rolled back to READY_FOR_PAYMENT. Nothing was captured; retrying with a fresh payment token is safe.Retry the same callYesThis call only
PAYMENT_NOT_APPROVEDThe payer did not approve the payment. Nothing was captured and the session was rolled back to READY_FOR_PAYMENT — a fresh approval attempt is safe.Retry the same callYesThis call only
CART_ATTRIBUTE_WRITE_FAILEDAn attribute the enforcement layer depends on could not be written to the platform cart, so the step was refused rather than leaving a rule unable to fire. Retry shortly.Retry with backoffYesThis call only
ENFORCEMENT_UNAVAILABLEThe enforcement layer could not be consulted — the signed policy snapshot was unreadable, or the evaluator itself errored. The operation was refused fail-closed rather than proceeding unprotected. Retry shortly.Retry with backoffYesThis call only
INTERNAL_ERRORAn unexpected fault on our side. Retry with backoff; if it persists, report it with the tool name and arguments.Retry with backoffYesThis call only
UPSTREAM_ERRORThe merchant's commerce platform returned an error for this read or write. Retry with backoff; if it persists the merchant's platform is at fault, not the request.Retry with backoffYesThis call only
UPSTREAM_UNAVAILABLEA dependency this tool needs is temporarily unreachable. Retry with backoff.Retry with backoffYesThis call only

Retry Guidance

Not all errors are worth retrying. Follow these rules to build resilient integrations.

Retryable errors

429, 500, 503

Backoff: 1s → 2s → 4s → 8s (max 4 retries)

Non-retryable errors

400, 401, 403, 404, 409, 422

Fix the request before retrying

Recommended backoff strategy

// Exponential backoff — max 4 retries
const RETRYABLE = new Set([429, 500, 503]);

async function callWithRetry(fn: () => Promise<Response>): Promise<Response> {
  let attempt = 0;
  while (attempt <= 4) {
    const res = await fn();
    if (res.ok || !RETRYABLE.has(res.status)) return res;

    if (res.status === 429) {
      const reset = res.headers.get("X-RateLimit-Reset");
      const waitMs = reset ? (Number(reset) * 1000 - Date.now()) : 1000;
      await sleep(Math.max(waitMs, 0));
    } else {
      await sleep(1000 * 2 ** attempt); // 1s → 2s → 4s → 8s
    }
    attempt++;
  }
  return fn();
}

function sleep(ms: number): Promise<void> {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

Back to Developer Docs

Explore authentication, MCP tools, webhooks, and more.

Developer Documentation
API Error Reference | Trusteed Developers