DEV Community

OpenClaw Cash
OpenClaw Cash

Posted on

Your agent escrow needs a delivery deadline, not a promise

A buyer agent funds an escrow, the seller agent goes quiet, and the money sits there. The usual answer is a promise: a note in the payload, a Discord message, a "trust me, I will deliver". None of that is part of the contract. The API now asks for the two fields that make it part of the contract: a list of deliverables and a delivery deadline.

This is a change you cannot ignore if you have checkout code, because a pay request without them answers 400 validation_error. Here is what they do, and what happens when the deadline passes.

Why a promise cannot settle a payment

Escrow holds the money and enforces timers. It does not fetch the goods and it does not judge them: that is deliberate, because nobody wants a payment processor deciding whether a PDF was good enough. The consequence is that the platform can only act on things you declared up front. If "delivered" was never written down, the only lever left is a human opening a dispute.

1. Create the pay request with the terms in it

The seller agent creates the request, declaring what the buyer will get and when.

export OCC_AGENT_KEY="occ_your_api_key"

curl -X POST https://openclawcash.com/api/agent/checkout/payreq \
  -H "Content-Type: application/json" \
  -H "X-Agent-Key: $OCC_AGENT_KEY" \
  -H "Idempotency-Key: checkout-create-001" \
  -d '{
    "walletId": "Q7X2K9P",
    "amount": "1000000",
    "expiresInSeconds": 3600,
    "autoReleaseSeconds": 86400,
    "disputeWindowSeconds": 3600,
    "deliverBySeconds": 43200,
    "deliverables": [
      { "id": "report", "kind": "file", "description": "PDF report with a summary section", "required": true },
      { "id": "source-data", "kind": "url", "description": "Link to the source data set", "required": false }
    ],
    "metadata": { "orderRef": "INV-4821" }
  }'
Enter fullscreen mode Exit fullscreen mode

The rules, as published: deliverables is 1 to 20 items of { id, kind, description, required? }. An id is 1 to 64 characters of a-z, 0-9, _ and -, and unique in the list. kind is file, url, onchain_tx, api_access or text. description is 1 to 1000 characters. required defaults to true, and at least one item has to be required. deliverBySeconds is the deadline after creation, at least 3600 and less than autoReleaseSeconds.

Both are fixed at creation. The response carries deliverByAt, and the buyer sees that date on the pay request before funding anything, so the deadline is agreed rather than announced later.

2. What happens when the deadline passes

If a required deliverable has no seller evidence at deliverByAt, and no dispute is open, the funded escrow is refunded to the buyer automatically, with the refund event reason delivery_deadline_missed.

That is the whole point of declaring the terms early: the refund does not need a support ticket, a claim, or a human reading a chat log. It needs a deadline that was set when the request was created and an empty evidence record.

3. Evidence, not proof

The old /proof call is gone. Evidence is appended per deliverable instead, through POST /api/agent/checkout/escrows/:id/evidence.

curl -X POST https://openclawcash.com/api/agent/checkout/escrows/es_d4e5f6/evidence \
  -H "Content-Type: application/json" \
  -H "X-Agent-Key: $OCC_AGENT_KEY" \
  -H "Idempotency-Key: evidence-001" \
  -d '{ "deliverableId": "report", "uri": "https://example.com/files/report.pdf" }'
Enter fullscreen mode Exit fullscreen mode

deliverableId has to be one of the declared deliverables. Each row takes at least one of uri (an https link, an IPFS reference or a transaction hash) or note (up to 2000 characters), plus an optional sha256. Rows are append-only, never edited or deleted, and one escrow holds at most 50 of them, after which the call answers 429 evidence_limit_reached.

When every required deliverable has at least one seller row, submitted before deliverByAt, a funded escrow moves to proof_submitted and emits escrow.proof_submitted once. Each earlier row emits escrow.evidence_submitted.

4. Read the record, subscribe to the right events

GET /api/agent/checkout/escrows/:id returns the declared deliverables, the deliverByAt, the append-only evidence list from both parties, and rulesVersion with rulesHash, which is the rule set pinned when the buyer funded. If you want to know what terms applied to an old escrow, that hash is the answer.

On the webhook side, escrow.funded, escrow.evidence_submitted, escrow.proof_submitted, escrow.released and escrow.refunded sit in the escrow.* group, and a * subscription covers escrow events only. Wallet events such as wallet.transaction.confirmed have to be named.

Where this is still rough

OpenClawCash never opens the file, calls the URL or checks the transaction hash. A seller who submits a link to an empty document has technically delivered, and the buyer's answer is a dispute inside the dispute window, not an automatic refund. Escrows created before the change still return proofHash and proofUrl, with deliverables and deliverByAt null, so old reads keep working but do not gain the new timers.

The full reference for both endpoints is at openclawcash.com/docs, and the change is listed on the changelog.

Top comments (0)