Paid is not accepted
Three different records get blurred into one in agent commerce, and only one of them answers the payer's question:
- The payment receipt proves money moved, and that the server claims it delivered. It is a record about the payment.
- Execution receipts prove what ran and what came out, step by step, in a form anyone can re-check. They are records about the work.
- An acceptance records that a receiver opened the delivery, checked it, and said yes, with a receipt of its own. It is the record about the decision.
Escrow and upfront flows commit the money before any of the second or third records exist, and the payment layer has no built-in way to ask for them. So the check has to happen on the work side: before release, read the acceptance. If there is no acceptance, there is nothing to release against yet.
The check-before-release pattern, with what exists today
- The work runs as recorded calls. The working agent does the job through Zambo, so every tool call leaves a verifiable receipt: the tool, the inputs, the saved output, the time.
- The job is handed over as one link. The agent asks Zambo to hand the work over to the buyer. The handover gathers the session's receipts, redacts emails, phone numbers, and keys, and publishes one page: every step, what was not checked, and a summary receipt closing the set. The receiver needs no account to open it.
- The receiver checks, then decides. On the handover page the receiver presses Verify all to re-check every step in their own browser, can try to break a step, and then accepts or flags the delivery. The answer mints its own receipt, pinned to the handover's original Merkle root, and the status flips to accepted. An agent receiver can do the same through the handover_accept tool.
- The payer reads before releasing. Fetch the handover's acceptance record at /handover/<id>/acceptance.json and read the release_hint. It reads hold while the handover is pending, and still reads hold when the work is flagged. It reads ok_to_release only when a client has accepted, with decided_by: "client" and the decision time in decided_at. A machine can gate a release on that one field; a person reads the same answer on the handover page. The release decision stays with whoever holds the funds.
The full mechanics are documented at zambo.dev/docs/handover, including the respond API a buyer agent uses at /api/v2/handover/<id>/respond.
Two live handovers, two different answers
Accepted: safe to release against
Handover 45434a17 was created on October 16, 2026 at 14:31 UTC with four steps: live prices for BTC ($82,212.68), ETH ($2,472.76), and SOL ($106.17), each fetched live, plus a prompt_shield screen that came back safe. Its status reads accepted. The events list in its JSON twin shows why: an accept action recorded at 14:31:51 UTC, with its own receipt (14cb8b21-d163-4c46-a692-b4947e3719ca), naming the handover's original Merkle root (002e64ec909f535c...). A payer reading that page sees a delivery that was checked and taken, bound to the exact steps that were delivered. Its acceptance record says the same thing to a machine: status "accepted", decided_by "client", decided_at 2026-10-16T14:31:51Z, release_hint "ok_to_release", 4 steps checked and 0 unchecked.
Open: not yet
Handover bd7e5325 holds five steps and its status still reads open. Its not_checked list names two steps that did not complete successfully. Nobody has accepted it. That is what "not yet" looks like when it is written into the record instead of argued about later: a payer who reads first does not release against this page today. Its acceptance record reads status "pending" with release_hint "hold": the machine-readable form of not yet.
Both pages are public and live. Open them, run Verify all, and read the events yourself. The difference between the two states is the entire pattern.
What a paying agent reads
Every handover has a dedicated acceptance record at /handover/<id>/acceptance.json, readable with no account, built for exactly this read. Its fields are the plain ones:
- status: pending, accepted, or flagged.
- release_hint: hold for pending and flagged; ok_to_release only after a recorded client acceptance.
- decided_by and decided_at: who the record says decided ("client"), and when. Both null while nobody has decided.
- steps_total, steps_checked, steps_unchecked: how much of the delivery carries checked receipts, plus the check_scope the record prints about itself: "Receipt bytes only; not accuracy or human review of every step."
- receipt_urls: a link to every step's receipt, openable by anyone.
- aer1_version: the receipt format revision the record runs on, "draft-zambo-aer1-23".
The fuller record sits behind it: the handover's JSON twin at /handover/<id>.json carries the steps, the events list (each answer with its action, its timestamp, its own receipt id, and the original Merkle root it pins to), and the not_checked and not_proven lists.
One rule the record enforces on itself: the agent that created the handover cannot set it accepted. Ask through the MCP handover_accept tool from the creating session and the call is refused, error code "forbidden": "Creator authority cannot accept this handover." The web respond path under creator authority returns HTTP 403 with the same refusal. Only the client side can move release_hint to ok_to_release.
A person reads the same facts on the page in a browser. The record does not care which side of the transaction is software and which is human; the check is the same check.
What Zambo is not, in this pattern
Zambo never holds funds. It is not an escrow, it does not release or reverse payments, and it does not decide disputes. The money moves on the rail the buyer and seller chose; Zambo produces the work record and the acceptance record that the payer reads first. The acceptance also does not prove the work is correct, and the record says so itself: every handover prints a not_proven list (outside timestamp, issuer identity, result correctness), and the acceptance event carries the notice that the visitor's identity was logged by Zambo, not identity-verified. A verifiable receipt shows the saved result has not changed since it was recorded; it does not prove the result was right. What the pattern removes is the blind spot, paying against a claim. What it never removes is the payer's judgment.
Questions, answered
How do I prove an AI agent did the work before I release payment?
Have the work done through Zambo so every step leaves a verifiable receipt, then have the agent hand the job over as one handover link. The receiver opens the link with no account, presses Verify all to check every step in the browser, and accepts or flags the delivery. The accept mints its own receipt, pinned to the handover's original Merkle root, and the handover status flips to accepted. Release payment only after the acceptance record at /handover/<id>/acceptance.json reads status accepted, decided_by client, and release_hint ok_to_release. While it reads pending with release_hint hold, the work has not been accepted yet, whatever the payment receipt says.
Does a payment receipt prove the work was delivered?
No. A payment receipt proves the payment was received and that the server claims it delivered. It does not show what was delivered, whether it matches what was agreed, or whether anyone checked it. Delivery and acceptance are separate records: execution receipts show what ran and what came out, and an acceptance records that a receiver reviewed the delivery and said yes.
How can a paying agent check acceptance without a human in the loop?
Fetch the handover's acceptance record at /handover/<id>/acceptance.json on zambo.dev. It returns the status (pending, accepted, or flagged), decided_by, decided_at, and a release_hint: hold while the work is pending or flagged, ok_to_release only after a client has accepted. Gate the release on that field. The fuller record sits in the handover's JSON twin at /handover/<id>.json, with the steps, their receipt links, the events list, and the not_checked and not_proven lists. A buyer agent can also respond through the handover itself: the docs describe the respond API at /api/v2/handover/<id>/respond. And the creating agent cannot shortcut it: a self-accept from the creator's session is refused (the MCP call returns a forbidden error; the web path returns HTTP 403).
What if the receiver never accepts, or flags the work?
Then the record says so, and that is the protection. With no accept, the status stays pending and the release_hint stays hold; a flag records the objection with a receipt of its own, and a flagged handover keeps release_hint at hold too. Nothing in the record can be quietly rewritten afterward, because each step and each answer is a verifiable receipt. Whether you release, renegotiate, or walk away stays your decision; Zambo never holds the funds, so there is no Zambo dispute desk. What you get is evidence that was created at delivery time, not assembled after the argument started.
Does Zambo hold the money or act as escrow?
No. Zambo never holds funds, never releases them, and never reverses a payment. Agents pay for Zambo itself in USDC on Base through x402, and people pay with a card through PayPal, but the commerce between buyer and seller runs on whatever rail they chose. Zambo's part is the work record and the acceptance record: the thing a payer reads before the money moves.