Skip to main content
A collection does not complete when you call /pay. That call only pushes a prompt to the customer’s phone (or creates a Wave checkout). The customer then approves it, the operator confirms to Kori, and only then is your balance credited. So every payment has three possible states:
pending is not a failure. A payment can sit pending for minutes while the customer finds their phone. Treat it as unresolved, never as declined.

1. Webhooks — the way you should build

Subscribe to payment_received and let Kori tell you. No polling, no timers, and it works when nobody has a browser open.
Webhook payload
See Webhooks for signature verification and retries.

2. Read your transaction record

Returns Kori’s stored record, including status. This is what you use for a dashboard, a reconciliation job, or any time you want the answer as Kori currently knows it. Requires the balance scope.

3. Ask the operator now

This one queries the mobile-money operator directly and settles the transaction if it has completed, so it is the authoritative answer while a payment is still open. Keyed by reference, not by transaction_id. It needs no authentication — the reference is the credential — which is what lets a hosted checkout page poll it straight from the customer’s browser.
Use this while a customer is waiting. For everything else, prefer the webhook: it costs nothing and arrives even if the customer closes the tab.

Polling, if you must

  • Poll every 3 to 5 seconds, no faster.
  • Stop as soon as state is success or failed.
  • Give up after about two minutes of waiting on a customer — but treat that as “still unknown”, not as a failure.
  • A 500 means the operator could not be reached. Retry; it says nothing about the payment.
Settlement is idempotent: the webhook, this endpoint, and Kori’s own reconciler can all resolve the same payment, and your balance is credited exactly once whichever gets there first.

Payments that are never confirmed

If a customer never answers the prompt, the payment stays pending and Kori marks it expired after 24 hours. A late confirmation still credits you — expired means “we stopped waiting”, not “the money is gone”.
Never decide a payment failed because you stopped hearing about it. The only failures are state: "failed" and status: "failed".