← Back to Zado

Zado Agent Trust Protocol v0.1

The consumer-side policy layer for AI agents that spend real money.

Field Value
Version 0.1
Status Public draft
Date 2026-04-25
Author Evolving Intelligence AI, LLC
License (spec) CC-BY 4.0
License (reference implementation) See project LICENSE
Canonical URL https://zadofi.ai/protocol/zado-agent-trust-protocol-v0.1

Provenance

This specification describes behavior that is shipped and operating in the Zado backend as of the publication date. It is not aspirational. Every normative claim traces to a backing source file in backend/app/{models,services,mcp,core}/.

The protocol was first exercised end-to-end on 2026-04-03, when an OpenClaw agent registered a Zado spend-scoped token, called check_budget, called authorize_purchase(43.20, "groceries", "Whole Foods") and received an authorized: true response with a transaction id and remaining envelope balance. The agent had no access to the user's bank credentials, account numbers, or transactions outside its bound category. That run is the existence proof for v0.1.

Items labeled "v0.2+ roadmap" are explicitly NOT shipped today. Integrators MUST NOT depend on roadmap items.


1. Introduction

Zado is a policy and permission layer for AI agents that need to make purchases on behalf of a human. It sits above a person's real bank accounts (connected via Plaid) and below the agent-payment rails being built by the card networks.

When an AI agent wants to spend money, three questions must be answered:

  1. Who said it could? — identity and scope (Zado: agent_token.scope)
  2. How much can it spend, on what? — policy and budget (Zado: envelopes, caps, binding, pacing)
  3. How does the money actually move? — settlement (Zado: today, a ledger entry; in v0.2+, optionally a Stripe Issuing one-time virtual card)

Most existing agent-payment work focuses on question 3. Zado focuses on questions 1 and 2 — the policy gate that any settlement rail can consult before money moves.

The spec is intentionally compatible with, and complementary to:

  • Google AP2 (Agent Payments Protocol) — Zado tokens map to AP2 Intent Mandates; authorize_purchase success can produce a signed Cart Mandate.
  • Stripe Machine Payments Protocol (MPP) — Zado is the consumer-side policy layer an MPP-scoped agent can consult before invoking MPP-routed payment.
  • Visa Intelligent Commerce / Mastercard Agent Pay — Zado is the consumer-side budget gate that complements network-side spend controls and tokenization.
  • Model Context Protocol (MCP) — Zado exposes its policy surface via four MCP tools.

What Zado is not:

  • Zado is not a money transmitter. It does not move funds.
  • Zado is not a card issuer in v0.1. (v0.2+ may issue one-time virtual cards via Stripe Issuing — Stripe remains the regulated entity.)
  • Zado is not a replacement for AP2, MPP, Visa IC, or MC Agent Pay. It is a complement to them.

2. Positioning

Zado occupies a layer that, before this spec, had no public name.

Player Layer Primary Consumer Spend Authority Settlement
Zado Agent Trust Protocol (this spec) Consumer policy / permission gate End user (with ADHD-friendly UX) Envelope balance + agent token scope + caps + binding + pacing None today (ledger entry); Stripe Issuing virtual card in v0.2+
Google AP2 Open agent-payment protocol (rails-agnostic) Merchants + agents + payment processors Intent Mandate + Cart Mandate (cryptographic VDCs) Pluggable (cards, wallets, instant rails)
Stripe MPP Agent-payments protocol on Stripe rails Stripe-integrated merchants Stripe-issued credentials, scope-bound Stripe rails
Visa Intelligent Commerce Connect Network-side AI payment platform Visa-accepting merchants Tokenization + spend controls per agent Visa network
Mastercard Agent Pay Network-side agentic payment program All Mastercard cardholders Mastercard Agentic Tokens, consumer-set scopes Mastercard network

The pattern: card networks and protocol-level players answer how money moves once authorized. Zado answers what an agent is allowed to spend, on behalf of which envelope, against which budget, in the first place. A Visa-Intelligent-Commerce-routed payment authorized through MPP can ask a Zado-equivalent policy layer "is this purchase permitted by the user's budget?" before it ever touches the network. Today that consultation is done inside each card-network's own platform; Zado is the first open, MCP-native, real-bank-account-backed implementation of that consumer-side gate.

The single-sentence positioning: Zado is the consumer-side budget gate for AI agents — the layer that says "yes" or "no" to a spend before any settlement rail moves money.

3. Core concepts

This section is normative. Each term is defined once. Every term cites the source file that implements it.

3.1 Envelope

A named pool of money allocated to a spending category for a calendar month. "Groceries: $400 budgeted, $123.50 spent, $276.50 remaining." The envelope balance is the hard ceiling for any agent or human spend in that category. Source: backend/app/models/envelope.py and backend/app/services/envelope.py.

3.2 Envelope-as-Permission

The fundamental design pattern of this protocol: the envelope balance IS the spending limit for any agent bound to that envelope. No separate "agent budget" exists. There is only the user's envelope, which the agent may draw against subject to scope and guards. When an agent asks "can I spend $43?", the answer is computed from the same envelope balance the user sees in the app. There are no parallel ledgers and no agent-only allowances. Source: backend/app/services/agent_spending.py (the authorize_purchase flow checks the live envelope via SpendingService.check_can_afford).

3.3 Agent Token

A SHA-256-hashed bearer credential issued to a single AI agent on behalf of a single user. Token plaintext is shown once at creation and never stored. Tokens carry: id, user_id (FK), name, scope, is_active, spending guards, optional allowed_category_ids, optional pace_multiplier, optional expires_at. Tokens are revocable individually or in bulk (the kill switch). Source: backend/app/models/agent_token.py.

3.4 Scope

A token has exactly one of two scopes:

  • read — May call read-only tools (check_budget, list_envelopes, get_daily_status). Cannot authorize purchases.
  • spend — May call all read-only tools AND authorize_purchase. Subject to all spending guards.

Scope is checked server-side as the first gate in authorize_purchase. Source: backend/app/services/agent_spending.py.

3.5 Envelope Binding

A token MAY be bound to a specific set of envelopes via allowed_category_ids (a JSON array of stable category UUIDs). When bound, the agent can only authorize purchases against those categories. An attempt to spend in any other category is rejected with envelope_not_bound. A null binding means the agent may spend in any of the user's envelopes (subject to all other guards). Legacy allowed_category_slugs is honored for backwards compatibility. Source: backend/app/services/agent_guard.py (check_envelope_binding).

3.6 Per-Transaction Cap

A hard maximum dollar amount per single authorize_purchase call, configurable per token (default $50). Exceeding it returns per_transaction_cap_exceeded. Source: backend/app/services/agent_guard.py.

3.7 Session Cumulative Cap

A rolling total spending limit across all of an agent's purchases since the last session reset (default $100). Exceeding it returns session_cap_exceeded. The session auto-resets after 24 hours of inactivity to prevent permanent lockout from a one-time spike. Source: backend/app/services/agent_guard.py (SESSION_RESET_SECONDS).

3.8 Rate Limit

A maximum of three authorize_purchase calls per minute per agent. Exceeding it returns rate_limited with a retry_after_seconds field. The minute window is sliding, anchored to last_transaction_at. Source: backend/app/services/agent_guard.py (AGENT_RATE_LIMIT).

3.9 Budget Pacing

A guard that rejects purchases consuming budget faster than a sustainable daily pace. Computed as daily_pace = envelope_remaining / days_remaining, then pace_limit = daily_pace * pace_multiplier. Default pace_multiplier = 3.0. Exceeding the pace returns exceeds_budget_pace with the computed daily_pace, pace_limit, and days_remaining. Source: backend/app/services/agent_guard.py (check_budget_pace).

3.10 Kill Switch

A single user-initiated action that revokes ALL of a user's agent tokens simultaneously (POST /agents/revoke-all, exposed in the desktop app under Settings → Agent Access → Freeze All Agents). Revoked tokens immediately return 401 on next use. Designed for the "something is wrong, stop everything right now" moment. Source: backend/app/api/agents.py.

3.11 Human Approval Gate (v0.2+ roadmap)

A future capability where an agent's authorize_purchase request can be deferred to an explicit human approve/deny in the desktop UI before proceeding. Not implemented in v0.1 — today, authorize_purchase is synchronous and decided fully by the guards above. Roadmap.

3.12 Audit Context

Every mutation initiated by an agent is automatically tagged with actor_type="mcp_agent" and actor_details containing the agent's id, name, scope, and current session_spend_so_far. Capture is automatic via AuditMiddleware — no per-route plumbing required. Source: backend/app/core/audit_context.py and backend/app/middleware/audit.py.


4. MCP tools

This section is normative. Zado exposes four Model Context Protocol tools via a local stdio MCP server (backend/app/mcp/server.py) that bridges to the Zado HTTPS API (backend/app/mcp/client.py). All authentication, scope enforcement, guard checks, audit logging, and database access happen server-side at zadofi.ai. The local MCP process is a pure transport layer and cannot bypass policy.

AI Agent → MCP stdio → ZadoAPIClient → HTTPS Bearer → zadofi.ai → policy + envelopes

4.1 check_budget

Scope required: read HTTP backing route: GET /api/spending/category/{category}

Returns the budgeted, spent, and remaining amounts for a single envelope.

Request schema:

Field Type Required Description
category string yes Category slug (e.g., "groceries", "dining", "fun-money")

Success response schema:

Field Type Description
category string Human-readable envelope name
remaining number Available balance in dollars
budgeted number Monthly budgeted amount in dollars
spent number Amount spent so far this month in dollars
percentage_used number spent / budgeted * 100

Example request:

{ "category": "groceries" }

Example success response:

{
  "category": "Groceries",
  "remaining": 276.50,
  "budgeted": 400.00,
  "spent": 123.50,
  "percentage_used": 30.875
}

4.2 list_envelopes

Scope required: read HTTP backing route: GET /api/envelopes/summary?month=YYYY-MM

Returns all envelope balances for the current calendar month.

Request schema: No parameters. The MCP tool implicitly uses the current month in YYYY-MM form (UTC).

Success response schema:

Field Type Description
month string YYYY-MM of the month being reported
total_budgeted number Sum of all envelope budgets
total_spent number Sum of all envelope spending
total_available number total_budgeted - total_spent
envelopes array Per-envelope detail (see below)

Each envelopes[] entry:

Field Type Description
name string Envelope name
budgeted number This envelope's budget
spent number This envelope's spending
remaining number This envelope's available balance
percentage_used number spent / budgeted * 100
status string Envelope health status (e.g., "on_track", "warning", "empty")

Example success response:

{
  "month": "2026-04",
  "total_budgeted": 2400.00,
  "total_spent": 1820.30,
  "total_available": 579.70,
  "envelopes": [
    {
      "name": "Groceries",
      "budgeted": 400.00,
      "spent": 123.50,
      "remaining": 276.50,
      "percentage_used": 30.875,
      "status": "on_track"
    },
    {
      "name": "Dining",
      "budgeted": 200.00,
      "spent": 198.00,
      "remaining": 2.00,
      "percentage_used": 99.0,
      "status": "warning"
    }
  ]
}

If the agent token has allowed_category_ids set, the response is filtered to only those envelopes (server-side, via backend/app/services/agent_guard.py:filter_by_binding).

4.3 get_daily_status

Scope required: read HTTP backing route: GET /api/spending/status

Returns today's spending posture across all envelopes — total available, daily allowance, and any active alerts.

Request schema: No parameters.

Success response schema:

Field Type Description
total_available number Sum across all envelopes (or bound envelopes) for current month
daily_allowance number Sustainable daily spend rate for the rest of the month
days_remaining number Days left in the current month (including today)
alerts array Active spending alerts

Each alerts[] entry:

Field Type Description
category string Envelope name the alert targets
type string Alert type (e.g., "envelope_empty", "pace_warning", "upcoming_bill")
message string Human-readable alert text

Example success response:

{
  "total_available": 579.70,
  "daily_allowance": 96.62,
  "days_remaining": 6,
  "alerts": [
    {
      "category": "Dining",
      "type": "pace_warning",
      "message": "You're spending faster than the envelope allows for the rest of the month."
    }
  ]
}

4.4 authorize_purchase

Scope required: spend HTTP backing route: POST /api/agents/purchase

Authorizes and logs a purchase if and only if the envelope has enough funds AND all guards pass. This is the load-bearing tool of the spec — it implements the envelope-as-permission pattern end-to-end.

Request schema:

Field Type Required Description
amount number yes Purchase amount in dollars (e.g., 43.20)
category string yes Category slug — must match an envelope and (if bound) the token's allowed_category_ids
vendor string yes Merchant or vendor name (free text, e.g., "Whole Foods")

Authorization flow (from backend/app/services/agent_spending.py):

  1. Scope check. Token must have scope == "spend". Otherwise reject with insufficient_scope.
  2. Envelope binding check. If the token has allowed_category_ids, the request's category must resolve to one of them. Otherwise reject with envelope_not_bound.
  3. Guards. In order: per-transaction cap, session cumulative cap, rate limit, budget pacing. Any failure rejects with the corresponding code.
  4. Envelope balance check. The envelope must have enough remaining funds for the purchase. Otherwise reject with envelope_empty.
  5. Atomic commit. Guard state update + transaction creation + envelope-balance update commit together. Any exception rolls back all three.

Success response schema:

Field Type Description
authorized boolean Always true for this shape
transaction_id string UUID of the created transaction
amount number Echoed authorized amount
category string Echoed category slug
vendor string Echoed vendor name
envelope_remaining number Envelope balance AFTER the purchase

Example success response:

{
  "authorized": true,
  "transaction_id": "8f2a1e3c-7b40-4d8e-9c71-c1d2e3f4a5b6",
  "amount": 43.20,
  "category": "groceries",
  "vendor": "Whole Foods",
  "envelope_remaining": 233.30
}

Rejection response schema:

Field Type Description
authorized boolean Always false for this shape
reason string One of the rejection codes below
detail object | string Code-specific context (see below)

Rejection codes:

Code Triggered by Context fields Recommended agent retry
insufficient_scope Token has scope != "spend" detail (string explanation) Do NOT retry. Re-register the agent with scope: "spend" and a fresh token.
envelope_not_bound Request category not in token's allowed_category_ids category, bound_category_ids (array) Do NOT retry the same category. Either rebind the token to include this category, or use a different category that IS bound.
per_transaction_cap_exceeded amount > token.per_transaction_cap limit (the cap, in dollars) Do NOT retry the same amount. Split the purchase into multiple smaller calls (each ≤ cap), or raise the cap.
session_cap_exceeded session_spending_total + amount > token.session_spending_cap limit (cap), session_total (current spent) Do NOT retry. Wait for the 24-hour session reset, or have the user raise the session cap.
rate_limited More than 3 calls in the last 60 seconds limit (3), retry_after_seconds (int) Wait retry_after_seconds then retry.
exceeds_budget_pace Purchase consumes more than pace_multiplier × daily_pace of the envelope daily_pace, pace_limit, days_remaining, envelope_remaining, pace_multiplier Either retry with a smaller amount ≤ pace_limit, or have the user raise pace_multiplier.
envelope_empty Envelope remaining < amount after all prior guards passed detail (recommendation string) Do NOT retry. Either pick a different category or have the user fund the envelope.

A transport-level error (network failure, malformed response from the API) returns reason: "api_error" — this is not a policy decision and the agent SHOULD treat it as a retryable infrastructure fault.

Example rejection:

{
  "authorized": false,
  "reason": "exceeds_budget_pace",
  "detail": {
    "allowed": false,
    "reason": "exceeds_budget_pace",
    "daily_pace": 17.16,
    "pace_limit": 51.49,
    "days_remaining": 6,
    "envelope_remaining": 102.97,
    "pace_multiplier": 3.0
  }
}

4.5 Concurrency and atomicity

authorize_purchase uses a row-level FOR UPDATE lock on the agent token row (backend/app/services/agent_guard.py) to prevent TOCTOU race conditions between guard evaluation and guard-state update. On SQLite (which does not support FOR UPDATE), the underlying connection-level write serialization provides the same guarantee. Concurrent calls on the same token are serialized; concurrent calls on different tokens for the same envelope race on the envelope row, with the loser receiving envelope_empty if the balance flipped between check and commit.

5. Audit log

Every mutation initiated by an agent is automatically tagged with an audit context capturing who acted, what they did, and when. Coverage is automatic via AuditMiddleware — no per-route plumbing required, so future endpoints inherit audit coverage without code changes.

5.1 Actor model

The audit system supports six actor types (backend/app/core/audit_context.py):

Actor type Source
user A human using the desktop UI with a session cookie
assistant The Zado in-app AI coach
rule An automated rule (categorization, recurring detection)
sync Bank sync operations (Plaid / Teller)
system Internal system operations
mcp_agent An external AI agent via an MCP token

For agent-initiated requests, actor_type is set to mcp_agent and actor_details is populated with the agent's identity and current spend posture (backend/app/api/deps.py populates the audit context as part of get_user_context / get_agent_context).

5.2 Audit context schema

For an agent-initiated mutation:

Field Type Description
actor_type string Always "mcp_agent" for agent calls
actor_details.agent_id string (UUID) The agent token's id
actor_details.agent_name string Human-readable agent name (e.g., "Claude Code", "OpenClaw")
actor_details.scope string "read" or "spend"
actor_details.session_spend_so_far string (decimal) Cumulative session spending at the moment of the action
batch_id string (UUID) Optional, groups related changes for bulk undo

Example audit entry for an authorize_purchase success:

{
  "id": "9b7f8c2d-1a4e-4f6b-83c1-d5e6f7a8b9c0",
  "actor_type": "mcp_agent",
  "actor_details": {
    "agent_id": "1d73176f-5a24-4c8e-b21f-3a4b5c6d7e8f",
    "agent_name": "OpenClaw",
    "scope": "spend",
    "session_spend_so_far": "47.50"
  },
  "action": "transaction.create",
  "entity_type": "transaction",
  "entity_id": "8f2a1e3c-7b40-4d8e-9c71-c1d2e3f4a5b6",
  "before": null,
  "after": {
    "amount": 43.20,
    "category_slug": "groceries",
    "vendor": "Whole Foods",
    "agent_token_id": "1d73176f-5a24-4c8e-b21f-3a4b5c6d7e8f"
  },
  "occurred_at": "2026-04-25T18:42:11.382Z"
}

5.3 Coverage and exportability

  • Coverage: All POST/PUT/PATCH/DELETE requests are audited via backend/app/middleware/audit.py. Read-only requests are NOT audited (they do not mutate state).
  • Exportability: The audit log is exportable by the user. The export shape is a stable JSON list of audit entries; the export endpoint is part of the data-export surface (backend/app/api/export/).

6. Security model

6.1 What agents CAN see

For a token with the read scope:

  • Envelope names and balances (budgeted, spent, remaining, percentage used, status) for the user's envelopes — filtered to bound categories if the token has allowed_category_ids.
  • Daily allowance, days remaining in month, and spending alerts.
  • Affordability check results (whether a hypothetical purchase would fit).

For a token with the spend scope:

  • All of the above, AND
  • The transaction id, amount, category, vendor, and remaining envelope balance for any purchase the agent itself authorized.

6.2 What agents CANNOT see

For ANY scope:

  • Bank account numbers, routing numbers, or institution-issued identifiers.
  • Bank connection tokens (Plaid / Teller access_token values). These are encrypted at rest and never returned in any API response, audit entry, or coach context.
  • Raw transaction history outside the agent's bound categories.
  • Personal information beyond what the user explicitly exposes via envelope names (e.g., the user's email, phone, address, or other accounts).
  • Any other agent's tokens, transactions, or activity.
  • Any other user's data.

This separation is enforced by the architecture: the local MCP process has zero direct database access. It can only call the four documented HTTPS endpoints with its Bearer token. There is no MCP tool that returns raw bank data because no such tool exists at any layer (backend/app/mcp/tools.py).

6.3 Defense in depth

A successful authorize_purchase traverses seven distinct enforcement layers, any one of which can deny:

# Layer Where enforced
1 Token hashing (SHA-256 at rest) backend/app/models/agent_token.py (hash_token)
2 Bearer token transport over HTTPS backend/app/mcp/client.py (httpx Bearer header)
3 Token lookup + is_active check (revoked → 401) backend/app/api/deps.py (get_agent_context)
4 Scope enforcement (spend required for authorize_purchase) backend/app/services/agent_spending.py + backend/app/middleware/agent_scope.py
5 Envelope binding check (category must be in allowed_category_ids) backend/app/services/agent_guard.py (check_envelope_binding)
6 Spending guards (per-tx cap, session cap, rate limit, pacing) backend/app/services/agent_guard.py (check_and_record_spend)
7 Atomic commit with FOR UPDATE row lock on the token row backend/app/services/agent_guard.py + backend/app/services/agent_spending.py

A request that passes layer 4 but fails layer 5 produces an envelope_not_bound rejection. A request that passes layer 6 but fails the final balance check produces envelope_empty and the guard-state mutation is rolled back so the rejected attempt does not consume the agent's session cap.

6.4 Threat model (informative, non-normative)

This subsection is informative; it captures the threats the spec is designed to resist and the threats it explicitly does not.

In scope:

  • Stolen agent token used by an attacker. Mitigation: per-transaction cap + session cap + envelope binding + kill switch contain blast radius. An attacker with a stolen token can spend at most min(per_tx_cap, session_cap, envelope_remaining) before either being kill-switched or hitting a guard.
  • Compromised local agent runtime. Mitigation: MCP process has no direct DB access; all enforcement is server-side.
  • Race condition between guard check and guard update. Mitigation: FOR UPDATE row lock on agent_tokens row.
  • Cross-user data leakage via agent token. Mitigation: tokens carry user_id FK; every server-side query filters by user_id.

Out of scope (not addressed by this spec):

  • Compromised user account (session-cookie theft). The user is the root of trust.
  • Compromised Zado server. The spec assumes the policy-evaluation server is operating correctly.
  • Side-channel inference attacks (an agent learning user habits from envelope balances over time).

7. Integration examples

This section is informative. Three patterns cover the common integration shapes. All examples assume the integrator has already registered an agent in the Zado app (Settings → Agent Access → Create Agent) and obtained a Bearer token.

7.1 Claude Code (.mcp.json)

The native Claude Code integration. Add to your project's .mcp.json:

{
  "mcpServers": {
    "zado": {
      "command": "python",
      "args": ["-m", "app.mcp.server"],
      "cwd": "/path/to/zado/backend",
      "env": {
        "ZADO_API_URL": "https://zadofi.ai",
        "ZADO_AGENT_TOKEN": "zado_agent_aBcDeFgHiJkLmNoPqRsTuVwXyZ..."
      }
    }
  }
}

Once Claude Code restarts, the four tools (check_budget, list_envelopes, get_daily_status, authorize_purchase) appear in the available tool set under the zado namespace. (v0.2+ roadmap: a packaged zado-mcp PyPI distribution will replace the python -m app.mcp.server invocation with a single command: "zado-mcp" line.)

7.2 Generic MCP stdio client

For non-Claude agent runtimes, any MCP-compatible client can call Zado tools over stdio. Conceptual Python sketch:

import asyncio, os
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def main():
    params = StdioServerParameters(
        command="python",
        args=["-m", "app.mcp.server"],
        env={
            "ZADO_API_URL": "https://zadofi.ai",
            "ZADO_AGENT_TOKEN": os.environ["ZADO_AGENT_TOKEN"],
        },
    )
    async with stdio_client(params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            budget = await session.call_tool(
                "check_budget", {"category": "groceries"}
            )
            print(budget)
            result = await session.call_tool(
                "authorize_purchase",
                {"amount": 43.20, "category": "groceries", "vendor": "Whole Foods"},
            )
            print(result)

asyncio.run(main())

The same pattern works in any language with an MCP client library (TypeScript SDK, Go SDK, etc.). The wire protocol is identical; only the SDK surface differs.

7.3 OpenClaw end-to-end (real-world reference)

This is the integration that produced the 2026-04-03 first-loop. The flow:

  1. Agent receives a shopping intent from the user via OpenClaw's normal intake (e.g., "buy groceries at Whole Foods, around $45").
  2. Agent calls check_budget("groceries") to confirm the envelope has capacity. Response: { "remaining": 47.50, "budgeted": 400.00, "spent": 352.50, ... }.
  3. Agent reads the response and decides the purchase fits.
  4. Agent calls authorize_purchase(43.20, "groceries", "Whole Foods"). The server runs scope check → binding check → guards (caps, rate limit, pacing) → envelope balance check → atomic commit. Response: { "authorized": true, "transaction_id": "...", "envelope_remaining": 4.30 }.
  5. The transaction appears immediately in the user's ledger with actor_type: "mcp_agent" and actor_details.agent_name: "OpenClaw" in the audit log.
  6. The user can revoke the agent at any time via Settings → Agent Access → Freeze All Agents, instantly invalidating the token.

Settlement note: in v0.1, authorize_purchase records a Zado ledger entry representing a policy decision that the spend is permitted. It does not itself move money on a card rail. v0.2+ roadmap describes how a Zado authorization can bind to a Stripe Issuing one-time virtual card so the authorization and the card-rail charge become a single closed-loop transaction.


8. Compatibility with adjacent protocols and platforms

This section is informative. It describes how Zado v0.1 maps to (or deliberately does not map to) the agent-payment work being done by other players. Zado is designed to complement, not compete with, each of these.

8.1 Google AP2 — Agent Payments Protocol

Google AP2 defines two cryptographically-signed Verifiable Digital Credential (VDC) primitives for agent payments:

  • Intent Mandate — captures the conditions under which an agent may purchase on behalf of a user (human-not-present scenarios).
  • Cart Mandate — captures the user's explicit authorization for a specific cart and price (human-present scenarios).

Zado mapping:

AP2 primitive Closest Zado primitive in v0.1 Notes
Intent Mandate An agent token's combined scope + allowed_category_ids + caps + pace_multiplier Zado expresses Intent as policy stored on the token, not as a signed VDC. v0.2+ roadmap: emit AP2-format Intent Mandate VDCs alongside token issuance.
Cart Mandate An authorize_purchase success response The transaction_id and envelope_remaining are the canonical record of the user's policy authorization. v0.2+ roadmap: produce a signed VDC at authorize_purchase success for AP2 interop.
Verifiable Digital Credentials (VDCs) Not in v0.1 v0.2+ roadmap.

The v0.1 spec is AP2-compatible at the policy layer — a Zado token is sufficient to express the policy half of an Intent Mandate — but does not yet emit AP2 wire-format signed credentials. An agent runtime that wraps Zado calls in AP2-format VDCs today would do the signing externally.

8.2 Stripe Machine Payments Protocol (MPP)

Stripe MPP (launched March 18, 2026) defines scope-bound, time-bound credentials for agents to invoke Stripe-rails payments. MPP focuses on the execution side of agent payments: how an agent presents itself to Stripe, what scope of payment it can initiate, and how Stripe authorizes the rail call.

Zado mapping: Zado is the consumer-side policy layer an MPP-scoped agent can consult before invoking MPP-routed payment. The flow:

  1. Agent receives intent.
  2. Agent calls Zado check_budget and/or authorize_purchase to confirm the spend is permitted by the user's envelope policy.
  3. If Zado authorizes, the agent invokes its MPP credential to actually move money on Stripe rails.
  4. The Zado authorization + Stripe MPP charge are two halves of a closed-loop transaction (today, the linking is done by the integrator; v0.2+ roadmap binds a Stripe Issuing virtual card to a Zado authorization so the link is automatic).

Zado does not duplicate or replace any Stripe MPP capability. They sit at different layers.

8.3 Visa Intelligent Commerce Connect

Visa Intelligent Commerce Connect (April 2026, GA expected June 2026) provides network-side payment infrastructure for AI agents — tokenization, spend controls, multi-network acceptance. Like MPP, it focuses on the execution side, not consumer-side policy.

Zado mapping: Zado is the consumer-side budget gate that complements network-side spend controls. A Visa-Intelligent-Commerce-routed agent purchase can consult a Zado-equivalent policy layer for envelope-level authorization before the network sees the request. v0.2+ roadmap: Zado publishes a network-callable HTTPS endpoint (POST /api/agents/network/preauth) that returns a normalized permitted: true|false response keyed by Visa agent ID for direct integration with network-side tokenization flows.

8.4 Mastercard Agent Pay

Mastercard Agent Pay (rolling out to all US Mastercard cardholders by Q4 2026 holiday season) provides Agentic Tokens that consumers can configure with per-agent spend scopes. Like Visa IC, this is network-side and consumer-controlled — but the consumer interface is provided by Mastercard's own platform.

Zado mapping: Zado offers an alternative consumer surface for the same job — bank-account-backed (via Plaid) rather than card-network-bound, with envelope semantics that fit how ADHD-impacted users actually budget. The two are not mutually exclusive: a consumer can have both a Mastercard Agentic Token (for card-rail purchases) AND a Zado token (for any ACH-funded purchase or any agent-runtime that uses MCP rather than a card network's SDK).

8.5 Model Context Protocol (MCP)

Zado tools are exposed via standard MCP. The protocol-level conformance:

  • All tools registered via FastMCP.tool() (backend/app/mcp/server.py).
  • Stdio transport (local-only in v0.1).
  • No bespoke extensions to the MCP protocol — any MCP-compatible client works.

v0.2+ roadmap: HTTP+OAuth2.1 transport per the MCP Authorization spec, allowing remote Zado MCP without a local Python process.

9. Future capabilities (v0.2+ roadmap)

The following capabilities are explicitly NOT shipped in v0.1. Integrators MUST NOT depend on them today. They are listed here so partners can plan and so the spec's evolution is transparent.

  • Human Approval Gate. A UI inbox in the desktop app where an agent's authorize_purchase request can be deferred to an explicit human approve/deny before it commits. Today, authorize_purchase is synchronous and decided fully by the policy guards. The v0.2 design adds an optional requires_human_approval: true flag on the token (or per-request) that parks the request in a pending state, fires a notification, and resolves on user action.
  • Stripe Issuing one-time virtual card binding. A successful authorize_purchase issues a single-use virtual card via Stripe Issuing bound to the authorized amount, vendor (where supported), and a short expiry. The card-rail charge and the Zado authorization become a closed loop that reconciles automatically via Plaid transaction sync.
  • Refund and dispute reconciliation. When a merchant refunds an agent-initiated purchase, the corresponding envelope balance auto-corrects and the agent's session_spending_total credits back. Dispute lifecycle events update the audit trail.
  • AP2 Verifiable Digital Credential emission. Token issuance produces a signed AP2 Intent Mandate VDC; authorize_purchase success produces a signed AP2 Cart Mandate VDC. Lets Zado-authorized spend flow into AP2 ecosystems without external signing.
  • HTTP+OAuth2.1 MCP transport. Per the MCP Authorization spec, remote Zado MCP without a local Python process. v0.1 is stdio-only.
  • Multi-user envelope approval. Couples and small businesses where envelope spending requires N-of-M human approvers. Same envelope-as- permission model, with an additional approval-quorum primitive on the envelope.
  • Network-side preauth endpoint. POST /api/agents/network/preauth returns permitted: true|false with envelope context for direct consumption by Visa Intelligent Commerce or Mastercard Agent Pay network-side flows.

The version that ships any of these capabilities will increment to v0.2 or later per the versioning policy in Section 10.


10. License and versioning

10.1 License

This specification is licensed under CC-BY 4.0. You may share, adapt, and build upon it for any purpose, including commercial use, with attribution to Evolving Intelligence AI, LLC and a link to this canonical URL.

The reference implementation in this repository is licensed under the project's existing license (see project root LICENSE).

10.2 Versioning policy

The spec follows a relaxed semantic versioning model:

  • Patch versions (0.1.x): editorial corrections, clarifications, fixes to ambiguous wording. No behavioral changes. No deprecations.
  • Minor versions (0.x): MAY add new tools, fields, rejection codes, or scopes. MUST NOT remove or rename existing tools, fields, rejection codes, or scopes. MUST NOT change the meaning of an existing field.
  • Major versions (1.0+): MAY break compatibility. A migration guide MUST accompany any major version.

An integrator targeting v0.1 can rely on every documented surface continuing to work in any v0.x release.

10.3 Citation

Cite this spec as:

Zado Agent Trust Protocol v0.1, Evolving Intelligence AI, 2026-04-25. https://zadofi.ai/protocol/zado-agent-trust-protocol-v0.1

BibTeX is in docs/protocol/README.md.


Specification version: 0.1 Date: 2026-04-25 Author: Evolving Intelligence AI, LLC Spec license: CC-BY 4.0 Reference implementation license: see project LICENSE Canonical URL: https://zadofi.ai/protocol/zado-agent-trust-protocol-v0.1 Source repository: see project README.md Reference implementation source files: backend/app/models/agent_token.py, backend/app/services/agent_spending.py, backend/app/services/agent_guard.py, backend/app/mcp/server.py, backend/app/mcp/tools.py, backend/app/mcp/client.py, backend/app/core/audit_context.py, backend/app/middleware/audit.py.

Source markdown: docs/protocol/zado-agent-trust-protocol-v0.1.md (canonical, repository private)

Specification licensed under CC-BY 4.0.