Products
Receivables
A receivable is an invoice you register with Multigate. Each one gets its own virtual account, and every payment into that account is matched to the invoice automatically, including partial payments and overpayments.
When to use it
Use receivables when you issue invoices and want to stop reconciling payments by hand: a payment is matched by the account it was paid into, not by its narration or amount. For general collections, use a regular virtual account.
In short: create the receivable, wait until it has an account, give that account to the payer, and receive a webhook for each payment.
Before you start
MG_BASE_URLandMG_API_KEYare set, as in the Quickstart.- Set-up by Multigate, per currency: a receivable pool account (where collected money is credited) and a receiving bank that supports receivables. Without them, creating a receivable fails with
400. - An active business. Receivables are for your own business; there is no
onBehalfOf. - An API user with the create receivables permission (
can_, to create) and view receivables (create_ receivables can_, to read and report).view_ receivables - A webhook URL, to receive
receivables/inflow. See Webhooks.
How it works
sequenceDiagram
autonumber
participant You as Your backend
participant MG as Multigate
participant Payer
You->>MG: POST /receivables (invoiceRef, amount, dueDate, uniqueRequestId)
MG-->>You: 200, status processing, fundingDetails empty
Note over MG: Account opened in the background
loop Until status is unpaid or failed
You->>MG: GET /receivables/{entityId}
MG-->>You: status, fundingDetails
end
You->>Payer: Invoice with the account in fundingDetails
Payer->>MG: Pays (one or several transfers)
MG-)You: receivables / inflow (invoice status, amountPaid)
MG-)You: account / inflow and a Payment Received email
Generate a UUID for unique and store it with your invoice, then create the receivable:
export MG_REQUEST_ID=$(uuidgen | tr 'A-Z' 'a-z')
curl -s -X POST "$MG_BASE_URL/client-service/api/v1/receivables" \
-H "mp-api-key: $MG_API_KEY" \
-H "Content-Type: application/json" \
-d @- <<EOF
{
"invoiceRef": "INV-2026-0042",
"amount": 500000,
"currencyIso3Code": "NGN",
"payer": "Kola Stores Ltd",
"issueDate": "2026-10-01",
"dueDate": "2026-10-31",
"uniqueRequestId": "$MG_REQUEST_ID"
}
EOF
{
"message": "Receivable created successfully",
"entityId": "Rc7mT2qL9xV4nB8kD1sFa",
"invoiceRef": "INV-2026-0042",
"amount": 500000,
"amountPaid": 0,
"currencyIso3Code": "NGN",
"payer": "Kola Stores Ltd",
"issueDate": "2026-10-01T00:00:00.000Z",
"dueDate": "2026-10-31T00:00:00.000Z",
"paidDate": null,
"uniqueRequestId": "4f6a0e1c-2b3d-4e5f-8a9b-0c1d2e3f4a5b",
"status": "processing",
"fundingDetails": []
}
amountis in major units (500000 is 500,000.00 NGN), stored with 2 decimal places.issueDatedefaults to today (UTC).dueDatecan't be earlier thanissueDate.uniquemust be a UUID that no other receivable on Multigate has used. A reused ID is refused withRequest Id 400and the original isn't returned, so storeentityIdfrom the first response.- Store
entityId(hereRc7m): it's the receivable's ID for every later call. Put it inT2q L9x V4n B8k D1s Fa MG_for the next step.RECEIVABLE_ ID
Poll Get a receivable, for example every 10 seconds for a few minutes, until status is unpaid or failed. No receivables webhook is sent for either outcome.
for i in $(seq 1 30); do
STATUS=$(curl -s "$MG_BASE_URL/client-service/api/v1/receivables/$MG_RECEIVABLE_ID" \
-H "mp-api-key: $MG_API_KEY" | jq -r .status)
echo "$STATUS"
[ "$STATUS" != "processing" ] && break
sleep 10
done
If the bank opens the account after a delay, you also receive an account / account_ event with accountType: "receivable" and the receivable's unique. Treat it as a hint to poll; it isn't sent when the account opens straight away.
If status becomes failed, the account couldn't be opened. Create a new receivable with a new unique, or contact Multigate if it keeps failing.
Once status is unpaid, send the payer the account in funding (account name, number, bank and codes). Don't share it while status is still processing.
The account name is the receivables account name Multigate configured for you, if any. Otherwise it is the receivable's payer.
For USD, Multigate asks the bank for an account tied to the invoice amount that expires 60 days after dueDate. This stops payers sending less than the invoice and leaving a shortfall that has to be chased or refunded. Depending on the bank, a payment of a different amount, a second payment, or a payment after expiry can be refused, so ask USD payers to pay the exact amount in one transfer. Instalments (partially_) are for NGN receivables.
Each payment into the account sends receivables / inflow:
{
"header": { "notification_type": "receivables", "notification": "inflow", "hash": "5a6b7c8d…" },
"body": {
"receivableDetails": {
"entityId": "Rc7mT2qL9xV4nB8kD1sFa",
"invoiceRef": "INV-2026-0042",
"amount": 500000,
"amountPaid": 200000,
"currencyIso3Code": "NGN",
"uniqueRequestId": "4f6a0e1c-2b3d-4e5f-8a9b-0c1d2e3f4a5b",
"status": "partially_paid",
"paidDate": null
},
"transactionDetails": {
"transactionId": "IN-261012-0930151234",
"amount": 200000,
"senderAccountName": "KOLA STORES LTD",
"narration": "INV-2026-0042 part",
"transactionDate": "2026-10-12T09:30:15.000Z"
}
}
}
The payload is shortened here; see the event in Webhooks › Events for every field. Match it to your invoice with receivable or unique, and use transaction to ignore a repeated delivery. Get a receivable lists every payment in payments.
How payments are matched
- By account. Any payment into the receivable's account counts towards that invoice, whatever its narration, sender or amount.
- Running total. Each payment adds to
amountPaid.paidmeans the total equalsamount;overpaidmeans it's more. - Once per inflow. A payment is recorded once, even if Multigate receives the bank's notification twice.
- Where the money goes. Payments are credited to your receivable pool account for that currency. Collection fees apply as for any other inflow.
- After
paidoroverpaid, Multigate closes the account with the bank. A payment that still arrives before the closure takes effect is added to the total and the status becomesoverpaid, but that money stays in the receivable's own account instead of your pool account. Contact Multigate to have it moved.
Receivable statuses
stateDiagram-v2
direction LR
[*] --> processing: created
processing --> unpaid: account ready
processing --> failed: account couldn't be opened
unpaid --> partially_paid: part paid
partially_paid --> partially_paid: more paid
unpaid --> paid: paid in full
partially_paid --> paid: paid in full
unpaid --> overpaid: paid more
partially_paid --> overpaid: paid more
paid --> overpaid: late extra payment
paid --> [*]
overpaid --> [*]
failed --> [*]
| Status | Meaning | Webhook |
|---|---|---|
processing |
The account is being opened. | None |
unpaid |
Ready: give the payer funding. |
None (poll) |
partially_ |
Some money received; amountPaid is less than amount. |
receivables / inflow |
paid |
Exactly the invoice amount received. The account is closed. paidDate is set. |
receivables / inflow |
overpaid |
More than the invoice amount received. The account is closed. | receivables / inflow |
failed |
The account couldn't be opened. | None (poll) |
Webhooks and emails
| When | Webhook | |
|---|---|---|
| The account opens after a delay | account / account_ (accountType: "receivable") |
None |
The receivable becomes unpaid or failed |
None | None to you |
| Each payment | receivables / inflow, and account / inflow |
"Payment Received" to your business contact |
Receivables aren't reported to any third party.
Reporting
- List receivables: newest first, filtered by
statusandcurrencies. The response hascount(items on this page) andtotalCount, not the usual paging fields. - Get a receivables summary: totals invoiced (by creation date) and collected (by payment date) per currency, for all time or a date range.
- Get receivables history: the same totals per month, for the current month and the 5 before it.
Errors and recovery
| Status | Message | Cause | What to do |
|---|---|---|---|
400 |
A receivable with this uniqueRequestId already exists | The ID was used before, by you or anyone. | Find your receivable with List receivables, or use a new UUID. |
400 |
A receivable pool account must be configured for this currency before creating a receivable account | Receivables aren't set up for the currency. | Ask Multigate to set it up. |
400 |
No partner configuration found for this currency or integration | No receiving bank supports receivables for the currency. | Ask Multigate. |
400 |
dueDate must be larger than or equal to {issueDate} | The dates are the wrong way round. | Fix the dates. |
400 |
uniqueRequestId must be a valid GUID | The ID isn't a UUID. | Generate a UUID. |
400 |
Client is inactive | Your business isn't active. | Contact Multigate. |
404 |
Receivable not found | Unknown ID, or another business's receivable. | Check the entityId. |
404 |
Resource not found | Your API user lacks the permission. | Ask your administrator for the permission. |
500 |
Something went wrong… | Unexpected error, including two simultaneous creates with the same unique. |
Check with List receivables before retrying with the same ID. |
Next steps
- Invoice collections: an end-to-end example.
- Event catalogue: the
receivables/inflowpayload. - Balances and statements: see collected money in your pool account.