Facilitator API Reference
The T402 Facilitator API provides payment verification and on-chain settlement services. The public facilitator is available at https://facilitator.t402.io.
Base URL
| Environment | URL | API Key |
|---|---|---|
| Production | https://facilitator.t402.io | Required (X-API-Key) |
| Sandbox | https://sandbox.t402.io | Not needed |
The Sandbox supports 7 testnets with magic test addresses for deterministic testing. Try the playground to explore the API interactively.
Authentication
The production facilitator requires an API key for /verify and /settle endpoints. Get a free key at facilitator.t402.io/register.
Pass the key via header:
Authorization: Bearer t402_sk_...Or via X-API-Key header. Free tier includes 1,000 transactions/month at 0% fee.
| Tier | Quota | RPM | Fee |
|---|---|---|---|
| Free | 1,000 tx/month | 10 | 0% |
| Builder | 100K tx/month | 100 | 0.1% |
| Scale | 1M tx/month | 1,000 | 0.08% |
| Enterprise | Unlimited | 10,000 | Custom |
Endpoints
GET /health
Health check endpoint for liveness probes.
Response
{
"status": "ok"
}GET /ready
Readiness check endpoint. Returns 200 when the service is ready to accept requests.
Response
{
"status": "ready",
"checks": {
"redis": "ok",
"rpc": "ok"
}
}GET /metrics
Prometheus metrics endpoint for monitoring.
Response: Prometheus text format
GET /supported
Returns the supported networks, schemes, and signers.
Response
{
"kinds": [
{
"t402Version": 2,
"scheme": "exact",
"network": "eip155:8453"
},
{
"t402Version": 2,
"scheme": "exact",
"network": "ton:mainnet"
}
],
"extensions": [],
"signers": {
"eip155:*": ["0xC88f67e776f16DcFBf42e6bDda1B82604448899B"],
"ton:*": ["EQDjv9CUEJ__D_3-3J4trQtqVklMBiNoGVSf3Fu6AaDGkEUe"]
}
}Response Fields
| Field | Type | Description |
|---|---|---|
kinds | array | List of supported scheme/network combinations |
kinds[].t402Version | number | Protocol version supported (2 for v2) |
kinds[].scheme | string | Payment scheme (e.g., exact, upto) |
kinds[].network | string | CAIP-2 network identifier |
extensions | array | Extension identifiers the facilitator has implemented |
signers | object | Map of CAIP-2 patterns (e.g., eip155:*) to signer addresses |
POST /verify
Verify a signed payment authorization without executing settlement.
Request Body
{
"paymentPayload": {
"t402Version": 2,
"resource": {
"url": "https://api.example.com/premium",
"description": "Premium API access",
"mimeType": "application/json"
},
"accepted": {
"scheme": "exact",
"network": "eip155:8453",
"amount": "1000000",
"asset": "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee",
"payTo": "0xRecipientAddress",
"maxTimeoutSeconds": 600,
"extra": {
"name": "USDT0",
"version": "2"
}
},
"payload": {
"signature": "0x...",
"authorization": {
"from": "0xPayerAddress",
"to": "0xRecipientAddress",
"value": "1000000",
"validAfter": "0",
"validBefore": "1706745600",
"nonce": "0x..."
}
}
},
"paymentRequirements": {
"scheme": "exact",
"network": "eip155:8453",
"amount": "1000000",
"asset": "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee",
"payTo": "0xRecipientAddress",
"maxTimeoutSeconds": 600,
"extra": {
"name": "USDT0",
"version": "2"
}
}
}Response (Success)
{
"isValid": true,
"payer": "0xPayerAddress"
}Response (Invalid)
{
"isValid": false,
"invalidReason": "Signature verification failed",
"payer": "0xPayerAddress"
}Response Fields
| Field | Type | Description |
|---|---|---|
isValid | boolean | Whether the payment authorization is valid |
invalidReason | string | Reason for invalidity (omitted if valid) |
payer | string | Address of the payer’s wallet |
POST /settle
Execute on-chain settlement for a verified payment.
Headers
| Header | Required | Description |
|---|---|---|
Idempotency-Key | Recommended | Unique key (max 64 chars, alphanumeric/hyphens) to prevent duplicate settlements |
Request Body
{
"paymentPayload": {
"t402Version": 2,
"resource": {
"url": "https://api.example.com/premium",
"description": "Premium API access",
"mimeType": "application/json"
},
"accepted": {
"scheme": "exact",
"network": "eip155:8453",
"amount": "1000000",
"asset": "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee",
"payTo": "0xRecipientAddress",
"maxTimeoutSeconds": 600,
"extra": {
"name": "USDT0",
"version": "2"
}
},
"payload": {
"signature": "0x...",
"authorization": {
"from": "0xPayerAddress",
"to": "0xRecipientAddress",
"value": "1000000",
"validAfter": "0",
"validBefore": "1706745600",
"nonce": "0x..."
}
}
},
"paymentRequirements": {
"scheme": "exact",
"network": "eip155:8453",
"amount": "1000000",
"asset": "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee",
"payTo": "0xRecipientAddress",
"maxTimeoutSeconds": 600,
"extra": {
"name": "USDT0",
"version": "2"
}
}
}Response (Success)
{
"success": true,
"transaction": "0x...",
"network": "eip155:8453",
"payer": "0xPayerAddress"
}Response (Failure)
{
"success": false,
"errorReason": "Insufficient balance",
"transaction": "",
"network": "eip155:8453",
"payer": "0xPayerAddress"
}Response Fields
| Field | Type | Description |
|---|---|---|
success | boolean | Whether settlement was successful |
transaction | string | Blockchain transaction hash (empty string if failed) |
network | string | Network where settlement occurred |
payer | string | Address of the payer’s wallet |
errorReason | string | Reason for failure (omitted if successful) |
Idempotency: Always use the Idempotency-Key header for settlement requests to prevent double payments in case of network errors or retries.
GET /info
Returns API version, feature flags, and all available endpoints.
Response: JSON with name, version, description, endpoints[], features{}.
GET /tiers
List all available pricing tiers and their configurations.
Response
{
"tiers": {
"free": { "name": "Free", "monthlyQuota": 1000, "rpmLimit": 10, "feeRate": 0, "priceCents": 0 },
"builder": { "name": "Builder", "monthlyQuota": 100000, "rpmLimit": 100, "feeRate": 0.001, "priceCents": 0 },
"scale": { "name": "Scale", "monthlyQuota": 1000000, "rpmLimit": 1000, "feeRate": 0.0008, "priceCents": 9900 },
"enterprise": { "name": "Enterprise", "monthlyQuota": -1, "rpmLimit": 10000, "feeRate": 0.0005, "priceCents": 0 }
}
}Registration & Key Management
POST /register
Create a new API key. Idempotent per email + project name.
Request Body
{ "email": "you@company.com", "projectName": "My App" }Response (201 Created)
{
"apiKey": "t402_sk_...",
"projectName": "My App",
"quota": { "limit": 1000, "period": "monthly", "used": 0, "tier": "free", "feeRate": 0, "resetsAt": "2026-05-01T00:00:00Z" },
"isNew": true
}Rate limited: 5 registrations per IP per hour.
POST /lookup
Recover an existing API key by email. Rate limited separately from registration.
Request Body
{ "email": "you@company.com" }Dashboard API
All dashboard endpoints require Authorization: Bearer t402_sk_... header.
GET /dashboard/info
Get key info, tier, quota, and settings. Does not increment quota.
Response
{
"tier": "free",
"config": { "name": "Free", "monthlyQuota": 1000, "rpmLimit": 10, "feeRate": 0 },
"quota": { "limit": 1000, "used": 42, "tier": "free", "feeRate": 0, "resetsAt": "2026-05-01T00:00:00Z" },
"email": "you@company.com",
"project": "My App",
"status": "active",
"createdAt": "2026-04-07T06:28:26Z",
"webhookUrl": "",
"allowedIPs": null,
"allowedRef": null
}GET /dashboard/keys
List all API keys for the authenticated user’s email.
Response
{
"keys": [
{ "apiKey": "t402_sk_...", "project": "My App", "tier": "free", "status": "active", "createdAt": "...", "current": true }
],
"email": "you@company.com"
}GET /dashboard/usage
Daily usage data for the last 7 days (for charts).
Response
{
"days": 7,
"usage": [
{ "date": "2026-04-01", "count": 0 },
{ "date": "2026-04-02", "count": 15 }
]
}POST /dashboard/rotate
Regenerate API key. The old key is immediately invalidated. All settings (webhook, restrictions, tier) are preserved.
Response
{ "apiKey": "t402_sk_NEW...", "project": "My App", "message": "Key rotated successfully. Old key is now invalid." }POST /dashboard/webhook
Set a webhook URL for settlement notifications. Must use HTTPS. Send empty string to clear.
Request Body
{ "webhookUrl": "https://your-server.com/t402-webhook" }POST /dashboard/webhook/test
Send a test payload to the configured webhook URL.
Response
{ "success": true, "statusCode": 200, "message": "Webhook responded with 200 OK" }POST /dashboard/restrictions
Set IP and referrer domain restrictions. Empty arrays allow all.
Request Body
{ "allowedIPs": ["203.0.113.1"], "allowedRef": ["myapp.com"] }Credit Scoring API
GET /v1/credit/score?wallet=0x…
Get credit score (0-1000) for a wallet based on payment history.
Response
{
"wallet": "0x...",
"score": 750,
"breakdown": { "volumeScore": 200, "frequencyScore": 180, "reliabilityScore": 250, "ageScore": 120 },
"stats": { "paymentCount": 150, "successRate": 0.98, "totalVolume": "5000000000", "accountAgeDays": 90 }
}POST /v1/credit/refresh
Force a fresh score computation. Accepts query param ?wallet=0x... or JSON body {"wallet":"0x..."}.
GET /v1/credit/top?limit=20
Leaderboard of top wallets by credit score.
Stats API
GET /stats/keys
Public count of registered API keys.
{ "totalKeys": 27 }GET /stats/requests
Aggregate request statistics (requires database).
{ "totalRequests": 235000, "successfulRequests": 234300, "failedRequests": 700, "avgDurationMs": 5.1 }GET /stats/settlements
Settlement status counts.
{ "confirmed": 0, "failed": 32, "pending": 0 }Error Responses
All endpoints return structured error responses:
{
"code": "INVALID_REQUEST",
"message": "Invalid request body"
}Error Codes
| Code | HTTP Status | Description |
|---|---|---|
INVALID_REQUEST | 400 | Malformed request body |
INVALID_IDEMPOTENCY_KEY | 400 | Invalid idempotency key format |
VERIFICATION_FAILED | 500 | Payment verification failed |
SETTLEMENT_FAILED | 500 | On-chain settlement failed |
REQUEST_IN_PROGRESS | 409 | Duplicate request still processing |
PAYLOAD_MISMATCH | 409 | Payload doesn’t match previous request with same idempotency key |
PREVIOUS_REQUEST_FAILED | 409 | Previous request with this key failed |
IDEMPOTENCY_UNAVAILABLE | 503 | Idempotency service unavailable |
Rate Limiting
Global limit: 1,000 requests/minute per IP (anonymous) or per API key (authenticated).
Endpoint-specific limits:
| Endpoint | Limit | Reason |
|---|---|---|
POST /settle | 100/min | On-chain transactions |
POST /register | 30/min | Registration (also 5/hour per IP) |
POST /lookup | 30/min | Key recovery (also 5/hour per IP) |
POST /dashboard/rotate | 5/min | Key rotation (destructive) |
POST /dashboard/webhook/test | 5/min | Webhook test |
GET /dashboard/info | 120/min | Dashboard reads |
POST /v1/intent/execute | 100/min | Intent execution |
Response headers (on authenticated requests):
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 978
X-RateLimit-Reset: 1712592000
X-RateLimit-Tier: freeSupported Networks
EVM Networks
| Network | Chain ID | CAIP-2 | Facilitator Address |
|---|---|---|---|
| Ethereum | 1 | eip155:1 | 0xC88f67e776f16DcFBf42e6bDda1B82604448899B |
| Base | 8453 | eip155:8453 | 0xC88f67e776f16DcFBf42e6bDda1B82604448899B |
| Arbitrum | 42161 | eip155:42161 | 0xC88f67e776f16DcFBf42e6bDda1B82604448899B |
| Optimism | 10 | eip155:10 | 0xC88f67e776f16DcFBf42e6bDda1B82604448899B |
| Ink | 57073 | eip155:57073 | 0xC88f67e776f16DcFBf42e6bDda1B82604448899B |
| Berachain | 80094 | eip155:80094 | 0xC88f67e776f16DcFBf42e6bDda1B82604448899B |
Non-EVM Networks
| Network | CAIP-2 | Facilitator Address |
|---|---|---|
| Solana | solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp | 8GGtWHRQ1wz5gDKE2KXZLktqzcfV1CBqSbeUZjA7hoWL |
| TON | ton:mainnet | EQDjv9CUEJ__D_3-3J4trQtqVklMBiNoGVSf3Fu6AaDGkEUe |
| TRON | tron:mainnet | TT1MqNNj2k5qdGA6nrrCodW6oyHbbAreQ5 |
| NEAR | near:mainnet | facilitator.t402.near |
| Stacks | stacks:1 | SP36B1B191JTQAZTRKKWRN7J0YHHM41W9P9P7EPR5 |
| Cosmos (Noble) | cosmos:noble-1 | noble1ejc2c2gvk46h7kyulx9fus85vdpq0zdjwkfav0 |
| Aptos | aptos:1 | Verification only (exact-direct) |
| Tezos | tezos:NetXdQprcVkpaWU | Verification only (exact-direct) |
| Polkadot | polkadot:68d56f15f85d3136970ec16946040bc1 | Verification only (exact-direct) |
Code Examples
# Check supported networks
curl https://facilitator.t402.io/supported
# Verify payment
curl -X POST https://facilitator.t402.io/verify \
-H "Content-Type: application/json" \
-d '{
"paymentPayload": {...},
"paymentRequirements": {...}
}'
# Settle payment
curl -X POST https://facilitator.t402.io/settle \
-H "Content-Type: application/json" \
-H "Idempotency-Key: unique-key-123" \
-d '{
"paymentPayload": {...},
"paymentRequirements": {...}
}'Self-Hosting
You can run your own facilitator for greater control and privacy. See the Deployment Guide for instructions.
# Docker
docker run -p 8080:8080 ghcr.io/t402-io/facilitator:latest
# Environment variables
export EVM_PRIVATE_KEY=0x...
export TON_MNEMONIC="word1 word2 ..."
export REDIS_URL=redis://localhost:6379Related
- Protocol Specification - Full protocol details
- TypeScript SDK - Client library
- Go SDK - Server library
- Deployment - Self-hosting guide