What is x402?
x402 is a protocol for HTTP payment-required responses that enables:
Machine-readable payment challenges using HTTP 402 status
Automatic payment settlement without custom billing logic
Retry mechanisms after successful payment
Transparent pricing embedded in API responses
ActumX implements a simplified x402 flow. The protocol is inspired by HTTP 402 Payment Required and designed for AI agent interactions.
The x402 Flow
Make Initial Request
Request a paid endpoint without payment proof: curl -X GET "http://localhost:3001/v1/protected/quote?topic=ai" \
-H "Authorization: Bearer xk_live_..."
Response (402 Payment Required): {
"error" : "payment_required" ,
"message" : "This endpoint requires payment. Settle first and retry with payment proof." ,
"x402" : {
"version" : "0.1-draft" ,
"paymentId" : "x402tx_abc123def456" ,
"amountCents" : 25 ,
"amountUsd" : "0.25" ,
"currency" : "USD" ,
"endpoint" : "/v1/protected/quote" ,
"settlementEndpoint" : "/v1/x402/settle" ,
"facilitator" : "internal-simulator" ,
"expiresAt" : "2026-03-03T23:40:00.000Z"
}
}
The system automatically creates a pending transaction in the database with a unique paymentId.
Settle the Payment
Use the paymentId to settle the payment challenge: curl -X POST http://localhost:3001/v1/x402/settle \
-H "Authorization: Bearer xk_live_..." \
-H "Content-Type: application/json" \
-d '{
"paymentId": "x402tx_abc123def456"
}'
Response (200 Success): {
"receiptId" : "receipt_xyz789ghi012" ,
"paymentId" : "x402tx_abc123def456" ,
"status" : "settled" ,
"amountCents" : 25 ,
"settledAt" : "2026-03-03T23:35:00.000Z"
}
The settlement deducts 25 cents from your account balance and generates a receiptId as proof of payment.
Retry with Payment Proof
Make the original request again, this time including payment proof headers: curl -X GET "http://localhost:3001/v1/protected/quote?topic=ai" \
-H "Authorization: Bearer xk_live_..." \
-H "X-Payment-Id: x402tx_abc123def456" \
-H "X-Payment-Proof: receipt_xyz789ghi012"
Response (200 Success): {
"data" : {
"topic" : "ai" ,
"insight" : "x402 allows machine-readable payment requirements using HTTP 402 so clients can settle and retry without custom per-API billing logic." ,
"generatedAt" : "2026-03-03T23:36:00.000Z"
},
"payment" : {
"paymentId" : "x402tx_abc123def456" ,
"receiptId" : "receipt_xyz789ghi012" ,
"amountCents" : 25 ,
"status" : "completed"
}
}
Success! The transaction is marked as completed and a usage event is recorded.
Complete Example: Automated Flow
Here’s a complete example that handles the entire x402 flow automatically:
async function makeX402Request ( endpoint , apiKey , queryParams = {}) {
const url = new URL ( endpoint );
Object . entries ( queryParams ). forEach (([ key , value ]) => {
url . searchParams . append ( key , value );
});
// Step 1: Initial request
let response = await fetch ( url , {
headers: {
'Authorization' : `Bearer ${ apiKey } `
}
});
let data = await response . json ();
// Step 2: Handle payment required
if ( response . status === 402 && data . x402 ) {
console . log ( `💰 Payment required: $ ${ data . x402 . amountUsd } ` );
// Settle the payment
const settleResponse = await fetch ( 'http://localhost:3001/v1/x402/settle' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ apiKey } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
paymentId: data . x402 . paymentId
})
});
const settlement = await settleResponse . json ();
if ( settleResponse . status !== 200 ) {
throw new Error ( `Settlement failed: ${ JSON . stringify ( settlement ) } ` );
}
console . log ( `✅ Payment settled: ${ settlement . receiptId } ` );
// Step 3: Retry with proof
response = await fetch ( url , {
headers: {
'Authorization' : `Bearer ${ apiKey } ` ,
'X-Payment-Id' : settlement . paymentId ,
'X-Payment-Proof' : settlement . receiptId
}
});
data = await response . json ();
}
return data ;
}
// Usage
const result = await makeX402Request (
'http://localhost:3001/v1/protected/quote' ,
process . env . ACTUMX_API_KEY ,
{ topic: 'ai' }
);
console . log ( result );
Payment States
An x402 transaction goes through these states:
Pending
Initial state when payment challenge is issued
Transaction created in database
paymentId generated
User has not settled yet
Settled
Payment has been deducted from user’s balance
Credits deducted from account
receiptId generated
Ready for endpoint access
Completed
User successfully accessed the endpoint with proof
Usage event recorded
API key’s lastUsedAt updated
Transaction finalized
Implementation Details
Creating Payment Challenge
From api/src/modules/x402/service.ts:352-375:
if ( ! paymentId || ! paymentProof ) {
const txId = newId ( "x402tx" );
const timestamp = TimeService . nowIso ();
await db . insert ( x402Transactions ). values ({
id: txId ,
userId: apiKey . userId ,
apiKeyId: apiKey . id ,
endpoint: X402_PAID_ENDPOINT ,
method: "GET" ,
amountCents: X402_PAID_REQUEST_COST_CENTS ,
status: "pending" ,
// ...
});
return {
statusCode: 402 ,
body: X402Service . buildPaymentRequiredResponse ( txId ),
};
}
Settling Payment
From api/src/modules/x402/service.ts:307-324:
await db . insert ( creditLedger ). values ({
id: newId ( "ledger" ),
userId: apiKey . userId ,
direction: "debit" ,
amountCents: transaction . amountCents ,
source: "api_request" ,
referenceId: transaction . id ,
createdAt: timestamp ,
});
await db
. update ( x402Transactions )
. set ({
status: "settled" ,
receiptId ,
updatedAt: timestamp ,
})
. where ( eq ( x402Transactions . id , transaction . id ));
Error Handling
Insufficient Balance (402)
Error Response: {
"error" : "insufficient_balance" ,
"requiredCents" : 25 ,
"balanceCents" : 10 ,
"message" : "Top up balance in dashboard before settling this x402 payment."
}
Solution: Top up your account balance before settling.
Invalid Payment Proof (402)
Error Response: {
"error" : "invalid_payment_proof"
}
Causes:
paymentId doesn’t exist
receiptId doesn’t match the transaction
Payment belongs to a different user/API key
Solution: Verify you’re using the correct IDs from the settlement response.
Payment Not Settled (402)
Error Response: {
"error" : "payment_not_settled" ,
"status" : "pending"
}
Solution: Complete the settlement step before retrying the endpoint.
Error Response: {
"error" : "payment_not_found"
}
Causes:
Invalid paymentId
Payment belongs to different user
Payment was already consumed and expired
Best Practices
Automate the Flow Build helper functions that handle the 3-step flow automatically
Cache Receipt IDs Store receipt IDs to avoid duplicate charges for the same request
Handle Retries Implement exponential backoff for network errors during settlement
Monitor Balance Check your balance before making paid requests to avoid 402 errors
Available Paid Endpoints
Quote Endpoint Endpoint: GET /v1/protected/quoteCost: 25 cents per requestQuery Parameters:
topic (optional): Topic for the quote (default: “general”)
Example: curl "http://localhost:3001/v1/protected/quote?topic=blockchain" \
-H "Authorization: Bearer xk_live_..."
More paid endpoints will be added. Each will follow the same x402 protocol.
Next Steps