Skip to content
Warrantv0.1

Pay an API with conditional release

Hold, check, settle — the whole loop in two calls.

Two calls: open holds the money against a spec, verify checks the deliverable and lets the policy settle it.

export WARRANT_API_KEY=wr_test_8f0c1b4e2b1a4d599d0e3a2f7c1b9e44

Everything here runs against sandbox with a wr_test_ key.

1. Open

curl -sS 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-16T12:00:00.000Z",
    "spec": {
      "clauses": [
        { "kind": "http.status", "equals": 200 },
        { "kind": "data.freshness", "maxAgeSeconds": 60 }
      ]
    }
  }'

"8400000" is 8.40 USDC. It is a string of the smallest unit — send 8400000 as a JSON number, or "8.40", and you get 400 validation_failed. This is the mistake most first integrations make.

The response comes back "state": "held". The money is committed and has not moved.

2. Verify

curl -sS https://api.gowarrant.xyz/v1/warrants/$WARRANT_ID/verify \
  -H "Authorization: Bearer $WARRANT_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "source_url": "https://api.exampledata.io/invoices/8812" }'

The engine fetches the source, evaluates every clause, and hands the score to your policy. The response is the warrant in whatever state the policy chose:

stateMeaning
releasedClean pass. The payee is paid.
partially_releasedA share paid, the rest returned.
escalatedA person has to decide. Funds still held.
refundedNothing paid; the hold returned.

Verify is not a request to pay

It is a request to decide. escalated is a legitimate, successful outcome — it means the policy did its job and the result needs a person. Treat it as a normal branch, not an error.

In TypeScript

import { Warrant, usdc } from "@gowarrant/sdk";

const warrant = new Warrant();

const w = await warrant.open({
  amount: usdc("8.40"),
  payee: "api.exampledata.io",
  spec: {
    clauses: [
      { kind: "http.status", equals: 200 },
      { kind: "data.freshness", maxAgeSeconds: 60 },
    ],
  },
  deadline: new Date(Date.now() + 3_600_000),
});

const after = await warrant.verify(w.id, {
  source_url: "https://api.exampledata.io/invoices/8812",
});

switch (after.state) {
  case "released":            break;   // paid
  case "partially_released":  break;   // paid in part
  case "escalated":           break;   // waiting on a person
  case "refunded":            break;   // not paid
}

usdc("8.40") returns the Amount type. Passing 8.40 does not compile, which is the point of the helper.

Try the spec first

Before spending anything, run the clauses against a sample response:

curl -sS https://api.gowarrant.xyz/bench/run \
  -H "Content-Type: application/json" \
  -d '{
    "clauses": [{ "kind": "data.freshness", "maxAgeSeconds": 60 }],
    "deliverable": {
      "reached": true,
      "status": 200,
      "generated_at": "2026-08-15T11:00:00.000Z"
    },
    "now": "2026-08-15T12:00:00.000Z"
  }'

No key, nothing held. score here is a fraction 0–1; a stored score elsewhere is basis points 0–10000.

What you get afterwards

Every transition leaves a trace, and a settled warrant has a public receipt at /t/:id you can send the payee. They can read why they were or were not paid without an account here.