Envelope: Quote / Proof / Receipt
Three objects describing one payment. Shared rules: JWS (Ed25519), amounts as
decimal strings, stipendex: "<type>/0.1". Mandate and Confirmation —
mandate-wallet.md.
Quote — the receiver's offer
A single-use offer: "pay X — get Y". Issued by the receiver in response to an unpaid call (see transports.md).
{
"stipendex": "quote/0.1",
"quote_id": "q_8f3d1c…",
"receiver": {
"id": "mcp:exa-search",
"key": "ed25519:GfHq…",
"registry": "https://registry.4agents.io/receivers/exa-search"
},
"amount": { "value": "0.0042", "currency": "USD" },
"description": "web_search: ~3k tokens",
"rails": [
{ "scheme": "account", "facilitator": "https://pay.4agents.io" },
{ "scheme": "x402", "network": "base", "asset": "USDC", "to": "0x9a…" }
],
"session": { "id": "s_42", "mode": "tab", "cap": "5.00" },
"expires_at": "2026-07-16T12:05:00Z",
"sig": "<receiver JWS>"
}
| Field | Req. | Rules |
|---|---|---|
quote_id |
yes | single-use; a second presentation MUST be rejected |
receiver.id |
yes | stable identifier (mcp:<slug> or URL) |
receiver.key |
yes | signing key; MUST match the key in the registry descriptor if the receiver is verified |
receiver.registry |
no | link to the trust registry descriptor |
amount or estimate |
yes (exactly one) | amount — fixed price (service); estimate — estimate with tolerance (checkout/metered), below |
description |
yes | human-readable: shown to the principal during HITL |
rails[] |
yes | acceptable settlement schemes; per-scheme fields in rails.md |
session |
no | a tab-aggregation offer, below |
expires_at |
yes | SHOULD: minutes for service, 10–15 minutes for checkout |
estimate + tolerance (amounts unknown upfront)
For metered tools and goods checkout the final amount is known only after execution (tokens, shipping, tax, feed price drift):
"estimate": {
"value": "84.00",
"currency": "USD",
"tolerance": { "value": "8.40" }
}
Semantics: the wallet authorizes (holds) estimate + tolerance; only the confirmed
final amount ≤ estimate + tolerance may be captured (invariant I3). Exceeding the
tolerance is not an "over-capture" but a new cycle: re-quote and re-confirmation.
The mechanics are borrowed from 4agents order-flow v2.1 ("ACCEPT is the source of
truth for the amount").
session — tab session (micropayment aggregation)
Per-call settlement is expensive and unnecessary for micro-amounts. A receiver MAY offer a tab:
mode: "tab",cap— the session ceiling. Opening a tab is a hold oncap(or an internal pre-authorization on theaccountrail).- Subsequent Quotes of the same session (
session.id) are accepted by the wallet without network settlement: it checks limits locally, accumulates the total, signs the increment. - Settlement happens once: on session close, on reaching
cap, or on timeout. The closing Receipt lists allquote_ids of the session and the total. capMUST fit within the mandate limits — otherwise the tab is not opened.
Proof — proof of payment
Issued by the wallet after successful settlement (or hold) on the chosen rail.
{
"stipendex": "proof/0.1",
"proof_id": "p_c81a…",
"quote_id": "q_8f3d1c…",
"mode": "capture",
"scheme": "account",
"evidence": "<rail-specific: facilitator JWS | tx hash | payment_intent id>",
"mandate_ref": "sha256:9e1b…",
"confirmation_id": null,
"payer_key": "ed25519:BBBB…",
"sig": "<wallet JWS>"
}
| Field | Rules |
|---|---|
mode |
capture (charged, service) or hold (authorized, checkout) |
evidence |
rail-verifiable proof; format is per-scheme (rails.md) |
mandate_ref |
hash of the mandate, not the mandate itself — the principal's limits and allowlist are not disclosed to the receiver (privacy); the full chain is available to the principal and, in a dispute, to the facilitator |
confirmation_id |
reference to a Confirmation when HITL occurred (mandatory for checkout) |
Idempotency: a receiver that has seen a proof_id before MUST return the previous
result/Receipt without re-execution or re-charging.
Receipt — the receiver's acknowledgement
Issued by the receiver after execution. Closes the cycle and serves as evidence in audit and disputes.
{
"stipendex": "receipt/0.1",
"receipt_id": "r_11ac…",
"quote_id": "q_8f3d1c…",
"proof_id": "p_c81a…",
"status": "settled",
"amount_final": { "value": "0.0042", "currency": "USD" },
"result_hash": "sha256:44fe…",
"issued_at": "2026-07-16T12:04:31Z",
"sig": "<receiver JWS>"
}
status |
Meaning |
|---|---|
settled |
charged and executed (service happy path) |
held |
authorized, execution awaits confirmation (checkout before ACCEPT) |
captured |
final amount charged after confirmation (checkout after ACCEPT) |
voided |
hold released without charge (DECLINE / timeout / out of tolerance) |
refunded |
refund (extension; the refund itself is executed by the rail) |
result_hash — hash of the call result (service) or the order identifier
(checkout). The link proof → receipt(result_hash) is the proof of "paid and
received / did not receive".
Idempotency and retry summary
- Retrying a call with the same Proof → the same result, zero new charges.
- Response lost after payment → the agent retries the call with the Proof; the
receiver must restore the Receipt by
proof_id. - Quote expired before payment → an ordinary re-quote; nothing is lost (no money moved).
- Hold expired before capture (checkout) → auto-void; normative timings — checkout-profile.md.