Approval and budget checks before agent spending or tool use. Cooperative MCP; host must ask.
The independent authorization plane for AI agents.
Before an agent spends money, invokes a tool, touches a credential, or provisions a resource, it asks Sanction. Sanction approves, escalates to a human, or denies. Every decision is logged and auditable. Sanction belongs to no platform: one policy engine answers across model providers, payment rails, identities, and agent ecosystems.
One policy decision engine governs every kind of agent action:
| Governed action | What Sanction enforces |
|---|---|
Spend (/authorize) | Auto-approve floor, human-escalation band, per-transaction hard cap, daily and monthly budgets — checked and debited atomically. |
Tools (/authorize/tool) | Block/allow/escalate lists for any MCP tool or external action. Escalations reach the approval inbox like spend does. |
Credentials (/exec + /mandate/verify + /credentials/inject) | AES-256-GCM envelope-encrypted vault (KMS-wrapped, rotating keys). Injection requires a scoped 15-minute mandate JWT and clearance ≥ the credential's bar. Counterparties verify the mandate with no API key. Every access audit-logged. |
Provisioning (/authorize/provision) | Seats, licenses, infrastructure — resource, line item, quantity, and dollars authorized in one call. |
Capability (/authorize/capability) | Skills, plugins, new APIs — acquiring capability is governed like spending money. One ordered rule list (block / allow / escalate, prefix-glob patterns) gates new power before it lands in an agent. |
What a decision looks like in practice — one POST /authorize with an
amount, three possible outcomes, all of them terminal or resumable:
{ "status": "approved" }; budget counters debit in the
same transaction the decision persists (an advisory lock makes sibling
agents queue, not race).{ "status": "escalated", "request_id": "…" }; a human
sees it in the approval inbox, and approving mints a one-use grant the
agent redeems by retrying with grant_id. Policy decides what a timeout
means (approve or deny) — nothing hangs forever.{ "status": "denied", "decision_code": "PER_TXN_LIMIT", "remediation": "Amount exceeds the per-transaction limit. Split into smaller charges or ask the owner to raise the limit." }. Codes are
stable machine strings (DAILY_BUDGET_EXCEEDED,
CATEGORY_BLOCKED, WALLET_FROZEN, …) so agents branch and replan
instead of parsing prose. Replays of the same request return the same code.Around the engine:
resolveApproval path as the dashboard, actor recorded. Each route subscribes
to its own events. GuideGET /authorize/{id}/evidence re-runs the pure rules
over the stored context and proves the outcome reproduces.POST /policy/simulate replays stored
decisions under a candidate policy — which calls flip, what spend wouldn't
clear — before you change anything.GET /audit-events merges every decision, token log,
and secret access into one feed (CSV export included);
GET /reporting/summary spans any period with day buckets and per-seat
rollups; wallet stats project burn pace and exhaustion ETAs; a weekly
digest lands in Slack every Monday.GET /audit/export hands you a signed,
hash-chained snapshot of your governed decisions: altering, dropping, or
reordering any row breaks the chain, and the head is HMAC-signed by Sanction.
A regulator or the governed customer runs POST /audit/verify — self-contained,
no database — to prove nothing changed after signing, down to the first broken link.owner / admin / viewer), a switcher across every membership, and a
viewer who can read everything and change nothing.POST /outcomes) and a
wallet over its cost-per-outcome ceiling throttles to human-gated spend.
Wallets can be frozen outright, and budget reallocated across the tree.https://getsanction.com/api/gateway/<provider> with x-sanction-key —
usage is metered and budget-capped with zero per-call instrumentation.Every security claim above maps to enforcing code and a regression test in docs/TRACEABILITY.md — 1,100+ tests behind a coverage gate of 90% statements/lines, 94% functions, and 83% branches, including concurrency and Postgres row-level-security suites.
Eleven installable policy packs cover the common shapes — Startup defaults,
Coding agent seat, MCP tool governance, Compliance baseline,
Client-safe launch, and No-egress (Sanction Local) among them. GET /policy/packs lists them;
POST /policy/packs/{id}/preview simulates one against your last 30 days of
real decisions before anything changes; apply writes it as a policy revision.
Policy edits are never a leap of faith:
POST /policy/simulate replays your stored decision history under the
candidate — see exactly which calls flip and what spend wouldn't clear.GET /authorize/{id}/evidence re-runs
the rules over the stored context and proves the outcome reproduces.Use Sanction's vault when credentials should flow through the same policy, approval, and audit trail as spend and tools — one clearance model, no separate secrets cluster. Keep your existing Vault or Secrets Manager when you need fleet-scale secret lifecycle management independent of agent governance; Sanction consumes upstream identity and secrets rather than replacing them. Threat model: docs/SECURITY.md.
Platform vendors govern agents inside their own walls. Sanction authorizes agents wherever they run. Pick the shortest path to your stack:
| You want to… | Use | First step |
|---|---|---|
| Govern any MCP host (Claude Desktop, Cursor, …) | MCP wallet | Paste https://getsanction.com/mcp or npx sanction-mcp |
| Intercept tools/call and filter tools/list on an MCP server | MCP broker | Register the upstream, point the host at /mcp/broker/<name> |
| Meter model spend with zero code changes | LLM gateway | Point the SDK base URL at /api/gateway/<provider> |
| Govern agents in a TypeScript app | SDK | npm install sanction-sdk |
| Call the engine from anything else | REST API | POST /v1/authorize with an x-api-key |
| Plug into an AuthZEN enforcement point | PDP | Point it at /api/access/v1/evaluation |
| Orchestrate on AWS Bedrock | Action Group | docs/BEDROCK.md |
The full menu:
https://getsanction.com/mcp (Streamable HTTP, x-api-key) or npx sanction-mcp in any MCP host. The agent carries the wallet. Wallet Card · guidenpm install sanction-sdk (source): SanctionClient (agent plane) and SanctionAdminClient (management plane), plus framework adapters (SanctionMiddleware, sanctionTool)/api/openapi.json (Bedrock-compatible)Fastest first decision: bash examples/setup.sh — wallet, agent, and a demo
policy in one command, printing the env exports your agent needs
(examples/ has runnable clients to point at it).
Agent wallet · Quickstart · Starter kit · LangChain · CrewAI · Vercel AI SDK · Bedrock Agents · Agent fleets · AuthZEN PDP · Multi-tenant runbook · Notifications
Base URL: https://getsanction.com/api/v1 — grouped by workflow. Agent
endpoints take x-api-key pxy_…; management endpoints take the owner's
x-mgmt-key sk_….
Ask permission — the data plane an agent calls:
POST /authorize — Authorize a spend action (grant_id redeems an approval)
POST /authorize/quote — Authorize an x402 payment challenge before the wallet signs
POST /authorize/tool — Authorize a tool invocation
POST /authorize/provision — Authorize provisioning (resource + line item + $)
POST /authorize/capability — Authorize acquiring capability (skill/plugin/API)
GET /authorize/{id} — Poll an escalated decision (grant receipt included)
POST /tokens — Log LLM token consumption against the daily budget
POST /outcomes — Report an outcome; spend becomes answerable to results
Escalate to a human — over the line, someone decides:
GET/POST /approvals — The approval inbox; approving mints a one-use grant
Carry credentials — scoped mandates instead of raw secrets:
POST /exec — Mint a scoped mandate JWT (15-min TTL)
POST /mandate/verify — Counterparty checks a presented mandate (no API key)
POST /credentials/inject — Inject a decrypted credential (Bearer JWT)
POST /credentials/vault — Store an encrypted credential (owner)
POST /exec/revoke — Kill a live execution token (owner)
Prove what happened — audit, evidence, reporting:
GET /authorize/{id}/evidence — Audit proof: re-run the pure rules over the stored
context and confirm the recorded outcome reproduces
GET /audit-events — Unified audit feed (decisions, tokens, secret access; ?format=csv)
GET /reporting/summary — Any range ≤92 days: totals, day buckets, per-agent
GET /reporting/daily-summary — One-day rollup
GET /audit/export — Signed, hash-chained decision export (owner; ?download=1)
POST /audit/verify — Verify a tamper-evident export (recompute chain + signature)
Shape policy — edit safely, prove impact first (owner):
GET/PATCH /wallets/policy — Read / update budgets, thresholds, lists, capability rules
POST /policy/simulate — Replay real history under a candidate policy (what-if)
GET /policy/packs — List installable policy packs (public)
POST /policy/packs/{id}/preview — Simulate a pack against your last 30 days
POST /policy/packs/{id}/apply — Install a pack as the wallet policy (writes a revision)
Run the fleet — wallets, seats, keys (owner):
POST /wallets — Create a wallet + policy (management key shown once)
GET /wallets/stats — Today + month-to-date stats + burn projections
GET /wallets/tree — Subtree spend rollup
POST /wallets/keys/rotate — Rotate the wallet's data-encryption key
POST /agents — Register a seat (holder, expiry; key shown once)
POST /agents/batch — Stamp one template across up to 50 seats
GET/PATCH /agents — List / per-seat budgets, clearance, holder, expiry
POST /agents/rotate — Rotate a seat's key (optionally pass to a new holder)
POST /webhooks — Register a notification route (per-event subscriptions)
POST/GET/DELETE /broker/upstreams — Register the MCP servers the broker may front (BROKER-1)
POST /wallets/freeze — Pause every agent action in this wallet and its subtree
POST /wallets/unfreeze — Resume exactly where the fleet stopped
POST /wallets/reallocate — Move budget across the wallet tree
POST /wallets/bootstrap-key — Mint a management key for a legacy wallet
GET /outcomes — Reported outcomes + cost-per-outcome state
GET /activity — Recent decision activity for the console
Speak the standard — AuthZEN PDP + AARP (agent key; base
https://getsanction.com/api — spec-canonical paths):
POST /access/v1/evaluation — OpenID AuthZEN 1.0 evaluation (decision-only)
POST /access/v1/evaluations — AuthZEN batch, all three evaluation semantics
POST /access/v1/access-request — AARP: open an escalation from a signed binding token
GET /access/v1/access-request/{id} — AARP task status (maps to the profile's states)
Full schemas: /api/openapi.json.
The agent carries a Sanction wallet. Discovery: GET /.well-known/wallet-card.json.
stdio MCP is cooperative — the host must ask before acting.
Cursor plugin (MCP + skills): cursor-plugin/.
{
"mcpServers": {
"sanction": {
"command": "npx",
"args": ["sanction-mcp"],
"env": {
"SANCTION_API_URL": "https://getsanction.com/api/v1",
"SANCTION_API_KEY": "pxy_..."
}
}
}
}
Next.js 16 (App Router) + TypeScript · Prisma 7 on Neon Postgres (row-level security enforced at the database) · Vercel · jose (HS256, alg-pinned) · AES-256-GCM envelope encryption with AWS KMS root of trust in production.
CONTRIBUTING.md gets you from clone to green PR
(npm install && npm run check — no database needed for unit tests).
Security model and disclosure: docs/SECURITY.md.
Vocabulary: docs/DOMAIN.md.
It's free, or it's an agreement. Free for individuals — no card, personal and production use. Beyond that, an enterprise agreement shaped to your deployment: SSO, policy administration, audit export, SLA. Talk to us.
packages/sanction-mcp (the MCP client) — MIT. Embed it anywhere.Source-derived launch command. Check the maintainer’s required arguments and credentials before running:
npx -y sanction-mcpMerge this template into ~/Library/Application Support/Claude/claude_desktop_config.json. Keep existing servers. Add any arguments, credentials, and permissions required by the maintainer; this template has not been install-tested.
{
"mcpServers": {
"io-github-ericlovold-sanction": {
"command": "npx",
"args": [
"-y",
"sanction-mcp"
]
}
}
}Restart Claude Desktop completely for changes to take effect. Confirm the server appears connected in the client’s tool list, then try a read-only example from its documentation.
Claude Desktop setup referenceSanction works with any MCP-compatible client. Copy the config snippet from the Configuration section above and add it to the file shown for your client, then restart the application.
~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.~/.cursor/mcp.jsonRestart Cursor for changes to take effect..vscode/mcp.jsonReload VS Code window for changes to take effect.~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect..mcp.jsonSave at the project root, then start Claude Code in that project and review the MCP server approval prompt. Keep real credentials out of shared files.