Stablecoin API SDKs: how one OpenAPI spec keeps five languages in sync

BlindPay's stablecoin API is described by one OpenAPI 3.1 spec that drives validation, docs, SDKs for Node, Python, Go, PHP, and Swift, and the MCP server.

BlindPay's stablecoin API is described by a single OpenAPI 3.1 specification at https://api.blindpay.com/doc. That one spec drives request validation on the server, the API reference, the official SDKs for Node, Python, Go, PHP, and Swift, and the tool surface of the MCP server. When a field changes, it changes everywhere at once, so the docs, the SDK, and the API can't disagree.

That's the design goal. Here's how it works, and why it matters more for a payments API than for most.

Why does drift hurt more in a payments API?

Every API drifts. A field gets renamed, an enum gets a new value, an error changes shape, and the docs page updates a week later. Or never.

In most APIs that's annoying. In a payments API it's money. If your code doesn't know about a new payout status, a payout that went on_hold can look like it vanished. If the docs say an amount is in dollars and the API wants minor units, somebody sends 100 times what they meant. (Amounts in our API are integers in minor units. 500000 is 5,000.00. Every time.)

So the rule we work by: there is exactly one place a data shape is defined, and everything else is generated from it.

Where does the spec come from?

Inside the API, every request and response shape is a Zod schema in one shared contract package. Those schemas do three jobs from one definition:

  1. Validate requests and responses in the API itself.
  2. Generate the OpenAPI 3.1 document published at api.blindpay.com/doc.
  3. Validate forms in the BlindPay dashboard.

Nobody hand-writes the OpenAPI file. When an engineer changes a schema, the spec is regenerated from it. The spec on the server is always the spec in the code.

What gets generated from that spec?

ArtifactWhereWhat it gives you
OpenAPI 3.1 spechttps://api.blindpay.com/docThe full contract, machine-readable
API referencehttps://api.blindpay.com/referenceEvery endpoint, body, and response, interactive
Node.js / TypeScript SDKnpm install @blindpay/nodeTyped methods and responses
Python SDKpip install blindpayThe same surface in Python
Go SDKgo get github.com/blindpaylabs/blindpay-goThe same surface in Go
PHP SDKComposerThe same surface in PHP
Swift SDKSwift Package ManagerThe same surface for iOS and macOS
MCP servernpx -y @blindpay/mcpAPI tools for AI coding assistants

Five SDKs, one contract. A new field shows up as a typed property, not a surprise inside a JSON blob.

What does calling the API look like from an SDK?

The Node SDK takes your API key and instance id once, then every method returns the same { data, error } shape:

TypeScript

No exceptions to catch for API errors, no guessing the error shape. You check error, you move on. The SDK readmes on GitHub have the full surface per language.

The same call over raw HTTP, if you'd rather skip the SDK:

Bash

There's one base URL. Which environment you hit, development or production, is decided by which instance your key belongs to.

What if my language isn't one of the five?

Generate a client. The spec is standard OpenAPI 3.1, so any generator works. For TypeScript types without the SDK:

Bash

Rust, Java, Kotlin, Ruby, C#: point your generator at the same URL. You'll get the same field names and enums the official SDKs use.

One caveat. A small number of endpoints are excluded from every generated SDK, currently POST /upload/extract, which reads invoices with AI. Call those over raw HTTP. The SDK docs keep that list current.

How do error codes stay stable?

Errors are part of the contract too. Every error response carries a machine-readable code, like idempotency_key_payload_mismatch or idempotency_key_in_flight. Your code can branch on the code, not on a message string that might get reworded.

Idempotency is contract-level as well. Send an Idempotency-Key header on any write and a retry with the same body replays the original response with Idempotency-Replayed: true. A retry with a different body gets a 422. That one header is what keeps a network timeout from turning into two payouts. Details in the idempotency docs.

How does this help AI coding assistants?

The same contract generates the tools in the BlindPay MCP server. When an endpoint is added or removed, the MCP tool surface changes with it, so an agent never calls a tool the API no longer has.

Around it:

  • llms.txt and markdown pages. The docs publish an llms.txt index and a markdown version of every page, so an agent reads the same reference you do.
  • Agent Skills. Install with npx skills add blindpaylabs/skills to give Claude Code, Cursor, or Codex working knowledge of payouts, payins, and customers.
  • The CLI. JSON output and predictable exit codes, so scripts and agents can drive it.

The practical effect: an assistant can build and test a payout flow against a development instance with the right field names on the first try, because it's reading the same contract the API enforces.

What does this mean for an integration timeline?

Faster, and fewer surprises in production. A first payout on a development instance is usually a day of work, and most of that day is your own ledger and UI, not the API. Development instances are free, skip banking-partner review, and accept sentinel amounts (66600 forces failed, 77700 forces refunded) so you can test every path. Payout timing by rail is in how long a stablecoin payout takes. The full flow, endpoint by endpoint, is in our developer guide to integrating a stablecoin API.

Where does BlindPay fit?

BlindPay is a stablecoin API for cross-border payouts and collections: USDC or USDT in, local currency out over Pix, SPEI, ACH, RTP, SEPA, and SWIFT (POBO/COBO) to 100+ countries, with no pre-funding. KYC, KYB, and sanctions screening run inside the API. Virtual USD accounts turn ACH, wire, and SWIFT deposits into stablecoins. And every piece of it is described by the one spec above.

What to do next

Install the SDK for your stack, or run npx openapi-typescript https://api.blindpay.com/doc and read the generated types for ten minutes. You'll understand the whole API surface faster than any docs page could explain it. Then create a development instance and send your first test payout with the payout quickstart.

This article is for general information only and is not legal, tax, or financial advice.

FAQ