Payment systems
Two SDKs and a sandbox that cannot move real money
Official Python and TypeScript SDKs for a stablecoin payments API, and a local sandbox that lets a developer drive the whole payment flow without moving anything real.
- Our role
- Built the SDKs and the sandbox
- Built with
- Python · TypeScript · OpenAPI · Node
- Where it got to
- Built and tested
- Open it
- qadi.io

The idea
Give developers an official way to call a stablecoin payments API from Python and TypeScript, instead of every integrator writing their own HTTP client and getting the edges wrong.
What it actually needed
The SDK was the smaller half. On an API that moves stablecoins a developer learns the flow by making mistakes, and a mistake there moves real money on a chain that will not give it back. So the integration problem was never the client library. It was that there was nowhere safe to be wrong.
What got built
- Python SDK
- Sync and async, for Python 3.9 upward, and typed, so an integrator finds out at their desk rather than in production.
- TypeScript SDK
- Ships both module formats for Node 18 upward, so it drops into a modern project and an older one without the consumer rewriting their build.
- A sandbox that cannot move money
- A local simulator of the API. A developer runs it on their own machine and drives the whole path, creating a payment, simulating the deposit arriving and verifying the webhook signature, without touching production.
- One spec both SDKs are built against
- A pinned specification with an overlay for the parts specific to the SDKs, so the two libraries cannot drift apart from each other or from the API.
The decisions that shaped it
The sandbox was not a testing convenience
It would have been cheaper to hand integrators a test key against the real service and let them be careful. Careful is an intention. A simulator with no ability to move money is that intention enforced in code, and it is the difference between a developer exploring freely and a developer afraid to press the button.
The test drives the full path, not the pieces
It boots the sandbox and runs a payment from creation through deposit to webhook verification. Testing each call on its own proves the calls work. Only the whole path proves the SDK is usable, and the whole path is what an integrator actually does on their first afternoon.
Two languages, one definition
Two SDKs maintained separately become two slightly different APIs within a year, and the client then owns three things instead of one. Both are built against the same pinned spec, so they can only disagree on purpose.
In production
Built and tested, with the sandbox driving the full flow end to end. Publication to the public package registries is pending the client's own accounts.
What this is worth
A developer can learn the whole payment flow on their own machine and get it wrong as many times as they need to, without anything real moving. What that avoids is the first integration mistake being a real one.
An API that moves stablecoins has a property most APIs do not. When a developer integrating it gets something wrong, the mistake is not a failed request. It is money that moved, on a chain, to somewhere it will not come back from.
That changes what an SDK is for. Two libraries were wanted, one in Python and one in TypeScript, so integrators stop writing their own HTTP clients and getting the edges wrong. That part is ordinary work and it was the smaller half.
The real problem was that there was nowhere safe to be wrong
The obvious answer is a test key against the live service. It is cheaper and it is not a control. A test key still sits in front of a real system, and the only thing between a curious developer and a live transfer is their own care. Care is an intention.
So the sandbox is a simulator that runs on the developer's own machine and has no ability to move anything. They create a payment, simulate the deposit arriving and verify the webhook signature, in the same sequence and through the same calls they will use in production. Getting it wrong there costs nothing, which is the point. A developer who is afraid to press the button never learns the flow.
One definition, so the two cannot drift
Two SDKs maintained separately become two slightly different APIs within a year, and the client then owns three things instead of one. Both are built against a single pinned specification with an overlay for the parts specific to the SDKs, so they can only disagree on purpose.
The integration test boots the sandbox and drives the full path rather than each call on its own. Testing the calls proves the calls work. Only the whole path proves the SDK is usable, and the whole path is what an integrator does on their first afternoon.
