One billing model.
Every integration.
Create invoices, issue secure checkout links and respond to precise state changes through one integration.
const session = await rw.createCheckoutSession(
"inv_10482",
"2026-08-23T18:00:00Z",
"idem_8f2a1c"
);
return session.checkoutUrl;✓ 201 Created · 184 msFrom invoice to a checked attempt status in four steps.
- 01Create an invoice
Fix seller, amount, currency and purpose before payment.
- 02Open checkout
Give the customer an opaque, expiring URL.
- 03Start an attempt
The customer chooses and authorises an enabled wallet.
- 04Check the result
Read the attempt status using its checkout token. If the result is unknown, wait for provider evidence before deciding what happens next.
Primitives that map to the real collection lifecycle.
{
"id": "payatt_7J4M2Q",
"invoiceId": "inv_10482",
"status": "unknown",
"nextAction": "wait_for_update",
"failureCode": null
}Ambiguity is data. `unknown` remains distinct from `failed` and `succeeded` until provider evidence resolves the attempt.
Precise events behind every attempt.
Each payment attempt emits the same vocabulary the platform uses to keep payment truth. Events are at-least-once and deduplicated by a stable event id.
Integrations read current attempt states by polling. Merchant webhooks are unavailable on this route.
Contracts that survive retries and tenants.
A retried create returns the original result instead of a second effect. Send the same key, get the same object.
Every request resolves identifiers within one workspace. A customer or invoice from another workspace is simply not found.
KES 890 is 89000 minor units. Money never travels through the API as a float.
created, initiation_queued, customer_action_required, provider_accepted, unknown, failed, cancelled, expired, succeeded, reversed.
Build the billing integration once.
Connect your billing workflow to the payment routes enabled for your business.
Discuss an integration