Escrows

Create a conditional deal, attest delivery, and read its state. Release is the contract's job, not an endpoint.

Escrow over the API is the mechanism escrow describes: funds locked in a programmable contract on Base or Solana, settlement in USDC, release decided by the conditions the parties wrote — never by us.

Create an escrow

POST/v1/escrows

Callable with an agent key (the agent is the buyer) or an organization key naming the buyer. The request runs the full policy evaluation first: an escrowed deal that breaks a mandate is denied before anything is locked, and the response then carries the denial instead of an escrow.

FieldTypeRequiredMeaning
agentstringorg keyThe buyer
sellerstringyesCounterparty: another agent, or a settlement address
amountnumberyesAmount to lock
currencystringyesUSDC
chainstringyesbase or solana
condition.typestringyesattestation — a named party confirms delivery; deadline — time decides
condition.attestorstringif attestationWho may attest: a principal or an agent
condition.deadlinestringyesWhen an unmet condition refunds the buyer
arbiterstringnoA third party both sides chose, written in before funding
metadataobjectnoUp to 20 string keys
{
  "escrow": "esc_2f8d",
  "state": "created",
  "decision": "dec_77b1",
  "agent": "agt_procurement_01",
  "seller": "agt_datamart_02",
  "amount": 1250.00,
  "currency": "USDC",
  "chain": "base",
  "contract": "base:0x5f3a...9c1e",
  "condition": { "type": "attestation", "attestor": "role:procurement_lead", "deadline": "2027-09-15T00:00:00Z" },
  "arbiter": null,
  "disputed": false,
  "created_at": "2026-09-01T10:20:00Z"
}

contract is the on-chain address holding the funds — public, and checkable on the chain itself without asking us. Public in both directions: whoever has the address reads the amount, both sides and the full state history, which is what the chain shows.

Lifecycle

StateMeaningWhat moves it on
createdContract deployed, waiting for fundingBuyer's funds arrive at contract
fundedFunds lockedAttestation, or the deadline
deliveredCondition attestedRelease runs on the contract
releasedFunds with the seller; deal closed—
refundedFunds back with the buyer; deal closed—

Each transition fires the matching webhook: escrow.funded, escrow.delivered, escrow.released, escrow.refunded.

Attest

POST/v1/escrows/{id}/attest

Records that the condition was met. Only the named attestor may attest — the call is authorized against that identity, not against whoever holds an organization key. The state moves to delivered and release runs.

Dispute

POST/v1/escrows/{id}/dispute

Either side may dispute. It sets disputed: true and fires escrow.disputed — and changes nothing else by itself. Without an arbiter, a contested deal follows the same rule as any other: condition unmet by the deadline, contract refunds the buyer. An arbiter named at creation can release or refund within the terms the parties agreed.

Why there is no release endpoint

Because we cannot build one. The funds sit in a contract we do not control, on conditions we did not write — nobody at Saifuro can release an escrow early or freeze one, and an API that pretended otherwise would be describing a different product. Attest, dispute, and read are the whole surface.

Read

GET/v1/escrows/{id}
GET/v1/escrows?agent=agt_procurement_01&state=funded

Filters: agent, seller, state, disputed. Paginated.

Fees

0.25% of the escrowed amount, minimum $1 per deal, charged to the customer that created the escrow when it releases, on top of the settlement fee on the released amount. A refunded or expired deal carries no fee, and nothing accrues while funds sit. Same numbers as pricing.

On this page