warrant-client
v0.1.6
Published
Buy resources for an AI agent over x402 on Hedera: a name, an inbox, a phone number, inference and permanent memory, one call at a time.
Downloads
1,108
Maintainers
Readme
warrant-client
Buy what an agent needs, one call at a time, in USDC on Hedera.
A permanent name. An email inbox it owns and receives replies at. Sealed agent-to-agent mail. A real phone number and SMS. Language model inference. Memory written to a file nobody can edit. No API key, no signup, no human in the loop: the account that pays is the identity.
Console · Docs · Ledger · Catalogue · Spec · Skill file · MCP · Source
An agent that has to ask a human before it can send an email is not autonomous. This is the other half: it pays for what it uses, and a contract on Hedera decides whether it may.
npm i warrant-clientQuick start
The fastest path from zero to an agent with an on-chain name, a real inbox, and a memory that outlives the process. Every paid step is a real settlement on Hedera, and every resource is owned by the account that paid for it.
npm i -g warrant-client
# 1. Make an agent. The key is generated on your machine and sent nowhere;
# it lands in ~/.warrant/agent.json, readable only by you.
warrant create
# 2. Fund it. Prints a QR and walks both steps: HBAR at the Hedera faucet,
# whose transfer creates the account and gives it its 0.0.x id, then USDC
# at Circle's, which asks for that id. Watches until the money lands.
# After this, buying needs no HBAR: the facilitator sponsors the fee.
warrant fund
# 3. See what is for sale, and what it costs. Free, and needs no key.
warrant catalogue
# 4. Mint the agent's identity — soulbound ERC-721 on Hedera ($0.10)
warrant buy identity.mint
# 5. Claim an inbox it owns and receives replies at ($1.00)
warrant buy email.inbox --name scout
# 6. Send a real email from it ($0.20)
warrant buy email.send --from [email protected] --to [email protected] \
--subject "Hello" --body "from the agent, paid for by the agent"
# 7. Ask a model, paid per call ($0.02)
warrant buy inference --prompt "What is Hedera in one sentence?"
# 8. Write something it will still know tomorrow ($0.05)
warrant buy memory.write --content "the peer at 0x7510 answers on [email protected]"
# 9. Check what it owns and what it has spent
warrant status
# 10. Search real phone inventory (free — an agent that cannot see the price
# before it commits is not choosing)
warrant read '/v1/phone/search?country=US&area=415'Without installing, put the package name in front, because the command is
warrant and the package is warrant-client:
npx --package warrant-client warrant catalogueIn code
import { Warrant } from "warrant-client";
const warrant = new Warrant({
accountId: process.env.WARRANT_ACCOUNT_ID!,
privateKey: process.env.WARRANT_PRIVATE_KEY!,
});
const { result, settlement } = await warrant.buy("inference", {
prompt: "summarise x402 in one line",
});
console.log(result); // the resource
console.log(settlement); // the Hedera transaction it was paid for inOne call replaces three: the request that comes back 402, the signed payment,
and the retry. Endpoints and prices are read from the service's own catalogue,
so a service that adds a resource does not need this package republished.
Command reference
Prices are quoted in USDC and settled at request time over
x402 — one payment per call, no
subscriptions, no minimum. Flags become the JSON body: --prompt hello is
{"prompt":"hello"}.
Free — no key, no account, no payment
| Command | Cost | Notes |
|---|---|---|
| warrant create | free | Generates a secp256k1 key locally and saves it to ~/.warrant/agent.json (mode 600). Nothing is sent anywhere. --force makes another. |
| warrant fund | free | Prints the faucet links, then watches the mirror node until the first transfer lands and the account exists. --once to check and exit. |
| warrant status | free | Balance, spend, and what this agent owns. |
| warrant catalogue | free | What is for sale and the live price. The same table the server charges against. |
| warrant read /v1/receipts | free | Every settled purchase, signed. Add ?limit=3. |
| warrant read /v1/purchases | free | The public ledger, including what was refused. |
| warrant read /v1/contracts | free | Addresses of the three contracts, and the receipt issuer. |
| warrant read /v1/stats | free | Totals: purchases, agents, spend. |
| warrant read /v1/agents/0.0.10514332 | free | What one agent owns and what it has left. |
| warrant read '/v1/phone/search?country=US&area=415' | free | Real inventory, before you commit to buying one. |
| warrant read /v1/memory/<fileId> | free | Read back a memory file from Hedera. |
| warrant read /v1/email/inbox/<address> | free | Read what arrived in an inbox the account owns. |
Paid — settled in USDC, one payment per call
| Command | Cost | Status | Notes |
|---|---|---|---|
| warrant buy identity.mint | $0.10 | ✅ Live | Soulbound ERC-721 on Hedera's EVM, one per address, plus the public key other agents seal mail to. Optional --metadataURI, --encryptionKey. Needs WARRANT_AGENT_ADDRESS. |
| warrant buy inference --prompt "…" | $0.02 | ✅ Live | One language model call, returned in OpenAI shape. Optional --model, --maxTokens. |
| warrant buy email.inbox --name scout | $1.00 | ✅ Live | Provisions [email protected], owned by the paying account, and receives replies — not just outbound. |
| warrant buy email.send --from … --to … --subject … --body … | $0.20 | ✅ Live | A real email from an address the agent owns, via Resend. |
| warrant buy email.sealed --from … --to … --toAgent 0x… --subject … --body … | $0.25 | ✅ Live | Encrypted to the recipient's on-chain key. This service relays it and cannot read it. |
| warrant buy memory.write --content "…" | $0.05 | ✅ Live | A permanent Hedera file, up to 4096 bytes, that nobody — this service included — can edit. |
| warrant buy phone.provision --country US | $0.50 | ✅ Live | A real number in 170+ countries, SMS capable, via Telnyx. Or --phoneNumber +1… from a search. |
| warrant buy sms.send --from +1… --to +1… --text "…" | $0.01 | ✅ Live | One text from a number the agent owns. |
Nothing is listed as coming soon. An offer whose provider is not configured is not shown and not sold, so the catalogue never advertises a price that will not be honoured.
Where the faucets are
An address becomes an account on Hedera the moment the first transfer reaches
it, and only then does it have the 0.0.x id that faucets ask for. So funding a
brand-new agent is two steps, and warrant fund walks you through both — with a
QR code, because the faucet is usually on a laptop and the wallet on a phone.
| Step | Where | What you paste |
|---|---|---|
| 1. Create the account | portal.hedera.com/faucet | The 0x… address from warrant create. The HBAR transfer is what brings the account into existence and gives it its 0.0.x id. |
| 2. Get USDC | faucet.circle.com | The 0.0.x account id, not the 0x address — choose Hedera Testnet. |
Run warrant fund again after step 1 and it picks up the new account id, shows
it with a HashScan link and a fresh QR, and then watches for the USDC to land.
Buying through Warrant needs no HBAR at all after that: the facilitator sponsors
the network fee.
How payment works
- Call a paid endpoint with no payment. It answers
402 Payment Requiredwith the amount, the asset and where to pay. - Sign an x402 payment for exactly that amount and retry the same request with
a
payment-signatureheader. - The resource runs, and its owner is the account that paid. The settlement id
comes back in the
payment-responseheader.
This package does all three. Settlement is USDC 0.0.429274 on
hedera:testnet, through the Blocky402
facilitator, which sponsors the network fee — so a paying agent carries
stablecoin and no HBAR.
The limit that makes this safe
The interesting part of Warrant is not that an agent can pay. It is that it can be stopped.
An owner funds an AgentTreasury contract on Hedera with a lifetime cap, a
rolling window ($5 a day), and an allowlist of resource kinds. The agent draws
from it without asking anyone, and the contract refuses an out-of-scope or
over-budget draw before any money moves. The refusal comes from the chain, not
from a vendor's database, and it holds even if this service disappears.
const { result } = await warrant.buy("inference", { prompt: "…" });
// → WarrantError: WindowCapExceeded — wanted 20000 atomic, remaining 0,
// reopens at 2026-09-13T13:51:27Z| Contract | Address |
|---|---|
| AgentIdentity | 0xb791348d9896Bce2FA5cA724d8F548413AcbaDf6 |
| ResourceMarket | 0x7442BdF9240e42Df0B25CedefeAF13799Fd377C0 |
| AgentTreasury | 0xF33E2E0ecc982416f788759083129de6A147a1FE |
Registration and listing are permissionless. Anyone may register an identity and anyone may list a resource, because a registry behind an admin key is a customer list, not a registry.
Receipts
Every settled purchase is signed over a canonical digest, so it can be checked without trusting this service:
warrant read '/v1/receipts?limit=1'digest = sha256(version|id|agent|kind|resource|amount|asset|network|settlement|issuedAt)Rebuild the digest from the fields, recover the signer, compare it with the
issuer published at /v1/contracts, and read the settlement back from Hedera's
mirror node rather than from us.
The one error that matters
A payment settles before the resource runs, so an upstream failure can happen
after the money has moved. That comes back as a WarrantError with
settled: true, and retrying it buys the failure twice.
import { WarrantError } from "warrant-client";
try {
await warrant.buy("email.send", { from, to, subject, body });
} catch (err) {
if (err instanceof WarrantError && err.settled) {
// charged, and the resource failed. Collect the receipt, do not retry.
}
}err.free is the inverse, and is true whenever nothing was charged — a bad
request, a refusal by the limit, an unknown kind. Those are safe to correct and
repeat.
API
| | |
|---|---|
| new Warrant({ accountId, privateKey, baseUrl?, network? }) | A paying client. baseUrl defaults to the hosted service. |
| warrant.offers(refresh?) | The whole catalogue, cached after the first read. |
| warrant.offer(kind) | One offer, or undefined. |
| warrant.buy<T>(kind, body?, agentAddress?) | Pay for one call. Returns { kind, result, settlement }. |
| warrant.read<T>(path) | Any free endpoint. |
Environment
warrant create saves these for you, so set them only to override it.
| Variable | What it is |
|---|---|
| WARRANT_WALLET | Where the key lives. Defaults to ~/.warrant/agent.json. |
| WARRANT_ACCOUNT_ID | The Hedera account paying, e.g. 0.0.10514332. |
| WARRANT_PRIVATE_KEY | Its private key, DER or hex. Read it from the environment; never commit it. |
| WARRANT_AGENT_ADDRESS | The agent's EVM address. Only identity.mint needs it. |
| WARRANT_BASE_URL | Point at a different service. Defaults to the hosted one. |
Other ways in
- Plain HTTP — everything above is one
curland a signature away. The spec is at/openapi.json. - MCP — the same catalogue as tools, through the gateway listed on Bazantic:
https://qmt6sdhe5ffmva3zl2iagc6hkm.bazgateway.com/mcp - Recipes — agent-starter-kit stands up a new agent end to end; spend-audit reads back what one spent and what it was refused.
- Coding agents — drop in
/skill.md.
Licence
MIT. Source at github.com/martinvibes/warrant.
