Stables: a stablecoin payments API that treats Solana as a settlement rail
Stables (stables.money) is a developer API for stablecoin on/off-ramps, FX, virtual accounts, and built-in compliance across 10 chains including Solana. Here's the flow, the endpoints, and the honest read.
Accepting a bank deposit and landing USDC in a Solana wallet sounds simple until you list what it actually requires: KYC/KYB on the customer, sanctions and AML screening, a guaranteed FX rate, a fiat on-ramp, the on-chain transfer, and reconciliation when it all settles asynchronously. Stables (stables.money) is a developer API that hides that entire stack behind a handful of REST endpoints — and Solana is one of its settlement rails.
It sits in the same category as Bridge and Squads' Grid: stablecoin payments infrastructure, not a protocol or a token. Here's how it works.
The five primitives
Everything is composed from five resources, and every money movement is asynchronous — the API call starts the work; the final state arrives on a webhook.
/customer— create & verify a person or business (KYC/KYB, source-of-funds, sanctions)./quotes— request a guaranteed FX price for a fiat↔stablecoin conversion./transfer— execute it. Off-ramp = crypto→bank; on-ramp = bank→crypto./virtual-accounts(Payment Routes) — issue a bank account number that auto-converts every incoming fiat deposit to stablecoins./webhooks— real-time settlement state. The source of truth, not the API response.
Networks and assets
Stables is multi-chain: Solana, Ethereum, Base, Arbitrum, Optimism, Polygon, Avalanche, Celo, Stellar, and Tron. Stablecoins: USDC, USDT, DAI, PYUSD, EURC. For a Solana product, you simply name solana as the network and the funds settle as SPL tokens to the address you specify.
A Solana off-ramp, concretely
Auth is an API key (sti_test_… in sandbox, sti_live_… in production), and every write needs an Idempotency-Key so a retry never double-pays.
const BASE = "https://api.stables.money" // sandbox: api.sandbox.stables.money
const headers = {
"Authorization": `Bearer ${process.env.STABLES_API_KEY}`,
"Content-Type": "application/json",
}
// 1. Lock an FX quote: 100 USDC on Solana -> EUR bank payout
const quote = await fetch(`${BASE}/quotes`, {
method: "POST", headers,
body: JSON.stringify({
source: { network: "solana", asset: "USDC", amount: "100" },
destination: { currency: "EUR", rail: "SEPA" },
}),
}).then(r => r.json())
// 2. Execute the transfer against that quote (idempotent)
const transfer = await fetch(`${BASE}/transfer`, {
method: "POST",
headers: { ...headers, "Idempotency-Key": crypto.randomUUID() },
body: JSON.stringify({
customerId: "cus_...",
quoteId: quote.id,
destination: { iban: "DE89...", beneficiaryName: "Jane Doe" },
}),
}).then(r => r.json())
// transfer.status is "pending" — the real outcome comes by webhook.The customer sends 100 USDC to the Solana deposit address Stables returns; Stables screens it, converts at the locked rate, and pays the EUR bank account — emitting transfer.completed (or transfer.failed) to your webhook.
Virtual accounts: fiat in, SPL out
The Payment Routes primitive is the interesting one for a Solana app: mint a customer a real account/IBAN, and any fiat wired to it is auto-converted to USDC and pushed to their Solana wallet.
const route = await fetch(`${BASE}/virtual-accounts`, {
method: "POST",
headers: { ...headers, "Idempotency-Key": crypto.randomUUID() },
body: JSON.stringify({
customerId: "cus_...",
sourceCurrency: "USD", // fiat the user funds with
destination: { // where converted USDC lands
network: "solana",
asset: "USDC",
address: "<user-solana-address>",
},
}),
}).then(r => r.json())
// route.depositInstructions -> ACH/Wire details to show the user.
// Every deposit thereafter auto-settles as SPL USDC. No further API calls.Headless or hosted
You can run it headless (full API control, build your own KYC UI) or use hosted flows where Stables serves the compliance forms. The docs explicitly call out enabling AI agents to manage payments programmatically — the idempotent, webhook-driven design fits an autonomous agent better than a screen-scraping one.
The honest read
Stables is centralised payments infrastructure, not a Solana-native protocol. You're trusting their licenses, banking partners, and compliance stack — and Solana is one rail among ten, not the point. That's exactly right for the job: regulated fiat↔stablecoin movement is a licensing-and-compliance problem, not a smart-contract one, and you do not want to build KYC, sanctions screening, and FX settlement yourself.
The trade-offs are the usual ones for this layer: it's an API key and a counterparty, not a permissionless contract; pricing/limits depend on your agreement; and the async, webhook-only settlement model means you must build durable webhook handling, not poll. For a Solana app that needs to get users' dollars on- and off-chain without becoming a money-services business, that's a reasonable trade.
References
- docs.stables.money — API reference
- stables.money
- Stablecoins on Solana — what actually settles
- x402 — the permissionless end of the payments spectrum
Stablecoin rails are becoming a commodity API. Stables' bet is that most builders want USDC-in-a-Solana-wallet without the money-transmitter license — and they're probably right.