Skip to content
Metamynd
Developers · Reference

The reason-code register & the /magp/schema atoms

Every verdict carries a stable reasonCode — 59 of them, generated from the same registry that serves GET /magp/schema, so a caller can branch on a string instead of parsing prose. This page explains how to consume the register; the full generated list lives in one place, checked by CI against the reference implementation, so it never drifts from what actually ships.

01

Fetch it live instead of hard-coding it

GET /magp/schema returns the same registry this page and the spec's Appendix A are generated from, and GET /standards/atoms returns the live atom catalog that drives Standards and SOP molecules. Read from either at startup rather than pinning a copied list in your own code — new governance requirements ship as new atoms and codes, without a protocol version bump.

02

The codes you'll actually hit first

  • AUTHORIZED — allow. Every check passed.
  • SOP_RULE / STANDARD_RULE — a bound rule fired; decision is whatever the molecule declared (block, escalate, suspend or quarantine)
  • SPEND_LIMIT_EXCEEDED — the mandate's own budget constraint failed
  • MERCHANT_NOT_ALLOWED / COUNTERPARTY_NOT_ALLOWED — outside the mandate's allow-list
  • NO_PERMISSION_FOR_ACTION — the mandate never granted this action at all
  • CONTEXT_UNVERIFIABLE — escalate. A rule needed a context field (riskLevel, most often) and couldn't get a well-formed, sufficiently trusted value — missing or garbled is treated as unverified, never as "nothing wrong"
  • AMOUNT_NOT_DETERMINABLE — the gate couldn't establish how much value the action moves, so no cap could be checked against it (fails closed, not open)
  • REPLAY_DETECTED — the nonce was already consumed
  • GATE_UNREACHABLE — the guard failed closed on a transport error; the action did not happen
  • AGENT_KEY_UNVERIFIED — a bring-your-own-key registration hasn't completed its proof-of-possession yet
  • AGENT_SUSPENDED / AGENT_QUARANTINED — the agent is contained by a prior firing rule; reinstate it to act again
  • AMOUNT_MISMATCH / COMMITMENT_MISMATCH — an x402 payment's amount, or the executed transaction itself, didn't match what was actually authorized
  • ESCALATION_PENDING / ESCALATION_EXPIRED — held for a human, or the hold ran out before one resolved it
03

Reading the shape

Every code maps to exactly one decision family — allow, observe, block, escalate, suspend, quarantine, decommission or void — and the register groups codes by what they're ABOUT (identity, envelope integrity, mandate, exposure, policy, containment, human review, payments) rather than by decision, since two codes with the same decision are rarely the same fix. Appendix A also carries a SAFR §30 alias for each code that has a canonical SAFR-vocabulary equivalent, for anyone mapping decisions to that standard.

Example

See it in code

branch-on-reason-code.ts
const decision = await guard.authorize({ action: "flight-purchase", amount: 600, currency: "USD", merchant: "skyward-air" });

switch (decision.reasonCode) {
  case "SOP_RULE":
  case "SPEND_LIMIT_EXCEEDED":
    // A policy or budget refused it — safe to surface to the user as-is.
    break;
  case "AGENT_KEY_UNVERIFIED":
    // Your own integration isn't finished yet — not a policy decision.
    throw new Error("Finish BYOK proof-of-possession before calling authorize()");
  case "GATE_UNREACHABLE":
    // Failed closed on a transport error — retry, don't treat as a policy refusal.
    break;
}

Prepare your organisation for the Agentic Economy