thru

Agent payments (x402 / MPP)

Not enabled on api.thru.la. Every /v1/facilitator/* endpoint currently returns 404. This page is here so you can plan for it. Contact thru before building on it, and use payments or checkout-sessions for anything you need today.

What it is

Your API charges per request. A caller, often an AI agent, requests a paid route without paying and gets 402 Payment Required, with the price in a PAYMENT-REQUIRED header. The caller signs a stablecoin transfer authorization and repeats the request with it in a PAYMENT-SIGNATURE header (or Authorization: Payment … for MPP clients). Your server asks thru to verify and settle it, and then serves the response with a PAYMENT-RESPONSE header as the receipt.

The money moves from the payer directly to your settlement address (payTo), not through a payment address.

The package

bash
npm install @thru-payment/x402

The canonical example is examples/express-server.ts in the thru-sdk repository; it is type-checked in CI. The essentials:

ts
import express from 'express';
import { createFacilitatorClient, resolveEvmRoute } from '@thru-payment/x402';
import { paymentMiddleware } from '@thru-payment/x402/express';

const facilitator = createFacilitatorClient({ apiKey: process.env.THRU_API_KEY! });

const routes = {
  'GET /reports/annual': await resolveEvmRoute(facilitator, {
    chain: 'robinhood',
    network: 'mainnet',
    asset: '0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168', // USDG, 6 decimals
    amountAtomic: 5_000_000n, // 5 USDG
    payTo: process.env.THRU_ROBINHOOD_SETTLEMENT_ADDRESS!,
    resource: '/reports/annual',
    maxTimeoutSeconds: 300,
  }),
};

const app = express();
app.use(paymentMiddleware(routes, facilitator));
  • paymentMiddleware(routes, client, options?) takes three positional arguments and is imported from @thru-payment/x402/express, not from the package root.
  • Route keys are "METHOD /exact/path", matched exactly: no parameters, no wildcards.
  • asset is a token contract address (a coin type on Sui), not a symbol.
  • amountAtomic is a bigint in the token's smallest unit. USDC and USDT on BNB Chain have 18 decimals; most others have 6.
  • payTo must be your settlement address for that chain and network (GET /v1/settlement).
  • resolveEvmRoute and resolveSuiRoute choose the scheme for you from what the facilitator supports. Call them once at startup.
  • createFacilitatorClient takes a baseUrl without /v1 (default https://api.thru.la), unlike the other thru packages.
  • For MPP clients, pass { mppSecret } as the third argument. Your workspace's value comes from GET /v1/facilitator/mpp-secret, which returns { secret }.

For other frameworks, gateRequest(headers, route, client, options) is exported from the package root.

Chains and schemes

chainSchemes
bnb, robinhoodeip3009_exact: the token checks the payer's signature itself; no approval needed. permit2_exact: works with any ERC-20, but the payer first makes a one-time on-chain approve for the Permit2 contract and pays that gas.
suisui_direct: gasless, for eligible stablecoins. sui_sponsored: thru pays the gas. Native SUI cannot be paid this way.

The facilitator also supports arc on the server, but the 0.2.0 SDK's Chain type lists only bnb, robinhood and sui. GET /v1/facilitator/supported (no key needed) returns the live chains, schemes and assets.

Endpoints

EndpointAuthDoes
GET /v1/facilitator/supportednoneChains, schemes and assets available now
POST /v1/facilitator/verifykeyChecks a payment without broadcasting it
POST /v1/facilitator/settlekeyBroadcasts it
GET /v1/facilitator/mpp-secretkeyYour workspace's MPP binding secret
GET /v1/facilitator/payments?limit=&cursor=keyYour agent-payment history

The middleware calls verify and settle for you. Outcomes are also sent as the facilitator.payment.settled and facilitator.payment.failed webhook events.

Failure reasons

A failed verify or settle carries a machine-readable reason. The ones that need action on your side:

reasonWhat to do
no_settlement_accountAdd a settlement address for that chain and network (Treasury → Settlement).
payto_mismatchpayTo is not your settlement address; read it from GET /v1/settlement.
unsupported_asset, asset_not_gasless_eligibleThe scheme does not support this asset. Let resolveEvmRoute or resolveSuiRoute choose.
deadline_expired, not_yet_valid, nonce_usedThe authorization is stale or already used. Answer with a fresh 402.
settle_in_progressA settlement for this payment is already running. Do not settle again; check GET /v1/facilitator/payments.

Other codes include unknown_asset, unsupported_chain, amount_over_cap, daily_cap_exceeded, amount_mismatch, token_mismatch, invalid_signature, signer_mismatch, insufficient_balance, insufficient_allowance, transaction_failed, gas_tank_empty, deadline_too_far, wrong_payload_kind, simulation_did_not_run, and, on Sui, gas_budget_not_set, gas_price_not_set, gas_cost_exceeds_sponsor_limit, gas_owner_mismatch, sender_mismatch, not_gasless, missing_or_unbounded_expiration, malformed_transaction, invalid_sender_signature, payto_not_credited, underpayment, sponsor_non_gas_outflow and sui_native_not_supported_for_sponsored. Treat any code you do not recognise as a failed payment.