Three calls. One coffee.
Connect your shop, your billing system or your institution to BitriPay: create an intent, let the customer pay over the rail Smart Route picks, receive a signed webhook. A key-authenticated REST API with 116 documented operations, idempotent money movement and a sandbox that runs the same state machine as production.
Sign in → Developer portal → Create key
Secret keys (sk_) carry every scope, restricted keys (rk_) only the scopes you list, publishable keys (pk_) can only start a payment. The secret is shown once.
Bearer on every request
Authorization: Bearer sk_test_… — test keys hit the sandbox, live keys the real rails, on the same base URL. Send Idempotency-Key on every money-moving POST.
Intent → checkout → webhook
Create a payment intent, redirect to its checkout URL or show its BitriQR, then act on payment_intent.succeeded and payment_intent.settled after verifying the signatures.
# Base URL (test and live keys share it) https://api.bitripay.com/v1 # 1. Create an intent curl -X POST https://api.bitripay.com/v1/payment_intents \ -H "Authorization: Bearer sk_test_…" -H "Idempotency-Key: order-1042" -H "Content-Type: application/json" \ -d '{"amount_minor":250000,"currency":"CDF","description":"Order 1042","allowed_operators":["orange_cd","mpesa_cd","airtel_cd","africell_cd"]}' # 2. Send the customer to checkout_url (or render qr_payload), then # 3. Verify the webhook: BitriPay-Signature (HMAC of the endpoint secret) and the platform Ed25519 key from /v1/keys
Endpoints you will use first
| Method | Path | Description |
|---|---|---|
| POST | /payment_intents | Create a payment intent (returns checkout_url, qr_payload and client_secret) |
| GET | /payment_intents | List payment intents |
| GET | /payment_intents/{id} | Retrieve a payment intent |
| POST | /payment_intents/{id}/cancel | Cancel an intent that has not been captured |
| POST | /checkout_sessions | Create a hosted checkout session with line items |
| GET | /checkout_sessions | List checkout sessions |
| POST | /payment_links | Create a single-use or reusable payment link |
| GET | /payment_links | List payment links |
| POST | /qr_codes | Create a static BitriQR (EMVCo + signed extension) |
| GET | /qr_codes | List QR codes |
| POST | /verifications | Scan-to-Verify: did a payment reach me? (reference, or MSISDN + amount) |
| POST | /refunds | Refund a captured intent (atomic reservation; wallet or processor) |
| GET | /refunds | List refunds |
| POST | /payouts | Pay out to a bank account or mobile-money number |
| GET | /payouts | List payouts |
| GET | /balance | Balance classes: available, pending, reserved, settlement_pending, held, disputed, frozen |
| POST | /webhook_endpoints | Create an endpoint (returns whsec_ secret once) |
| GET | /keys | Public signing-key registry (ed25519, ETag cached) |
| GET | /status | Operating state (guardian mode, degraded flags) |
Every operation, every schema and every error code: OpenAPI 3.1 document · platform signing keys · operating status.
Scopes
A restricted key holds only the scopes you choose; a scope that is missing returns scope_denied.
| Scope | Grants |
|---|---|
wallets:read | Read wallets. |
transfers:read | Read wallet transfers. |
transfers:write | Send wallet transfers under the organisation policy. |
remittances:read | Read remittances. |
remittances:write | Quote and send remittances on authorised corridors. |
payouts:approve | Approve a payout batch (four eyes: never the author). |
ai:run | Run metered assistant operations (consumes prepaid units). |
subscriptions:read | Read plans, subscriptions and invoices. |
subscriptions:write | Create plans, record usage, cancel subscriptions. |
credit:read | Read the credit-readiness signal a customer consented to share. |
routes:read | Read routes and receipts. |
routes:write | Quote and create any-to-any routes. |
payment_intents:read | Read intents, timelines and refundable amounts. |
payment_intents:write | Create and cancel payment intents. Publishable pk_ keys get only this: safe to ship in a browser, they can start a payment and never read your data. |
checkout_sessions:write | Create hosted checkout sessions. |
payment_links:write | Create and deactivate payment links. |
qr_codes:read | List your QR codes and their analytics. |
qr_codes:write | Create and revoke QR codes and locations. |
refunds:read | Read refunds. |
refunds:write | Create refunds; every refund is reserved atomically against the refundable amount. |
verifications:write | Submit a customer reference or operator SMS for verification. |
payouts:read | Read payouts and batches. |
payouts:write | Create payouts and bulk batches (approval stays with a person). |
balance:read | Read balances by class (available, held, escrow, suspense). |
webhooks:manage | Register endpoints, read deliveries, replay. |
events:read | Read the event feed. |
payments:create | Initiate national-switch payments. |
payments:read | Read national-switch payments. |
payments:cancel | Cancel a pending national-switch payment. |
refunds:create | Create national-switch refunds. |
participants:read | Read national-switch participants. |
qr:create | Create switch-compatible QR codes. |
reconciliation:read | Read reconciliation reports and receipts. |
bindings:manage | Manage account bindings. |
settlements:read | Read settlement profiles, cycles and statements. |
settlements:write | Manage settlement profiles and close cycles. |
disputes:read | Read disputes and chargebacks. |
disputes:write | Respond to disputes with evidence. |
accounts:read | List and read your connected accounts: status, verification, balances. |
accounts:write | Create your customers' connected accounts, send claim links, set application fees, detach. |
* | Full account scope (sk_ keys). |
Drop-in checkout: pay by mobile money, automatically
Add “Pay with BitriPay” to any website or marketplace. The customer picks the operator, pays to the collection number, and BitriPay matches the operator's confirmation; the order moves forward on its own. Two integration paths:
1 · Hosted checkout (recommended)
Create an intent or a checkout session and redirect to its URL. BitriPay renders the operators, the reference, the QR and the confirmation step, then returns the customer to your success URL and sends the webhook.
2 · Embedded widget
Load the checkout script with a publishable key and mount the panel in your page; the browser can start a payment but never read your data. WooCommerce and Shopify plugins ship in the repository.
Platforms and aggregators: onboard your customers by API
Marketplaces, SaaS, billing systems, cooperatives, schools, institutions: create each customer's merchant account with one call, take their payments with your own key, keep your fee, and hand the account over when they are ready. BitriPay holds the aggregator licence and remains the regulated party; each customer is the merchant of record with its own wallets, settlement profile, statements and verification.
# 1. Create the customer's account (they get a claim link to own it) curl -X POST https://api.bitripay.com/v1/accounts -H "Authorization: Bearer sk_live_…" -H "Content-Type: application/json" -d '{"business_name":"Pharmacie Lumière","type":"merchant","email":"owner@pharmacie.example","country":"CD","application_fee_bps":150}' # → {"id":"acct_…","status":"active","kyb_status":"none","onboarding":{"claimed":false}, …} # 2. Take a payment for that customer with YOUR key: add the account header (works on every operation) curl -X POST https://api.bitripay.com/v1/payment_intents -H "Authorization: Bearer sk_live_…" -H "BitriPay-Account: acct_…" -H "Idempotency-Key: inv-88" -H "Content-Type: application/json" -d '{"amount_minor":250000,"currency":"CDF","description":"Invoice 88","application_fee_minor":3750}' # 3. Their events reach your webhooks with "account":"acct_…"; send them a claim link when they should sign in themselves curl -X POST https://api.bitripay.com/v1/accounts/acct_…/account_links -H "Authorization: Bearer sk_live_…"
Merchant of record
The customer's money lands in the customer's wallet and settles by the customer's settlement profile; your application fee is a transparent split on the intent, paid at capture and shown on both statements.
Every operation, one header
Intents, checkout sessions, payment links, QR codes, refunds, payouts, balance, disputes, settlement: whatever your key may do for you, it does for a connected account with BitriPay-Account. Scopes still apply.
Hand-over and revocation
The customer claims the account with a one-time link and sees you under Team as administrator. They can remove you at any time, or you detach: keys, wallets and history stay theirs.
Install BitriPay for your clients
BitriPay holds the aggregator licence; you integrate it into your clients' websites, apps, billing and institutional systems. Each client is the merchant of record: their money settles to their own account, their statements are theirs, and you never hold their funds or their password.
Or you open it with their details
Every business, NGO, institution or government body you integrate registers as a merchant-class account and passes business verification as its volume grows.
Command centre → Team → developer role
You sign in with your own credentials and act for the client with the developer role: API keys, webhooks and payment creation — nothing on settlement, payouts or customer exports. Every action is written to the client's audit trail under your name.
Keys per client, sandbox first
Pick the client in the workspace selector, create its test key, integrate with the hosted checkout, the embedded widget, the WooCommerce or Shopify plugin or an SDK, then switch to a live key. The client can remove you at any time; its keys stay its own.
Sandbox magic numbers
With a test key, these MSISDNs drive an intent through the real attempt machine so you can test every outcome without an operator.
| Number | Outcome | What happens |
|---|---|---|
+243000000501 | succeed | Customer approves → captured, settled to the test balance, payment_intent.succeeded webhook |
+243000000404 | fail | Wallet not found → attempt fails (invalid_msisdn), intent returns to REQUIRES_PAYMENT_METHOD |
+243000000408 | ambiguous | Provider outcome unknown → payment parked in MANUAL_REVIEW, intent AMBIGUOUS, payment_intent.ambiguous_hold webhook |
+243000000500 | timeout_then_succeed | Timeout then success → pending for 6 seconds, then succeeds and settles |
+243000000503 | provider_unavailable | Provider unavailable → retryable failure (provider_unavailable) |
any number ending in 0000 | declined | Customer rejects the prompt → declined |
Everything else in the catalogue
| Method | Path | Description |
|---|---|---|
| GET | /payment_intents/{id}/timeline | Every state change and attempt of an intent |
| GET | /payment_intents/{id}/methods | Payment methods available to the payer for this intent |
| GET | /payment_intents/{id}/refundable | Amount still refundable on an intent |
| GET | /payment_intents/{id}/splits | Split payouts made from a captured intent |
| GET | /checkout_sessions/{id} | Retrieve a checkout session |
| POST | /checkout_sessions/{id}/expire | Expire an open session |
| POST | /payment_links/{id}/deactivate | Deactivate a link |
| GET | /qr_codes/analytics | Scan analytics |
| POST | /qr_codes/{id}/revoke | Revoke a code (lost, stolen, tampered, replaced, retired) |
| POST | /resolve | Resolve any scanned payload (public; trust from the key registry) |
| POST | /locations | Create a merchant location |
| POST | /locations/{id}/terminals | Add a terminal to a location |
| GET | /refunds/{id} | Retrieve a refund |
| GET | /verifications/quota | Free quota and price |
| GET | /wallets | Wallet balances with the available amount after holds |
| POST | /transfers/quote | Quote a transfer with the full fee and FX disclosure, plus rails ranked by the smart router |
| POST | /transfers | Send from the wallet to another BitriPay account |
| GET | /transfers/{id} | Retrieve a transfer |
| POST | /routes/quote | Quote any → any: fund from the wallet or any card / bank / mobile money, deliver to a wallet, QR, bank account, mobile money number or agent anywhere, in the currency the recipient will receive; rate, margin and every fee disclosed |
| POST | /routes/payout-currencies | Which currencies the recipient can receive for a destination right now (corridor rules, licence, liquidity) |
| POST | /routes | Create a route: the funding leg runs through the processor (card / bank / mobile money) or the wallet, the payout leg follows automatically once funds are confirmed |
| GET | /routes | List routes |
| GET | /routes/{id} | Retrieve a route with its stage, quote, funding payment and payout |
| GET | /routes/{id}/receipt | Receipt for a settled route |
| POST | /routes/{id}/cancel | Cancel before the payout is executed (funds return; card funding refunded where the processor allows) |
| GET | /remittances/quote | Quote an international transfer |
| POST | /remittances | Send an international transfer to a wallet, bank account, mobile money number or cash pickup in the recipient currency; add a `source` (any card, bank or mobile money) to fund it through a processor instead of the wallet |
| GET | /remittances | List remittances |
| GET | /payouts/batches/columns | CSV columns accepted for a batch upload |
| POST | /payouts/batches | Upload a payout batch (rows or CSV); every row is validated and the totals are returned before approval |
| GET | /payouts/batches | List payout batches |
| GET | /payouts/batches/{id} | Retrieve a batch with its rows and readiness (funds, invalid rows, approval rule) |
| POST | /payouts/batches/{id}/approve | Approve and execute a batch: four-eyes by another account holder, or the creator under PIN / passkey step-up |
| POST | /payouts/batches/{id}/cancel | Cancel a batch awaiting approval |
| POST | /ai/{agent} | Run an agent (canonical names such as RouteOptimiser, FraudScorer, SavingsAdvisor accepted); metered in ACU, provider and model never disclosed |
| GET | /ai/runs/{id} | Retrieve an agent run |
| GET | /plans | List your subscription plans |
| POST | /plans | Create a plan (interval, trial, tax, metered usage) |
| GET | /plans/{id} | Retrieve a plan |
| POST | /plans/{id}/archive | Stop offering a plan (existing subscriptions continue) |
| GET | /subscriptions | List subscriptions with a billing overview (recurring revenue, collections, dunning) |
| GET | /subscriptions/{id} | Retrieve a subscription with its invoices |
| POST | /subscriptions/{id}/usage | Record metered usage for the current period |
| POST | /subscriptions/{id}/cancel | Cancel at period end (or immediately) |
| GET | /invoices | List invoices |
| GET | /credit_readiness/{code} | Read a consented credit-readiness signal (score, band, factors); never the transactions, never a lending decision |
| GET | /settlement_profiles | Settlement profiles per rail and currency |
| POST | /settlement_profiles | Create or update a profile (T0/T1/T2/weekly/manual, cut-off, destination, minimum) |
| GET | /settlement_calendar | Upcoming cut-offs, obligations and recent cycles |
| GET | /settlement_cycles | List cycles |
| POST | /settlement_cycles | Close the running period now (optionally pay) |
| GET | /settlement_cycles/{id}/statement | Numbered, hashed statement (format=json|csv|pdf) |
| POST | /settlement_cycles/{id}/pay | Pay a closed cycle to its destination |
| GET | /disputes | Disputes against my payments |
| POST | /disputes | Open a dispute on a payment I received |
| POST | /disputes/{id}/respond | Respond with evidence before the deadline |
| GET | /webhook_endpoints/{id}/deliveries | Deliveries with attempts and status |
| POST | /webhook_deliveries/{id}/replay | Replay a delivery |
| GET | /events | Event log |
| POST | /events/{id}/replay | Replay an event to every endpoint |
| GET | /api_keys | List API keys (session only; keys cannot mint keys) |
| POST | /api_keys | Create sk_ / rk_ / pk_ key with scopes |
| GET | /sandbox | Magic MSISDNs and simulated outcomes |
| POST | /offline/devices | Register a device offline subkey (ed25519 SPKI, 72h) |
| POST | /offline/qr | Server-signed offline QR with nonce |
| POST | /offline/nonces | Prefetch nonces for a merchant device |
| POST | /offline/sync | Submit signed promises in order; each is SETTLED, REJECTED (with restoreMinor) or DUPLICATE |
| GET | /offline/promises | My offline promises |
| GET | /diaspora/rate-cards | Published, platform-signed rate cards (≤ 4h validity) |
| GET | /institutions | Verified institutions and their purpose codes |
| POST | /diaspora/quotes | Purpose-locked quote at the current rate card |
| POST | /diaspora/quotes/{id}/pay | Pay a quote from the source-currency wallet |
| POST | /payments | Create an interinstitutional payment (IDM-001/002/003; route computed server-side) |
| GET | /payments/{id} | Five status dimensions and journal |
| POST | /payments/{id}/cancel | Cancel before emission (never after) |
| POST | /payment_intents/{id}/capture | Capture an AUTHORISED intent (capture_method manual); optional partial amount, the rest returns to the payer |
| POST | /refunds/{id}/reject | Reject a refund awaiting execution (REQUESTED / APPROVED): reservation released, intent keeps its prior state |
| GET | /payment_resolution | Did this payment happen? CONFIRMED (ledger posting), PENDING, AMBIGUOUS or NOT_FOUND, with the matches |
| POST | /fx/quotes | Lock an FX quote (mid-market rate, margin, fee, recipient amount); usable as quote_id on transfers and routes |
| GET | /fx/quotes/{id} | Retrieve an FX quote (LOCKED, INDICATIVE or EXPIRED) |
| POST | /money_requests | Request a payment from a named payer (@tag, phone or email) |
| GET | /money_requests/{code} | Retrieve a money request (requester or payer) |
| POST | /money_requests/{code}/cancel | Cancel an unpaid money request |
| GET | /transactions/{id} | A ledger transaction with its balanced entries and the payment intent it settled |
| POST | /api_keys/{id}/rotate | Rotate a key (same label, kind, scopes, allowlist; old secret revoked). Live keys need PIN / passkey step-up (403 step_up_required) |
| GET | /webhook_events/types | Event catalogue with the receiver rules (`notes`): at-least-once, dedupe by event id |
| GET | /settlements/{id} | A settlement (cycle) with its items and statement summary: provider fee, BitriPay fee and tax on it as separate lines, plus the disclosed conversion when the settlement currency differs |
| GET | /settlement_profiles/{id}/preview | What the next cycle of a profile would settle now: collection-currency totals with the fee lines, and the conversion into the settlement currency (rate, mid rate, margin bps) |
| GET | /settlement_profiles/{id} | Retrieve a settlement profile (settlement_currency, auto_convert) |
| GET | /payment_intents/{id}/split_refunds | What each split recipient gave back on the refunds of an intent (pro_rata) or kept (merchant_absorbs) |
| POST | /payment_intents/{id}/split_refunds/retry | Retry failed split refund recoveries |
| POST | /accounts | Create a customer's merchant account (user, organisation, wallets) that your key can act for with the BitriPay-Account header |
| GET | /accounts | List your connected accounts |
| GET | /accounts/{id} | A connected account: status, verification, onboarding, balances |
| PATCH | /accounts/{id} | Change the default application fee or metadata |
| POST | /accounts/{id}/account_links | A one-time claim link (7 days) the customer opens to set a password and own the account |
| POST | /accounts/{id}/detach | End your access; the account, its keys and its money stay the customer's |