Saltar al contenido
Referencia API

Referencia de Errores

Catálogo completo de errores de la API v1 y MCP Gateway de Trusteed. Úsalo para escribir retry logic preciso y gestionar fallos de forma elegante.

Formato de Respuesta de Error

REST y MCP fallan de forma distinta, y la diferencia importa al escribir la lógica de reintentos. REST devuelve un código HTTP con un sobre JSON; una herramienta MCP devuelve HTTP 200 con isError dentro del resultado.

// REST — GET/POST /api/v1/...
{
  "success": false,
  "error": "Mensaje legible por humanos",
  "code": "MACHINE_CODE",   // presente en algunos endpoints, no en todos
  "details": { ... }        // opcional
}

// Resultado de herramienta MCP — el estado del transporte es 200
{
  "isError": true,
  "content": [{ "type": "text", "text": "Mensaje legible por humanos" }],
  "structuredContent": {
    "error": {
      "code": "CART_NOT_MUTABLE",
      "remedy": "refresh_state",
      "retryable": false,
      "scope": "call",
      "details": { ... },
      "reference": "https://trusteed.xyz/es/developers/errors#CART_NOT_MUTABLE"
    }
  }
}

En REST, error lleva el mensaje y code todavía no es uniforme en todos los endpoints. Decide por el código HTTP. En MCP, structuredContent.error.code es el discriminador estable, y la tabla de abajo se genera desde la misma fuente que emite el servidor.

Errores de Autenticación

Se devuelven cuando falta la API key, es inválida o carece de los permisos necesarios para realizar la acción solicitada.

Código HTTPErrorSignificadoAcción Recomendada
401unauthorizedAPI key ausente o inválidaVerifica que el header X-Agent-Api-Key esté presente y con formato correcto (agnt_xxx)
401token_expiredEl token OAuth ha expiradoRefresca el token con POST /api/v1/oauth/token
403forbiddenLa clave carece del scope necesario para esta acciónRevisa los scopes de la clave en el Dashboard bajo Agent Keys
403store_suspendedEl comercio no está disponible por el momentoElige un merchant diferente del directorio

Errores de Rate Limit

Los rate limits se aplican por clave y por herramienta. Lee el header X-RateLimit-Reset para saber cuándo se renueva la ventana.

Código HTTPErrorSignificadoAcción Recomendada
429rate_limit_exceededLímite de peticiones por clave alcanzadoPausa y reintenta tras el timestamp Unix en X-RateLimit-Reset

Headers de respuesta de rate limit

  • X-RateLimit-LimitMáximo de peticiones permitidas en la ventana actual
  • X-RateLimit-RemainingPeticiones restantes antes de alcanzar el límite
  • X-RateLimit-ResetTimestamp Unix (segundos) en que se renueva la ventana

Errores de Lógica de Negocio

Se devuelven cuando una petición es estructuralmente válida pero falla por el estado de la aplicación (p.ej. carrito expirado, tienda no encontrada).

Código HTTPErrorSignificadoAcción RecomendadaReintentable
404store_not_foundLa tienda no existe o está inactivaVerifica el slug en el directorio de merchantsNo
500internal_errorError de servidor inesperadoReintenta con backoff exponencial (ver Guía de Reintentos abajo)Sí
503service_unavailableSobrecarga temporal o mantenimientoReintenta después de 30 segundosSí

Errores de Herramientas MCP

Cuando una herramienta MCP falla, el transporte sigue devolviendo 200. El fallo va en el resultado. Decide por structuredContent.error.remedy: es un conjunto cerrado, así que puedes cubrirlo por completo. Los códigos marcados como fondos capturados significan que puede existir ya un cargo; reintentar uno de ésos puede cobrar dos veces al comprador. PAYMENT_OUTCOME_UNKNOWN es el otro código con posible cargo: la petición de cobro llegó al procesador y no se sabe qué hizo, así que el checkout queda retenido: para, no reintentes con ninguna clave ni método de pago, y escala.

CódigoQué significaRemedio¿Reintentar?Vale para
HUMAN_CONFIRMATION_DECLINEDA human was asked and declined. Nothing was changed. Re-asking without new information is not acceptable behaviour.Para: no reintentesNoSólo esta llamada
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.Para: no reintentesNoSólo esta llamada
PAYMENT_AMOUNT_MISMATCHPuede haber fondos ya capturadosThe captured amount does not match the amount the session authorised. Funds have moved. Do not retry; this needs reconciliation.Para: no reintentesNoSólo esta llamada
PAYMENT_CAPTURED_FINALIZATION_FAILEDPuede haber fondos ya capturadosThe 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`.Para: no reintentesNoSólo esta llamada
PAYMENT_OUTCOME_UNKNOWNPuede haberse cobrado al compradorThe charge request reached the payment processor and its outcome is unknown: the buyer MAY have been charged. Do not retry with this or any other key or payment method — the checkout session is held until the attempt is reconciled by the merchant. Escalate with the details (`rail`, and `paypal_order_id` when present).Para: no reintentesNoSólo esta llamada
AP2_MERCHANT_AUTHORIZATION_MISSINGThis checkout session is locked to AP2 and the merchant could not sign the checkout (`ap2.merchant_authorization`; UCP error `merchant_authorization_missing`), so no unsigned checkout is returned. Nothing was charged. Retrying reproduces it until the merchant signing key is fixed; report the checkout id.Debe decidirlo una personaNoSólo esta llamada
AP2_STATE_UNREADABLEThe AP2 state stored on this checkout session could not be read, so the session was not completed (fail-closed) and nothing was charged. Retrying reproduces it; report the checkout id.Debe decidirlo una personaNoSólo esta llamada
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.Debe decidirlo una personaNoSólo esta llamada
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.Debe decidirlo una personaNoEste comercio (cacheable)
ORDER_NOT_CANCELLABLEThe order has advanced past the point where an agent may cancel it. `details.status` carries the current status.Debe decidirlo una personaNoSólo esta llamada
RECEIPT_ISSUANCE_OVERDUEThe purchase completed but its signed receipt did not arrive within the expected window. Do not retry; escalate with the checkout session id.Debe decidirlo una personaNoSólo esta llamada
RETURN_WINDOW_EXPIREDThe merchant's return window has closed for this order. Only a human on the merchant side can override it.Debe decidirlo una personaNoSólo esta llamada
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.Corrige los argumentosNoSólo esta llamada
AP2_INVALID_PLATFORM_PROFILE`meta['ucp-agent'].profile` is not an https URL without credentials (max 2048 characters). Nothing was changed. Send a valid platform profile URL or omit `meta`.Corrige los argumentosNoSólo esta llamada
AP2_MANDATE_REJECTEDAn AP2 mandate presented to `ucp_complete_checkout` did not verify or is not the one that settled the session; this call charges nothing and changes nothing (on a session that was already completed, that settlement happened in an earlier call). `details.ucp_code` carries the normative UCP error (`agent_missing_key`, `mandate_invalid_signature`, `mandate_expired`, `mandate_scope_mismatch`, `merchant_authorization_invalid`, `merchant_authorization_missing`), `details.reason` a stable identifier and `details.next_step` what to do: for `mandate_scope_mismatch` read the checkout again with `ucp_get_checkout` and have the user sign new mandates over exactly that checkout; for `mandate_expired` sign again.Corrige los argumentosNoSólo esta llamada
CURRENCY_MISMATCHThe requested items do not share a single currency. Split them into one cart per currency.Corrige los argumentosNoSólo esta llamada
IDENTITY_TOKEN_REJECTEDThe supplied identity token failed verification. `details.reason` carries the reason.Corrige los argumentosNoSólo esta llamada
IDENTITY_TOKEN_STORE_MISMATCHThe identity token is valid but was issued for a different store. Tokens are not portable across merchants.Corrige los argumentosNoSólo esta llamada
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.Corrige los argumentosNoSólo esta llamada
INSUFFICIENT_STOCKThe store catalog reports fewer units available than the line item asks for. `details` carries `requested_quantity`, `available_quantity` and `source` — the latter says which reading was compared (`catalog_snapshot` is the replicated catalog, which may lag the platform, not a live inventory call). Reduce the quantity; retrying the identical call will not change the answer.Corrige los argumentosNoSólo esta llamada
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.Corrige los argumentosNoSólo esta llamada
MANDATE_AUDIENCE_MISMATCHThe payment mandate was issued for a different store: its `aud` does not name this one. Mandates are not portable across merchants. The cart is not the reason — present a mandate whose `aud` is this store.Corrige los argumentosNoSólo esta llamada
MANDATE_CURRENCY_MISMATCHThe payment mandate authorises spending in a currency other than the one this cart is priced in, so its `max_amount_cents` cannot be compared against the cart total at all. The cart is not the reason — present a mandate denominated in the cart's currency. `details.currency` is the MANDATE's currency.Corrige los argumentosNoSólo esta llamada
MANDATE_EXPIREDThe payment mandate presented had already expired (`exp` in the past). The cart is not the reason and reducing it changes nothing — present a mandate with a later `exp`. `details.remediation` is `renew_mandate`.Corrige los argumentosNoSólo esta llamada
MANDATE_LIMIT_EXCEEDEDThe cart total exceeds the `max_amount_cents` of the payment mandate the caller itself presented. `details` carries that limit and the cart total, because both are already known to the caller. Reduce the cart (or present a mandate that covers it); the identical call cannot succeed. The mandate's other boundaries have their own codes — `MANDATE_EXPIRED`, `MANDATE_AUDIENCE_MISMATCH`, `MANDATE_CURRENCY_MISMATCH`.Corrige los argumentosNoSólo esta llamada
MANDATE_SUBJECT_MISMATCHThe payment mandate's `sub` is not the identity this call authenticated as. A mandate is a spending cap the caller declares for itself, so `sub` must name the caller exactly as the server identifies it; `details.expected_sub` carries that value. The cart is not the reason — present the same mandate with `sub` set to `details.expected_sub`.Corrige los argumentosNoSólo esta llamada
MISSING_REQUIRED_ARGUMENTA required argument was omitted, or a conditionally-required one was omitted for the chosen mode. `details.field` names it.Corrige los argumentosNoSólo esta llamada
ORDER_NOT_FOUNDNo order matches the given id for this store.Corrige los argumentosNoSólo esta llamada
PAYMENT_METHOD_REQUIREDThis store requires the buyer's chosen payment method before completing the purchase. Ask the person which option they want from `details.payment_options`, then call again with `payment_option` set to their choice. Do not choose for them.Corrige los argumentosNoSólo esta llamada
PAYMENT_OPTION_MISMATCHThis checkout already has a saved `payment_option` from a previous call and this call asked for a different one. Retry with the saved option (`details.approved_option`), or start a new checkout if the person wants a different method.Corrige los argumentosNoSólo esta llamada
PAYMENT_OPTION_UNAVAILABLEThe requested `payment_option` is not available for this store. Ask the person to choose one of the available options in `details.payment_options`; this option will not become available on retry.Corrige los argumentosNoEste comercio (cacheable)
PAYMENT_REQUIREDNothing was charged: the checkout needs a signed payment. `details.x402_offer` carries the server-computed offer (network, payTo, asset, atomic amount). Sign exactly that and call again with `x402_payload`.Corrige los argumentosNoSólo esta llamada
PRODUCT_NOT_FOUNDNo product matches the given id in this store. Re-run discovery rather than retrying the id.Corrige los argumentosNoSólo esta llamada
QUANTITY_LIMIT_EXCEEDEDA line item asks for more units than a single cart line accepts. `details` carries `requested_quantity` and `maximum_quantity`, and `details.field` names the line. Split the order or reduce the quantity; the identical call cannot succeed. This is the protocol's structural ceiling — a merchant rule capping quantities blocks earlier and reports as `CHECKOUT_BLOCKED_BY_POLICY`.Corrige los argumentosNoSólo esta llamada
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.Corrige los argumentosNoEste comercio (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.Relee el estado y vuelve a llamarNoSólo esta llamada
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.Relee el estado y vuelve a llamarNoSólo esta llamada
CHECKOUT_SESSION_NOT_FOUNDNo checkout session exists for that id, or it has expired. Start from cart creation.Relee el estado y vuelve a llamarNoSólo esta llamada
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.Relee el estado y vuelve a llamarNoSólo esta llamada
CHECKOUT_TOTAL_NOT_RESOLVABLEThe session carries no readable total, so there is no amount to charge. This is NOT a free purchase: a missing total used to read as zero and skip the human-approval gate, completing an order that nobody paid. Re-price the cart (select shipping, apply discounts) so the session carries a `total` entry with `amount_cents`, then call again.Relee el estado y vuelve a llamarNoSólo esta llamada
MERCHANT_CATALOG_STALEThe merchant's catalogue has not been synchronised recently enough to verify the approved price against it, so the checkout was refused before any charge. `details` carries `age_seconds` and `max_age_seconds`. Nothing was charged. Retrying now will not help: the merchant must restore catalogue synchronisation first; once it is fresh, the same idempotency key may be reused.Relee el estado y vuelve a llamarNoEste comercio (cacheable)
OPEN_CART_LIMIT_EXCEEDEDThe agent already holds the maximum number of open carts in this store. `details` carries `open_carts`, `max_open_carts` and `retry_after_seconds` (until the oldest open cart expires). Complete or cancel an open cart, resume one with its `agent_session_id`, or wait; the identical call cannot succeed until a slot frees up.Relee el estado y vuelve a llamarNoSólo esta llamada
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.Relee el estado y vuelve a llamarNoSólo esta llamada
SESSION_INACTIVEThe session is no longer active and accepts no further changes.Relee el estado y vuelve a llamarNoSólo esta llamada
STATE_PREVIEW_REQUIREDThis checkout could not proceed against a reconfirmed merchant state: the session could not be re-priced to it, its total changed after it was evaluated, or the reconfirmation no longer holds. `details.reason` says which. Nothing was charged. Call preview_checkout again to approve the current state, then complete_checkout — the same idempotency key may be reused.Relee el estado y vuelve a llamarNoSólo esta llamada
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.Relee el estado y vuelve a llamarNoSólo esta llamada
VERIFICATION_NOT_FOUNDNo attribute verification exists for that reference, or it has expired before being polled. Start a new verification.Relee el estado y vuelve a llamarNoSólo esta llamada
AP2_MANDATE_REQUIREDThis checkout session is locked to AP2 (UCP `mandate_required`): `ucp_complete_checkout` needs `ap2.checkout_mandate` and the Payment Mandate in `payment.instruments[].credential.token`, and at least one is missing (`details.missing`). Nothing was charged.Prueba otra víaNoSólo esta llamada
AP2_MANDATES_NOT_SUPPORTEDAP2 mandates (`ap2.checkout_mandate`) cannot be used on this checkout (AP2 `mandates_not_supported`): the session was not negotiated with AP2, this endpoint cannot verify AP2 mandates, or this store has no settlement rail for them (AP2 settles only through the test payment options: card or x402 on Base Sepolia). Nothing was charged. Complete the checkout without mandates, or create a new session announcing a platform profile that advertises `dev.ucp.common.payment.ap2_mandate`.Prueba otra víaNoSólo esta llamada
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.Prueba otra víaNoSólo esta llamada
FEATURE_NOT_ENABLEDThe merchant has not enabled this capability. It is a configuration state, not a fault — retrying never flips it.Prueba otra víaNoEste comercio (cacheable)
NOT_ELIGIBLEThe request is valid and the capability is enabled, but this subject does not qualify under the merchant's rules.Prueba otra víaNoSólo esta llamada
PAYMENT_DECLINEDThe payment instrument was declined by the processor. Nothing was captured. Retrying the same instrument reproduces the decline.Prueba otra víaNoSólo esta llamada
PAYMENT_METHOD_NOT_CONFIGUREDThe requested payment rail is not configured for this store. Call get_payment_methods and pick one that is.Prueba otra víaNoEste comercio (cacheable)
PLAN_UPGRADE_REQUIREDThe merchant's plan does not include this path. Only the merchant can change that; pick another payment method or tool.Prueba otra víaNoEste comercio (cacheable)
SHIPPING_COUNTRY_UNSUPPORTEDThe merchant's enforced rules restrict shipping to a list of countries and the requested destination is not on it, so no rate is offered — an estimated rate towards an unserved country reads as usable and is not. `details` carries `requested_country`, `allowed_countries` and `derived_from` (the rules the list comes from). Retry with a destination on the list; nothing about the cart was changed.Prueba otra víaNoSólo esta llamada
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.Prueba otra víaNoSólo esta llamada
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.Prueba otra víaNoEste comercio (cacheable)
TOOL_IN_OTHER_BUCKETThe tool exists for this store but is served by a different MCP bucket endpoint than the one this session is connected to. The name is right; call it on the bucket named in the message. Its schema is not introspectable from here.Prueba otra víaNoEste comercio (cacheable)
UPSTREAM_RESPONSE_INVALIDA dependency answered, but its response failed schema validation. Retrying reproduces it — the upstream contract is broken, not the connection.Prueba otra víaNoSólo esta llamada
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.Reintenta la misma llamadaSíSólo esta llamada
PAYMENT_FAILED_ROLLED_BACKThe payment attempt failed BEFORE any charge was made — the processor was never asked to charge, or it rejected the request — and the session was rolled back to READY_FOR_PAYMENT. Nothing was captured; retrying (with a fresh payment token for KYApay) is safe.Reintenta la misma llamadaSíSólo esta llamada
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.Reintenta la misma llamadaSíSólo esta llamada
AP2_LOCK_DECISION_FAILEDWhether this checkout requires an AP2 mandate could not be decided, so no session was created (fail-closed). Retry shortly.Reintenta con espera crecienteSíSólo esta llamada
AP2_MANDATE_VERIFICATION_UNAVAILABLEThe AP2 mandates could not be checked right now (the platform profile that holds the trust anchor could not be fetched). Nothing was charged. Retry shortly with the same mandates.Reintenta con espera crecienteSíSólo esta llamada
AP2_MERCHANT_AUTHORIZATION_UNAVAILABLEThis checkout session is locked to AP2 and its response could not be signed right now because the session changed while it was being signed (UCP error `merchant_authorization_missing`, transient). No unsigned checkout is returned and nothing was charged. Read the checkout again with `ucp_get_checkout`.Reintenta con espera crecienteSíSólo esta llamada
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.Reintenta con espera crecienteSíSólo esta llamada
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.Reintenta con espera crecienteSíSólo esta llamada
INTERNAL_ERRORAn unexpected fault on our side. Retry with backoff; if it persists, report it with the tool name and arguments.Reintenta con espera crecienteSíSólo esta llamada
RECEIPT_NOT_ISSUEDNo receipt for this session yet. If complete_checkout was called seconds ago, its receipt may still be queued: fetch again shortly. If it was not called, there is nothing to fetch yet; do not create a new cart.Reintenta con espera crecienteSíSólo esta llamada
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.Reintenta con espera crecienteSíSólo esta llamada
UPSTREAM_UNAVAILABLEA dependency this tool needs is temporarily unreachable. Retry with backoff.Reintenta con espera crecienteSíSólo esta llamada

Guía de Reintentos

No todos los errores vale la pena reintentar. Sigue estas reglas para construir integraciones resilientes.

Errores reintentables

429, 500, 503

Backoff: 1s → 2s → 4s → 8s (máx. 4 reintentos)

Errores no reintentables

400, 401, 403, 404, 409, 422

Corrige la petición antes de reintentar

Estrategia de backoff recomendada

// Backoff exponencial: máximo 4 reintentos
const REINTENTABLES = new Set([429, 500, 503]);

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

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

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

Volver a la Documentación

Explora autenticación, herramientas MCP, webhooks y más.

Documentación para Desarrolladores
Referencia de Errores API | Desarrolladores Trusteed