API Reference
Base URL: http://<host>:<port>/api/v1
All request and response bodies are JSON. Successful responses are wrapped in a data envelope:
{ "data": { ... } }Errors return a consistent structure:
{
"code": "ERROR_CODE",
"message": "Human-readable message",
"details": "Optional extra detail"
}Authentication Headers
| Header | Used by | Description |
|---|---|---|
Authorization: Bearer <token> | User JWT endpoints | Access token obtained from /login or /token/refresh. |
X-Client-ID | /verify, /settle, /supported | Client ID from POST /clients. |
X-Client-Token | /verify, /settle, /supported | Client token from POST /clients. |
Health
GET /health
Returns server liveness. No authentication required.
Response 200
{
"status": "healthy",
"timestamp": 1712345678,
"version": "1.0.0"
}System
GET /api/v1/status
Returns detailed system status including database health and supported networks.
Response 200
{
"data": {
"status": "healthy",
"version": "1.0.0",
"uptime": 86400,
"networks": [
{
"x402Version": 1,
"scheme": "exact",
"network": "base-sepolia",
"status": "active",
"block": 0,
"feePayer": "0xYourAddress"
}
],
"database": {
"status": "healthy"
}
}
}Networks
GET /api/v1/networks
List all enabled networks. No authentication required.
Response 200
{
"kinds": [
{
"x402Version": 1,
"scheme": "exact",
"network": "base-sepolia",
"status": "active",
"block": 0,
"feePayer": "0xYourAddress"
}
]
}GET /api/v1/networks/:id
Get a single network by its network_name.
Path params
| Param | Description |
|---|---|
id | Network name (e.g. base-sepolia). |
Response 200
{
"x402Version": 1,
"scheme": "exact",
"network": "base-sepolia",
"status": "active",
"block": 0,
"feePayer": "0xYourAddress",
"extra": {
"feePayer": "0xYourAddress"
}
}Response 400 - unknown network
{ "code": "INVALID_REQUEST", "message": "unsupported network", "details": "" }Assets
GET /api/v1/assets
List all registered, active assets. No authentication required.
Response 200
{
"data": [
{
"id": 1,
"network": "base-sepolia",
"asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
"symbol": "USDC",
"decimals": 6,
"active": true
}
]
}x402 - Core Endpoints
These endpoints require client API key authentication (X-Client-ID + X-Client-Token).
POST /api/v1/verify
Verify that a payment payload is valid against the given requirements. Does not settle on-chain.
Request
{
"paymentPayload": {
"x402Version": 1,
"scheme": "exact",
"network": "base-sepolia",
"payload": "<base64-encoded-signed-data>"
},
"paymentRequirements": {
"scheme": "exact",
"network": "base-sepolia",
"maxAmountRequired": "1000000",
"resource": "/api/protected",
"description": "Access to protected endpoint",
"mimeType": "application/json",
"payTo": "0xMerchantAddress",
"maxTimeoutSeconds": 60,
"asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
"x402Version": 1
}
}| Field | Type | Description |
|---|---|---|
paymentPayload | object | The payer's signed payment object. |
paymentPayload.x402Version | number | Protocol version. Must be 1. |
paymentPayload.scheme | string | Payment scheme ("exact"). |
paymentPayload.network | string | Network identifier. |
paymentPayload.payload | string | object | Base64-encoded signed payload, or a JSON object (auto-encoded server-side). |
paymentRequirements | object | The requirements originally returned by the resource server. |
Response 200 - valid
{
"isValid": true,
"invalidReason": "",
"payer": "0xPayerAddress",
"timestamp": 1712345678,
"cachedResult": false
}Response 200 - invalid (bad signature, wrong amount, expired, etc.)
{
"isValid": false,
"invalidReason": "signature mismatch",
"payer": "",
"timestamp": 1712345678,
"cachedResult": false
}Response 400 - malformed request
{ "code": "INVALID_REQUEST", "message": "Invalid request format", "details": "..." }Response 500 - verification error
{ "code": "VERIFICATION_FAILED", "message": "Payment verification failed", "details": "..." }POST /api/v1/settle
Verify and settle a payment on-chain. Broadcasts the transaction and returns the tx hash.
Call
/verifyfirst if you want to check validity without incurring gas costs.
Request - same shape as /verify
{
"x402Version": 1,
"paymentPayload": {
"x402Version": 1,
"scheme": "exact",
"network": "base-sepolia",
"payload": "<base64-encoded-signed-data>"
},
"paymentRequirements": {
"scheme": "exact",
"network": "base-sepolia",
"maxAmountRequired": "1000000",
"resource": "/api/protected",
"description": "Access to protected endpoint",
"mimeType": "application/json",
"payTo": "0xMerchantAddress",
"maxTimeoutSeconds": 60,
"asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
"x402Version": 1
}
}Response 200 - settled
{
"success": true,
"transaction": "0xabc123...",
"errorReason": "",
"network": "base-sepolia",
"payer": "0xPayerAddress",
"blockNumber": 12345678,
"timestamp": "2025-04-07T12:00:00Z"
}Response 200 - settlement failed
{
"success": false,
"transaction": "",
"errorReason": "insufficient balance",
"network": "base-sepolia",
"payer": "",
"blockNumber": 0,
"timestamp": "2025-04-07T12:00:00Z"
}Response 500 - settlement error
{ "code": "SETTLEMENT_FAILED", "message": "Payment settlement failed", "details": "..." }GET /api/v1/supported
Returns the list of supported networks (same as /networks) but requires client auth. Useful for validating that a client key is active.
Response 200 - same as GET /api/v1/networks
Authentication
POST /api/v1/signup
Create a new user account.
Request
{
"email": "user@example.com",
"password": "StrongPassword123!"
}Response 200
{
"data": {
"message": "Account created",
"user": {
"id": 1,
"email": "user@example.com",
"isAdmin": false,
"createdAt": "2025-04-07T12:00:00Z"
}
}
}POST /api/v1/login
Authenticate and receive JWT tokens.
Request
{
"email": "user@example.com",
"password": "StrongPassword123!"
}Response 200
{
"data": {
"message": "Login successful",
"access_token": "eyJ...",
"refresh_token": "eyJ...",
"id": 1
}
}Response 401 - bad credentials
{ "message": "Invalid email or password" }POST /api/v1/token/refresh
Exchange a valid refresh token for a new access token.
Request
{
"refresh_token": "eyJ..."
}Response 200
{
"data": {
"access_token": "eyJ..."
}
}Response 401 - expired or invalid refresh token
{ "message": "invalid session token", "details": "..." }Client API Keys
Requires Authorization: Bearer <access_token>.
POST /api/v1/clients
Create a new client API key pair.
Request
{
"title": "my-resource-server",
"scope": "verify,settle",
"ttl_hours": 8760
}| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Human-readable name for this client. |
scope | string | No | Comma-separated list of allowed operations. |
ttl_hours | number | No | Key expiry in hours. Omit for no expiry. |
Response 200
{
"data": {
"client": {
"clientId": "abc123",
"token": "hmac-signed-token",
"title": "my-resource-server",
"scope": "verify,settle",
"createdAt": "2025-04-07T12:00:00Z",
"expiresAt": null
}
}
}Important: Store the
tokensecurely - it is not stored in plaintext and cannot be retrieved after creation.
GET /api/v1/clients
List your client keys. Admins see all clients; regular users see only their own.
Query params
| Param | Default | Description |
|---|---|---|
offset | 0 | Pagination offset. |
limit | 20 | Page size. |
Response 200
{
"data": [
{
"clientId": "abc123",
"title": "my-resource-server",
"scope": "verify,settle",
"createdAt": "2025-04-07T12:00:00Z",
"expiresAt": null
}
]
}DELETE /api/v1/clients/:id
Soft-delete (revoke) a client key.
Path params
| Param | Description |
|---|---|
id | clientId of the key to revoke. |
Response 200
{
"data": {
"deleted": "abc123"
}
}Resources
Requires Authorization: Bearer <access_token>.
Resources are the protected URLs that merchants register. The facilitator uses this registry to look up accepted payment methods when a client queries a resource.
POST /api/v1/resources
Register a new protected resource.
Request
{
"resource": "/api/premium-data",
"type": "endpoint",
"x402Version": 1,
"accepts": [
{
"scheme": "exact",
"network": "base-sepolia",
"maxAmountRequired": "1000000",
"asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
"payTo": "0xYourAddress"
}
],
"metadata": {}
}Response 201
{ "data": { "status": "created" } }GET /api/v1/users/resources
List resources belonging to the authenticated user.
Query params: offset (default 0), limit (default 100).
Response 200
{
"data": {
"resources": [ { ... } ],
"offset": 0,
"limit": 100
}
}GET /api/v1/resources
List all resources (public, no auth required).
Response 200 - same shape as /users/resources.
PUT /api/v1/resources/:id
Update an existing resource.
Path params: id - resource database ID.
Request - same shape as POST /api/v1/resources.
Response 200
{ "data": { "updated": 42 } }Metrics
Requires Authorization: Bearer <access_token>.
GET /api/v1/metrics/daily
HTTP request metrics aggregated by day.
Query params
| Param | Description |
|---|---|
from | Start date (ISO 8601). |
to | End date (ISO 8601). |
GET /api/v1/metrics/monthly
HTTP request metrics aggregated by month.
GET /api/v1/metrics/summary
Aggregated totals across all time.
GET /api/v1/x402-metrics/daily
x402 settlement metrics (amounts, fees, networks) aggregated by day.
GET /api/v1/x402-metrics/monthly
x402 settlement metrics aggregated by month.
GET /api/v1/x402-metrics/summary
x402 settlement totals across all time.
Admin Endpoints
Requires Authorization: Bearer <admin_token> (static token from config.yaml).
Base path: /admin
POST /admin/users
Create a new user (admin-seeded accounts).
Request
{
"email": "newuser@example.com",
"password": "InitialPassword1!",
"is_admin": false
}GET /admin/users
List all users.
POST /admin/assets
Register a new asset (token) in the asset registry. Assets must be registered before they can be used in payment requirements.
Request
{
"network": "base-sepolia",
"asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
"symbol": "USDC",
"decimals": 6,
"active": true
}Error Codes
| Code | HTTP Status | Meaning |
|---|---|---|
INVALID_REQUEST | 400 | Malformed JSON or missing required fields. |
INVALID_PAYLOAD | 400 | The payment payload could not be decoded. |
VERIFICATION_FAILED | 500 | x402 library returned an error during verification. |
SETTLEMENT_FAILED | 500 | x402 library returned an error during settlement. |
UNAUTHORIZED | 401 | Missing or invalid credentials. |
INTERNAL_ERROR | 500 | Unexpected server error. |