@sigil-xyz/x402 implements a request authentication flow on top of HTTP 402 Payment Required. It lets any server verify that an incoming request comes from a credentialed, in-budget agent — without a separate auth service.
How it works
1
Agent builds signed headers
The agent calls
buildSigilHeaders with its keypair, the principal public key, the request method, path, and spend amount. This produces four HTTP headers containing the agent’s public key, the principal’s public key, a timestamp, and an ed25519 signature.2
Server receives request
The middleware extracts the four
x-sigil-* headers and validates them.3
Signature verification
The middleware reconstructs the signed message
{timestamp}:{METHOD}:{path}:{spendAmount} and verifies the ed25519 signature against the agent’s public key.4
Timestamp check
Rejects the request if the timestamp is older than
maxRequestAgeMs (default 60 seconds). Prevents replay attacks.5
On-chain Sigil check
Calls
client.verifySigil(agentPubkey, { requiredCapability, maxSpendAmount }). Returns 402 if the Sigil is missing, revoked, expired, or lacking the required capability.6
Record spend
If
spendAmount > 0, calls client.recordSpend(agentPubkey, amount) on-chain. This debits the agent’s daily limit and rejects the request if the limit would be exceeded.7
Request forwarded
On success, the verified agent public key is attached to the request object (
req.sigilAgent) and the next handler is called.Required headers
Agents must attach these four headers to every request:Signature payload
1714300000000:POST:/api/generate:50000
The message is UTF-8 encoded and signed with the agent’s ed25519 secret key using nacl.sign.detached.
402 response body
When authorization fails, the middleware returns HTTP402 with:
SigilMiddlewareConfig
All adapters (Express and Next.js) share the same config interface:spendAmount to zero (or omitting it) runs verification only — the Sigil is checked but no spend is recorded.