Token issuance
After an agent pays for access, Key0 issues a signed JWT that the agent uses to call your protected endpoints. This page covers the classes and functions involved in creating, signing, and verifying those tokens.Token issuance flow
Challenge transitions to PAID
The
ChallengeEngine verifies the on-chain payment and transitions the challenge from PENDING to PAID.Callback returns credentials
Your callback returns
{ token: string } (or any credential shape you define).AccessTokenIssuer
TheAccessTokenIssuer class handles JWT creation and verification. It supports two signing algorithms: HS256 (shared secret) and RS256 (RSA key pair).
Constructor
The constructor accepts either a plain string (backward-compatible) or a config object:HS256 secrets must be at least 32 characters. The constructor throws immediately if the secret is shorter.
Config type
Methods
sign
Creates a signed JWT with the given claims and TTL.verify
Verifies a token using the primary secret. HS256 only — RS256 tokens should be verified withvalidateKey0Token using the public key.
verifyWithFallback
Tries the primary secret first, then falls back to a list of previous secrets. Use this during secret rotation.TokenClaims
Every Key0 JWT contains these custom claims alongside the standardiat and exp:
| Claim | Type | Description |
|---|---|---|
sub | string | The requestId that initiated the payment flow |
jti | string | The challengeId for this specific challenge |
resourceId | string | The resource the agent paid to access |
planId | string | The pricing plan the agent selected |
txHash | string | The on-chain transaction hash proving payment |
iat | number | Issued-at timestamp (seconds since epoch) |
exp | number | Expiration timestamp (seconds since epoch) |
validateToken middleware
ThevalidateToken function is a framework-agnostic utility that extracts and verifies a Bearer token from an Authorization header. The framework integrations (Express, Hono, Fastify) wrap this function into middleware.
Error handling
validateToken throws a Key0Error with the following codes:
| Condition | Error code | HTTP status |
|---|---|---|
Missing or malformed Authorization header | INVALID_REQUEST | 401 |
| Expired token | CHALLENGE_EXPIRED | 401 |
| Invalid signature or tampered token | INVALID_REQUEST | 401 |
validateKey0Token (lightweight validator)
If your backend service only needs to verify tokens and does not need the full SDK, use the standalone validator from@key0ai/key0/validator. It supports both HS256 and RS256 and has no blockchain dependencies.
sub, jti, resourceId, planId, txHash) and throws if any are missing.
HS256 vs RS256
| HS256 | RS256 | |
|---|---|---|
| Key type | Shared secret (>= 32 chars) | RSA private/public key pair |
| Sign with | Secret | Private key (PEM) |
| Verify with | Same secret | Public key (PEM) |
| Use case | Single service, or services that share the same secret | Distributed verification — sign on one server, verify on many without sharing the private key |
| Rotation | verifyWithFallback with old secrets | Publish new public key, keep old key in rotation |
Token issuance timeout
TheChallengeEngine wraps your fetchResourceCredentials callback in a Promise.race with a configurable timeout.
tokenIssueRetries times with exponential backoff (500ms base delay — 500ms, 1s, 2s, and so on).
If token issuance fails permanently, the challenge stays in the PAID state. The refund cron can pick it up and settle an on-chain refund automatically.
