Documentation
Authorize AI agent actions. Prove them afterward.
One REST API and one MCP server in front of one authority engine. Your agent asks; Quattrino intersects standing, task and delegated authority and answers AUTHORIZED, DENIED, APPROVAL_REQUIRED or NEEDS_INFORMATION. Nothing here can bypass a policy, an envelope, a delegated ceiling or an approval. TEST/SANDBOX: no production money moves.
First verified authorization in about 10 minutes
The Golden Path: discover -> sign up -> connect (standing authority + key) -> authorize (see all four decisions) -> verify (bind, act, outcome, explain). SDK, MCP or curl.
Follow the Golden Path →
MCP for assistants
Claude and other MCP clients connect to our streamable-HTTP server and get authorize, compile_intent, explain and outcome as native tools.
See the endpoints →
Break things safely
The sandbox has named failure scenarios — declines, timeouts, over-limit — so you can test every path without real money.
Browse scenarios →
Golden Path (about 10 minutes, TEST/SANDBOX)
One canonical path: discover → sign up → connect → authorize → verify. Everything else is optional.
- Discover — this page,
GET https://quattrino.io/api/v1/public/discovery,GET https://quattrino.io/api/v1/authorize/schema. - Sign up — open signup (no invite; MFA optional). The owner session token is not an agent credential.
- Connect — give an agent standing authority: an ACTIVE policy ($50 per action, ask first above $20), a sandbox wallet (the organization's granted ceiling; simulated money) and a scoped key
qtrn_sk_key_…(shown once). In the app, or in one command: bootstrap_sandbox.py --signup. - Authorize —
$19.99with task max$20→AUTHORIZED·$25.00→APPROVAL_REQUIRED·$75.00→DENIED· no amount →NEEDS_INFORMATION. Handle the four answers the same way, always. - Verify —
mode: "binding",execution: "external",Idempotency-Key→ you execute with your own provider →/outcome(CONSUMED · COMPLIANT) →/explain. That is first success.
This page is the short form. The canonical, detailed documentation is served as Markdown at https://quattrino.io/api/v1/public/docs: DECISIONS · NEEDS_INFORMATION · APPROVAL_REQUIRED · DENIED_AND_REASON_CODES · AUTHORIZE_ONLY · PROOF_VERIFICATION · OUTCOME_REPORTING · WEBHOOKS · IDEMPOTENCY_AND_RETRIES · MCP_SETUP · SDK_USAGE · ERRORS · GLOSSARY.
The four decisions (real reason codes)
| decision | Golden Path trigger | field to read | your code |
|---|---|---|---|
AUTHORIZED | $19.99 under a $20 task (standing $50) | effective_maximum_minor, effective_authority.binding_layer | act (binding: single use until expires_at) |
APPROVAL_REQUIRED | $25.00 — above the $20 ask-first line, inside $50 (above_autonomous_limit) | approval.approval_id, question.question | relay the question; poll GET /agent/approvals/{id}; never re-submit |
DENIED | $75.00 — above standing authority (exceeds_effective_authority, agent_standing) | denial_reasons[].code, remedy | stop; never retry with altered wording |
NEEDS_INFORMATION | no amount (amount_required) · envelope gap (action_class_not_yet_authorized) | question (null → term missing), needs_information[].field, missing_authority[] | supply the term, or relay the ONE question; keep doing safe_actions_remaining |
Every reason carries code · family · severity · message · remedy from the public registry: GET https://quattrino.io/api/v1/public/reason-codes.
SDKs 0.31.0 (single file, zero dependencies, not yet on PyPI/npm)
Thin wrappers over the authorization API: authorize, authorize_binding,record_outcome, explain, compile_intent, discover,gap, release_disclosure. The SDK never decides anything - Quattrino decides; the SDK transports.
Python
Standard library only (Python 3.9+).
Download quattrino.pyfrom quattrino import Quattrino
q = Quattrino(api_key="qtrn_sk_key_...", base_url="https://quattrino.io")
r = q.authorize(amount_minor=1999, merchant="Amazon", purpose="USB-C cable",
commitment="ONE_TIME", task={"max_amount_minor": 2000})
r.decision, r.effective_maximum_minor # AUTHORIZED, 2000
auth = q.authorize_binding(..., execution="external")
q.record_outcome(auth.authorization_id, success=True, actual_amount_minor=1999)
q.explain(auth.authorization_id)["plain_language"]TypeScript
Fetch-based (Node 18+), zero dependencies.
Download quattrino.tsimport { Quattrino } from "./quattrino";
const q = new Quattrino({ apiKey: "qtrn_sk_key_...", baseUrl: "https://quattrino.io" });
const r = await q.authorize({ amountMinor: 1999, merchant: "Amazon", purpose: "USB-C cable",
commitment: "ONE_TIME", task: { max_amount_minor: 2000 } });
r.decision; // AUTHORIZED
const auth = await q.authorizeBinding({ ...same, execution: "external" });
await q.recordOutcome(auth.authorization_id, { success: true, actualAmountMinor: 1999 });
(await q.explain(auth.authorization_id)).plain_language;Also public: the open service directory (GET https://quattrino.io/api/v1/public/directory) and agent-credential verification (POST https://quattrino.io/api/v1/public/identity/verify).
Authentication and idempotency
Agents authenticate with their key qtrn_sk_key_… in X-API-Key or Authorization: Bearer (both work; the SDKs use Bearer) — a scoped, revocable credential, never a card or a login. Every binding authorization and every execution POST requires an Idempotency-Key; a retry with the same key returns the original result and never authorizes or charges twice.
curl -X POST https://quattrino.io/api/v1/authorize \
-H "X-API-Key: qtrn_sk_key_..." \
-H "Idempotency-Key: order-1029" \
-H "Content-Type: application/json" \
-d '{
"mode": "binding",
"action_type": "purchase",
"amount_minor": 1499, "currency": "USD",
"merchant": "Amazon",
"purpose": "USB-C cable for the office",
"commitment": "ONE_TIME",
"task": {"max_amount_minor": 2000},
"execution": "external"
}'Execution adapters (optional, sandbox). When you ask Quattrino to execute, the certified transaction engine (POST /agent/transactions with authorization_id) consumes the authorization once and picks the route before any sandbox money moves: agent-to-agent services draw on Credits, merchant checkout uses a Stripe Shared Payment Token in test mode (merchant_purchase), machine endpoints use x402 on testnet (machine_payment). external_purchase is a sandbox rules-check simulation only — the legacy direct-card rail is retired (LEGACY_EXTERNAL_PURCHASE_RAIL_RETIRED).
Endpoints
Authorize-only (you execute) — the primary path
- POST /authorize ← start here (mode dry_run | binding)
- GET /authorizations/{id} · GET /authorizations
- POST /authorizations/{id}/outcome ← what YOUR executor did
- GET /authorizations/{id}/explain
- POST /authorizations/{id}/revoke
- GET /authorizations/{id}/proof · POST /authorizations/verify (public)
- GET /agent/approvals/{id} ← poll after APPROVAL_REQUIRED
- POST /intents/compile ← plain-language task -> authority envelope
- GET /authority/me · POST /authority/gap ← what may I do / what exactly is missing
- POST /authorizations/{id}/disclosures/{scope}/release
- GET /agent/delegations · GET /agent/whoami
- MCP /api/mcp/ ← same operations as tools (same credentials)
Execution adapters (optional; Quattrino executes in the sandbox)
- GET /agent/wallet ← standing authority + sandbox funds (legacy object name)
- POST /agent/transactions ← pass authorization_id; Idempotency-Key required
- GET /agent/transactions · GET /agent/transactions/{id} · POST .../cancel
- GET /agent/quote?amount_minor=2000
- GET /agent/marketplace/search · POST /agent/services/{id}/purchase (Credits)
- POST /agent/merchant-payments/quote · /purchase (Stripe SPT test merchant)
- GET /agent/jobs · POST /agent/jobs/{id}/complete (you sell services)
Authorize-only integrations never need this column. Pass execution: "external" explicitly.
MCP tools are thin adapters over exactly these operations — they cannot bypass the authority engine, envelopes, delegated ceilings, approvals, ledger or rate limits.
MCP (Claude Desktop, Cursor, any MCP client)
Streamable-HTTP server at https://quattrino.io/api/mcp/, header Authorization: Bearer qtrn_sk_key_… on every request. 40 tools, grouped; start with whoami → authority_discover → authorize → authorization_outcome → authorization_explain. In MCP the idempotency key is the idempotency_key tool argument.
{ "mcpServers": { "quattrino": { "url": "https://quattrino.io/api/mcp/", "transport": "streamable-http",
"headers": { "Authorization": "Bearer qtrn_sk_key_..." } } } }Webhooks and idempotency
Owners register HTTPS endpoints (POST /webhooks, secret shown once). Deliveries are signed X-Quattrino-Signature: t=<unix>,v1=HMAC_SHA256(secret, "<t>.<raw body>") and retried 5 times (30 s · 2 min · 10 min · 60 min). Authorize-only approvals and alerts arrive as notification.created (data.notification_type: approval_request | firewall_alert); approval.pending / transaction.* fire only for Quattrino-executed transactions.
Binding authorizations require an Idempotency-Key (8+ chars, stable per unit of work): same key + same body → the original result (idempotent_replay: true); same key + different body → 409 idempotency_key_reuse. Outcomes need no key — a second /outcome is 409 outcome_already_recorded. Canonical: WEBHOOKS.md · IDEMPOTENCY_AND_RETRIES.md.
Errors
{
"error": {
"code": "idempotency_key_required",
"message": "An Idempotency-Key header is required for binding authorizations.",
"request_id": "req_396516089a9549efa820a7b6",
"details": {},
"retryable": false
}
}
// Decisions are NOT errors: DENIED / APPROVAL_REQUIRED / NEEDS_INFORMATION are 200 OK with a "decision".
// 401 missing_api_key / invalid_api_key carry details.how_to_fix + details.where_to_get_one.Full table: ERRORS.md.
Transaction states (26)
Sandbox scenarios
success— Payment authorizes and settles normallydecline— Provider declines the paymentinsufficient_funds— Funding source reports insufficient fundsprovider_timeout— Provider does not respond in timeprovider_timeout_charged— Provider times out AFTER it actually authorized (the dangerous ambiguous case)provider_unavailable— Provider is temporarily unavailableduplicate_webhook— Provider sends the settlement webhook twicerefund— Payment succeeds then is fully refundedpartial_refund— Payment succeeds then is partially refundeddispute— Payment succeeds then is disputedservice_failure— Payment succeeds but the purchased service failsfunding_unavailable— No funding source is available for this railcredential_creation— A single-use payment credential is issued
Autonomy levels
- 0 — Disabled: No spending permitted
- 1 — Approve every transaction: Every transaction requires human approval
- 2 — Threshold-based: Auto-approve below the autonomous limit
- 3 — Budget autonomy: Operate freely within policy budgets
- 4 — Mission autonomy: Authority tied to an active mission and expiry
Agent scopes
wallet.read · transactions.read · transactions.request · transactions.cancel · approvals.read · marketplace.read · marketplace.purchase
