Look up a bill
One endpoint does both kinds of lookup — POST /api/app/payment-processor/lookup,
scope unipay:bills.
LOOKUP=$(curl -sS --fail-with-body -X POST "$BASE_URL/api/app/payment-processor/lookup" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d "{\"billNo\": \"$BILL_NO\"}")
echo "$LOOKUP" | jq '{successful, message, name: .bill.name, currency: .bill.currency, payableNow, acceptedCurrencies}'public static async Task<LookupResult> LookUpBillAsync(UniPayClient client, string billNo){ var result = await client.PostAsync<LookupResult>("/api/app/payment-processor/lookup", new { billNo }); if (!result.Successful) throw new InvalidOperationException(result.Message); // e.g. "Bill '4444INV0009' not found" return result;}static JsonNode lookUpBill(UniPayClient client, String billNo) throws IOException, InterruptedException { JsonNode result = client.post("/api/app/payment-processor/lookup", Map.of("billNo", billNo)); if (!result.get("successful").asBoolean()) { throw new RefusedException(result.get("message").asText()); // e.g. "Bill '4444INV0009' not found" } return result;}fun lookUpBill(client: UniPayClient, billNo: String): JsonNode { val result = client.post("/api/app/payment-processor/lookup", mapOf("billNo" to billNo)) if (!result["successful"].asBoolean()) { throw RefusedException(result["message"].asText()) // e.g. "Bill '4444INV0009' not found" } return result}def look_up_bill(client: UniPayClient, bill_no: str) -> dict: result = client.post("/api/app/payment-processor/lookup", {"billNo": bill_no}) if not result["successful"]: raise RuntimeError(result["message"]) # e.g. "Bill '4444INV0009' not found" return resultasync function lookUpBill(client: UniPayClient, billNo: string): Promise<LookupResult> { const result = await client.post<LookupResult>('/api/app/payment-processor/lookup', { billNo }); if (!result.successful) throw new Error(result.message); // e.g. "Bill '4444INV0009' not found" return result;}func LookUpBill(c *UniPayClient, billNo string) (*LookupResult, error) { var result LookupResult err := c.Post("/api/app/payment-processor/lookup", map[string]string{"billNo": billNo}, &result) if err != nil { return nil, err } if !result.Successful { return nil, errors.New(result.Message) // e.g. "Bill '4444INV0009' not found" } return &result, nil}function lookUpBill(UniPayClient $client, string $billNo): array{ $result = $client->post('/api/app/payment-processor/lookup', ['billNo' => $billNo]); if (!$result['successful']) { throw new RefusedException($result['message']); // e.g. "Bill '4444INV0009' not found" } return $result;}Request
Section titled “Request”Send exactly one of: a billNo, or a serviceProvider with an accountNumber.
{ "billNo": "4444INV0009" }{ "serviceProvider": "MUT1", "accountNumber": "100-2345", "currency": "USD" }| Field | Notes |
|---|---|
billNo |
The bill number from the customer’s bill. Spaces, dashes and dots are ignored. |
serviceProvider |
The provider’s number (4444) or its short code (MUT1). Find them in the directory. |
accountNumber |
The customer’s account number at that provider. Separators don’t need to match. Up to 64 characters. |
currency |
Optional: the currency the customer wants to pay in. See Currencies. |
Response
Section titled “Response”{ "successful": true, "message": "Bill found successfully", "status": 1, "bill": { "billNo": "4444INV0009", "uniqueNo": "INV0009", "serviceProviderNumber": "4444", "serviceProviderName": "City of Mutare", "accountNumber": "********2345", "name": "J. Moyo", "description": "Rates - September 2026", "billDate": "2026-09-01T00:00:00Z", "dueDate": "2026-09-30T00:00:00Z", "expiryDate": null, "currency": "USD", "totalAmount": 220.00, "totalPaid": 50.00, "remainingBalance": 170.00 }, "amountDueNow": 170.00, "payableNow": 170.00, "balanceIsStale": false, "acceptedCurrencies": ["USD", "ZWG"], "otherCurrenciesAvailable": null, "conversionApplied": false}Check successful first, then status — see Lookup status. Take payment only when
status is 1 (Owing), and:
- ask the customer for at most
payableNow, in the bill’scurrencyor one ofacceptedCurrencies; - pay with the
billNofrom this response; - refuse before debiting if
expiryDatehas passed — the payment would be refused.
The fields are explained in Bills and accounts, and every field is listed in the
reference. The bill also carries serviceProvider, accountNo and amount:
older names for serviceProviderNumber, accountNumber and totalAmount, kept for existing integrations.
When nothing is found
Section titled “When nothing is found”A lookup that can’t find a bill still answers HTTP 200, with successful: false and a message:
message |
Cause |
|---|---|
Bill '4444INV0009' not found |
No bill with that number. |
No outstanding bill found and no account on record for this number |
Account lookup: no bill and no account (status 0). |
Account standing is stale; no current information for this number |
Account lookup: account known, balance out of date (status 3). |
Account is settled; nothing currently due |
Account lookup: nothing owed (status 2) — successful is true. |
Account is in credit |
Account lookup: in credit (status 4) — successful is true. |
Service provider 'X' not found |
Unknown provider number or short code. |
Service provider short code 'X' is ambiguous; use the provider number |
Use the provider number instead. |
Provide either a bill number or a service provider and account number, not both |
Send one or the other. |
Provide a bill number, or a service provider and account number |
Neither was sent. |
Bill number must be at least 4 characters / cannot exceed 50 characters / can only contain letters and numbers |
Malformed bill number. |
Currency must be a 3-letter ISO 4217 code |
Malformed currency. |
Show the message to your operator or log it; don’t match on its exact wording, which may be improved over time. The full list is in Errors and messages.
UniPay API v1 · build e1eb5c49 · Powered by Axis Solutions