# FastPay Documentation > FastPay provides enterprise crypto payment acceptance and mass payout/disbursement APIs. The relay is currently staging-only; verify deployment and tenant entitlement before sending funds. ## Machine-readable references - [Documentation hub](https://f-pay.com/docs): text/html - [Complete written reference](https://f-pay.com/api): text/html - [Machine-readable catalogue](https://f-pay.com/docs.json): application/json - [Relay OpenAPI specification](https://xwefglhexusimsmdgvtd.supabase.co/functions/v1/openapi-spec): application/vnd.oai.openapi+json - [Static HTML reference (no JavaScript required)](https://f-pay.com/api-reference.html): text/html - [Markdown reference](https://f-pay.com/api-reference.md): text/markdown - [Complete LLM reference](https://f-pay.com/llms-full.txt): text/plain - [Relay architecture](https://f-pay.com/docs/relay-gateway): text/html - [Disbursement engine](https://f-pay.com/docs/disbursement-engine): text/html - [Webhook tester](https://f-pay.com/docs/webhook-tester): text/html - [Webhook delivery log](https://f-pay.com/docs/webhooks): text/html ## Product boundaries - The custodial Disbursement Engine uses FastPay-controlled funds and operator authentication. - Relay Mass Payout is non-custodial and uses tenant funds with scoped permissions. - Relay Checkout lets a customer pay a merchant directly; some lifecycle surfaces are beta. - Operator-only routes are not a public integration surface. # Complete API reference Generated: 2026-09-04T19:08:39.673Z ## Authentication - Relay tenant key: Authorization: Bearer fp_live_…. Mass payout and merchant-authenticated checkout routes. Failure: 401 with missing_api_key or invalid_api_key. - Sandbox tenant key: Authorization: Bearer fp_test_…. Payment Gateway checkout and refund routes on Ethereum Sepolia testnet, or against a simulated chain adapter. Failure: Same envelope as a live key; only the on-chain interaction is faked. - Engine/admin key: x-admin-api-key. Custodial engine and operator-only administration. Failure: 403 when absent or invalid. - Public checkout: None. Public request read, intent, and confirmation. Failure: The unguessable request id is the capability token. ## Platform conventions Every response carries an X-API-Version header, and the /v1/ surface is covered by an explicit no-breaking-changes policy. Relay list endpoints use Stripe-style cursor pagination (limit, starting_after, has_more) and every relay error body includes a request_id. Relay errors have converged on one envelope: error_type, error_slug and error_message. Webhooks carry a stable, dedupe-safe event id and a dot-namespaced type such as payment.completed, on both the relay and the disbursement engine. A published payment lifecycle (six canonical states, sourced from the state machine in code), per-route rate-limit numbers — including the routes with no limit enforced today — and a changelog of externally visible API changes are maintained alongside this reference. Sandbox tenants are scoped to testnet: every payment link they create settles on Ethereum Sepolia in testnet tokens (Sepolia USDC 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238, or native test ETH), the hosted checkout switches the wallet to Sepolia, and confirmation is verified against a Sepolia RPC — so an end-to-end payment can be run with faucet funds and no real value ever moves. Sepolia has no canonical USDT, so USDT links are rejected in the sandbox. An fp_test_ key routes checkout and refund building/confirming through a fully simulated adapter: no real chain call, no real-world address validation. Two reserved confirm hashes control the outcome — 0x followed by 64 f characters forces failed, 0x followed by 64 e characters forces pending, and any other well-formed hash succeeds. Everything downstream is real: a sandbox payment that reaches paid fires the real webhook and posts to the real double-entry ledger. Sandbox covers the Payment Gateway's anonymous checkout and refund routes only; the mass disbursement and relay mass-payout APIs have no sandbox equivalent. ## Limits - Engine payments per request: 500 (MAX_PAYMENTS_PER_REQUEST) - Relay payments per request: 50 (RELAY_MAX_PAYMENTS_PER_REQUEST) - Amount per payment: 1,000,000 (MAX_AMOUNT_PER_PAYMENT) - Engine daily payment count: 2,000 / UTC day (DAILY_PAYMENT_COUNT_CAP) - Tenant daily payment count: Operator configured (Per-tenant setting) - Public checkout routes: 30 requests / minute / IP (RELAY_PUBLIC_PAYMENT_REQUEST_RATE_LIMIT) - Engine request body: ~500 × 600B + 16KB (MAX_REQUEST_BODY_BYTES) - List page size: Default and maximum are configured (PAGINATION_DEFAULT_LIMIT / PAGINATION_MAX_LIMIT) - Reconciliation window: Operator configured (RELAY_RECONCILIATION_DEFAULT_WINDOW_DAYS) ## Chains and assets - Ethereum: ETH, USDC, USDT. Engine + relay; hosted checkout is Ethereum-only. - Solana: SOL, USDC, USDT. Engine + relay verification; checkout fulfillment is not enabled. - Tron: TRX, USDT. Engine + relay; direct-transfer behavior, no contract fee split. - Base: ETH, USDC. Custodial engine coverage. - BNB Chain: BNB, USDC. Custodial engine coverage. - Arbitrum: ETH. Engine coverage; native asset only. - Polygon: POL. Engine coverage; native asset only. ## Disbursement engine FastPay-funded, custodial disbursements. FastPay operates the wallet and signs the transaction; this is separate from the client-funded relay gateway. ### GET /health — Health check Authentication: none. Status: documented. Checks that the service and its broadcast ledger are reachable. It does not prove RPC or bundler connectivity. Response example: ```json { "status": "ok", "ledger": "ok", "paused": false } ``` ### POST /disburse — Trigger a disbursement Authentication: admin. Status: documented. Accepts an asynchronous batch funded from FastPay's custodial wallet and returns a job identifier. Request example: ```json { "payments": [ { "ALLOCATION_ID": "payroll-2026-09-0001", "CHAIN": "ETH", "WALLET": "0x1111111111111111111111111111111111111111", "CURRENCY": "USDC", "AMOUNT": 1250.00 } ], "callbackUrl": "https://client.example.com/webhooks/fastpay", "resetDeduplication": false } ``` Response example: ```json { "jobId": "b3f1...", "payments_accepted": 1, "duplicates_skipped": 0, "skipped_allocations": [] } ``` - Maximum 500 payments by default. ALLOCATION_ID is the engine's deduplication key. - The response is not the payment result; consume webhooks for terminal outcomes. ### POST /cancel — Cancel a job Authentication: admin. Status: documented. Stops work that has not yet been broadcast. It cannot recall an on-chain transaction and is in-memory only. Request example: ```json { "job_id": "b3f1..." } ``` Response example: ```json { "success": true, "message": "Job cancelled." } ``` ### GET /status/:jobId — Get job status Authentication: none. Status: documented. Returns a best-effort progress snapshot for a job. Webhooks remain the authoritative delivery mechanism. Response example: ```json { "jobId": "b3f1...", "status": "running", "progress": { "total": 10, "completed": 4, "failed": 0, "pending": 6 } } ``` ### GET /payments/:allocationId — Get a payment by allocation id Authentication: admin. Status: documented. Reads the engine's own durable money-safety stores (deduplication table and broadcast ledger) for a single ALLOCATION_ID. Response example: ```json { "allocation_id": "payroll-2026-09-0001", "status": "completed", "chain": "ETH", "wallet": "0x...", "tx_hash": "0x..." } ``` - A failure reason is deliberately not returned here; that detail is only ever sent once, on the webhook, and is not durably persisted by the engine. ### GET /payments — List payments Authentication: admin. Status: documented. Enumerates every allocation the engine has accepted, using the same cursor pagination as the relay list endpoints. Response example: ```json { "object": "list", "data": [{ "allocation_id": "payroll-2026-09-0001", "status": "completed" }], "has_more": false } ``` - Pagination is limit / starting_after / has_more. Defaults come from PAGINATION_DEFAULT_LIMIT and PAGINATION_MAX_LIMIT. ### POST /admin/pause · /admin/resume — Pause or resume intake Authentication: admin. Status: documented. Operator kill switch that blocks new jobs without touching work already in flight. Response example: ```json { "success": true, "paused": true, "pausedAt": "2026-09-02T10:00:00Z" } ``` ### POST /admin/reset · /admin/remove-processed-ids — Manage deduplication Authentication: admin. Status: documented. Reset all tracked allocation IDs or surgically remove named IDs so they can be admitted again. Response example: ```json { "success": true, "removed": ["alloc-123"], "notFound": [] } ``` ### GET /admin/stats — Get operational stats Authentication: admin. Status: documented. Returns deduplication counters and configured connector usage for operator monitoring. Response example: ```json { "success": true, "deduplication": { "processedCount": 1204, "processingCount": 2, "totalTracked": 1206 }, "timestamp": "2026-09-02T10:00:00Z" } ``` ## Relay · mass payout Client-funded, non-custodial payouts. A tenant's own smart account funds the transfer and grants FastPay a bounded permission to submit it; no private key is transferred. ### POST /v1/instructions — Create instructions Authentication: bearer. Status: documented. Accepts a batch for asynchronous execution and returns immediately with a job id. Request example: ```json { "network": "ethereum", "payments": [ { "ALLOCATION_ID": "payroll-2026-09-0001", "CHAIN": "ETH", "WALLET": "0x1111111111111111111111111111111111111111", "CURRENCY": "USDC", "AMOUNT": 1250.00 } ], "callbackUrl": "https://client.example.com/webhooks/fastpay" } ``` Response example: ```json { "jobId": "bc163be2-0979-4a50-bd71-4b87445fe627", "status": "accepted" } ``` - Relay batches are capped at 50 by default. - network is a body field: ethereum, tron, or solana. Ethereum is token-only on this unattended path; native ETH is rejected. - A tenant daily payment cap may return 402 daily_payment_cap_exceeded. ### GET /v1/payouts/:payoutId — Get a payout Authentication: bearer. Status: documented. Returns one payout — an individual entry inside a mass-payout job, addressable as its own resource. Response example: ```json { "id": "po_...", "object": "payout", "job_id": "bc163be2-0979-4a50-bd71-4b87445fe627", "status": "completed", "network": "ethereum", "currency": "USDC", "amount": "1250.00", "wallet": "0x...", "tx_hash": "0x..." } ``` ### GET /v1/jobs/:jobId/payouts — List payouts for a job Authentication: bearer. Status: documented. Lists the payout entries belonging to one job, newest first, with cursor pagination. Response example: ```json { "object": "list", "data": [{ "id": "po_...", "object": "payout", "status": "completed" }], "has_more": false } ``` - Use limit and starting_after to page. These endpoints no longer return an entire history in one response. ### GET /v1/jobs/:jobId — Get a payout job Authentication: bearer. Status: documented. Returns the tenant-scoped job, lifecycle status, and per-payment results for polling or a status page. Response example: ```json { "jobId": "bc163be2-0979-4a50-bd71-4b87445fe627", "status": "processing", "network": "ethereum", "payments": [{ "ALLOCATION_ID": "payroll-2026-09-0001", "status": "completed", "TX_HASH": "0x..." }] } ``` ### POST /v1/jobs/:jobId/cancel — Cancel a payout job Authentication: bearer. Status: documented. Cancels a job only while it is accepted or awaiting_onboarding. Once execution starts, the job is not cancellable. Response example: ```json { "jobId": "bc163be2-0979-4a50-bd71-4b87445fe627", "status": "cancelled" } ``` - A successful cancellation does not undo anything already broadcast. ## Relay · checkout A merchant creates a payment request and the payer's own wallet signs and broadcasts. Public read, intent, and confirmation routes do not require a tenant key; the request id is the capability token. ### POST /v1/payment-requests — Create a payment request Authentication: bearer. Status: documented. Creates a single-use invoice-like payment request with a future expiry of up to seven days. Request example: ```json { "reference": "order-1042", "network": "ethereum", "currency": "USDC", "amount": "250.00", "expiresAt": "2026-09-05T12:00:00Z", "recipient": "0x1111111111111111111111111111111111111111", "callbackUrl": "https://merchant.example.com/fastpay" } ``` Response example: ```json { "id": "pr_...", "reference": "order-1042", "status": "pending", "amount": "250.00", "expiresAt": "2026-09-05T12:00:00Z" } ``` - Use Idempotency-Key when supported by the deployed relay; same key and body is replay-safe, while a different body returns 409. - The backend payment-request flow is available for Ethereum and Tron; the hosted page and SDK currently execute Ethereum only. ### GET /v1/payment-requests/:requestId — Get a payment request Authentication: bearer. Status: documented. Returns the merchant's full tenant-scoped payment-request record. Response example: ```json { "id": "pr_...", "reference": "order-1042", "network": "ethereum", "currency": "USDC", "amount": "250.00", "status": "pending" } ``` ### GET /v1/payment-requests/:requestId/public — Get a public payment request Authentication: none. Status: documented. Returns the sanitized fields needed by a hosted checkout, without tenant ids, callback secrets, or private metadata. Response example: ```json { "id": "pr_...", "reference": "order-1042", "network": "ethereum", "currency": "USDC", "amount": "250.00", "recipient": "0x...", "status": "pending", "redirectUrl": null } ``` - Only http and https redirect URLs are exposed; unsafe or missing redirects become null. ### POST /v1/payment-requests/:requestId/intent — Create a payment intent Authentication: none. Status: documented. Builds unsigned transaction data for the payer's connected wallet. The wallet, not FastPay, signs and broadcasts. Request example: ```json { "payerAddress": "0x2222222222222222222222222222222222222222" } ``` Response example: ```json { "paymentRequestId": "pr_...", "chainId": 1, "to": "0x...", "data": "0x...", "value": "0x0", "approval": null, "fee": "1.25", "total": "251.25" } ``` - An approval transaction may be returned before a token transfer when allowance is insufficient. - Solana intent execution is not enabled in the current hosted checkout. ### POST /v1/payment-requests/:requestId/confirm — Confirm a payment Authentication: none. Status: documented. Verifies the exact on-chain transfer before marking the request paid; the reported hash is never trusted by itself. Request example: ```json { "txHash": "0x9f2b7c4d81e0a3f65c2b9d7e4a10f83b6c5d24e97a0b1c3d5e7f9a2b4c6d8e01" } ``` Response example: ```json { "status": "paid", "txHash": "0x...", "verification": "verified" } ``` - A pending chain confirmation returns 202 and should be retried; a mismatch returns 422 payment_not_verified. - The first successful confirmation emits the checkout webhook when a callback URL was configured. ## Relay · Checkout Sessions (beta) (Beta) Beta reference: these surfaces are documented from the uploaded FastPay API Reference, but they are not part of the current public OpenAPI contract. Do not send production funds against them until FastPay confirms a deployed URL and completes live-network verification. ### POST /v1/checkout/sessions — Create a checkout session Authentication: bearer. Status: beta. Creates a fiat-denominated session, resolves the tenant's registered payout wallet, and quotes a supported settlement currency. Request example: ```json { "amount": "100.00", "currency": "EUR", "network": "ethereum", "settlement_currency": "USDC", "success_url": "https://merchant.example/success", "cancel_url": "https://merchant.example/cancel", "callback_url": "https://merchant.example/webhooks/fastpay", "merchant_reference": "order-1042", "customer": { "id": "cus_..." } } ``` Response example: ```json { "id": "cs_...", "object": "checkout_session", "status": "open", "payment_status": "created", "amount": "100.00", "currency": "EUR", "network": "ethereum", "payment_amount": "108.000000", "payment_currency": "USDC", "exchange_rate": "1.08", "payment_url": "https://relay.fastpay.app/checkout/index.html?id=cs_...", "expires_at": "...", "created_at": "..." } ``` - currency is EUR or USD. settlement_currency must have a rate available; unsupported quotes are rejected rather than priced 1:1. - Rates come from a live CoinGecko-backed engine with a recent-cache fallback and an operator static table as a last resort, which is labelled as a fallback rather than presented as live. - callback_url is optional and SSRF-checked. When set, a checkout_session-shaped paid webhook fires on confirmation, carrying checkout_session_id, payment_id and net_settlement — not the payment_request shape used by the mature checkout flow. - Recipient resolution uses the tenant's registered payout wallet; it is never client-supplied or guessed. The ledger credits the fee-net settlement amount, not the gross charge. ### GET /v1/checkout/sessions/:id — Get a checkout session Authentication: bearer. Status: beta. Returns a session and its linked payments. Response example: ```json { "id": "cs_...", "object": "checkout_session", "status": "open", "amount": "100.00", "currency": "EUR", "payments": [{ "id": "pay_...", "status": "created", "merchant_reference": "order-1042" }] } ``` - Use the linked pay_... id with GET /v1/payments/:id; it is not the same resource as the session. ### POST /v1/checkout/sessions/:id/expire — Expire a checkout session Authentication: bearer. Status: beta. Closes an open checkout session. Response example: ```json { "id": "cs_...", "object": "checkout_session", "status": "expired" } ``` ## Payments (beta) (Beta) A full lifecycle view for one payment, including immutable status history, financial fields, blockchain data, and related resource ids. ### GET /v1/payments/:id — Get a payment Authentication: bearer. Status: beta. Returns more than the current status: every recorded transition, the financial breakdown, known chain data, and related objects. Response example: ```json { "object": "payment", "id": "pay_...", "status": "paid", "lifecycle": [{ "status": "created", "at": "2026-09-02T10:00:00.000Z" }, { "status": "paid", "at": "2026-09-02T10:04:12.000Z" }], "financial": { "originalAmount": "100.00", "originalCurrency": "EUR", "paymentAmount": "108.000000", "paymentCurrency": "USDC", "exchangeRate": "1.08", "gatewayFee": "1.00", "networkFee": "1.5", "conversionFee": "0.20", "netSettlement": "99.00", "settlementCurrency": "EUR", "settlementType": "ledger_credit" }, "blockchain": { "network": "ethereum", "asset": "USDC", "address": "0x...", "txHash": "0xdeadbeef...", "confirmations": 12 }, "related": { "checkoutSessionId": "cs_...", "customerId": null, "refundIds": ["re_..."], "ledgerTransactionIds": ["le_..."] } } ``` - lifecycle is backed by insert-only history, not synthesized. - settlementType is always ledger_credit today: crypto moves payer → merchant wallet non-custodially, and the ledger posting is bookkeeping, not a balance FastPay holds. An auto-converted fiat balance and an external bank payout are not built. ### GET /v1/payments/:id/transactions — List payment transactions Authentication: bearer. Status: beta. Exposes the on-chain transaction records already stored for a payment, including real Ethereum confirmation counts and reorg detection. Response example: ```json { "object": "list", "data": [{ "txHash": "0x...", "network": "ethereum", "confirmations": 12, "status": "confirmed", "reorged": false }] } ``` - Confirmation monitoring is implemented for Ethereum. A reorged transaction is surfaced rather than silently left as confirmed. ## Refunds (beta) (Beta) Non-custodial refunds for a checkout-session-originated payment. FastPay never held the funds, so a refund is an unsigned transaction the merchant's own wallet signs and broadcasts. ### POST /v1/payments/:id/refunds — Create a refund Authentication: bearer. Status: beta. Builds an unsigned refund transaction for the merchant's wallet. Partial refunds are supported. Request example: ```json { "amount": "25.00", "reason": "customer_request" } ``` Response example: ```json { "id": "re_...", "object": "refund", "payment_id": "pay_...", "status": "pending", "amount": "25.00", "currency": "EUR", "transaction": { "to": "0x...", "data": "0x...", "value": "0x0", "chainId": 1 } } ``` - Ethereum only, and only for a payment created through a checkout session. The older standalone payment-request flow has nothing to attach a refund to. ### GET /v1/refunds/:id — Get a refund Authentication: bearer. Status: beta. Returns one tenant-scoped refund and its current state. Response example: ```json { "id": "re_...", "object": "refund", "payment_id": "pay_...", "status": "confirmed", "amount": "25.00", "tx_hash": "0x..." } ``` ### POST /v1/refunds/:id/confirm — Confirm a refund Authentication: bearer. Status: beta. Verifies the broadcast refund on-chain and posts the matching ledger debit once confirmed. Request example: ```json { "txHash": "0x..." } ``` Response example: ```json { "id": "re_...", "status": "confirmed", "tx_hash": "0x..." } ``` - The full refund lifecycle is written to the admin audit trail. ## Balance & Ledger (beta) (Beta) A real double-entry balance and ledger API. Every payment or refund posts two balanced legs under one transaction id — the merchant's payable balance and the exact mirror entry on FastPay's internal settlement account. It is still a two-account chart, not a general ledger. ### GET /v1/balance — List balances Authentication: bearer. Status: beta. Lists each currency for which the tenant has a ledger account. Response example: ```json { "object": "list", "data": [{ "currency": "EUR", "available": "99.00", "pending": "0.00", "total": "99.00" }] } ``` - Balances are derived from balanced ledger legs and are credited net of FastPay's gateway fee, not gross. ### GET /v1/balance/:currency — Get a balance Authentication: bearer. Status: beta. Returns one currency balance, creating the account on first read when needed. Response example: ```json { "currency": "EUR", "available": "99.00", "pending": "0.00", "total": "99.00" } ``` ### GET /v1/balance/transactions — List ledger transactions Authentication: bearer. Status: beta. Lists immutable ledger entries for the tenant, most recent first, with cursor pagination and an optional CSV export. Response example: ```json { "object": "list", "data": [{ "id": "le_...", "object": "ledger_transaction", "transaction_id": "txn_...", "direction": "credit", "amount": "99.00", "currency": "EUR", "balance_before": "0.00", "balance_after": "99.00", "reference_type": "payment", "reference_id": "pay_...", "created_at": "..." }], "has_more": false } ``` - ?format=csv returns a reconciliation-friendly export of the same rows. - Paginate with limit and starting_after; has_more indicates a further page. ### GET /v1/reconciliation-report — Reconciliation report Authentication: bearer. Status: beta. Cross-references business state, on-chain state, and ledger state for paid payments and flags where the three disagree, including reorgs. Response example: ```json { "object": "reconciliation_report", "window_days": 7, "checked": 128, "discrepancies": [{ "payment_id": "pay_...", "reason": "onchain_reorged" }] } ``` - The default window comes from RELAY_RECONCILIATION_DEFAULT_WINDOW_DAYS. ### GET /v1/webhook-deliveries — List webhook deliveries Authentication: bearer. Status: beta. Shows every webhook this gateway has attempted to send you, paginated, with the detail route returning the full payload. Response example: ```json { "object": "list", "data": [{ "id": "whd_...", "event_id": "evt_...", "type": "payment.completed", "status": "delivered", "attempts": 1, "response_status": 200, "response_body": "ok" }], "has_more": false } ``` - Each attempt records the receiver's real HTTP status code and a capped response body; both are null when no response was ever received (a network failure, not a receiver error). ### POST /v1/webhook-deliveries/:deliveryId/redeliver — Redeliver a webhook Authentication: bearer. Status: beta. Retries a specific delivery on demand instead of waiting for the periodic sweep. Response example: ```json { "id": "whd_...", "status": "pending", "attempts": 2 } ``` - Safe to call on an already-delivered row — your endpoint simply receives the same event id again. - Returns 409 if a redelivery for that row is already in flight, so the webhook is never double-sent. ## Customers & Payment Channels (beta) (Beta) Lightweight customer records and server-generated deposit addresses. Addresses are real, but deposit detection and automatic ledger linking are not implemented. ### POST /v1/customers — Create a customer Authentication: bearer. Status: beta. Creates a lightweight customer record; this is not a KYC or identity object. Request example: ```json { "email": "payer@example.com", "name": "Example Payer", "country": "US" } ``` Response example: ```json { "id": "cus_...", "object": "customer", "email": "payer@example.com", "name": "Example Payer", "country": "US", "created_at": "..." } ``` - email only receives a basic @ format check; country is free text and is not a compliance or eligibility check. ### GET /v1/customers/:id — Get a customer Authentication: bearer. Status: beta. Returns one tenant-scoped customer record. Response example: ```json { "id": "cus_...", "object": "customer", "email": "payer@example.com", "name": "Example Payer", "country": "US", "created_at": "..." } ``` ### GET /v1/customers — List customers Authentication: bearer. Status: beta. Lists customer records for the tenant. Response example: ```json { "object": "list", "data": [{ "id": "cus_...", "object": "customer", "email": "payer@example.com" }] } ``` ### POST /v1/payment-channels — Create a payment channel Authentication: bearer. Status: beta. Mints one dedicated address per requested network for a customer. Request example: ```json { "customer_id": "cus_...", "networks": ["ethereum", "tron"] } ``` Response example: ```json { "id": "pc_...", "object": "payment_channel", "customer_id": "cus_...", "status": "active", "addresses": [{ "currency": "ETHEREUM", "network": "ethereum", "address": "0x..." }, { "currency": "TRON", "network": "tron", "address": "T..." }], "created_at": "..." } ``` - Addresses are generated server-side through Turnkey and never accepted from the caller. - Generated does not mean monitored: no chain watcher currently detects deposits, links them to the customer, updates the ledger, or emits a deposit webhook. ### GET /v1/payment-channels/:id — Get a payment channel Authentication: bearer. Status: beta. Returns one tenant-scoped payment channel and its addresses. Response example: ```json { "id": "pc_...", "object": "payment_channel", "customer_id": "cus_...", "status": "active", "addresses": [{ "network": "ethereum", "address": "0x..." }] } ``` ### GET /v1/payment-channels — List payment channels Authentication: bearer. Status: beta. Lists payment channels for the tenant. Response example: ```json { "object": "list", "data": [{ "id": "pc_...", "object": "payment_channel", "customer_id": "cus_...", "status": "active" }] } ``` ### POST /v1/payment-channels/:id/close — Close a payment channel Authentication: bearer. Status: beta. Closes a payment channel so it is no longer active. Response example: ```json { "id": "pc_...", "object": "payment_channel", "status": "closed" } ``` ## Discovery & rates Public, unauthenticated endpoints for discovering what the API accepts and what an amount converts to. Rates come from the same live pipeline Checkout Sessions quote with — they are indicative, not a locked quote. ### GET /v1/networks — List networks Authentication: none. Status: documented. Lists every network the API validates a request for, with an honest live flag separating request-validation support from fulfillment support. Response example: ```json { "object": "list", "data": [ { "network": "ethereum", "live": true }, { "network": "tron", "live": true }, { "network": "solana", "live": false } ] } ``` - solana reports live: false today: a payment request for it validates, but checkout fulfillment still rejects it. ### GET /v1/currencies — List currencies Authentication: none. Status: documented. Lists the assets accepted per network, using the same live flag semantics as /v1/networks. Response example: ```json { "object": "list", "data": [{ "currency": "USDC", "network": "ethereum", "decimals": 6, "live": true }] } ``` ### GET /v1/rates?from=EUR&to=USDC — Get a rate Authentication: none. Status: documented. Returns the current indicative rate from the live rate engine used internally by Checkout Sessions. Response example: ```json { "object": "rate", "from": "EUR", "to": "USDC", "rate": "1.08", "as_of": "2026-09-04T10:00:00.000Z", "locked": false } ``` - Not a locked quote: only a checkout session locks a rate for its own lifetime. - Not a second rate source — it wraps the same pipeline the checkout quote uses. ## Relay · admin & tenants (Operator only) Operator-run setup for tenants, API keys, wallets, limits, and chain-specific onboarding. These routes are not a merchant-facing integration surface. ### POST /admin/tenants — Create a tenant Authentication: admin. Status: documented, operator-only. Creates a tenant and returns its webhook secret once. Request example: ```json { "name": "Example business" } ``` Response example: ```json { "id": "tn_...", "name": "Example business", "status": "active", "webhookSecret": "whsec_..." } ``` ### POST /admin/tenants/:id/api-keys — Issue an API key Authentication: admin. Status: documented, operator-only. Issues a tenant Bearer key; the raw key is returned once and stored hashed at rest. Request example: ```json { "test": false } ``` Response example: ```json { "id": "key_...", "apiKey": "fp_live_...", "prefix": "fp_live_", "warning": "Store this key now; it will not be shown again." } ``` - API keys are passwords. Never put the raw value in browser code, source control, logs, or support tickets. ### POST /admin/tenants/:id/api-keys/:keyId/revoke — Revoke an API key Authentication: admin. Status: documented, operator-only. Revokes a tenant key so subsequent Bearer requests fail authentication. Response example: ```json { "success": true } ``` ### POST /admin/tenants/:id/wallets — Register a wallet Authentication: admin. Status: documented, operator-only. Registers a validated payout or receiving address for a supported network. Request example: ```json { "chain": "ethereum", "address": "0x1111111111111111111111111111111111111111" } ``` Response example: ```json { "success": true, "tenantId": "tn_...", "chain": "ethereum", "address": "0x..." } ``` - chain uses lowercase ethereum, solana, or tron. Do not send abbreviations such as eth; the address is validated for the selected chain. ### GET / DELETE /admin/tenants/:id/wallets[/:chain] — List or remove wallets Authentication: admin. Status: documented, operator-only. Lists registered wallets or removes a wallet for a tenant and optional chain. Response example: ```json { "success": true, "tenantId": "tn_...", "wallets": [{ "chain": "ethereum", "address": "0x..." }] } ``` ### POST /admin/tenants/:id/status · /admin/tenants/:id/limits — Set tenant status and limits Authentication: admin. Status: documented, operator-only. Suspends or reactivates a tenant and configures request or daily payment-count limits. Request example: ```json { "status": "active" } { "requestsPerMinute": 60, "dailyPaymentCountCap": 2000 } ``` Response example: ```json { "success": true } ``` ### POST /admin/tenants/:id/onboard · .../tron/onboard · .../solana/onboard — Complete chain onboarding Authentication: admin. Status: documented, operator-only. Binds the tenant's smart account, Tron permission, or Solana guarded authority after the operator-led setup flow. Response example: ```json { "success": true, "sessionKeyBindingVerified": true } ``` - Ethereum uses a Safe or simple account with a bounded guard; Tron uses native Active Permission; Solana uses a program-owned vault authority when configured. No private key transfer is part of this flow. ### GET /admin/audit-log — Read the audit log Authentication: admin. Status: documented, operator-only. Returns the immutable record of admin and tenant writes: tenant, wallet and API-key creation or revocation, status and limit changes with before/after values, kill-switch use, and the refund lifecycle. Response example: ```json { "object": "list", "data": [{ "id": "aud_...", "action": "tenant.limits_updated", "target_type": "tenant", "target_id": "tn_...", "actor": { "type": "admin", "ip": "203.0.113.10" }, "before": { "dailyPaymentCountCap": null }, "after": { "dailyPaymentCountCap": 2000 }, "created_at": "..." }] } ``` - Optional tenant_id, target_type, target_id and limit filters. tenant_id matches rows where the tenant is the actor or the target. - Raw API keys never appear in an audit entry — only the key id and prefix. Admin actions carry no per-user id because operators share one key; the IP address is the only actor signal. ## Roadmap — not built - Payout links: Payouts require a known recipient wallet; there is no claim flow. - Fiat settlement and bank payout: Settlement is always a ledger credit against a non-custodial transfer. There is no auto-converted fiat balance and no external bank payout leg. - Recurring billing: Each checkout payment is single-use and requires a fresh wallet signature. - Beneficiaries and KYC: Recipients are raw wallet addresses, not compliance contacts. - Per-payment void requests: Cancellation is currently job-level and pre-execution only. - Deposit detection: Payment Channel addresses are generated, but no chain watcher links deposits to customers or the ledger. - Refunds beyond Ethereum checkout sessions: Refunds exist only for a checkout-session-originated payment on Ethereum; the standalone payment-request flow has nothing to attach one to. - Hosted public documentation: Both OpenAPI specs are served live via a Redoc-rendered page on the relay itself; assigning a dedicated public docs domain is still an operator decision. - Beneficiary and batch objects: There is no Beneficiary/payout-destination CRUD resource and no first-class batch object for bulk disbursement; recipients and batches exist only inside a request payload. - Supported countries and industries: No geographic or industry eligibility API is published.