Day settlements
If you pay a service provider directly — one bank transfer covering a day’s payments — tell UniPay about each transfer. UniPay matches every payment you list against the payments you reported, finds the ones you left out, and tells you at once whether the transfer balances. The service provider sees the same report when it reconciles its bank statement.
Send one day settlement per transfer with
POST /api/app/payment-processor/settle-day, scope unipay:bills.
When to send it
Section titled “When to send it”Send it when you make the transfer — usually the business day after the payments were taken.
settlementDateis the day of the transfer. UniPay reconciles it against your payments dated the day before, in your business timezone (normallyAfrica/Harare).- One settlement is one transfer, to one service provider, in one currency. If you pay a provider in two currencies, or pay two providers, send one settlement for each transfer.
- List every payment the transfer covers, by its
rrn.
sequenceDiagram
participant PP as You
participant U as UniPay
participant SP as Service provider
Note over PP,U: Day 1: payments reported as usual
PP->>SP: Day 2: bank transfer for day 1
PP->>U: settle-day (transfer reference, every rrn)
U-->>PP: Matched, differences, balanced?
SP->>U: Read the processor settlements feed
U-->>SP: Your transfer, line by line
Request
Section titled “Request”{ "settlementDate": "2026-09-30", "transferReference": "RTGS20260930-0042", "billerCode": "4444", "creditedAccountNumber": "1234567890", "currency": "USD", "totalAmount": 368.50, "feeAmount": 1.50, "lines": [ { "rrn": "PX20260929000123", "billNo": "4444INV0009", "amount": 170.00 }, { "rrn": "PX20260929000124", "amount": 200.00 } ]}curl -X POST https://test.unipay.co.zw/api/app/payment-processor/settle-day \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d @settlement.json| Field | Required | Rules |
|---|---|---|
settlementDate |
Yes | The day of the transfer, yyyy-MM-dd. Not in the future and not more than 90 days ago. A date-time is accepted, but only its date is used, exactly as written — the time and offset are ignored. |
transferReference |
Yes | Your bank or RTGS reference for the transfer, up to 100 characters. It makes the request safe to retry. |
billerCode |
Recommended | The service provider paid: its number or short code, up to 64 characters. Every rrn must then be one of its payments. |
creditedAccountNumber |
Recommended | The provider’s bank account you credited, up to 64 characters. Providers bank with several banks and can’t otherwise tell which account received the money. |
currency |
Yes | Three-letter ISO 4217 code of the transfer. |
totalAmount |
Yes | What you transferred: the sum of the lines less feeAmount. Greater than zero. |
feeAmount |
Commission you kept from the transfer, if any. Default 0. Must be less than the sum of the lines. |
|
lines |
Yes | 1 to 5,000 lines, one per payment. |
lines[].rrn |
Yes | The payment’s rrn, as you reported it. Up to 50 characters, each at most once. |
lines[].amount |
Yes | The amount of that payment. Greater than zero. |
lines[].billNo |
The bill paid, up to 100 characters. When you send it, it must be the bill UniPay recorded; spaces, dashes and case don’t matter. |
Without billerCode, UniPay takes the provider from the payments you list. Send it anyway: if none of the rrns is recognised, a
settlement with no provider isn’t shown to anyone but you. An account number UniPay doesn’t recognise is recorded as you sent it and
flagged to Axis for checking; it doesn’t refuse the settlement, because the money has already moved.
Response
Section titled “Response”"successful": true means UniPay recorded and reconciled the settlement — not that it balanced. Check isBalanced.
{ "successful": true, "message": "Settlement accepted with 2 difference(s).", "isNewRecord": true, "settlementId": "3a24048c-e7c8-c3bc-e816-72a95370cb3b", "settlementDate": "2026-09-30", "transferReference": "RTGS20260930-0042", "currency": "USD", "reconDate": "2026-09-29T00:00:00Z", "reconTimeZone": "Africa/Harare", "reconWindowStartUtc": "2026-09-28T22:00:00Z", "reconWindowEndUtc": "2026-09-29T22:00:00Z", "reportedTotal": 370.00, "feeAmount": 1.50, "transferredAmount": 368.50, "matchedTotal": 170.00, "reportedCount": 2, "matchedCount": 1, "unmatchedByUnipay": [ { "rrn": "PX20260929000124", "billNo": null, "amount": 200.00, "unipayAmount": 150.00, "reason": "AmountMismatch" } ], "unsettledByProcessor": [ { "rrn": "PX20260929000131", "billNo": "4444INV0011", "amount": 40.00, "unipayAmount": 40.00, "reason": "NotSettledByProcessor" } ], "difference": 10.00, "isBalanced": false}| Field | Meaning |
|---|---|
settlementId |
UniPay’s ID for the settlement. Store it to read it back. |
reconDate |
The day whose payments were reconciled — the day before settlementDate. Only the date part means anything. |
reconTimeZone, reconWindowStartUtc, reconWindowEndUtc |
That day as a UTC window: payments with a paymentDate inside it were expected. |
reportedTotal, reportedCount |
The sum and count of your lines, before commission. |
feeAmount, transferredAmount |
As you sent them. transferredAmount is your totalAmount. |
matchedTotal, matchedCount |
The lines that matched a payment exactly. |
unmatchedByUnipay |
Your lines that didn’t match, each with a reason. amount is yours; unipayAmount is what UniPay recorded, when it found the payment. |
unsettledByProcessor |
Payments you reported for that day, to that provider and in that currency, that the settlement left out. |
difference |
reportedTotal less what UniPay holds as owed — its own amount for every payment you listed that it counts, plus the ones you left out. Negative: you sent less. |
isBalanced |
true only when every line matched and nothing was left out. Differences can cancel out, so a zero difference alone doesn’t mean balanced. |
The message is Settlement balanced — all payments matched. when it balances, otherwise Settlement accepted with N difference(s).
Why a line doesn’t match
Section titled “Why a line doesn’t match”reason |
Meaning | Counted as owed? |
|---|---|---|
UnknownRrn |
You reported no payment with this rrn. |
No |
AmountMismatch |
The payment exists, with a different amount. | Yes, at UniPay’s amount |
BillMismatch |
The payment exists, against a different bill from the billNo you sent. |
Yes |
CurrencyMismatch |
The payment was made in a different currency from the transfer. | No |
Reversed |
The payment has been reversed: there’s nothing to settle. | No |
AlreadySettled |
An earlier settlement already matched this payment. | No |
NotSettledByProcessor |
In unsettledByProcessor: a payment for that day you left out. |
Yes |
A payment counts as settled once a line matches it. So if you left a payment out, or reported the wrong amount, include it in a later
transfer: that settlement matches it and clears the earlier difference. Include a matched payment again and it comes back
AlreadySettled. New reasons may be added; treat one you don’t recognise as a difference.
Every difference is also raised with Axis operations. Once you use day settlements, Axis is also alerted when a day on which you took payments gets no settlement.
Refusals
Section titled “Refusals”A settlement UniPay can’t accept answers HTTP 200 with "successful": false. Nothing is recorded; fix the request and send it again —
you can reuse the transferReference.
message |
Cause |
|---|---|
Settlement date is required · Settlement date cannot be in the future · Settlement date cannot be more than 90 days in the past |
Check settlementDate. |
Total amount must be greater than zero · Fee amount cannot be negative |
Fix the amount. |
Total amount X does not equal the sum of the lines Y |
With no fee, totalAmount must be the sum of the lines. |
Total amount X does not equal the sum of the lines Y less the fee Z |
totalAmount must be the sum of the lines less feeAmount. |
Fee amount X must be less than the sum of the lines Y |
Fix feeAmount. |
Currency must be 3 characters |
Fix currency. |
Transfer reference is required · Transfer reference must not exceed 100 characters |
Fix transferReference. |
Credited account number must not exceed 64 characters · Biller code must not exceed 64 characters |
Fix the field. |
At least one line is required · A settlement may contain at most 5000 lines |
Fix lines. |
Every line needs an RRN · RRN 'X' must not exceed 50 characters · RRN 'X' appears more than once |
Fix the line. |
Amount for RRN 'X' must be greater than zero · Bill number for RRN 'X' must not exceed 100 characters |
Fix the line. |
Biller 'X' not found · Biller code 'X' is ambiguous; use the service provider number |
Fix billerCode. |
Every RRN in the settlement must belong to biller 'X'. |
A payment you listed was made to another provider. |
The RRNs belong to N service providers. A settlement covers one biller's transfer; submit one per biller. |
Split the settlement by provider. |
Transfer reference 'X' was already submitted with a different date, currency or total. |
See Retrying. |
A settlementDate that isn’t a date at all — 30/09/2026, say — gets HTTP 400, with the message
'30/09/2026' is not a date. Use yyyy-MM-dd, for example "2025-01-17".
Retrying
Section titled “Retrying”The transferReference identifies the settlement. If a call times out, or you get a 429 or 5xx, send the same request again:
- Same reference, same
settlementDate,currencyandtotalAmount: you get the recorded result again, withisNewRecord: false. The lines aren’t compared — the answer is always the first one recorded. - Same reference with a different date, currency or total: refused, because that’s a different transfer or a mistake.
A settlement can’t be changed once recorded. If a transfer was wrong, correct it with your next transfer — the payments it matches are cleared as above. The general policy is in Idempotency and retries.
Reading back
Section titled “Reading back”Read a settlement again at any time, for example to recover an answer you lost. Both endpoints answer in the same shape as settle-day,
with isNewRecord: false, and only ever show your own settlements.
| Call | Returns |
|---|---|
GET /api/app/payment-processor/{id}/day-settlement |
One settlement, by settlementId. An unknown ID gets 404. |
GET /api/app/payment-processor/day-settlements |
Your settlements, newest settlementDate first, as { "totalCount": …, "items": [ … ] }. |
The list takes from and to (settlement dates, yyyy-MM-dd, inclusive, both optional), skipCount, and maxResultCount — 20 by
default, at most 100. A from after to is refused with the error object.
Your administrators also see every settlement and its differences in the UniPay portal, under My settlements.
What the service provider sees
Section titled “What the service provider sees”The provider reads your settlement from its processor settlements feed: your transfer reference, the account you credited and whether it recognises it, gross, fee and net amounts, whether it balanced, and every line with its outcome — including the payments you left out.
UniPay API v1 · build e1eb5c49 · Powered by Axis Solutions