Skip to main content

Overview

Discovery is the read half of Bill Payment: you ask a biller what an account currently owes, and you get back a list of payable bills. Nothing is charged, and nothing is committed. It happens in two parts. POST /v3/bills/discover accepts the request and gives you a transactionId. The bills themselves arrive on that transaction a moment later, when its status becomes READY.
The 200 from discover is an acknowledgement. It carries no bills and says nothing about what the account owes. Read the transaction before you tell your customer anything.

Building the request

Three fields, all required.

Choosing a ref

ref is what makes a discovery safe to retry. If the response is lost, you look the ref up instead of sending a second discovery — so a ref you cannot reconstruct from your own data is a transaction you cannot recover. A workable recipe: a fixed prefix, your own order or invoice identifier, and nothing else.
A ref is unique per biller, not globally. disc-inv-2026-0042 for ADE and the same string for SONELGAZ are two different references. Passing partner when you look one up removes any ambiguity.

Sending the discovery

Polling to READY

Read the transaction until its status leaves PENDING. A discovery normally settles in a few seconds.

Reading bills[]

A READY transaction carries the bills that are payable right now.
Show the customer amount + fee. That sum is what will be debited, and it is returned as total on the transaction once a bill is selected.
bills[] is present only while status is READY. Once a payment starts, the transaction carries selectedBill instead. Read the bills while you have them.

When bills[] is empty

An empty array is a normal, successful outcome — not an error.
It means one of two things, and the API does not distinguish between them:
  • the account owes nothing, or
  • everything the account owes is below the 200 DZD discovery floor.
Bills under 200 DZD are filtered out during discovery and never appear.
Word this carefully. “No bills are payable right now” is accurate. “You owe nothing” is not — a 150 DZD bill exists but cannot be paid through this API.

Recovering a lost response

If the discovery request fails in a way you cannot explain — a timeout, a crash, a redeploy — ask what the ref became. Never send a second discovery.
The same lookup answers 403 DUPLICATED_REF. That error means the discovery already exists; it is never a reason to retry with a different ref, which would start a second discovery for the same account.

Errors you will meet

A FAILED discovery carries its reason in error.code: INVALID_ACCOUNT, PARTNER_UNAVAILABLE, BILL_ALREADY_PAID or PAYMENT_DECLINED. Every code, with example bodies →

Best practices

Derive the ref

Build it from your own order identifier so you can always reconstruct it after a failure.

Poll, never resend

A slow discovery is not a lost one. Read the transaction instead of sending another request.

Cache nothing about bills

A discovery is a snapshot. If the customer waits, discover again rather than paying against stale figures.

Say payable, not owed

An empty bills[] means nothing is payable. It does not mean the account owes nothing.

Next step

Step 3: Paying bills

Choose a bill, confirm the total, and submit the payment safely

Discover Bills

The endpoint reference

Get Transaction by ID

The transaction object in full

Get Transaction by Reference

The recovery path

Partners and Accounts

Identifier rules per biller