Skip to content

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 ​

  1. The agent has a signed, enabled mandate in the payment's currency, inside its validity window, with enough remaining budget. Use GET /api/mandates/eligible to find one.
  2. 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-jwt
bash
curl -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. Its currency must match the payment's currency (USDC, FLUXA_MONETIZE_CREDITS, or XRP where enabled).

validityWindowSeconds defaults to 60. Values are clamped to 30–300.


Response (success) ​

json
{
  "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.

HTTPcodeMeaningWhat to do
400mandate_requiredmandateId 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.
400missing_fields / invalid_amount / invalid_pay_to / unsupported_networkThe request body is invalid.Fix the request.
401agent_auth_failedThe agent JWT is missing, invalid or expired. A VC token returns { "error": "wrong_token_type" }.Refresh the JWT.
403agent_not_authorizedThe agent isn't authorized in the user's wallet.Ask the user to link the agent.
403mandate_not_foundThe mandate doesn't exist or belongs to another agent.Check mandateId.
403mandate_not_signedThe mandate is awaiting the user's signature.Show the user the mandate's signUrl.
403mandate_disabledThe user turned this mandate off.Use a different mandate.
403mandate_expiredThe payment falls outside the mandate's validity window.Create a new mandate.
403mandate_inactiveThe mandate can't pay this, for example because it was revoked or its currency doesn't match.Use another mandate in the right currency.
403mandate_insufficient_budgetThe remaining budget is less than amount.Create a new mandate or a larger one.
403agent_monthly_limit_exceededThe 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.
403policy_deniedBlocked by the user's spending policy.Ask the user to review their settings.
403risk_control_rejectedBlocked by risk checks.Don't retry automatically.
200need_approval (status)The user must still approve this payment (some XRP flows). The body has approvalUrl.Show approvalUrl, then retry.
409duplicate_authorizationThis authorization was already used.Request a new payment.

Typical flow ​

  1. Receive HTTP 402 from a resource.
  2. Call GET /api/mandates/eligible?host=...&amount=...&currency=... to find a mandate.
  3. If one is found, call POST /api/payment/x402V3Payment with its mandateId.
  4. Send the returned xPaymentB64 as X-Payment and retry.
  5. If no mandate is eligible, create a mandate or fall back to V1.

x402 v2 payment requirements ​

POST/api/v2/payment/x402-paymentBearer agent-jwt

Use 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.

bash
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
        }
      ]
    }
  }'
FieldRequiredDescription
mandateIdYesA signed USDC mandate.
paymentRequestOne ofThe v2 PaymentRequired object, as JSON.
paymentRequestB64One ofThe same object, Base64-encoded (for example the Payment-Required header value).
preferredAssetsNo[{ "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):

json
{
  "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:

HTTPcodeMeaning
400invalid_payment_request / invalid_payment_request_b64 / invalid_resource_urlThe paymentRequest is missing, malformed, or not x402Version: 2.
400no_supported_payment_optionNo accepts entry is Base mainnet USDC with scheme: "exact", or none matches preferredAssets.
403insufficient_balanceThe wallet's USDC balance is too low.

Released under the MIT License.