BitriPay
ONE QR. ONE GATEWAY. EVERY ELIGIBLE RAIL.Pay Local. Fund Global. Settle Your Way.
developers

Three calls and a webhook: the BitriPay API

The documentation is generated from the same table that serves the requests, so it cannot describe an endpoint that does not exist. Idempotency that refuses rather than duplicates, errors you can branch on, and a sandbox that fails properly.

Most payment integrations are archaeology. You read the documentation, discover it describes last year's API, and learn the real behaviour from error messages in production at eleven at night.

We took one decision early that removes most of that: the documentation is generated from the same table that serves the requests.

One table, two renderings

There is a single list of operations inside the platform. The OpenAPI document is generated from it. The human-readable reference is generated from it. The requests are served from it.

This is not a documentation policy, it is an architectural one, and it has a specific consequence: the reference cannot describe an endpoint that does not exist, and an endpoint cannot exist without appearing in the reference. They are the same object rendered twice.

Idempotency that actually refuses

Every money-moving POST requires an Idempotency-Key.

Send the same key twice with the same body and you get the original object back — no second payment. Send the same key with a different body and the request is refused, with a specific error, rather than quietly creating something.

That second rule is the one that matters. A key collision in your code is a bug that would otherwise surface as a mysterious duplicate charge weeks later. Here it surfaces as a 409 with a readable message, in testing, where bugs are cheap.

Webhooks signed twice

Each delivery carries a timestamped HMAC-SHA256 over the exact body, using your endpoint's own secret. It also carries an Ed25519 signature from the platform key, which you can fetch from a public key registry that supports rotation.

Verify either. Verify both. Reject anything with a timestamp older than five minutes and you have closed replay.

Delivery is at-least-once on a published retry schedule stretching across 24 hours, and every event has an id so you can deduplicate. We tell you it is at-least-once because it is; a provider that promises exactly-once is describing something it cannot deliver.

Errors you can branch on

Every failure returns error.code — descriptive, for a human reading a log — and error.bp, a stable numeric family: authentication, validation, ledger, rail, compliance, intelligence.

Branch on the family. It does not change when we improve a message, and it does not require your code to parse English.

A sandbox that fails properly

Test keys drive the same state machine as production. The sandbox publishes magic numbers for specific outcomes: success, insufficient funds, a timeout that later succeeds.

That last one is the one nobody else gives you, and it is the one that finds the bug in your retry logic before your customers do.

Where to start

The reference is at /developers/api — every operation with its scope, its parameters, its body fields and a copy-ready example. The machine-readable document is the same content as OpenAPI 3.1.

Three calls and a webhook. The coffee will still be warm.

What you can check

These are facts about the running platform, not adjectives. Each one is visible in the product, in the API reference or in the ledger.

  • The OpenAPI document and the human reference are generated from one operation table, so they cannot disagree.
  • Every money-moving POST requires an Idempotency-Key: a replay returns the original object, and the same key with a different body is refused.
  • Webhooks are signed twice — HMAC-SHA256 over the timestamped body, and an Ed25519 platform signature — and retried on a published schedule across 24 hours.
  • Errors carry a descriptive code and a stable numeric family, so software can branch on the family without parsing English.
  • A sandbox with documented magic numbers drives the real state machine, including the failures, without touching an operator.

What it costs

The API costs nothing to use. You pay for the payments it moves, at the rate on your account.

Is it switched on?

Whether this is available depends on the authorisations in place and on what the administrator of a given deployment has switched on. That changes, and an article does not — so the live answer lives on the feature page, which reads it from the platform itself: The gateway API on the features page.

Frequently asked questions

How do I test a failure?

The sandbox publishes magic numbers that drive specific outcomes — success, insufficient funds, a timeout that later succeeds — through the same attempt machine production uses. You test the unhappy paths without an operator and without pretending.

What happens if my server retries a payment?

Send the same Idempotency-Key and you get the original object back rather than a second payment. The same key with a different body is refused outright, which turns a dangerous bug into a readable error.

How do I know a webhook is really from you?

Each delivery carries a timestamped HMAC over the exact body using your endpoint secret, plus an Ed25519 signature from the platform key you can fetch from the key registry. Verify either or both, and reject anything older than five minutes.

build on bitripay the gateway api
Try it with a free account

Send with a QR code, add money by card or mobile money, issue a virtual card. Sandbox balances let you explore before any real money moves.

Open an account

Keep reading