Quickstart
Open your first warrant in five minutes.
By the end of this page an agent will have paid an API only after the response passed a check you wrote.
npm i @warrant/sdk
export WARRANT_API_KEY=wr_test_...1. Get a sandbox key
Keys are created in the console under Developer → API keys. A sandbox key is
prefixed wr_test_ and can never move real money; see
Sandbox vs production.
Keys belong to agents, not people. A key can open and verify warrants, and it deliberately cannot resolve an escalation — if a key could approve a payment, the approval step would be decorative.
2. Open a warrant
Amounts are strings, never JSON numbers
Every amount crossing the wire is a string of whole units of the asset's
smallest denomination, in both directions. "8400000" is 8.40 USDC. The bare
number 8400000 is rejected, and so is "8.40" — the field is validated
against ^[0-9]+$, so there is no decimal point in it.
This is not a style preference. 9007199254740993 is a legal amount and is not
representable as a JSON number, and a payments API that silently rounds is worse
than one that refuses.
POST /v1/warrants HTTP/1.1
Host: api.gowarrant.xyz
Authorization: Bearer wr_test_...
Idempotency-Key: 8f0c1b4e-2b1a-4d59-9d0e-3a2f7c1b9e44
Content-Type: application/json
{
"amount": "8400000",
"asset": "USDC",
"payee": "api.exampledata.io",
"deadline": "2026-08-20T12:00:00.000Z",
"spec": {
"clauses": [
{ "kind": "http.status", "equals": 200 },
{ "kind": "data.freshness", "maxAgeSeconds": 60 }
]
}
}201
{
"id": "wr_01KZX3KD38A0253NGVXTC8K9MF",
"object": "warrant",
"state": "held",
"version": 1,
"amount": "8400000",
"asset": "USDC",
"released_bps": null,
"payee": "api.exampledata.io",
"spec_hash": "sha256:41f0...8ad2",
"deadline": "2026-08-20T12:00:00.000Z",
"opened_at": "2026-08-13T12:00:00.000Z",
"settled_at": null
}curl -X POST https://api.gowarrant.xyz/v1/warrants \
-H "Authorization: Bearer $WARRANT_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"amount": "8400000",
"asset": "USDC",
"payee": "api.exampledata.io",
"deadline": "2026-08-20T12:00:00.000Z",
"spec": { "clauses": [{ "kind": "http.status", "equals": 200 }] }
}'The funds are now held. Nothing has been sent.
3. Verify the deliverable
Point the verifier at the thing you are paying for. It fetches the URL itself; the agent's own account of what happened is never the evidence.
POST /v1/warrants/wr_01KZX3KD38A0253NGVXTC8K9MF/verify HTTP/1.1
Authorization: Bearer wr_test_...
Idempotency-Key: 1c9d6a02-77f1-4e2c-9c33-9a1d0b6f2e77
Content-Type: application/json
{ "source_url": "https://api.exampledata.io/v2/invoice/8812" }200
{
"id": "wr_01KZX3KD38A0253NGVXTC8K9MF",
"object": "warrant",
"state": "released",
"version": 3,
"amount": "8400000",
"released_bps": 10000,
"settled_at": "2026-08-13T12:00:04.118Z"
}curl -X POST https://api.gowarrant.xyz/v1/warrants/$ID/verify \
-H "Authorization: Bearer $WARRANT_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "source_url": "https://api.exampledata.io/v2/invoice/8812" }'released_bps is basis points of the held amount, so 10000 is all of it.
Had a clause failed, the state would be escalated and the amount would still
be held, waiting on a person. That is the interesting path, and
Escalations covers it.
4. Read the trace
Every settled warrant has a receipt at GET /t/:id, and that route needs no
key at all. It is the link you send the other side when they ask why they were
not paid.
Next
- The warrant lifecycle — the nine states and the legal moves
- Verifiers — the eight clause kinds the engine evaluates
- Idempotency — required on both calls above