ar.io Logoar.io Documentation
TurboPayment Service

x402

x402 protocol payment endpoints (EIP-3009 USDC payments)

Get x402 payment requirements for upload

Returns payment requirements for uploading data of a given size. This is the first step in the x402 payment flow.

IMPORTANT: Per x402 standard, this endpoint returns 200 OK (not 402). The actual 402 Payment Required response happens at the protected resource (upload endpoint at /v1/tx).

Three-Phase x402 Flow:

  1. Price Quote (this endpoint): Client gets payment requirements → 200 OK with requirements
  2. Upload Attempt: Client uploads to /v1/tx without payment → 402 Payment Required
  3. Paid Upload: Client uploads to /v1/tx with X-PAYMENT header → 200 OK with receipt

Content Negotiation:

  • API clients (Accept: application/json): Receive JSON payment requirements for programmatic use
  • Browser clients (Accept: text/html): Receive interactive HTML paywall (if X_402_CDP_CLIENT_KEY configured)

Browser Paywall Features (when X_402_CDP_CLIENT_KEY environment variable is set):

  • Interactive payment authorization UI with wallet connection
  • MetaMask integration for EIP-712 signature generation
  • Coinbase Onramp widget for purchasing USDC if user lacks funds
  • Automatic base64 encoding of payment authorization
  • Copy-to-clipboard functionality for X-PAYMENT header value

Pricing Calculation:

  • Converts byte count → Winston (Arweave storage cost)
  • Adds pricing buffer (default 5%) for volatility protection
  • Converts Winston → USD → USDC (6 decimals)
  • Returns maxAmountRequired in USDC smallest unit

Supported Networks: base-sepolia, base-mainnet (configurable via X402_NETWORKS)

GET
/x402/price/{signatureType}/{address}

Path Parameters

signatureTypeinteger

ANS-104 signature type for the data item:

  • 1: Arweave (RSA-PSS with SHA-256)
  • 3: Ethereum (ECDSA secp256k1)
  • 4: Solana (Ed25519)
Value in1 | 3 | 4
addressstring

User's wallet address in format matching signatureType. Used for tracking and analytics (not for payment verification).

Query Parameters

bytesinteger

Number of bytes to upload (data item size). Used to calculate storage cost and USDC payment amount.

Range1 <= value

Response Body

curl -X GET "https://turbo.ardrive.io/v1/x402/price/3/0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0?bytes=1024"

{
  "x402Version": 1,
  "accepts": [
    {
      "scheme": "exact",
      "network": "base-mainnet",
      "maxAmountRequired": "1000000",
      "resource": "/v1/tx",
      "description": "Upload 1024 bytes to Arweave via AR.IO Bundler",
      "mimeType": "application/json",
      "outputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Data item ID"
          },
          "timestamp": {
            "type": "number",
            "description": "Upload timestamp"
          },
          "x402Payment": {
            "type": "object",
            "description": "x402 payment details"
          }
        }
      },
      "payTo": "0x6A0A10FFD285c971B841bee8892878c0d583Bf67",
      "maxTimeoutSeconds": 300,
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "extra": {
        "name": "USD Coin",
        "version": "2"
      }
    }
  ]
}

"Invalid byte count"
"Pricing oracle unavailable"

Verify and settle x402 payment

Verifies an EIP-3009 payment authorization and settles payment on-chain according to the specified mode.

This is Phase 2 of the x402 flow - verifying and settling the payment.

Process:

  1. Decode base64 payment header to extract EIP-3009 authorization
  2. Verify EIP-712 signature matches authorization parameters
  3. Check authorization hasn't been used or expired
  4. Call USDC contract's receiveWithAuthorization() to settle payment
  5. Convert USDC → Winston (Arweave storage credits)
  6. Apply payment according to mode (payg, topup, or hybrid)
  7. Record transaction in database with payment ID
  8. Return success with transaction hash for verification

Payment Modes:

  • payg (Pay-as-you-go): Payment reserved for specific upload only
  • topup: Entire payment amount credited to user's account balance
  • hybrid: Reserve for upload + credit any excess to balance (DEFAULT)

Security:

  • EIP-712 signature verification prevents unauthorized transfers
  • Nonce prevents replay attacks
  • validBefore/validAfter enforce time bounds
  • On-chain settlement provides cryptographic proof

Called by: Upload service during data item upload when X-PAYMENT header is present

POST
/x402/payment/{signatureType}/{address}

Path Parameters

signatureTypeinteger

ANS-104 signature type (1=Arweave, 3=Ethereum, 4=Solana)

Value in1 | 3 | 4
addressstring

User's wallet address (used for tracking, not payment verification)

Payment authorization details extracted from X-PAYMENT header.

The upload service extracts this from the user's X-PAYMENT header and forwards it to the payment service for verification and settlement.

paymentHeaderstring

Base64-encoded x402 payment authorization (same value from X-PAYMENT header).

Decodes to JSON with:

  • x402Version: 1
  • scheme: "exact"
  • network: "base-mainnet" or "base-sepolia"
  • payload:
    • signature: EIP-712 signature of authorization
    • authorization:
      • from: Payer's Ethereum address
      • to: Recipient address (service's Ethereum wallet)
      • value: USDC amount in smallest unit (6 decimals)
      • validAfter: Unix timestamp
      • validBefore: Unix timestamp
      • nonce: Unique nonce (bytes32)
Formatbyte
dataItemId?string

Data item ID being uploaded (optional for PAYG mode).

For PAYG mode, this can be provided later via link endpoint. For hybrid mode, this is required upfront.

byteCountinteger

Declared byte count for the upload (required for PAYG and hybrid modes).

Used to calculate Winston cost and determine if payment is sufficient. Must match Content-Length header from upload request.

Range1 <= value
mode?string

Payment mode - determines how payment is applied.

payg: Payment reserved for this specific upload only topup: Entire payment credited to account balance hybrid: Reserve for upload + credit excess (DEFAULT)

Most x402 clients use "payg" for stateless pay-per-upload.

Default"hybrid"
Value in"payg" | "topup" | "hybrid"

Response Body

curl -X POST "https://turbo.ardrive.io/v1/x402/payment/3/0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0" \  -H "Content-Type: application/json" \  -d '{    "paymentHeader": "eyJ4NDAyVmVyc2lvbiI6MSwic2NoZW1lIjoiZXhhY3QiLCJuZXR3b3JrIjoiYmFzZS1tYWlubmV0IiwicGF5bG9hZCI6ey4uLn19",    "dataItemId": "QpmY8mZmFEC8RxNsgbxSV6e36OF6quIYaPRKzvUco0o",    "byteCount": 1024,    "mode": "payg"  }'
{
  "success": true,
  "paymentId": "550e8400-e29b-41d4-a716-446655440000",
  "txHash": "0xb8a52725cdc816d060392dcecaa1528872acf9ea0bd097a86e2432dd5d2213ca",
  "network": "base-mainnet",
  "wincPaid": "1000000",
  "wincReserved": "1000000",
  "wincCredited": "950000",
  "mode": "hybrid",
  "error": "Invalid payment signature"
}
"Invalid payment header"
{
  "success": true,
  "paymentId": "550e8400-e29b-41d4-a716-446655440000",
  "txHash": "0xb8a52725cdc816d060392dcecaa1528872acf9ea0bd097a86e2432dd5d2213ca",
  "network": "base-mainnet",
  "wincPaid": "1000000",
  "wincReserved": "1000000",
  "wincCredited": "950000",
  "mode": "hybrid",
  "error": "Invalid payment signature"
}
"Payment service unavailable"

Finalize x402 payment after upload

Finalizes an x402 payment after upload completes by comparing declared vs actual byte count.

This is Phase 3 of the x402 flow - fraud detection and finalization.

Called by: Upload service after data item is fully received and validated.

Process:

  1. Retrieve payment record by data item ID
  2. Compare actualByteCount vs declaredByteCount (from X-PAYMENT)
  3. Apply fraud tolerance threshold (default 5%)
  4. Determine status: confirmed, refunded, or fraud_penalty
  5. Issue refund if overpaid (actual < declared)
  6. Keep payment as penalty if underpaid (actual > declared + tolerance)
  7. Update payment record with final status

Fraud Detection:

  • Within tolerance: Payment confirmed, upload proceeds
  • Overpayment (actual < declared - 5%): Refund difference to user
  • Fraud (actual > declared + 5%): Keep payment as penalty, reject upload

Why this matters: Users must provide Content-Length header accurately. If they lie about upload size to pay less, the system detects this and penalizes them by keeping the payment.

Security: Prevents users from declaring small uploads but uploading large data.

POST
/x402/finalize

Actual upload details after data item has been fully received

dataItemIdstring

Data item ID that was uploaded (links to payment record)

actualByteCountinteger

Actual byte count after upload completed and data item validated.

This is the true size of the ANS-104 data item (headers + payload). Compared against declaredByteCount from payment verification phase.

Range1 <= value

Response Body

curl -X POST "https://turbo.ardrive.io/v1/x402/finalize" \  -H "Content-Type: application/json" \  -d '{    "dataItemId": "QpmY8mZmFEC8RxNsgbxSV6e36OF6quIYaPRKzvUco0o",    "actualByteCount": 1024  }'
{
  "success": true,
  "status": "confirmed",
  "actualByteCount": 1024,
  "refundWinc": "50000",
  "error": "Payment not found"
}
"Payment not found for data item"
"Payment service unavailable"

Top up credits using x402 payment

Top up your account balance by purchasing storage credits with USDC via x402.

This is a pure x402 flow - the resource being purchased is "storage credits."

Flow:

  1. First request (no X-PAYMENT header): Returns 402 Payment Required with payment requirements
  2. Retry with X-PAYMENT header: Verifies payment, settles USDC, credits balance

Key Differences from Upload Payment:

  • No data item involved - just purchasing credits
  • Supports multiple signature types (Arweave, Ethereum, Solana)
  • Entire payment amount converted to credits and added to balance
  • Can be used for any future uploads

Use Case: AI agents or users who want to prepay for storage using USDC

Note: While x402 currently only supports EVM chains (Base, Ethereum, Polygon), this endpoint accepts signatureType for forward compatibility with future payment methods.

POST
/x402/top-up/{signatureType}/{address}

Path Parameters

signatureTypeinteger

ANS-104 signature type:

  • 1: Arweave (RSA-PSS)
  • 3: Ethereum (ECDSA secp256k1)
  • 4: Solana (EdDSA ed25519)

Currently, x402 only supports signatureType 3 (Ethereum), but this parameter is included for forward compatibility.

Value in1 | 3 | 4
addressstring

User address that will receive the credits. Format depends on signatureType:

  • signatureType 1: Base64URL Arweave address
  • signatureType 3: 0x-prefixed Ethereum address (current x402 support)
  • signatureType 4: Base58 Solana address

Request body for top-up. On first request (without X-PAYMENT), this tells the service how many bytes worth of credits you want to purchase.

bytesinteger

Number of bytes worth of storage credits to purchase.

This determines the USDC price. For example:

  • 1 GB (1,000,000,000 bytes) might cost ~$0.005 USDC
  • 10 GB (10,000,000,000 bytes) might cost ~$0.05 USDC

The service calculates: bytes → Winston → USD → USDC

Range1 <= value

Response Body

curl -X POST "https://turbo.ardrive.io/v1/x402/top-up/3/0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0" \  -H "Content-Type: application/json" \  -d '{    "bytes": 1000000000  }'

{
  "success": true,
  "creditsGranted": "10000000000",
  "balance": "15000000000",
  "txHash": "0xb8a52725cdc816d060392dcecaa1528872acf9ea0bd097a86e2432dd5d2213ca",
  "paymentId": "550e8400-e29b-41d4-a716-446655440000",
  "network": "base-mainnet"
}
"Invalid bytes parameter"

{
  "x402Version": 1,
  "accepts": [
    {
      "scheme": "exact",
      "network": "base-mainnet",
      "maxAmountRequired": "50000",
      "resource": "/v1/x402/top-up/3/0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
      "description": "Top up storage credits for 10000000000 bytes",
      "mimeType": "application/json",
      "outputSchema": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "creditsGranted": {
            "type": "string",
            "description": "Winston credits granted"
          },
          "balance": {
            "type": "string",
            "description": "New total balance"
          },
          "txHash": {
            "type": "string",
            "description": "Blockchain transaction hash"
          },
          "paymentId": {
            "type": "string",
            "description": "Payment ID (UUID)"
          }
        }
      },
      "payTo": "0x6A0A10FFD285c971B841bee8892878c0d583Bf67",
      "maxTimeoutSeconds": 300,
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "extra": {
        "name": "USD Coin",
        "version": "2"
      }
    }
  ]
}
"x402 payments are not currently available"

How is this guide?