API reference
Three contracts, every operation, and the conventions that hold across all of them.
WalletD publishes three OpenAPI contracts. Two of them are yours to call; the third is the ledger underneath, documented because a partner running the stack has to operate it.
Wallet API
Users, balances, top-ups, transfers, payments, refunds, rewards, subscriptions, the catalog and the ecosystem. This is the integration surface.
Auth API
Wallet user tokens, staff login, tenant API keys, and the JWKS every verifier reads.
Ledger API
The standalone double-entry ledger. Cluster-internal: read Reaching the ledger before anything else here.
Conventions
These hold for the wallet and auth APIs without exception. The ledger API is a separate service with its own credential; its differences are called out on Reaching the ledger.
| Base URL | Your deployment's API host, for example https://apigw.walletd.io. The published contracts carry that host as a placeholder; substitute yours |
| Auth | Authorization: Bearer <credential> on everything except GET /healthz. See Authentication |
| Amounts | Integer minor units. 2500 is $25.00, and there are no floats in either direction |
| Ids | UUIDv7, so they sort by creation time |
| Money-moving POSTs | Require an Idempotency-Key header. See Idempotency |
| Errors | RFC 7807 application/problem+json. Branch on the stable code, never on title or detail. See Errors |
| Paging | Cursor-based: ?limit= and ?cursor=, newest first |
| Rate limits | 429 rate_limited and 503 bulkhead_full come from the API gateway, not from the service. Both carry the same problem shape; 429 carries Retry-After |
An object that exists but belongs to another tenant returns 404, not 403.
That is deliberate: an id you do not own has to be indistinguishable from an id
that does not exist, or the API becomes a way to enumerate other people's data.
There is no "try it" console
Deliberately. On an API whose calls move real balances, a button that fires a live request from a documentation page is a way to make a mistake, not a convenience. Every operation below shows the request in six languages and the response it returns, and none of them sends anything.
The consequence worth knowing about: this site makes no third-party requests at all. No playground client, no hosted search, no analytics, no fonts from a CDN. It renders on a laptop with no internet, and you can verify that from the artefact rather than taking our word for it.
To actually run a call, use a sandbox key against your own deployment. See Testing and sandbox.
Machine-readable contracts
| Document | URL |
|---|---|
| Wallet API | /openapi.yaml |
| Auth API | /openapi/authsvc.yaml |
| Ledger API | /openapi/ledgerd.yaml |
| Postman / Bruno collection (wallet) | /openapi/walletd.postman.json |
These are the contracts the services generate their own server stubs from,
copied verbatim. The only things added on the way here are the servers entry
(a contract declares a relative URL because it is served from its own host),
display names for the tags, the hand-written code samples, and the two problem
codes the gateway emits on a service's behalf.
The collection imports into Postman and Bruno. It carries one request per
operation with the example bodies from the contract, and two collection
variables, baseUrl and apiKey.
Generate a client
Generate one rather than hand-writing it; the contract is the thing that gets CI-gated, not your wrapper.
# Go — types and a client, from the published document
oapi-codegen -generate types,client -package walletd openapi.yaml > walletd.gen.go
# Python, TypeScript, PHP, Java and the rest
npx @openapitools/openapi-generator-cli generate -i openapi.yaml -g python -o ./walletd-pythonBoth wallet and auth documents are OpenAPI 3.0.3 and declare bearerAuth as
the global security scheme, so a generated client picks up the header for you.
A generated client gets you types and transport. It does not get you
Idempotency-Key on the money paths, problem-code handling, or Retry-After
backoff — those are yours to add, and the Go SDK has them already.
Reading an operation page
Each operation has its own page, grouped by tag. A page shows the path and method, the authentication it requires, its parameters, the request body schema with an example, and every documented response including the problem codes it can return.
The ten money-moving operations additionally carry hand-written samples in
curl, Go, Python, PHP, JavaScript and Java — the same six languages, in the
same order, as the guides. Those samples are written by hand and reviewed;
everywhere else the page shows generated snippets, which are fine for a plain
GET and should be read as illustrations rather than as tested code.