kredit

x403

The authorization protocol for agentic payments.

version 0.2, draftOctober 2026

0TLDR

x401 says who is asking. x402 says how to pay. Neither says whether the payment should happen. x403 is the question in between: the party about to move money asks a risk authority, gets back a signed yes, not yet, or no, with reasons in a shared vocabulary, bound to that one payment and that one executor, and both sides keep a receipt anyone can verify later. After the money moves, the executor reports what happened, so the next decision is better than this one.

Status: implemented by Kredit as the reference authority; open for other authorities, executors, requesters and evidence issuers. The protocol version is the version field of every object; the packages (@kredit/x403 0.3.0, x403 0.3.0) have their own release numbers, mapped in the repository README.

1Definition

x403 is a request and response protocol by which the party about to execute an agentic payment or transfer, called the executor (a merchant, PSP, acquirer, vault, wallet or issuer processor), obtains a signed risk decision from a risk authority before executing, and both sides keep a portable receipt.

The decision is one state, allowed, escalated or denied, computed from five things:

  1. the payment about to happen,
  2. the actor chain behind it, human to operator to agent to instance,
  3. the intent declared for it and the mandate it runs under,
  4. the evidence presented, made by other protocols,
  5. the actor's history, as the authority holds it.

The decision is explained with reason codes from a published registry. It is bound to one payment (subject) and one executor (audience). After execution an outcome report closes the loop.

x403 sits between x401 (identity) and x402, MPP and the card rails (payment), and between mandate formats (AP2, Verifiable Intent, Amex Proof of Intent, Visa VIC instructions) and their execution. It consumes their artifacts as evidence and issues nothing that competes with them.

1.1 Terminology

The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be read as in RFC 2119.

  • Executor: the party that moves the money and asks before doing so: a merchant, PSP, acquirer, vault, wallet, card program or issuer processor.
  • Requester: whoever signs and sends the risk query; the executor on the merchant leg, the operator's wallet or card program on the buyer leg.
  • Risk authority: the party that computes and signs the decision.
  • Agent: a registered software identity with a key, acting for an operator and a human principal.
  • Instance: one session of an agent, holding a short-lived key or token.
  • Principal: the human on whose behalf the payment is made.
  • Verifier: anyone checking a decision or receipt offline: a dispute desk, an auditor, a regulator, the next executor.
  • Reporter: a party that posts an outcome: the executor named in the receipt, the requester, the authority, or a party registered in the executor's policy.

2Design principles

  1. Reasons explain the decision. Reason codes come from a published registry. Whatever the authority computes internally stays inside the authority; a conformant decision contains no risk, trust or confidence field.
  2. Both legs, one object. The buyer leg (an operator's wallet or card program asking before its agent pays) and the merchant leg (an executor asking before accepting) use the same query and the same decision. direction is a field.
  3. Consume, don't compete. x401 results, AP2 mandates, Verifiable Intent chains, Amex Proof of Intent tokens, Visa VIC instructions, TAP and Mastercard WBA signatures, Skyfire KYA tokens, Stripe Shared Payment Tokens, MPP challenges, ERC-8004 registrations are evidence. x403 defines how they are referenced and hashed, not how they are made.
  4. Before, with a loop after. Decide before money moves. Report what happened afterwards.
  5. Signed on both ends, bound to the payment. The query is signed by the requester, the decision by the authority, the receipt countersigned by the executor, the outcome by its reporter. The decision names the payment and the executor it is for; presented anywhere else it is void.
  6. Portable. A receipt verifies offline against published keys.
  7. Thresholds, not histories. The authority discloses signed predicates about an actor, never another executor's data.
  8. Fail closed, with a path. Every decision that is not allowed names what would change it.
  9. Milliseconds, with a stated budget. The executor publishes its budget. A timeout is denied, or escalated to the executor's own desk when its policy says so, never allowed; the executor signs that itself as a local decision (4.6), never as the authority's.
  10. Status-code independent. Headers carry the protocol. The status codes in section 6 are the canonical mapping, not a requirement.

3Roles

  • Actor chain. Human principal, then operator (the business that runs the agent), then agent (a registered identity with a key), then instance (a session with a short-lived token). Each hop is verified with a stated basis, asserted, or absent.
  • Requester. Whoever sends the query: the operator's wallet, card program or payment runtime on the buyer leg; the executor on the merchant leg.
  • Executor. The party that moves the money.
  • Risk authority. Computes and signs the decision. Kredit is the reference. The protocol allows any authority, and an executor may chain several.
  • Reporters. Identity vendors, fraud engines, registries, networks and facilitators whose artifacts arrive as evidence or whose outcomes close the loop.
  • Verifier. A dispute desk, an auditor, a regulator, or the next executor, checking a receipt offline.

4Objects

All objects are JSON. Signed objects are canonicalized with RFC 8785 (JCS) and signed as compact JWS. ES256 is required of every implementation, EdDSA is optional, and a parallel slot for a post-quantum signature is reserved. A decision may also be wrapped as a W3C Verifiable Credential (jwt_vc_json).

Hashes are sha256: followed by lowercase hex of the canonical form. Amounts are decimal strings. Times are RFC 3339. Keys: executors publish a JWK set at /.well-known/x403/jwks.json; authorities publish /.well-known/x403/authority.json or resolve a did:web.

JSON Schemas for every object are in schemas/ of the repository.

4.1 Payment

The thing about to happen.

{
  "type": "payment",
  "amount": "189.00", "currency": "USD",
  "rail": "card",
  "executor": { "id": "bestbuy.com", "category": "5732", "jurisdiction": "US" },
  "counterparty": { "id": "bestbuy.com", "kind": "merchant", "age_days": 9124 },
  "items": [{ "sku": "6421743", "description": "USB-C hub", "class": "electronics", "qty": 1 }],
  "references": {
    "order_id": "BBY-2210-7731",
    "cart_hash": "sha256:…",
    "payment_requirements_hash": "sha256:…"
  },
  "idempotency_key": "ord_BBY-2210-7731"
}

payment_requirements_hash binds the decision to the exact x402 PaymentRequirements or MPP challenge. cart_hash binds it to an AP2 checkout_hash or a Verifiable Intent transaction_id. instruction_id binds it to a Visa VIC instruction. A decision is for one payment: change the payment and you need a new decision.

4.2 Actor presentation

The chain, hop by hop.

{
  "human":    { "status": "verified", "basis": "x401", "assurance": "IAL2", "ref": "sha256:…" },
  "operator": { "status": "verified", "basis": "kyb", "id": "did:web:acme.com", "via": "kredit" },
  "agent":    { "status": "verified", "basis": "registry", "id": "did:key:z6Mk…",
                "registry": [{ "name": "visa-tap" }, { "name": "erc-8004", "chain": "arc" }, { "name": "kredit-passport" }],
                "harness": "claude-code", "model": "haiku4.5", "attestation": null },
  "instance": { "status": "verified", "basis": "token", "token_ref": "sha256:…",
                "issued_at": "…", "expires_at": "…", "origin": "https://agent.acme.com" },
  "session":  { "ip": "…", "device": "…", "ua": "…", "provenance": "mcp" }
}

An unregistered agent still gets a decision, usually escalated on first contact and then a file. Lacking a registration is a reason code, not a refusal.

4.3 Intent

What the agent says it is doing, and the mandate that lets it.

{
  "mandate": {
    "format": "ap2",
    "ref": "sha256:…",
    "scope": { "executors": ["bestbuy.com"], "categories": ["electronics"],
               "max_amount": "250.00", "window": "2026-10-01/2026-10-31", "budget": "3000.00/month" },
    "issued_by": "human", "signed": true
  },
  "declared": { "goal": "replace broken USB-C hub for Denver office", "expected_amount_range": ["50", "200"] },
  "task_ref": "sha256:…"
}

A mismatch between declared, the payment, and mandate.scope becomes a reason code. It is not automatically denied: the executor's policy decides what a mismatch is worth.

4.4 Risk query

{
  "scheme": "x403", "version": "0.2",
  "policy_id": "bestbuy-checkout-v3",
  "direction": "inbound",
  "mode": "inline",
  "payment": { }, "actor": { }, "intent": { },
  "evidence": [
    { "type": "x401.result", "issuer": "did:web:x401-issuer.example", "ref": "sha256:…", "inline": "<jwt>" },
    { "type": "ap2.payment_mandate", "ref": "sha256:…", "inline": "<sd-jwt>" },
    { "type": "fraud.vendor", "issuer": "vendor.example", "ref": "sha256:…", "inline": "<jws>" },
    { "type": "x403.receipt", "issuer": "did:web:othermerchant.com", "ref": "sha256:…" }
  ],
  "challenge": { "nonce": "…", "expires_at": "…" },
  "budget_ms": 150,
  "on_timeout": "denied",
  "requester": { "id": "did:web:bestbuy.com", "kid": "2026-10" },
  "requester_sig": "<JWS>"
}

direction is inbound when the executor asks about a payment arriving, outbound when a wallet asks about a payment leaving. mode is inline (decide now, act on it), preflight (decide now, pay later citing the id) or shadow (decide, log, do not act). on_timeout is denied or escalated and nothing else: an allow on timeout would make a slow authority a bypass. budget_ms is the authority's time from receipt of the query to the first byte of its answer, at least 20 and at most 10000.

4.5 Decision

{
  "scheme": "x403", "version": "0.2",
  "id": "k_950a4d",
  "decision": "allowed",
  "direction": "inbound", "mode": "inline",
  "subject": { "payment_ref": "sha256:…", "amount": "189.00", "currency": "USD", "executor_id": "bestbuy.com" },
  "audience": "bestbuy.com",
  "query_ref": "sha256:…",
  "policy_id": "bestbuy-checkout-v3", "policy_hash": "sha256:…",
  "authority": { "id": "did:web:api.kredit.sh", "kid": "…" },
  "verified": { "human": "x401:IAL2", "operator": "kyb", "agent": "visa-tap+erc-8004", "instance": "token", "session": "present" },
  "checks": [
    { "key": "identity", "status": "pass" },
    { "key": "mandate",  "status": "pass" },
    { "key": "payee",    "status": "pass" },
    { "key": "behavior", "status": "warn", "reasons": ["first_use_of_product_class"] }
  ],
  "reasons": ["first_use_of_product_class", "amount_at_cap"],
  "constraints": { "max_amount": "250.00", "currency": "USD", "human_stepup_above": "100.00", "expires_at": "…" },
  "evidence_hashes": ["sha256:<query>", "sha256:<x401 result>", "sha256:<mandate>"],
  "disclosure": { "history": "threshold", "claims": [{ "clean_payments_gte": 50 }, { "disputes_12m_lte": 0 }] },
  "binding_hints": { "http_status": 200 },
  "supersedes": null,
  "issued_at": "…", "expires_at": "…",
  "decision_sig": "<JWS by authority>"
}
  • subject is the one payment this decision is for: the hash of the payment object as presented, its amount, currency and executor. audience is the executor it is for. Both are inside the signature. A verifier MUST check both before trusting an allow: a decision presented at an executor other than its audience, or for a payment other than its subject, is void, with reason audience_mismatch or subject_mismatch. query_ref is the hash of the query as received.
  • checks is the executor-facing summary, four keys, worst-of. Whatever the authority computes behind them stays behind them.
  • reasons are keys of the registry (section 8), in the order they matter.
  • constraints is a property of an allowed decision: the yes is scoped by a ceiling, a step-up line, the rails and executors it covers, and an expiry.
  • escalation is present only on an escalated decision, and its kind names who resolves it: agent_adjust (shrink the amount or scope and retry, no human needed), operator_confirm, human_stepup, executor_review. options says how, reduce_to says to what, retry_after says when.
  • binding_hints.http_status is the status the executor should answer with if it uses the HTTP binding, so the mapping in section 6 lives in the object and not in every client. It is advisory and outside the decision's meaning. Earlier decisions carried it as http_status at the top level, which verifiers still read.
  • supersedes links a re-decision to the hash of the one it replaces. revoked.json lists decisions by hash for the same reason: an id can be reissued, a hash cannot.
  • There is no latency_ms and no numeric risk, trust or confidence value of any kind. Timing stays in the authority's logs.
  • Decisions are single use. An executor that executes the same decision twice has a bug, and the authority will say decision_consumed the second time. An idempotent re-query (same idempotency_key, same payment) returns the same decision and does not consume it.

4.6 Local decision

What the executor signs itself when the authority gave no decision inside budget_ms, or could not be reached. It is never an allow, and it is never presented as the authority's: local is true, authority names the authority that did not answer, and the signature is the executor's.

{
  "scheme": "x403", "version": "0.2",
  "id": "local_1a2b",
  "decision": "denied",
  "local": true,
  "direction": "inbound",
  "subject": { "payment_ref": "sha256:…", "amount": "189.00", "currency": "USD", "executor_id": "bestbuy.com" },
  "audience": "bestbuy.com",
  "policy_id": "bestbuy-checkout-v3",
  "authority": { "id": "did:web:api.kredit.sh" },
  "reasons": ["authority_timeout"],
  "binding_hints": { "http_status": 403 },
  "executor": { "id": "bestbuy.com", "kid": "…" },
  "issued_at": "…", "expires_at": "…",
  "local_sig": "<JWS by executor>"
}

decision is denied, or escalated with kind: executor_review when the query's on_timeout said so. The executor SHOULD sign it; a verifier checks it against the executor's keys, never an authority's. The executor files it with the authority as a receipt (local_decision on the receipt), and the authority stores it as such and never lists it among its own decisions. Schema: schemas/local_decision.json.

4.7 Receipt

{
  "decision_ref": "k_950a4d", "decision_hash": "sha256:…",
  "executed": true, "executed_at": "…",
  "execution": { "rail": "card", "ref": "auth_7c…", "amount": "189.00", "currency": "USD" },
  "executor": { "id": "did:web:bestbuy.com", "kid": "…" },
  "executor_sig": "<JWS>"
}

A receipt is the decision plus the executor's countersignature. It is the dispute-file artifact, and what fills an a-comm AEP policy artifact (outcome, rules_triggered, policy_engine_vendor, policy_version). A receipt for a local decision carries the signed local decision as local_decision and its id as decision_ref; the authority files it without a decision of its own behind it.

4.8 Outcome report

{
  "decision_ref": "k_950a4d", "decision_hash": "sha256:…",
  "authority_id": "did:web:api.kredit.sh",
  "outcome": "settled", "category": "none", "reported_via": "executor",
  "at": "…", "amount": "189.00", "currency": "USD", "reason_code": null,
  "reporter": { "id": "did:web:bestbuy.com", "kid": "…" }, "sig": "<JWS>"
}

Outcomes: settled, delivered, disputed, chargeback, refund, reversal, fraud_confirmed, false_positive. The last two are the ones that make the next decision better. category says what kind of loss, if any: attack, accident, control_failure, commercial_dispute, none. reported_via says which channel the news came through. decision_hash ties the report to the decision as signed, so it cannot be moved to another decision with the same id.

Who may report: the executor named in the receipt, the requester named in the query, the authority itself, and reporters registered in the executor's policy document. The report is signed by its reporter; the authority MUST verify the signature against the reporter's published keys and MUST reject a report from anyone else.

5Flows

F1. Inbound, inline (merchant leg)

The agent attempts a payment. The executor builds a query from the request plus whatever x401, TAP, SPT or mandate artifacts arrived, signs it, and posts it to its authority with a budget. The authority verifies the evidence signatures, resolves the actor chain, evaluates against the executor's policy and returns a signed decision. The executor maps it to a status. The agent: allowed, done, within constraints; escalated with agent_adjust, shrink and retry citing the id; escalated with another kind, push it to the operator or human and retry with step_up_ref; denied, stop and carry the signed denial back to the operator. The executor posts a receipt, and later an outcome.

F2. Outbound, before the payment leaves (buyer leg)

The operator's wallet, card program's real-time authorization webhook or vault proxy builds a query from the agent's own intent and mandate, calls its authority, and releases the payment only when allowed. The decision id rides with the payment. The merchant verifies the signature offline. No merchant change is required.

F3. Preflight before a final-settlement rail

HEAD with a presentation, then allowed with expires_at and constraints, then pay over x402 or MPP citing the decision id, then the merchant checks the id against its own record or the authority's signature, then settle. This is what makes final-settlement rails safe: decide before paying.

F4. Shadow

mode: "shadow". The authority returns a decision, the executor logs it and does not act, outcomes still flow back. This is the pilot shape.

F5. Escalated, then re-evaluated

Modelled on OpenID AuthZEN AARP. The decision that is not allowed names a prerequisite; the requester satisfies it; a new decision is issued with supersedes pointing at the old one; the old one is consumed.

6HTTP binding

Two bindings carry the same objects. Header values are base64url JSON without padding. Every response that carries an x403 header MUST also carry Cache-Control: no-store: signed objects are never cached by an intermediary. A single header value MUST NOT exceed 8 KB; headers carry objects and references, and anything with inline evidence travels in a body with Content-Type: application/x403+json. An executor treats an oversized or unreadable presentation header as no presentation.

6.1 Header binding

The launch binding. It matches the x401 and x402 convention.

HeaderDirectionCarries
X403-Requiredexecutor to agenta risk query the agent must help satisfy, or a decision that is not allowed
X403-Presentationagent to executoractor presentation, intent and evidence, or the id of a prior preflight decision
X403-Decisionexecutor to agentthe signed decision

6.2 Auth-scheme binding

The IETF path, modelled on MPP's WWW-Authenticate: Payment.

WWW-Authenticate: Risk policy="bestbuy-checkout-v3", realm="checkout", request="<base64url query>", expires="…"
Authorization: Risk <base64url presentation>
Risk-Decision: <base64url decision>

6.3 Status mapping

The decision carries binding_hints.http_status. This is how it is computed. An executor may override it, and must not invent a status outside this table for an x403 answer. A 403 without an X403-Decision or Risk-Decision header is not an x403 answer: clients MUST read the decision from the header, never from the status.

DecisionStatusMeaning on the wire
allowed, with or without constraints200, 201 or 202the resource, plus X403-Decision
escalated, agent_adjust403the decision says what to shrink to; the agent retries citing the id
escalated, operator_confirm or human_stepup428a precondition is required: present it, then retry with step_up_ref
escalated, executor_review403a person on the executor's side decides; retry_after says when to ask again
denied, identity group401the chain failed: unverified human, expired instance token, revoked credential
denied, compliance group451sanctions, jurisdiction, or a legal threshold
denied, velocity group429too much, too fast; retry_after is set
denied, any other group403understood, and refused on policy
replay or expiry: nonce_consumed, decision_expired, decision_consumed, decision_superseded409the state of the conversation conflicts with the request
policy_unknown, presentation_malformed, evidence_hash_mismatch400the request itself is wrong
authority_timeout403the executor's own local decision (4.6) in the header, signed by the executor, denied or escalated per on_timeout
audience_mismatch, subject_mismatch, presentation_signature_invalid400a decision or presentation for another executor or payment
non-disclosure denied404no header at all, by the executor's choice

Six codes are normative for clients: 200, 401, 403, 428, 429, 451. A client library must handle all six. The rest are executor choices a client may treat as 403.

6.4 Preflight

HEAD or OPTIONS with X403-Presentation answers 200 with X403-Decision and no body. The decision id is then cited in the x402 PaymentPayload.extensions, the MPP Authorization: Payment payload, or the AP2 Payment Mandate risk_data.

6.5 Discovery

  • /.well-known/x403/policy/<id>.json: required hops and evidence, step-up lines, accepted authorities and mandate formats, latency budget, timeout behaviour. No weights.
  • /.well-known/x403/revoked.json: revoked decisions by hash (the id beside it for readers), and retired policy hashes. Served with Cache-Control of at most 60 seconds.
  • /.well-known/x403/authority.json: an authority's id, keys, endpoints, algorithms and the evidence types it reads.
  • /.well-known/x403/jwks.json: the keys.

6.6 The ladder

403then401then402then200

Risk posture first, then identity at the level the posture requires, then payment, then execution with the decision attached. Or 401, 403, 402, 200 where identity is needed to evaluate at all. The policy document says which.

7Other bindings

  • Authority API. POST /x403/decide, POST /x403/receipts, POST /x403/outcomes, GET /x403/decisions/{id}, GET /x403/reason-codes, and the well-known documents.
  • MCP. One tool, x403, with an action: decide, preflight, get, receipt, outcome, revoke, authority, jwks, policy, codes, revoked. An agent's payment runtime asks before it pays.
  • A2A. The decision as a task message part before settlement.
  • Verifiable Credential. The decision as a VC, so it rides inside an AP2 Payment Mandate (risk_data), an x401 result, a UCP signals entry or an AEP policy artifact.
  • Card authorization. id and policy_hash as merchant-defined data in the authorization message. The networks' agentic transaction indicator is the slot it eventually occupies.
  • Stablecoin. id in x402 extensions; preflight before settlement; the receiving side verifies offline.

8Reason codes

The registry is registry/reason-codes.json, served by every authority at /x403/reason-codes. Each code belongs to one group, each group folds into one of the four checks and names the status a denial on it produces, and each code carries a default effect: denied, escalated (with the kind), or note. The effect is the default; the executor's policy may be stricter or looser, and the decision says what the policy did.

CodeCheckStatusEffectResolved bySays
identityidentity401a hop in the actor chain is missing or failed
human_unverifiedidentity401escalatedhuman_stepupno verified human behind the chain
human_assurance_below_policyidentity401escalatedhuman_stepupthe human was verified below the level the policy asks for
operator_unverifiedidentity401escalatedoperator_confirmthe operator has no KYB on record
operator_pendingidentity401noteoperator verification is in progress
agent_unregisteredidentity401escalatedoperator_confirmthe agent is in no registry the policy accepts
agent_unverifiedidentity401escalatedoperator_confirmthe agent has no KYA on record
agent_pendingidentity401noteagent verification is in progress
agent_frozenidentity401deniedthe operator froze this agent
agent_key_rotated_recentlyidentity401notethe agent's key changed inside the policy window
instance_token_missingidentity401escalatedagent_adjustno instance token was presented
instance_token_expiredidentity401deniedthe instance token has expired
instance_token_revokedidentity401deniedthe instance token was revoked
credential_vouchedidentity401notea credential from another issuer was presented and recorded, not verified
credential_absentidentity401notenothing was presented to say who the actor is
origin_changedidentity401escalatedoperator_confirmthe instance speaks from a different origin than its token
relay_mismatchidentity401deniedthe relay named in the presentation is not the one that delivered it
mandatemandate403the payment is outside what the principal authorised
mandate_absentmandate403escalatedoperator_confirmno mandate was presented for a payment the policy requires one for
mandate_unsignedmandate403escalatedoperator_confirmthe mandate carries no signature
mandate_expiredmandate403deniedthe mandate's window has closed
mandate_revokedmandate403deniedthe principal revoked the mandate
off_mandate_categorymandate403escalatedoperator_confirmthe product class is outside the mandate
off_mandate_executormandate403escalatedoperator_confirmthe executor is outside the mandate
over_mandate_amountmandate403escalatedagent_adjustthe amount is above the mandate's ceiling
over_mandate_budgetmandate403escalatedoperator_confirmthe mandate's budget for the window is used up
intent_payment_mismatchmandate403escalatedagent_adjustthe declared goal and the payment do not agree
intent_counterparty_mismatchmandate403escalatedoperator_confirmthe declared payee and the actual payee differ
po_mismatchmandate403escalatedoperator_confirmthe purchase order does not match the payment
scopemandate403the agent was never granted this action
delegation_depth_exceededmandate403deniedthe chain is longer than the policy allows
scope_not_inheritedmandate403denieda hop holds more than the hop above it
separation_violationmandate403deniedone party fills two roles the policy keeps apart
policymandate403the executor's own policy stops it
blocked_counterpartymandate403deniedthe payee is on the executor's block list
counterparty_not_allowedmandate403escalatedoperator_confirmthe payee is not on the executor's allow list
category_not_allowedmandate403escalatedoperator_confirmthe product class is not on the allowed list
rail_not_allowedmandate403deniedthe rail is not one the policy permits
amount_over_capmandate403escalatedagent_adjustthe amount is above the per-action cap
above_review_thresholdmandate403escalatedhuman_stepupabove the line where a person decides
policy_floormandate403deniedbelow the executor's hard floor
policy_review_floormandate403escalatedexecutor_reviewbelow the executor's review floor
actor_blockedmandate403deniedthe executor blocked this actor
actor_trustedmandate403notethe executor trusts this actor
pending_escalationmandate403escalatedexecutor_reviewan earlier escalation for this actor is still open
financialbehavior403the amount or pace is out of line
amount_x_baselinebehavior403escalatedoperator_confirmwell above what this actor usually pays
amount_at_capbehavior403noteat the ceiling of the mandate or policy
cumulative_over_budgetbehavior403escalatedoperator_confirmthe window's budget would be exceeded
pace_x_baselinebehavior403escalatedoperator_confirmspending faster than its history
runaway_loop_patternbehavior403deniedthe same payment is repeating
concentrationbehavior403notemost of the window's spend goes to one payee
wallet_budget_exceededbehavior403escalatedhuman_stepupthe wallet's budget would be exceeded
card_budget_exceededbehavior403deniedthe card's budget would be exceeded
card_expiredbehavior403deniedthe card has expired
no_baselinebehavior403noteno history to compare against yet
fraudbehavior403signals of takeover, injection or collusion
cross_executor_burstbehavior403escalatedoperator_confirmthe actor is hitting several executors at once
takeover_signalsbehavior403deniedsignals that the agent is not under its operator's control
injection_patternbehavior403deniedprompt injection markers in the intent
collusion_patternbehavior403deniedpayer and payee look like the same party
device_newbehavior403notea device not seen for this actor before
automation_signalsbehavior403noteheadless or automation signals in the session
first_use_of_railbehavior403notethe actor has not paid on this rail before
first_use_of_product_classbehavior403notethe actor has not bought this class before
high_risk_product_classbehavior403escalatedoperator_confirma class that attracts fraud: gift cards, travel, crypto
high_risk_termsbehavior403escalatedoperator_confirmterms in the intent that attract fraud
off_briefbehavior403escalatedoperator_confirmthe purchase is not part of the agent's brief
velocitybehavior429too much, too fast
velocity_highbehavior429escalatedagent_adjusttoo many actions in the window
rate_cap_exceededbehavior429escalatedagent_adjustthe policy's rate cap for the window is used up
spend_cap_exceededbehavior429escalatedagent_adjustthe policy's spend cap for the window is used up
compliancepayee451sanctions, jurisdiction or reporting law
sanctions_hitpayee451deniedthe payee matches a sanctions list
sanctions_adjacentpayee451escalatedexecutor_reviewthe payee is close to a sanctioned party
jurisdiction_blockedpayee451deniedthe payee's jurisdiction is blocked
kyc_required_above_thresholdpayee451escalatedhuman_stepupthe amount needs a verified human by law
reporting_thresholdpayee451noteat or above a reporting threshold
screening_unavailablepayee451escalatedexecutor_reviewno screening answer in time
counterpartypayee403who is being paid
counterparty_newpayee403notethe payee is new to this actor
counterparty_youngpayee403escalatedoperator_confirmthe payee's account or address is days old
counterparty_unknownpayee403notenothing is known about the payee
disputespayee403what came back after earlier payments
dispute_historypayee403escalatedexecutor_reviewdisputes or chargebacks on record
dispute_rate_highpayee403escalatedoperator_confirmthe payee's dispute rate is above the policy line
refund_abuse_patternpayee403escalatedexecutor_reviewrefunds are looping
systemidentityper codethe protocol itself: replay, expiry, timeout
authority_timeoutidentity403deniedno decision inside the budget; the executor failed closed
evidence_signature_invalididentity401denieda piece of evidence did not verify
evidence_hash_mismatchidentity400deniedevidence inline does not match its ref
evidence_unsupportedidentity403noteevidence of a type this authority does not read
evidence_expiredidentity401denieda piece of evidence is past its expiry
requester_signature_invalididentity401deniedthe query's signature did not verify
policy_unknownidentity400deniedno policy by that id
presentation_malformedidentity400deniedthe presentation could not be read
nonce_consumedidentity409deniedthe challenge nonce was already used
idempotency_replayidentity200notethe same query was already decided; this is that decision
decision_expiredidentity409deniedthe decision is past its expiry
decision_consumedidentity409deniedthe decision was already executed
decision_supersededidentity409denieda later decision replaced this one
shadow_modeidentity200notedecided and logged, not acted on
superseded_by_reviewidentity409notea person re-decided this after an escalation; the earlier decision answers 409
audience_mismatchidentity400deniedthe decision names another executor as its audience
subject_mismatchidentity400deniedthe decision was issued for another payment
presentation_signature_invalididentity400deniedthe presentation's signature did not verify

New codes are added by pull request to the registry with a group, an effect and one sentence.

9Evidence

An evidence item is { type, issuer, ref, inline?, jwks_url? }. ref is the hash of the artifact. inline carries the artifact when the authority is expected to verify it. The authority records each item as verified (signature checked against the issuer's keys, hash matches, not expired), asserted (hash matches, signature not checkable by this authority), or invalid (hash mismatch, bad signature, expired), and the result lands on the decision as verified per hop and as reason codes.

Types this version names:

x401.resultx403.receiptx403.decisionap2.intent_mandateap2.cart_mandateap2.payment_mandateverifiable-intent.chainamex.proof_of_intentvisa.vic_instructionvisa.tap_signaturemastercard.wba_signatureskyfire.kyaskyfire.paykite.passportstripe.shared_payment_tokenmpp.challengex402.payment_requirementserc-8004.registrationkredit.passportkredit.agent_tokencircle.screeningvgs.aliasfraud.vendorother

An authority publishes which of these it verifies in authority.json under evidence_read. Anything else it records as asserted with evidence_unsupported on the decision.

10Trust model

  • Executor and authority exchange signed objects over TLS. Keys are published both ways.
  • Decisions verify offline against the authority's published key, and are then checked for audience, subject, expiry, and absence from the revocation list. Receipts verify against the executor's. A local decision verifies against the executor's key and is never an allow.
  • The actor chain is verified hop by hop and recorded as verified, asserted or absent with its basis. A hop without proof is a reason code, not a failure.
  • Policy belongs to the executor; computation belongs to the authority. Only a verified human may change a policy version, and the policy hash on every decision says which version decided it.
  • Replay and relay are stopped by challenge.nonce, idempotency_key, expires_at, single-use decisions, audience and subject.
  • Outcomes are accepted only from the executor named in the receipt, the requester, the authority and the reporters registered in the policy, each verified by signature.
  • Revocation is published at /.well-known/x403/revoked.json. Instance tokens are revoked at the authority.
  • Keys: executor jwks.json; authority document or DID; agent keys per the registry they come from.

11Privacy

  • No raw cross-executor history leaves the authority. disclosure.claims are signed threshold predicates. A later version adds zero-knowledge range proofs.
  • Executors see reasons, never other executors' data.
  • Presentations are selective: only the hops and claims the published policy asks for.
  • Retention: receipts for the rail's dispute window; outcomes pseudonymized at the authority.
  • Lawful basis: fraud prevention for the executor; the authority is a processor for the query and a controller for its own record.

12Security considerations

Each threat, and the field or rule that answers it.

ThreatAnswer
Replay of a decisionsingle use (decision_consumed), expires_at, nonce_consumed
Relay of a decision to another executoraudience, checked by every verifier
Laundering a decision onto another paymentsubject (payment_ref, amount, currency, executor), checked by every verifier
Moving an outcome to another decision with the same iddecision_hash on the outcome
Outcome poisoningonly the receipt's executor, the requester, the authority and registered reporters may report; signatures verified
Impersonating the authoritykeys from the well-known documents, pinned authority id, authority.kid in every decision
Forcing a timeout to get a yeson_timeout is never an allow; a local decision is executor-signed, never the authority's
Rolling a policy backpolicy_hash on every decision; retired hashes on the revocation list
Key compromiseretired keys stay published for verification; revocation by hash
A tampered objectthe signature is over the canonical form of the whole object; a changed field is a different payload
Header overflow and cache leakage8 KB per header value, bodies as application/x403+json, Cache-Control: no-store on every response that carries an x403 header
Reading the status instead of the decisiona 403 without an X403-Decision header is not an x403 answer

13What a receipt proves

That a named authority, under a named policy version, given the hashed evidence, issued this decision for this payment to this executor at this time, and that a named executor acted on it.

Not that upstream facts were true, that settlement happened, or who is liable. Liability is the rails' and the regulators' question. x403 gives them the record they have said they lack.

14Conformance

An authority conforms when it: signs decisions with ES256 over the RFC 8785 form; sets subject, audience and query_ref; publishes authority.json, jwks.json, reason-codes; honours budget_ms and on_timeout; refuses a consumed nonce and a consumed decision; accepts receipts, local decisions as receipts, and outcomes only from authorized, signed reporters; and never places a numeric risk value in a decision.

An executor conforms when it: asks before executing; checks audience and subject on any decision it did not receive directly from its authority; maps decisions to status per section 6.3 or documents its own mapping; attaches X403-Decision and Cache-Control: no-store to the response; publishes its policy document and keys; produces a signed local decision on timeout and files it; posts a receipt for every execution and an outcome when it knows one.

A client conforms when it: reads the decision from the header or body, not the status; handles the six normative codes; retries an agent_adjust within reduce_to; carries a decision that is not allowed back to its operator unchanged.

An offline verifier checks, in this order: the schema; decision_sig against the authority's published keys; audience against the executor presenting it; subject against the payment; expires_at against executed_at; absence from the revocation list; policy_hash not retired; the receipt's executor_sig; the receipt's decision_hash against the decision as signed.

Akredit as the reference authority

x403Kredit
Risk queryPOST /x403/decide, or POST /check with the same fields in Kredit's shape
Decisionthe decision record, signed; GET /x403/decisions/{id} returns the x403 object
allowed, escalated, deniedthe same three states in the console, the records and the signed object; POST /check carries the state in x403.decision next to Kredit's own outcome key
checksidentity, mandate, payee, behavior
escalationreview plus POST /decisions/{id}/review
ReceiptPOST /x403/receipts, and POST /decisions/{id}/execute for Kredit's own rails
OutcomePOST /x403/outcomes, and disputes in the console
Actor presentationthe Kredit Passport, POST /agents/{id}/passport/present, plus any evidence
Policy documentthe organization's rules and the agent's guardrails, versioned, passkey-approved
Authority keys/.well-known/x403/authority.json and /.well-known/x403/jwks.json
MCPthe x403 tool on the hosted MCP at /mcp