TypeScript SDK
@gowarrant/sdk — a typed client whose amount type will not let you pass a number.
@gowarrant/sdk wraps the API and makes the amount mistake unrepresentable: usdc("1.50") compiles, 1.50 does not.
import { Warrant, usdc } from "@gowarrant/sdk";
const warrant = new Warrant(); // reads WARRANT_API_KEY
const w = await warrant.open({
amount: usdc("1.50"), // never a number
payee: "api.exampledata.io",
spec: { clauses: [{ kind: "http.status", equals: 200 }] },
deadline: new Date(Date.now() + 3_600_000),
});Install
npm install @gowarrant/sdkThe client reads WARRANT_API_KEY from the environment. A wr_test_ key points
at sandbox; nothing else needs configuring.
export WARRANT_API_KEY=wr_test_8f0c1b4e2b1a4d599d0e3a2f7c1b9e44Amounts are a type, not a convention
Every other page in these docs tells you that an amount is an integer string of
the smallest unit, ^[0-9]+$, and that 8400000 as a JSON number is rejected.
The SDK is where that stops being something you have to remember.
import { usdc, amount, parseDecimal, formatAmount } from "@gowarrant/sdk";
usdc("8.40") // Amount — 8400000 of the smallest unit
usdc("1.50") // Amount — 1500000
amount("8400000", "USDC") // Amount, from the smallest unit directly
formatAmount(usdc("8.40")) // "8.40" for display
usdc(8.40) // does not compileAmount is an opaque type. open() takes one, so a raw number or a bare string
is a type error at the call site rather than a 400 at runtime.
addAmounts and scaleAmount do arithmetic without ever converting to a float,
and AmountError is thrown on a value that cannot be represented exactly.
Methods
| Method | Calls |
|---|---|
open(input, idempotencyKey?) | POST /v1/warrants |
get(id) | GET /v1/warrants/:id |
list(input) | GET /v1/warrants |
verify(id, input, idempotencyKey?) | POST /v1/warrants/:id/verify |
release(id, idempotencyKey?) | POST /v1/warrants/:id/release |
partialRelease(id, bps, idempotencyKey?) | POST /v1/warrants/:id/partial |
refund(id, idempotencyKey?) | POST /v1/warrants/:id/refund |
trace(id) | GET /v1/warrants/:id/trace |
receipt(id, detail?) | GET /t/:id |
bench(input) | POST /bench/run |
idempotencyKey is optional on every money-moving call because the client mints
a UUID when you omit one. Pass your own when you want a retry to replay rather
than open a second warrant — see idempotency.
release, partialRelease and refund need a person
These three call endpoints that refuse an API key with 403
user_session_required. They are on the client because the same client is used
from the console's own server, not because an agent key can call them. An agent
opens and verifies; a person settles.
A full run against sandbox
Runnable as-is with a wr_test_ key:
import { Warrant, usdc, formatAmount } 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),
});
console.log(w.id, w.state); // wrt_… held
const after = await warrant.verify(w.id, {
source_url: "https://api.exampledata.io/invoices/8812",
});
console.log(after.state); // released | escalated | refunded | partially_released
console.log(formatAmount(usdc(after.amount)), after.asset);verify returns the warrant in whatever state the policy put it in. Escalating
is one of the answers, not a failure — see
concepts: escalations.
Testing a spec with no money involved
const result = await warrant.bench({
clauses: [{ kind: "data.freshness", maxAgeSeconds: 60 }],
deliverable: {
reached: true,
status: 200,
generated_at: new Date(Date.now() - 3_600_000).toISOString(),
},
});
console.log(result.passed, result.score); // false 0bench needs no key, holds nothing and writes nothing.
Note that score here is a fraction 0–1 from the engine, while a stored
score on a check or escalation is basis points 0–10000. See the callout on
verifiers.
Errors
WarrantError carries the status and the parsed envelope, so you branch on
code rather than parsing a message.
import { Warrant, WarrantError, usdc } from "@gowarrant/sdk";
try {
await warrant.open({ /* … */ });
} catch (err) {
if (err instanceof WarrantError && err.code === "insufficient_funds") {
// Nothing was held. Top up and retry with the same idempotency key.
}
throw err;
}See errors for the code list.
Licence
Apache 2.0. The mark and wordmark are not covered by it.