{
  "$comment": "The reason-code vocabulary, by step of the seller's twelve-step sequence (architecture v0.2 §5.6, §8). `status` is the HTTP status the seller answers with; `entry` says whether a signed request.refused entry is written (check 1 never writes; it counts). `seller` says whether agent-bench raises the code today on the signed-envelope path; the self-issued authorization path (demo scripts) keeps its pre-v0.2 codes, marked `legacy`. Buyer-side codes are raised by the buyer against the seller and appear in the buyer's ledger.",
  "steps": {
    "1": {
      "name": "size, content type, method, protocol version, rate",
      "entry": false,
      "codes": {
        "request_too_large": {
          "status": 413,
          "seller": true,
          "note": "Also 400 on the UCP face for a catalog lookup naming more than 20 distinct ids (Catalog Lookup, Batch Size), -32602 over MCP (ucp-profile.md §8)."
        },
        "request_invalid": {
          "status": 415,
          "seller": true,
          "note": "Also 400 for a body that is not JSON, dropped at the entrypoint, 400 for a malformed correlation_id or one that differs between body and envelope, and 400 for a malformed exercise marker (the caller's test-vector claim; see ledger-entry.schema.json, claimed_exercise)."
        },
        "method_not_allowed": {
          "status": 405,
          "seller": true
        },
        "protocol_unsupported": {
          "status": 400,
          "seller": true,
          "note": "MCP-Protocol-Version the server does not speak."
        },
        "rate_limited": {
          "status": 429,
          "seller": false,
          "note": "A Cloudflare edge rule, so it never reaches the Durable Object. Not yet created."
        }
      }
    },
    "2": {
      "name": "request envelope: signature, typ, nonce, timestamp within 60 s, replay cache",
      "entry": true,
      "codes": {
        "signature_invalid": {
          "status": 401,
          "seller": true,
          "note": "Not a compact JWS; body edited after signing (R-07, body_sha256); method or path differ; nonce too short; signature fails under the attested key."
        },
        "header_forbidden": {
          "status": 401,
          "seller": true,
          "note": "alg other than ES256; jku, x5u, jwk, x5c (outside the key attestation) or crit present. R-21, R-22, R-23."
        },
        "typ_mismatch": {
          "status": 401,
          "seller": true
        },
        "audience_mismatch": {
          "status": 401,
          "seller": true,
          "note": "The envelope's aud is not this seller (also raised at check 5 for the mandate, 403)."
        },
        "nonce_replayed": {
          "status": 401,
          "seller": true,
          "note": "Per signer: a nonce is spent by the key that signed it."
        },
        "timestamp_out_of_window": {
          "status": 401,
          "seller": true
        }
      }
    },
    "3": {
      "name": "buyer identity: key attestation, SVID chain, bundle, SPIFFE ID, optional ANS status",
      "entry": true,
      "codes": {
        "signer_unknown": {
          "status": 401,
          "seller": true,
          "note": "No valid attestation at https://<trust domain>/.well-known/agent-keys.json names the envelope's kid, or it names it for another SPIFFE ID, or the trust domain is not on BUYER_TRUST_DOMAINS when that is set, or the buyer's documents cannot be fetched. Where the seller takes guests (guest-buyer.md, since 2026-09-18) the kid is looked for first in the TXT record set at _agent-keys.<trust domain>: a record that names it from a zone the resolver did not validate (no AD) is refused with this code and the sentence that says to sign the zone, and a kid found in neither form gets a message that names both."
        },
        "svid_invalid": {
          "status": 401,
          "seller": true,
          "note": "Raised by the seller's own SVID verifier (verify:ledger, watcher); the buyer's SVID is verified through the attestation."
        },
        "attestation_invalid": {
          "status": 401,
          "seller": true,
          "note": "Raised while resolving keys: bad signer (no kid, no x5c), chain does not reach a CA in the bundle, leaf names another SPIFFE ID, expired, sub is not the thumbprint of cnf.jwk. Surfaces to the caller as signer_unknown; the detail is in the seller's logs, not in the refusal."
        },
        "identity_revoked": {
          "status": 403,
          "seller": true,
          "note": "The envelope's kid is listed in the key-revocation+jwt the buyer's domain publishes as `revocations` in /.well-known/agent-keys.json (schemas/key-revocation.schema.json), verified under the bundle like an attestation. Raised after the envelope signature verified under the attested key, so the entry names the revoked instance (buyer, buyer_kid) and counts against its budget as an attested source. Read at the same fetch as the attestations, cached a minute per domain (F-07b)."
        },
        "ans_not_active": {
          "status": 403,
          "seller": true,
          "note": "Since 2026-09-16 (D2): the buyer's trust domain has an `_ans` TXT (it registered in the ANS) and the transparency-log entry its `_ans-badge` names is not ACTIVE, or the badge is missing or the entry carries no Merkle proof. Read once per trust domain per cache fill, beside the attestations; raised after the envelope signature verified, so the entry names the instance. A domain with no `_ans` TXT is not judged; a resolver or log that cannot be read is not consulted and never refuses (the identity anchor is the domain's own DNSSEC and DANE, the registry an opinion)."
        }
      }
    },
    "4": {
      "name": "mandate issuer on the allow-list, JWKS by domain, signature",
      "entry": true,
      "codes": {
        "issuer_not_allowed": {
          "status": 403,
          "seller": true,
          "note": "In the AP2 v0.2 form: the root's kid is in no trusted authority's key set, or the open content's iss is not the authority whose key verified. Where the seller takes guests (guest-buyer.md): an issuer that is neither listed nor the guest's own, https://<trust domain of the envelope> with its key in the DNS TXT record on the A2A face, the platform's origin with its key in the UCP profile on the UCP face, or a guest issuer whose _agent-keys record set came from a zone the resolver did not validate; the message says what a guest mandate needs. The guest form exists for mandate_jws only on the A2A face, where mandate_ap2 from an unlisted issuer is refused as before, and for the AP2 chains only on the UCP face."
        },
        "mandate_invalid": {
          "status": 403,
          "seller": true,
          "note": "Not a mandate+jwt; kid not in the issuer's key set (after one refresh); no sub; no act.sub; a delegation chain (R-12, act.act); action other than purchase; stage other than cart/payment at checkout; no exp; jti too short; unknown to the authority (check 10). In the AP2 v0.2 form (mandate_ap2, since 2026-09-13): not a root plus one terminal kb+sd-jwt hop (an open root alone, a third token, an intermediate kb+sd-jwt+kb hop, a KB-JWT-bound presentation); a disclosure nothing references, referenced twice, malformed, or an _sd_alg other than sha-256; a hop whose sd_hash is not this root's; cnf in the terminal hop; the two roots disagreeing on iss, sub, jti, policy_version, iat, exp, cnf or act; act.act; vct not the profile's. In the AP2 v0.2 form a root typ outside {example+sd-jwt, dc+sd-jwt, vc+sd-jwt} or a hop typ other than kb+sd-jwt is typ_mismatch at step 4."
        },
        "signature_invalid": {
          "status": 403,
          "seller": true,
          "note": "The mandate does not verify under the key its kid names."
        },
        "mandate_missing": {
          "status": 403,
          "seller": true,
          "note": "A signed envelope checking out without mandate_jws. Also the OAuth path: authenticated subject with no mandate granted."
        },
        "authority_unavailable": {
          "status": 503,
          "seller": true,
          "note": "The issuer's key set cannot be read."
        },
        "authorization_missing": {
          "status": 401,
          "seller": "legacy",
          "note": "Self-issued path: no bearer at all."
        },
        "authorization_invalid": {
          "status": 401,
          "seller": "legacy",
          "note": "Self-issued path: not a JWS, no kid, kid not in the key set, kid not the key's thumbprint."
        },
        "authorization_denied": {
          "status": 403,
          "seller": "legacy",
          "note": "Self-issued path: wrong issuer, no subject, missing scope, missing grant, ceiling below the amount."
        }
      }
    },
    "5": {
      "name": "audience",
      "entry": true,
      "codes": {
        "audience_mismatch": {
          "status": 403,
          "seller": true,
          "note": "In the AP2 v0.2 form: the hop's aud is not this seller's base URL."
        }
      }
    },
    "6": {
      "name": "merchant, action, stage, currency, amount equal to the stored quote total",
      "entry": true,
      "codes": {
        "merchant_mismatch": {
          "status": 403,
          "seller": true,
          "note": "In the AP2 v0.2 form: checkout.allowed_merchants does not carry our merchant id, the payee (open preset or closed) is not us, or checkout.line_items does not accept the quote's offer at quantity 1."
        },
        "currency_mismatch": {
          "status": 403,
          "seller": true
        },
        "amount_mismatch": {
          "status": 403,
          "seller": true,
          "note": "At the cart stage the amount must EQUAL the quote total. On the self-issued path the ceiling only has to cover it, and the refusal is authorization_denied. In the AP2 v0.2 form: the open payment mandate's preset payment_amount, or the closed one, differs from the quote total."
        },
        "guest_ceiling_exceeded": {
          "status": 403,
          "seller": true,
          "note": "A guest mandate (guest-buyer.md) for more than GUEST_CEILING_MINOR, 100000 minor units unless set. Raised after the issuer's signature held and the claims every mandate is held to passed, so the entry names the person hash, the buyer and the jti. Above the ceiling the mandate has to come from a listed authority."
        },
        "mandate_invalid": {
          "status": 403,
          "seller": true,
          "note": "action other than purchase (R-09), or a stage that is not cart/payment. In the AP2 v0.2 form: a constraint type this seller does not evaluate (the AP2 receipt says unresolved_constraint), no line item requirement or more than one, no payment instrument, no payment.reference."
        },
        "mandate_denied": {
          "status": 403,
          "seller": "legacy",
          "note": "OAuth path: mandate does not cover merchant, currency or amount."
        },
        "checkout_invalid": {
          "status": 400,
          "seller": true,
          "note": "Checkout body without the configured merchant_id, currency and a quote_id."
        }
      }
    },
    "7": {
      "name": "quote hash",
      "entry": true,
      "codes": {
        "quote_hash_mismatch": {
          "status": 409,
          "seller": true,
          "note": "The mandate's quote_hash, or a stated quote_hash in the body, differs from the seller's recomputation over the stored JWS (R-08). In the AP2 v0.2 form: the closed checkout_jwt differs from the stored quote JWS byte for byte, or checkout_hash / the payment's transaction_id is not the base64url SHA-256 of that JWS. A payment.reference naming another checkout root is mandate_invalid at this step."
        },
        "quote_unavailable": {
          "status": 409,
          "seller": true,
          "note": "Unknown or expired quote id, or a stored quote whose JWS no longer decodes."
        }
      }
    },
    "8": {
      "name": "time window (60 s skew)",
      "entry": true,
      "codes": {
        "mandate_not_yet_valid": {
          "status": 403,
          "seller": true
        },
        "mandate_expired": {
          "status": 403,
          "seller": true,
          "note": "In the AP2 v0.2 form the window is the open content's iat/exp; a hop iat outside it or not within the skew of now is mandate_invalid at this step."
        }
      }
    },
    "9": {
      "name": "single use (jti) and key binding equals the request signer",
      "entry": true,
      "codes": {
        "mandate_consumed": {
          "status": 409,
          "seller": true,
          "note": "Cites the first use: request_id, correlation_id, and once settled the payment_intent_id and ledger entry hash. R-06, R-26. In the AP2 v0.2 form the two roots share one jti, claimed once for the pair. A jti is spent under its issuer (since 2026-09-25): a guest's under its origin, so the same jti from a listed authority, or from another guest, is another mandate and is not refused for it. The detail says the next step (since 2026-09-25): a mandate buys once, so a new quote needs a new mandate, and a new quote under the same mandate is refused the same way."
        },
        "key_binding_mismatch": {
          "status": 403,
          "seller": true,
          "note": "cnf.jkt differs from the envelope signer's kid, or the mandate carries no cnf at all (R-05: both are a binding that fails at check 9, never mandate_invalid at check 4; settled 2026-09-12); act.sub differs from the envelope's iss; the quote was issued to another buyer key; on the self-issued path, another subject than the quote's. In the AP2 v0.2 form: the hop is not signed by the root's cnf.jwk, that key's thumbprint is not the envelope signer's kid, act.sub is not the envelope's buyer, or the root names no key. A hop answering another nonce than the quote's jti is mandate_invalid at this step."
        }
      }
    },
    "10": {
      "name": "revocation status at the authority, fail closed",
      "entry": true,
      "codes": {
        "mandate_revoked": {
          "status": 403,
          "seller": true,
          "note": "GET <issuer>/mandates/<jti> answered status revoked. The claim on the jti is released: nothing was spent. For a guest mandate (guest-buyer.md) there is no status to ask: the seller reads the guest's keys again, the DNS TXT record on the A2A face (_agent-keys.<domain>, uncached on its side), the UCP profile on the UCP face (under its 60-second floor), and refuses with this code when a key that signed the mandate is no longer published there (revoked_at null: neither says when). It takes effect within the record's TTL at the resolver, or within a minute for a profile."
        },
        "authority_unavailable": {
          "status": 503,
          "seller": true,
          "note": "No answer after one retry, a non-2xx other than 404, or a status the seller does not act on. Fail closed; the claim is released; retryable (R-18). For a guest mandate, at the fresh read of check 10: the resolver could not be read, or answered without AD, for the DNS TXT record on the A2A face; the UCP profile could not be read on the UCP face."
        }
      }
    },
    "11": {
      "name": "the payment adapter",
      "entry": true,
      "codes": {
        "stripe_unavailable": {
          "status": 502,
          "seller": true,
          "note": "Authentication or authorization failure at the rail: a misconfigured merchant."
        },
        "stripe_invalid": {
          "status": 502,
          "seller": true,
          "note": "A definite 4xx or an incomplete answer: nothing created, mandate released."
        },
        "payment_unknown": {
          "status": 502,
          "seller": true,
          "note": "NOT a refusal: writes payment.ambiguous, leaves the mandate retryable with the same idempotency key. The buyer moves to REVIEW_REQUIRED."
        }
      }
    }
  },
  "buyer_side": {
    "$comment": "Raised by agent-buyer (since 2026-09-09) in its own ledger. It also records seller-side and JWS words when it runs the same check itself: quote_invalid, authorization_missing and the JWS policy codes in request.refused; mandate_invalid and key_binding_mismatch in mandate.denied, when a mandate from the authority fails the buyer's own verification. Webhook counters with no reply and no entry: body_invalid, signature_missing, signature_invalid (X-Hub-Signature-256), verification_refused (subscribe handshake).",
    "dnssec_unvalidated": "The resolver did not validate the chain.",
    "dane_mismatch": "The TLSA on the connected host does not match the served certificate (strict, both seller hosts).",
    "record_signature_invalid": "The record's sig SvcParam does not verify against the zone's key set, or has lapsed.",
    "card_digest_mismatch": "The fetched card bytes do not hash to cap-sha256.",
    "endpoint_out_of_zone": "A validly signed card declares an endpoint outside the agent's zone (R-27). POC profile rule, stricter than DNS-AID, ARD and A2A.",
    "ans_not_active": "The registry entry is not ACTIVE in the window. Not raised yet: the buyer does not check ANS (optional, architecture §5.3).",
    "sender_unknown": "WhatsApp message from a number not on the allow-list: no reply, a counter, no entry (R-25).",
    "verification_failed": "A check of the seller failed after discovery; the buyer writes counterparty.verified with ok false and sends no request (added 2026-09-10, buyer state machine).",
    "quote_unavailable": "The seller answered the search with no option; nothing to bind, REJECTED.",
    "authority_unavailable": "The mandate authority could not be reached, or answered nothing usable, at any of its endpoints. Fail closed: no checkout, mandate.denied with this reason (R-18).",
    "mandate_denied": "The authority issued no mandate: the person denied, the standing ceiling does not cover it, the approval link expired. facts.authority_reason carries the authority's own word; mandate.denied, REJECTED.",
    "settlement_invalid": "The seller's checkout answer accounts for the money in a way the contract does not: a rail other than simulation or stripe-test, a charge on the simulation rail, a capture on stripe-test with charged false, or no word on the charge (ledger-entry.schema.json, settlement). The receipt is not sealed; the purchase waits in review (added 2026-09-09; the stripe-test capture, charged true in test mode, allowed since 2026-09-12).",
    "response_invalid": "The seller's answer to the checkout could not be read after it was sent; the outcome is unknown, so REVIEW_REQUIRED and payment.ambiguous, never REJECTED.",
    "dns_unavailable": "The resolver answered an error, or nothing, for a record discovery needs.",
    "record_missing": "No SVCB record at the agent's name.",
    "fetch_failed": "A pinned HTTPS fetch (card, key set) answered other than 200.",
    "correlation_mismatch": "The seller did not echo the buyer's correlation id, or a signed quote carries another.",
    "seller_error": "The seller refused with no code the buyer can read.",
    "capabilities_incompatible": "The seller's UCP profile and this buyer's have no usable intersection: the checkout and the AP2 Mandates extension at the release this buyer speaks are not both there, or a session opened requires_escalation (the seller would hand the person to continue_url). Read at discovery, before the person is asked anything (ucp_client.py, 2026-09-24).",
    "ucp_key_mismatch": "The seller's UCP profile publishes no key equal to the instance key its SVID vouches for: the party that would sign UCP responses and the merchant authorization is not the one the record led to.",
    "payment_handler_unsupported": "The seller's UCP profile declares no payment handler that takes a card instrument whose credential is the closed AP2 Payment Mandate; this buyer pays with nothing else.",
    "issuer_not_accepted": "The seller lists, under its own namespace, the mandate authorities it accepts, and none of this buyer's is among them: a mandate would be refused at check 4, so nobody is asked to approve one.",
    "transport_unsupported": "The seller's UCP profile declares no rest or no mcp endpoint for the shopping service; this buyer opens the session over rest and reads the catalog over mcp.",
    "seller_unreachable": "The seller did not answer the request that opens a checkout session; nothing was sent that could move money.",
    "no_offer_on_airlines": "Since 2026-09-26: the person allowed only some airlines for the purchase and the seller has no itinerary on them for the route and date (its search answered with no option and a note). request.refused on search_flights; no mandate is asked for.",
    "airlines_not_honored": "Since 2026-09-26: the person allowed only some airlines and a quote the seller returned does not keep to them: it does not sign the same airlines list, or a flight of its itinerary is on another airline (the designator that opens the flight number). request.refused on search_flights; no mandate is asked for."
  },
  "authority_side": {
    "$comment": "Raised by the mandate authority (authority-api.md). Except request_invalid, the ten codes of \"Who may ask\" (signature_required to agent_not_linked, which refuse before a request exists: HTTP 401 or 403, no request, no entry), assistant_unknown (\"Which assistant asked\", the same: HTTP 403, no request, no entry), the four codes of \"Who approves\" (passkey_required, passkey_unknown, passkey_invalid, invite_expired, which decide nothing and write no entry), the three codes of \"Standing rules\" (rules_off, rule_invalid, rule_limit, which create no rule and write no entry), link_used, admin_denied, not_found, method_not_allowed and internal_error, each is the reason of a denied or expired request document and of a mandate.denied entry in the authority's ledger; the buyer copies it into facts.authority_reason and never turns it into a check of its own.",
    "request_invalid": "POST /mandate-requests: malformed body (not JSON, over 65536 bytes, a field missing or not a string, a phone-shaped sub, act_sub not spiffe://, cnf_jkt not a thumbprint, correlation_id not a UUID). HTTP 400; no request, no entry.",
    "signature_required": "POST /mandate-requests without an envelope, under requester: require, or for a person with linked agents. HTTP 401; no request, no entry.",
    "signature_invalid": "The envelope is not a request+jwt under the header policy, carries no usable nonce, binds another method, path or body, or does not verify under the attested key its kid names. HTTP 401.",
    "timestamp_out_of_window": "The envelope's iat is more than 60 seconds from the authority's clock. HTTP 401.",
    "audience_mismatch": "The envelope's aud is not this authority's issuer. HTTP 401.",
    "signer_unknown": "The envelope's iss is not a SPIFFE ID under a public DNS name, the buyer's documents could not be read, no valid attestation of that domain names the kid for that instance, or the domain is not on the authority's allow-list. HTTP 401.",
    "attestation_invalid": "A key attestation that does not verify under the buyer's bundle (x5c chain or jwt-svid key), has expired, or vouches for another key than its sub names. Raised when reading one attestation; a request is refused with signer_unknown when no good one names the kid.",
    "identity_revoked": "The key that signed the envelope was withdrawn by its own domain (key-revocation+jwt). HTTP 403, after the signature held.",
    "nonce_replayed": "This envelope was already presented by this key in the last 120 seconds. HTTP 401.",
    "requester_mismatch": "The body's act_sub or cnf_jkt is not the instance and key that signed the envelope: an agent asks for a mandate bound to itself. HTTP 403.",
    "agent_not_linked": "The person has linked agents and the one that signed the envelope is not among them. HTTP 403.",
    "assistant_unknown": "POST /mandate-requests names the assistant it came through (assistant_token, authority-api.md v0.8) and the token does not hold: not an access token this authority signed, for another person, for a resource that is not the asking buyer's, without the purchase scope, expired more than ten minutes ago, its grant revoked, or presented while oauth is off. HTTP 403; no request, no entry.",
    "subject_unknown": "The sub is not in the authority's registry.",
    "seller_unknown": "The seller's domain (quote iss) publishes no usable spiffe-svid.json and spiffe-bundle.json, or they do not vouch for the quote's kid.",
    "quote_invalid": "Not a quote+jwt under the header policy, required fields missing, the signature does not verify under the seller's instance key, or the quote's correlation_id is not the request's.",
    "quote_expired": "The quote's exp plus 60 s skew is past.",
    "seller_not_allowed": "The authority is configured with a seller allow-list and the quote's iss is not on it.",
    "currency_not_allowed": "The quote's currency is not the currency of the person's ceiling.",
    "ceiling_exceeded": "The quote's total is above the person's standing ceiling.",
    "allowance_exceeded": "The purchase would pass the day's allowance whoever runs this authority set for the person (authority-api.md v0.14, The day's allowance): what was approved for them since the start of the calendar day, plus this total, is above it. HTTP 200, status denied; also at the moment of issuing.",
    "approval_undeliverable": "The notifier could not deliver the approval link to the destination on record.",
    "person_denied": "The person pressed Deny on the approval page.",
    "approval_expired": "The approval link's validity (600 s by default) passed with no decision, and the request is expired; or a revoke came after the cart mandate's exp plus 60 s (HTTP 410 on the page).",
    "link_used": "A second decision on a used approval link, or a passkey invitation already spent. HTTP 410; no entry.",
    "passkey_required": "A form approve on the approval page where the approval takes the person's passkey (authority-api.md, Who approves, since v0.3). HTTP 403, answered as HTML; nothing decided, no entry.",
    "passkey_unknown": "The credential posted with a passkey approval is not one enrolled for the person the request is for (another person's and a never-seen one answer alike). HTTP 403; nothing decided, no entry.",
    "passkey_invalid": "A WebAuthn check failed on a passkey approval or enrollment: client data (type, challenge, origin, framing), authenticator data (RP ID hash, user present, user verified, backup flags), signature, counter, or at enrollment the algorithm or the credential id; the sentence says which. HTTP 403; nothing decided or enrolled, no entry.",
    "invite_expired": "A passkey invitation past its expiry (15 minutes by default). HTTP 410; nothing enrolled.",
    "rules_off": "A standing rule proposed or confirmed at an authority whose /health says rules: off (authority-api.md, Standing rules, since v0.4). HTTP 403; nothing proposed or confirmed, no entry.",
    "rule_invalid": "A rule proposal outside what the person or the authority allows: the most per purchase above the person's ceiling or in another currency, a departure date in the past, a seller the authority does not issue for; or, for a rule the person makes on their own pages (since v0.11), a seller they never approved a purchase at, an agent they have not linked, a total outside the most per purchase and five times the ceiling, more than ten purchases or thirty days. HTTP 400; no rule, no entry. (The shape and the caps of a proposal, one purchase and 48 hours, are request_invalid.)",
    "rule_limit": "The person already has five rules proposed or active. HTTP 403; no rule, no entry.",
    "signups_full": "Signing in to enroll under signup: enroll (authority-api.md v0.10): every place of a person waiting for the operator is taken. HTTP 403 on the sign-in's callback page; nobody is made and no entry is written.",
    "limit_exceeded": "The purchase would pass one of the person's own limits (authority-api.md v0.9, The person's own limits): the most per purchase, the budget or the number of purchases of the rolling period, the same trip again too soon, a blocked seller, a seller scored under the least the person accepts (facts.limit seller_trust, authority-api.md v0.12, What a seller is trusted with), or what the person allows the assistant it came through. The reason of a denied request document and of its mandate.denied, which also names which limit (facts.limit) and the digest of the limits in force (facts.limits_sha256); at the moment of issuing, HTTP 409 on the approval, by the form or with a passkey, and the request is denied with it.",
    "limits_invalid": "The person's own pages, setting limits, on the limits page or the sellers page: the document does not fit (an unknown member, an amount above the ceiling or in another currency, a period that is not 1, 7 or 30 days, a seller_trust_at_least off the scale of 1 to 100, an assistant that is not the person's), HTTP 400; or nothing was drafted, or the limits changed meanwhile, HTTP 409. Nothing changes and no entry is written.",
    "person_paused": "The person stopped their agent at this authority (authority-api.md, Stopping): nothing is issued, proposed, confirmed or granted for them until they resume. HTTP 403 on POST /mandate-requests, POST /rules, an approval, a rule's confirmation and the consent page, with no entry; as the reason of a mandate.denied, a request that was waiting when the person stopped.",
    "session_expired": "A stop session ran out (ten minutes) or never signed in. HTTP 410; nothing stopped or resumed, no entry.",
    "not_paused": "Resume for a person who is not stopped. HTTP 409; no entry.",
    "provider_unknown": "POST /login named no identity provider this authority accepts (authority-api.md, Signing in to enroll). HTTP 400; nothing started, no entry.",
    "login_state": "The callback of a sign-in belongs to no attempt started in this browser, or to one already finished or older than ten minutes. HTTP 400; no invitation drawn, no entry.",
    "login_failed": "A sign-in that did not hold: the identity provider could not be read, refused, or its id_token failed a check (the sentence says which). HTTP 502, or 400 when the provider says the person refused; no invitation drawn, no entry.",
    "identity_unbound": "The sign-in held and no person of this registry is bound to that identity. HTTP 403; no invitation drawn, no entry. The person reads that their request is in; binding the identity, or letting a new person in, is the operator's.",
    "admin_denied": "POST /mandates/<jti>/revoke without the operator credential. HTTP 401; no entry.",
    "not_found": "Unknown jti or request_id, an unknown approval link (answered as HTML) or an unknown route. HTTP 404.",
    "method_not_allowed": "A route called with a method it does not take. HTTP 405; no entry.",
    "internal_error": "An unexpected fault, answered with the exception's class name and never a trace. HTTP 500; no entry."
  },
  "other_seller_codes": {
    "edge_attestation_invalid": "REQUIRE_EDGE_ATTESTATION mode only.",
    "offer_invalid": "The flight tool answered in another currency or with an unusable amount (502).",
    "google_flights_unavailable": "SerpApi quota or key failure (502). Not the fallback case.",
    "google_flights_no_offer": "A fare that cannot be expressed in minor units (502). Not the fallback case either: an empty answer falls back to the fixture, a malformed one does not.",
    "origin_refused": "MCP: a browser Origin this endpoint does not allow (403).",
    "sse_unsupported": "MCP: GET on the endpoint (405).",
    "not_found": "Unknown route (404).",
    "admin_denied": "Mandate administration without the operator credential (401).",
    "witness_unavailable": "Sealing: the witness could not be reached or answered non-2xx. The checkpoint is published unsealed with this error on it; the transaction is not affected. The buyer writes the same word on its own unsealed checkpoints.",
    "receipt_invalid": "Sealing: the witness receipt does not verify under its key set or does not name the checkpoint. Kept as an error on the unsealed checkpoint.",
    "evidence_request_invalid": "GET /evidence/<jti> (evidence-api.md): no bearer token, not a compact JWS of typ evidence-request+jwt, a kid the verifier's key set does not carry, a signature that does not verify, aud not this seller, iat outside the skew, exp past or more than 120 s after iat, no usable jti, a replayed jti, or mandate_jti not the path's (401).",
    "verifier_not_allowed": "GET /evidence/<jti>: the request's iss is not on VERIFIERS, or VERIFIERS is empty (403). A policy decision, as issuer_not_allowed is for authorities.",
    "verifier_unavailable": "GET /evidence/<jti>: the verifier's key set at <iss>/.well-known/jwks.json could not be read, so the request cannot be checked (503).",
    "evidence_not_found": "GET /evidence/<jti>[?issuer=<origin>]: no purchase in AP2 form presented that jti under the issuer asked for (a listed authority when issuer is absent, evidence-api.md v0.2), or its retention window (EVIDENCE_RETENTION_DAYS) passed (404).",
    "guest_budget_exhausted": "create_checkout under a guest mandate (guest-buyer.md) when the day's guest purchases are spent (429): GUEST_PURCHASES_PER_DAY, 20 unless set, per UTC day, all guests together (a count per domain would bound nobody: a zone has as many names as its owner likes). Taken after checks 4 to 10 passed and before the rail, so only a purchase that would have gone through spends one; the claim on the jti is released. Step null: a limit on what nobody this merchant knows vouched for, not one of the twelve checks.",
    "search_budget_exhausted": "search_flights with no credential at all, when the anonymous budget is spent (429): ANONYMOUS_SEARCHES_PER_DAY and ANONYMOUS_SEARCHES_PER_MONTH, 10 and 60 unless set, counted per UTC day and month, spent only after the input validated and before the provider is called. Written as request.refused with source anonymous and step null: a limit on what nobody vouched for, not one of the twelve checks. An operator token with commerce:search is never refused for this reason. An envelope under an attested key spends, since 2026-09-25, its trust domain's own daily budget (ATTESTED_SEARCHES_PER_DAY, 20 unless set, per UTC day and per domain) unless the domain is on UNBUDGETED_BUYER_DOMAINS, and is refused by it with source key:<kid> and the domain named; a guest's search (an envelope under a key from _agent-keys, guest-buyer.md) spends this budget and is refused by it, with source key:<kid>. Anonymous search is open since 2026-09-14 (D-097).",
    "signature_missing": "UCP face (ucp.mjs, httpsig.mjs): a completion under the AP2 extension, or any request that must be signed, without Signature-Input and Signature headers (401).",
    "key_not_found": "UCP face: the signature names a keyid that the platform profile's keys[] does not publish, after one forced refresh of the profile (401).",
    "digest_mismatch": "UCP face: Content-Digest does not match the SHA-256 of the body as received (400).",
    "algorithm_unsupported": "UCP face: the published key is of a type this verifier does not speak (RSA, an unknown curve); the key set stays valid, the signature is skipped (400).",
    "invalid_profile_url": "UCP face: no UCP-Agent header (or meta.ucp-agent on MCP), a profile URL that is not https, that carries credentials, or that names a special-use address (400).",
    "profile_unreachable": "UCP face: the platform profile could not be fetched, answered non-200 or redirected (424); or the seller's per-minute budget for resolving unknown profiles is spent (503, Retry-After).",
    "profile_malformed": "UCP face: the platform profile is not JSON, has no ucp object or no date-shaped ucp.version, or exceeds the size bound (422).",
    "version_unsupported": "UCP face: the platform profile declares a UCP version this seller does not speak (422).",
    "idempotency_key_reused": "UCP face: an Idempotency-Key (or meta.idempotency-key) reused with a different body, by the same platform and operation (409).",
    "forbidden": "UCP face: a checkout session read or changed by a platform other than the one that opened it (403).",
    "invalid_request": "UCP face: an Idempotency-Key that is not a string of at most 256 characters (400); a catalog request the Catalog schemas do not admit where the seller reads it, such as a lookup without ids, a get_product without id, a field of the wrong type or a cursor the seller did not issue (400, -32602 over MCP; ucp-profile.md §8).",
    "merchant_authorization_invalid": "UCP face, check 7 (in band, on the checkout with an AP2 error receipt): the checkout the closed mandate embeds (checkout_jwt, or the checkout object with ap2.merchant_authorization) is not this seller's own signature, or its header names another key (403 as a reason; the HTTP answer is 200 with messages[]).",
    "mandate_scope_mismatch": "UCP face, check 7 (in band): the checkout the closed mandate embeds differs from the session in id, currency, line items or totals (409 as a reason; the HTTP answer is 200 with messages[])."
  },
  "details": {
    "$comment": "Since 2026-09-26 (D-136): where one code has several causes, the refusal carries `detail`, one of these names, on every face (the error body over REST, error.data over A2A and UCP MCP, the message over UCP REST as <namespace>.detail, the text over MCP) and in the seller's request.refused entry (facts.detail). Every refusal also carries `contract`, the address of this contract's page for the code or the cause (reason-codes.html#<code> or #detail-<name>), when the seller is configured with CONTRACTS_URL. A code without a detail has one cause, or its message names it well enough. Names are stable: a cause that changes gets a new name.",
    "not_sd_jwt": {
      "code": "mandate_invalid",
      "text": "A chain token is not an SD-JWT (issuer JWT followed by ~-separated disclosures)."
    },
    "kb_jwt_not_accepted": {
      "code": "mandate_invalid",
      "text": "A token ends in a Key Binding JWT; this profile binds with a kb+sd-jwt hop, not a KB-JWT."
    },
    "disclosure_empty": {
      "code": "mandate_invalid",
      "text": "A token has an empty disclosure segment."
    },
    "sd_alg_unsupported": {
      "code": "mandate_invalid",
      "text": "_sd_alg is not sha-256."
    },
    "disclosure_repeated": {
      "code": "mandate_invalid",
      "text": "The same disclosure appears twice."
    },
    "disclosure_not_json": {
      "code": "mandate_invalid",
      "text": "A disclosure does not decode to JSON."
    },
    "disclosure_malformed": {
      "code": "mandate_invalid",
      "text": "A disclosure is not [salt, name, value] or [salt, value]."
    },
    "disclosure_referenced_twice": {
      "code": "mandate_invalid",
      "text": "One disclosure is referenced by two digests."
    },
    "disclosure_wrong_kind": {
      "code": "mandate_invalid",
      "text": "An object-property disclosure stands where an array element is referenced, or the reverse."
    },
    "sd_array_malformed": {
      "code": "mandate_invalid",
      "text": "An _sd array holds something other than digests."
    },
    "disclosure_name_reserved": {
      "code": "mandate_invalid",
      "text": "A disclosed claim name collides with another or is reserved (_sd, ...)."
    },
    "disclosure_unreferenced": {
      "code": "mandate_invalid",
      "text": "A disclosure is carried that no digest references."
    },
    "delegate_payload_not_one": {
      "code": "mandate_invalid",
      "text": "The open mandate does not disclose exactly one delegate_payload item."
    },
    "root_kid_unknown": {
      "code": "issuer_not_allowed",
      "text": "The root is signed under a kid no accepted issuer publishes (for a guest: not in its profile or TXT record)."
    },
    "root_iss_mismatch": {
      "code": "issuer_not_allowed",
      "text": "The root names an iss other than the issuer whose key signed it."
    },
    "vct_unexpected": {
      "code": "mandate_invalid",
      "text": "An open or closed mandate has a vct other than the profile's (mandate.checkout.open.1, mandate.checkout.1, mandate.payment.open.1, mandate.payment.1)."
    },
    "cnf_missing": {
      "code": "key_binding_mismatch",
      "text": "The open mandate names no P-256 key in cnf.jwk."
    },
    "intermediate_hop": {
      "code": "mandate_invalid",
      "text": "A hop is an intermediate kb+sd-jwt+kb: delegation chains are not accepted."
    },
    "hop_kid_mismatch": {
      "code": "key_binding_mismatch",
      "text": "The closing hop names a kid other than the key the root binds (cnf.jwk)."
    },
    "hop_signature_mismatch": {
      "code": "key_binding_mismatch",
      "text": "The closing hop is not signed by the key the root binds."
    },
    "sd_hash_mismatch": {
      "code": "mandate_invalid",
      "text": "The closing hop's sd_hash is not the hash of the root presented with its disclosures."
    },
    "issuer_jwt_hash_binding": {
      "code": "mandate_invalid",
      "text": "The closing hop binds by issuer_jwt_hash; this profile binds by sd_hash."
    },
    "hop_iat_missing": {
      "code": "mandate_invalid",
      "text": "The closing hop carries no iat."
    },
    "nonce_mismatch": {
      "code": "mandate_invalid",
      "text": "The closing hop's nonce is not this checkout's (over UCP: the session id)."
    },
    "terminal_hop_cnf": {
      "code": "mandate_invalid",
      "text": "The closing hop carries cnf: a terminal hop names no further key."
    },
    "constraints_missing": {
      "code": "mandate_invalid",
      "text": "The open mandate carries no constraints array."
    },
    "constraint_untyped": {
      "code": "mandate_invalid",
      "text": "A constraint has no type."
    },
    "constraint_unresolved": {
      "code": "mandate_invalid",
      "text": "A constraint type this seller does not evaluate (AP2 receipt: unresolved_constraint)."
    },
    "constraint_repeated": {
      "code": "mandate_invalid",
      "text": "The same constraint type appears twice."
    },
    "open_root_alone": {
      "code": "mandate_invalid",
      "text": "A chain is an open root with no closing hop."
    },
    "chain_too_long": {
      "code": "mandate_invalid",
      "text": "A chain has more than a root and one terminal hop."
    },
    "sub_missing": {
      "code": "mandate_invalid",
      "text": "The open mandate names no person (sub). In both mandate forms (the AP2 chains' open root, the mandate+jwt)."
    },
    "act_sub_invalid": {
      "code": "mandate_invalid",
      "text": "The open mandate's act.sub is absent or not a spiffe:// URI (a guest's: spiffe://<its domain>/buyer/guest/<kid>). In both mandate forms (the AP2 chains' open root, the mandate+jwt)."
    },
    "delegation_act_act": {
      "code": "mandate_invalid",
      "text": "The open mandate carries act.act (a delegation chain). In both mandate forms (the AP2 chains' open root, the mandate+jwt)."
    },
    "roots_disagree": {
      "code": "mandate_invalid",
      "text": "The checkout and payment roots differ in iss, sub, jti, policy_version, iat or exp: they are two halves of one approval."
    },
    "roots_bound_apart": {
      "code": "mandate_invalid",
      "text": "The two roots bind different keys (cnf) or name different buyers (act)."
    },
    "roots_issuers_differ": {
      "code": "mandate_invalid",
      "text": "The two roots are signed by different issuers."
    },
    "merchants_missing": {
      "code": "mandate_invalid",
      "text": "checkout.allowed_merchants allows no merchant."
    },
    "line_items_not_one": {
      "code": "mandate_invalid",
      "text": "checkout.line_items does not require exactly one item."
    },
    "payment_instrument_missing": {
      "code": "mandate_invalid",
      "text": "The closed payment mandate names no payment instrument (id and type)."
    },
    "payment_reference_missing": {
      "code": "mandate_invalid",
      "text": "The open payment mandate carries no payment.reference constraint."
    },
    "payment_reference_mismatch": {
      "code": "mandate_invalid",
      "text": "payment.reference names another checkout mandate than the one presented (sd_hash)."
    },
    "iat_exp_missing": {
      "code": "mandate_invalid",
      "text": "The open mandate carries no iat and exp."
    },
    "hop_outside_window": {
      "code": "mandate_invalid",
      "text": "A closing hop was signed outside the open mandate's window."
    },
    "hop_not_fresh": {
      "code": "mandate_invalid",
      "text": "A closing hop was signed more than the allowed skew away from now."
    },
    "jti_unusable": {
      "code": "mandate_invalid",
      "text": "The open mandate's jti is absent or shorter than 16 characters. In both mandate forms (the AP2 chains' open root, the mandate+jwt)."
    },
    "cnf_not_request_key": {
      "code": "key_binding_mismatch",
      "text": "The mandate binds a key other than the one that signed the request. In both mandate forms (the AP2 chains' open root, the mandate+jwt)."
    },
    "act_sub_not_requester": {
      "code": "key_binding_mismatch",
      "text": "The mandate names a buyer (act.sub) other than the one making the request. In both mandate forms (the AP2 chains' open root, the mandate+jwt)."
    },
    "quote_other_buyer_key": {
      "code": "key_binding_mismatch",
      "text": "The quote was issued to another buyer key."
    },
    "authorization_other_party": {
      "code": "key_binding_mismatch",
      "text": "The checkout authorization does not belong to the party the quote was issued to."
    },
    "detached_jws_malformed": {
      "code": "merchant_authorization_invalid",
      "text": "ap2.merchant_authorization is not a detached JWS (header..signature)."
    },
    "checkout_jwt_missing": {
      "code": "mandate_invalid",
      "text": "The closed checkout mandate carries neither checkout_jwt nor an embedded checkout with ap2.merchant_authorization."
    },
    "kid_not_seller": {
      "code": "merchant_authorization_invalid",
      "text": "The embedded checkout is signed under a kid other than this seller's instance key."
    },
    "signature_not_seller": {
      "code": "merchant_authorization_invalid",
      "text": "The embedded checkout does not verify under this seller's key: reattach the payload as the seller sent it (the checkout without ap2, RFC 8785) between header and signature."
    },
    "not_compact_jws": {
      "code": "the code of the token being read (authorization_invalid, mandate_invalid, merchant_authorization_invalid, …)",
      "text": "The token is not three non-empty base64url segments."
    },
    "jws_not_json": {
      "code": "the code of the token being read (authorization_invalid, mandate_invalid, merchant_authorization_invalid, …)",
      "text": "The token's header or payload is not JSON."
    },
    "jws_header_not_object": {
      "code": "the code of the token being read (authorization_invalid, mandate_invalid, merchant_authorization_invalid, …)",
      "text": "The token's header is not a JSON object."
    },
    "kid_missing": {
      "code": "the code of the token being read (authorization_invalid, mandate_invalid, merchant_authorization_invalid, …)",
      "text": "The token names no kid."
    },
    "action_not_purchase": {
      "code": "mandate_invalid",
      "text": "The mandate's action is not purchase (R-09)."
    },
    "stage_not_cart": {
      "code": "mandate_invalid",
      "text": "The mandate's stage is not cart or payment: a checkout needs a mandate bound to the quote."
    },
    "exp_missing": {
      "code": "mandate_invalid",
      "text": "The mandate carries no exp."
    },
    "guest_mandate_too_long": {
      "code": "mandate_invalid",
      "text": "A guest mandate lives longer, from its nbf (or iat) to its exp, than the guest tier allows (600 s)."
    },
    "guest_mandate_no_start": {
      "code": "mandate_invalid",
      "text": "A guest mandate carries neither nbf nor iat, so its life cannot be measured."
    },
    "mandate_unknown_to_authority": {
      "code": "mandate_invalid",
      "text": "The authority that issued the mandate does not know it (check 10)."
    }
  }
}
