X402 V3 payment endpoint
Get a signed x402 payment charged to a signed mandate. V1 may ask the user to approve each payment. V3 instead deducts from a budget the user already signed, so normally no user action is needed.
Prerequisites
- The agent has a signed, enabled mandate in the payment's currency, inside its validity window, with enough remaining budget. Use
GET /api/mandates/eligibleto find one. - The payment fits within the agent's monthly limit, if the user has set one.
Mandates limit spending by total budget and time window only. A mandate is not restricted to particular hosts.
Request
POST/api/payment/x402V3PaymentBearer agent-jwtcurl -X POST https://walletapi.fluxapay.xyz/api/payment/x402V3Payment \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $JWT_TOKEN" \
-d '{
"mandateId": "mand_Q2h8x0b1Yc3k9ZpLmN4rTw",
"scheme": "exact",
"network": "base",
"amount": "10000",
"currency": "USDC",
"assetAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "0xfA0eE7B2F13D80680F31771abc93D06a2C749150",
"host": "x402whois.com",
"resource": "https://x402whois.com/api/v1/whois?domain=fluxapay.xyz",
"description": "WHOIS lookup for fluxapay.xyz",
"tokenName": "USD Coin",
"tokenVersion": "2",
"validityWindowSeconds": 60
}'The fields are the same as V1, plus:
mandateId(required): the signed mandate to charge. Itscurrencymust match the payment's currency (USDC,FLUXA_MONETIZE_CREDITS, orXRPwhere enabled).
validityWindowSeconds defaults to 60. Values are clamped to 30–300.
Response (success)
{
"status": "ok",
"xPaymentB64": "<base64-encoded-xPayment>",
"xPayment": {
"x402Version": 1,
"scheme": "exact",
"network": "base",
"payload": {
"signature": "0x...",
"authorization": {
"from": "0x...",
"to": "0x...",
"value": "10000",
"validAfter": "1700000000",
"validBefore": "1700000060",
"nonce": "0x..."
}
}
},
"paymentRecordId": 12345,
"expiresAt": 1700000060,
"message": "Payment authorization signed successfully. ...",
"payment_model_context": { "instructions": "..." }
}Send xPaymentB64 as the X-Payment header when you retry the original API call, the same as in V1. The amount counts against the mandate's budget.
Error responses
Error bodies have the form { "status": "error" | "denied", "code", "message", "payment_model_context" }. The machine-readable reason is in code. payment_model_context.instructions gives LLM agents a next step to follow.
| HTTP | code | Meaning | What to do |
|---|---|---|---|
| 400 | mandate_required | mandateId is missing. The body may include intentOnboardUrl, a link for the user to create and sign a mandate. | Send a mandateId, or create a mandate. |
| 400 | missing_fields / invalid_amount / invalid_pay_to / unsupported_network | The request body is invalid. | Fix the request. |
| 401 | agent_auth_failed | The agent JWT is missing, invalid or expired. A VC token returns { "error": "wrong_token_type" }. | Refresh the JWT. |
| 403 | agent_not_authorized | The agent isn't authorized in the user's wallet. | Ask the user to link the agent. |
| 403 | mandate_not_found | The mandate doesn't exist or belongs to another agent. | Check mandateId. |
| 403 | mandate_not_signed | The mandate is awaiting the user's signature. | Show the user the mandate's signUrl. |
| 403 | mandate_disabled | The user turned this mandate off. | Use a different mandate. |
| 403 | mandate_expired | The payment falls outside the mandate's validity window. | Create a new mandate. |
| 403 | mandate_inactive | The mandate can't pay this, for example because it was revoked or its currency doesn't match. | Use another mandate in the right currency. |
| 403 | mandate_insufficient_budget | The remaining budget is less than amount. | Create a new mandate or a larger one. |
| 403 | agent_monthly_limit_exceeded | The payment would go over the agent's monthly limit set by the user. | Wait for the limit to reset, or ask the user to raise it. |
| 403 | policy_denied | Blocked by the user's spending policy. | Ask the user to review their settings. |
| 403 | risk_control_rejected | Blocked by risk checks. | Don't retry automatically. |
| 200 | need_approval (status) | The user must still approve this payment (some XRP flows). The body has approvalUrl. | Show approvalUrl, then retry. |
| 409 | duplicate_authorization | This authorization was already used. | Request a new payment. |
Typical flow
- Receive HTTP 402 from a resource.
- Call
GET /api/mandates/eligible?host=...&amount=...¤cy=...to find a mandate. - If one is found, call
POST /api/payment/x402V3Paymentwith itsmandateId. - Send the returned
xPaymentB64asX-Paymentand retry. - If no mandate is eligible, create a mandate or fall back to V1.
x402 v2 payment requirements
POST/api/v2/payment/x402-paymentBearer agent-jwtUse this endpoint when the resource returns an x402 v2 PaymentRequired object (x402Version: 2). Pass that object through unchanged. FluxA picks a supported option, charges the mandate, and returns a v2 PaymentPayload. It supports Base mainnet USDC (eip155:8453) only.
curl -X POST https://walletapi.fluxapay.xyz/api/v2/payment/x402-payment \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $JWT_TOKEN" \
-d '{
"mandateId": "mand_Q2h8x0b1Yc3k9ZpLmN4rTw",
"paymentRequest": {
"x402Version": 2,
"resource": { "url": "https://api.example.com/data", "description": "", "mimeType": "application/json" },
"accepts": [
{
"scheme": "exact",
"network": "eip155:8453",
"amount": "10000",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "0xfA0eE7B2F13D80680F31771abc93D06a2C749150",
"maxTimeoutSeconds": 60
}
]
}
}'| Field | Required | Description |
|---|---|---|
mandateId | Yes | A signed USDC mandate. |
paymentRequest | One of | The v2 PaymentRequired object, as JSON. |
paymentRequestB64 | One of | The same object, Base64-encoded (for example the Payment-Required header value). |
preferredAssets | No | [{ "network", "asset" }]. If given, only these options are considered. |
The signed payment is valid for the option's maxTimeoutSeconds, up to 300 seconds.
Response (200):
{
"status": "ok",
"paymentPayloadB64": "<base64 v2 PaymentPayload>",
"paymentPayload": {
"x402Version": 2,
"resource": { "url": "https://api.example.com/data" },
"accepted": { "scheme": "exact", "network": "eip155:8453", "amount": "10000", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "payTo": "0xfA0e..." },
"payload": { "signature": "0x...", "authorization": { "from": "0x...", "to": "0x...", "value": "10000", "validAfter": "0", "validBefore": "1700000060", "nonce": "0x..." } }
},
"paymentRecordId": 12345,
"expiresAt": 1700000060
}Send paymentPayloadB64 to the resource in the header its x402 v2 server expects (usually Payment-Signature).
Errors use the same code values as V3, plus:
| HTTP | code | Meaning |
|---|---|---|
| 400 | invalid_payment_request / invalid_payment_request_b64 / invalid_resource_url | The paymentRequest is missing, malformed, or not x402Version: 2. |
| 400 | no_supported_payment_option | No accepts entry is Base mainnet USDC with scheme: "exact", or none matches preferredAssets. |
| 403 | insufficient_balance | The wallet's USDC balance is too low. |
