Specification

The handoff protocol

Four calls. The assistant does the first three. A human does the fourth, in a browser, on purpose.

Step 1

Open a scope session

The assistant describes what its human needs, in priced units. It can call this repeatedly with the same session_id as the conversation changes. Nothing is binding yet.

POST /api/public/scope
{
  "caller_label": "Acme buying assistant",
  "note": "Q1 outbound list for UK fintech",
  "items": [
    { "sku": "records", "quantity": 25000 },
    { "sku": "enrichment", "quantity": 25000 }
  ]
}

-> { "session_id": "...", "line_items": [...], "preview_total_cents": 137500 }

Step 2

Lock a signed quote

The quote freezes the price. It is signed with a server-held key, valid for 30 minutes, and stored insert-only — a later scope change issues a new quote that points back at this one.

POST /api/public/quote
{ "session_id": "..." }

-> {
  "token": "q_...",
  "total_cents": 137500,
  "valid_until": "2026-01-01T12:30:00Z",
  "pay_url": "https://.../pay/q_...",
  "receipt_url": "https://.../api/public/receipt/q_...",
  "human_step_required": true
}

Step 3

Hand the link to a human

The assistant does not follow pay_url. It gives it to its human, with the total and the expiry. The page shows the line items, who scoped them, and the countdown before anyone can confirm.

There is no agent credential, API key or token that completes a payment. The confirmation timestamp can only be written by a browser session, and the database refuses to mark anything paid without it.

Step 4

Poll for the receipt

The assistant polls the receipt endpoint. Before the human acts it reads awaiting_human; after payment it gets the receipt, including the moment the human confirmed.

GET /api/public/receipt/q_...

-> {
  "status": "paid",
  "signature_valid": true,
  "receipt": {
    "paid_at": "...",
    "human_confirmed_at": "...",
    "amount_cents": 137500,
    "confirmation": "Paid by a human in a browser. No agent credential was used."
  }
}

Rule

Expiry and re-quoting

A quote past valid_until cannot be paid. The payment page offers a re-quote instead, and the new quote records the one it replaced, so the trail of what was offered when stays intact.

Connect

Same protocol over MCP

Assistants that speak MCP get the same operations as tools — list_catalog, scope_work, request_quote, check_receipt — with no payment tool, because there is no payment tool to expose.

{
  "mcpServers": {
    "ai2ai-handoff": { "url": "https://<this-site>/mcp" }
  }
}

Scope

What this deliberately is not

Not autonomous agent spending. Not a wallet, not delegated card credentials, not a bot with a spending limit. One currency, full payment, no subscriptions in this version. The human step is the product.