Step-by-step integration of a stablecoin API: authenticate, onboard a customer, create a virtual USD account, quote and send a cross-border payout, and handle signed webhooks. Endpoints, statuses, and SDKs for BlindPay.
Integrating a stablecoin API for cross-border payments takes five steps: authenticate with an instance-scoped API key, onboard a customer through KYC or KYB, fund a virtual USD account or register a stablecoin wallet, quote and create a payout to a local bank account, and handle signed webhooks for status changes. With BlindPay, a first payout in the development environment is typically a day of work using the REST API or one of the official SDKs.
This guide walks through that flow with the actual endpoints, statuses, and event names. It is written for a developer or technical founder who has never moved money over stablecoins before. For the category background, read what a stablecoin API is.
| Requirement | Where to get it |
|---|---|
| A BlindPay account and a development instance | Dashboard at app.blindpay.com; instances are created there, not through the API |
| An API key scoped to that instance | Instance settings, API Keys tab; shown once, so store it in a secrets manager |
| An SDK or an HTTP client | @blindpay/node on npm, blindpay on PyPI, blindpay-go, blindpay-php, or blindpay-swift; or generate a client from the OpenAPI spec |
| A blockchain wallet you control | Any EVM, Solana, or Stellar wallet on a supported network; testnets work on development instances |
| An HTTPS endpoint for webhooks | Public URL; local and private addresses are rejected, so use a tunnel in development |
Development instances are free, rate-limited to about 100 requests per minute, and skip banking-partner review so virtual accounts and payouts resolve immediately. Production access requires compliance onboarding and takes up to three business days. Read sandbox vs production for the exact behavioral differences.
Every request carries the API key as a Bearer token and targets a specific instance:
There is no separate sandbox hostname. Which environment you hit is decided entirely by which instance the key belongs to, so a development key fails against a production instance and vice versa. Keys are full read and write, optionally locked to an IPv4 allowlist.
Two headers are worth adopting from the first request. Idempotency-Key on any write makes a network retry safe: the same key with the same body returns the original response with Idempotency-Replayed: true, and a different body returns a 422. On development instances a 429 comes with Retry-After, so build the backoff in now rather than when production traffic arrives.
A customer is the person or business you will pay or collect from. Create one with type set to individual or business and the identity fields the docs list for that type. Compliance runs immediately.
| Onboarding path | Who | How it resolves | Typical time |
|---|---|---|---|
| KYC standard | Individuals in standard-risk countries | Automated | About 60 seconds |
| KYC enhanced | Individuals in high-risk countries | Review | 3 hours to 1 business day |
| KYB standard | Businesses | Review | 3 hours to 1 business day |
Poll or, better, listen for customer.update until kyc_status reads approved. Other statuses include verifying, pending_review, compliance_request when the team needs a document, and rejected with the reason in kyc_warnings or fraud_warnings. Customers in prohibited countries are blocked at creation and cannot be overridden, and KYC data on an existing customer cannot be edited, so treat a rejection as "create a corrected customer" rather than "patch this one."
This is the step that turns compliance from a project into an API call. Sanctions screening and risk scoring happen here and again on each transfer, which is why a payout can later sit in on_hold without anything being wrong on your side. Read how to automate KYC and KYB for stablecoin payments for the design behind it.
You need stablecoins in a wallet to send a payout. There are two ways to get there.
If you already hold USDC or USDT, register the wallet on the customer with POST /v1/instances/{instance_id}/customers/{customer_id}/blockchain-wallets, choosing the network and address. You will send the payout from this wallet.
If you hold dollars in a bank, create a virtual USD account. It is a dedicated US account number that accepts ACH, domestic wire, and SWIFT deposits (RTP for some account types) and converts every deposit to USDC or USDT in the linked wallet. First attach a blockchain wallet to the approved customer, then:
The request also takes a banking_partner from the documented list, and eligibility depends on the customer's country and business type. The response includes ach, wire, and where applicable rtp objects with routing and account numbers, and SWIFT details once the account is approved. In development the account is approved instantly with fake numbers. Each incoming deposit creates a payin and fires payin.new and then payin.complete, which is your signal that stablecoins have landed. Business customers need a few extra fields before a virtual account can be created, such as business type, industry, and ownership, and the API returns missing_required_fields naming exactly which ones.
This is the "no pre-funding" model in practice: you fund each transfer when you need it, from a wallet you control, rather than parking a balance in each destination currency.
Register the receiver's bank account once, with the rail as its type: pix, pix_safe, or ted for Brazil, spei_bitso for Mexico, ach_cop_bitso for Colombia, transfers_bitso for Argentina, ach, rtp, or wire for the United States, sepa for Europe, and international_swift for everywhere else. The public rails endpoint returns the live list, with countries, and needs no API key.
Then request a quote:
Amounts are integers in minor units, so the request above is 5,000.00 in the receiver's currency. currency_type decides whether you are fixing the stablecoin amount sent or the fiat amount received. The response returns commercial_quotation, blindpay_quotation, sender_amount, receiver_amount, and the fees as separate fields, plus expires_at in epoch milliseconds. A quote is valid for five minutes and backs exactly one payout. For EVM networks the response also includes a contract object with the address, ABI, and amount to approve.
Show the receiver amount to your user before they confirm. That one field is what makes stablecoin payouts comparable to any other rail: the receiver gets exactly this, and it was known before anything moved.
Approve the quoted stablecoin amount from your wallet on-chain using the contract details in the quote, then create the payout on the endpoint for your network: /payouts/evm for Ethereum, Polygon, Base, and Arbitrum, /payouts/solana, or /payouts/stellar.
The response is a payout with id, status, and five tracking objects covering the on-chain transaction, liquidity, partner fee, payment, and completion, each with its own step. The status values are:
| Status | Meaning | What your system should do |
|---|---|---|
processing | Funds pulled, conversion and delivery in progress | Show pending |
on_hold | Compliance or banking review before release; every SWIFT payout starts here, ACH, wire, and RTP pass through it | Show pending, do not retry |
completed | Local currency delivered over the rail | Mark paid, store the rail reference |
failed | Delivery could not complete | Surface the reason, funds return to the sender wallet |
refunded | Funds returned after a failure downstream | Reconcile the return |
Delivery time depends on the rail: minutes on Pix, SPEI, and Transfers 3.0, instant on RTP, one to two business days on ACH, TED, and SEPA, up to five on SWIFT, where each payout carries a UETR and an MT103 confirmation. SWIFT payouts have a 100 USD minimum. In development, a request_amount of 66600 forces failed and 77700 forces refunded, so you can test the unhappy paths without waiting for one to happen.
Polling works, but webhooks are how the integration is meant to run. Register an endpoint:
An empty events array subscribes to everything. Fetch the endpoint's signing secret with a GET on its /secret path; it starts with whsec_. The events you will handle most:
| Event | Fires when |
|---|---|
customer.update | KYC or KYB status changes |
payin.complete | A virtual account deposit has converted and landed |
payout.update | A payout changes status, including to failed |
payout.complete | Local currency has been delivered |
virtualAccount.complete | A virtual account has finished review |
Verify every delivery. The headers are svix-id, svix-timestamp, and svix-signature. Concatenate the id, the timestamp, and the raw body with dots, compute HMAC-SHA256 with the base64-decoded secret, and compare against each v1, entry in the signature header using a constant-time comparison. Reject anything older than five minutes.
Two details save debugging time. A failed payout fires payout.update, not payout.complete, so key your terminal-state logic on status rather than event name. And retries reuse the same svix-id, so store it and skip duplicates. Failed deliveries are retried with backoff over the following hours, and any event can be replayed from the dashboard without creating a new business event. Read the webhook events reference for the full list.
Every endpoint above is described in a single OpenAPI 3.1 document at https://api.blindpay.com/doc. The official SDKs for Node, Python, Go, PHP, and Swift are generated from it, so field names, enums, and error shapes match the docs exactly and a new API field shows up as a typed property rather than a surprise in a JSON blob.
The same spec is what makes AI-assisted integration work. The MCP server, npx -y @blindpay/mcp, lets a coding agent in Claude Code or Codex call the API against your development instance while you build. The docs publish llms.txt and a raw markdown twin of every page, so an agent can read the reference the same way you do. The practical effect is that the first payout in development is usually a day of work, and most of that day is your own ledger and UI, not the API.
Create a development instance, run the six steps above against testnet, and force a failed and a refunded payout with the sentinel amounts before you write a line of production code. Then read how to choose a stablecoin API to check that the provider you are integrating clears the bar on pre-funding, compliance, and pricing, or go straight to the payout quickstart.
This article is for general information only and is not legal, tax, or financial advice.
AP2, ACP, and x402 each verify that an AI agent had permission to spend. Here is what every protocol covers, who backs it, and the reconciliation gap none of them close.
Seven stablecoin payment platforms compared for US fintechs in 2026: what makes an API production-ready, how each provider handles compliance, settlement speed against ACH, and how to run the evaluation.
How to choose a stablecoin payment provider in 2026: the four provider types, a comparison of 10 options, and the questions that decide the fit.