API Reference

Complete documentation for Blocky402 facilitator API endpoints. The wire format is x402 v2 (x402Version: 2); the facilitator does not accept v1 envelopes.

Base URLs

  • Testnet: https://api.testnet.blocky402.com (or http://localhost:3002 for local development)
  • Mainnet: https://api.blocky402.com/v1 (coming soon)

Authentication

  • Testnet: No authentication required (open access)
  • Mainnet: API key required in X-Api-Key header (format: b402_<64_character_hex_string>)

Endpoints

GET /supported

Returns supported payment schemes and networks for the current environment.

Request:

GET /supported

Response:

{
  "kinds": [
    { "scheme": "exact", "network": "eip155:80002", "x402Version": 2 },
    {
      "scheme": "exact",
      "network": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
      "x402Version": 2,
      "extra": { "feePayer": "7B6Q2MvcJvNcy1A13wHmAzmmdo3L8DVriaXML7bvkojm" }
    },
    {
      "scheme": "exact",
      "network": "hedera:testnet",
      "x402Version": 2,
      "extra": { "feePayer": "0.0.7162784" }
    }
  ],
  "extensions": [],
  "signers": {
    "eip155:*": ["0xDCF7D72C2eE049DE4269ac6AAf925F33efdA18de"],
    "solana:*": ["7B6Q2MvcJvNcy1A13wHmAzmmdo3L8DVriaXML7bvkojm"],
    "hedera:*": ["0.0.7162784"]
  }
}

Solana and Hedera entries carry extra.feePayer — the account the facilitator co-signs as. Clients must place the same value into paymentRequirements.extra.feePayer when building the payment payload, or the client SDK will throw before signing.

Status Codes:

  • 200 OK: Success
  • 401 Unauthorized: Invalid or missing API key (mainnet only)
  • 429 Too Many Requests: Rate limit exceeded

POST /verify

Verifies a payment payload against the x402 protocol. Three input formats are accepted; the canonical form is paymentPayload (object) in the body, matching @x402/[email protected] httpFacilitatorClient.

Request (canonical — paymentPayload object, Hedera example): The same HTTP envelope works for EVM and SVM — only the payload shape inside paymentPayload differs (see Payload shapes by scheme below). Hedera additionally requires extra.feePayer and must match the fee-payer the facilitator advertises in GET /supported.

POST /verify
Content-Type: application/json
 
{
  "x402Version": 2,
  "paymentPayload": {
    "x402Version": 2,
    "scheme": "exact",
    "network": "hedera:testnet",
    "accepted": {
      "scheme": "exact",
      "network": "hedera:testnet",
      "amount": "100000",
      "payTo": "0.0.8011510",
      "maxTimeoutSeconds": 300,
      "asset": "0.0.0",
      "extra": { "feePayer": "0.0.7162784" }
    },
    "payload": {
      "transaction": "<base64-encoded TransferTransaction bytes>"
    }
  },
  "paymentRequirements": {
    "scheme": "exact",
    "network": "hedera:testnet",
    "amount": "100000",
    "payTo": "0.0.8011510",
    "maxTimeoutSeconds": 300,
    "asset": "0.0.0",
    "extra": { "feePayer": "0.0.7162784" }
  }
}

Request (x402 spec — X-PAYMENT header): the same paymentPayload object, base64-encoded, sent in the X-PAYMENT header. The body then only carries paymentRequirements.

POST /verify
X-PAYMENT: <base64(JSON(paymentPayload))>
Content-Type: application/json
 
{ "paymentRequirements": { /* same shape as above */ } }

Request (legacy — base64 paymentHeader in body): equivalent to the X-PAYMENT header form but with the base64 string in the body. Accepted for backwards-compatibility with pre-2.12 clients.

{
  "x402Version": 2,
  "paymentHeader": "<base64(JSON(paymentPayload))>",
  "paymentRequirements": { /* same shape as above */ }
}

Response (valid):

{
  "isValid": true,
  "payer": "0.0.7326075"
}

Response (invalid):

{
  "isValid": false,
  "invalidReason": "InvalidSignature",
  "invalidMessage": "Signature recovery did not match the expected payer"
}

Optional response fields: extensions, extra (scheme-specific blobs).

Status Codes:

  • 200 OK: Verification completed (check isValid)
  • 400 Bad Request: Invalid request format, unknown scheme, or unsupported network
  • 401 Unauthorized: Invalid or missing API key (mainnet only)
  • 429 Too Many Requests: Rate limit exceeded

POST /settle

Settles a payment by broadcasting the transaction. Same three input formats as /verify; the body shape is identical (you can reuse the same paymentPayload + paymentRequirements pair).

Request: identical to /verify. POST to /settle instead.

Response (success):

{
  "success": true,
  "transaction": "[email protected]",
  "network": "hedera:testnet",
  "payer": "0.0.7326075"
}

The transaction field is network-native: 0.0.<feePayer>@<seconds>.<nanos> for Hedera, a 0x… hash for EVM, base58 signature for SVM.

Response (error):

{
  "success": false,
  "errorReason": "transaction_failed",
  "errorMessage": "TOKEN_NOT_ASSOCIATED_TO_ACCOUNT",
  "transaction": "",
  "network": "hedera:testnet"
}

Optional response fields: amount (for schemes that allow partial settlement, e.g. upto), extensions, extra.

Status Codes:

  • 200 OK: Settlement attempt completed (check success)
  • 400 Bad Request: Invalid request format, unknown scheme, or unsupported network
  • 401 Unauthorized: Invalid or missing API key (mainnet only)
  • 429 Too Many Requests: Rate limit exceeded

GET /health

Health check endpoint for monitoring API status.

Request:

GET /health

Response:

{
  "status": "ok",
  "timestamp": "2026-05-17T10:30:00Z",
  "version": "1.0.0"
}

Status Codes:

  • 200 OK: Service is healthy

Payload shapes by scheme

The paymentPayload.payload field is scheme-specific. The facilitator detects the shape automatically.

Hedera exact (and SVM exact)

{ "transaction": "<base64-encoded transaction bytes>" }

For Hedera this is a partially-signed TransferTransaction produced by @x402/hedera’s ExactHederaScheme.createPaymentPayload(...). The facilitator’s fee-payer co-signs at settlement time.

EVM exact — EIP-3009 (default)

{
  "signature": "0x...",
  "authorization": {
    "from": "0x...", "to": "0x...", "value": "1000000",
    "validAfter": "0", "validBefore": "9999999999",
    "nonce": "0x0000000000000000000000000000000000000000000000000000000000000000"
  }
}

EVM exact — Permit2

{
  "signature": "0x...",
  "permit2Authorization": {
    "from": "0x...", "spender": "0x...",
    "nonce": "0", "deadline": "9999999999",
    "permitted": { "token": "0x...", "amount": "1000000" },
    "witness": { "facilitator": "0x...", "to": "0x...", "validAfter": "0" }
  }
}

Rate Limiting

Testnet

  • Type: Per-IP
  • Limit: 100 requests per minute
  • Burst: 10 requests

Mainnet (Coming Soon)

  • Type: Per-API-Key
  • Limit: 10 requests per second
  • Daily Quota: 10,000 settlements per day per key

Error Responses

NestJS-style error bodies on 4xx:

{
  "statusCode": 400,
  "message": "Network unsupported-network is not supported",
  "error": "Bad Request"
}

For validation failures, message is a comma-joined list of constraint violations, e.g. "Validation failed: amount must be a string, extra should not be null or undefined".

Common Error Messages

HTTP StatusTrigger
400DTO validation, unknown scheme/network, missing x402Version, malformed base64
401Mainnet only — missing or invalid X-Api-Key
429Rate limit exceeded
500Unexpected facilitator error

Payment Flow

  1. Discover capabilities: GET /supported
  2. Sign a v2 PaymentPayload using @x402/hedera, @x402/evm, or @x402/svm
  3. Verify the payment: POST /verify (check isValid)
  4. Settle the payment: POST /settle (check success, read transaction)

For complete examples, see the Code Examples page.