# 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](02-accounts.md).

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_URL` and `MG_API_KEY`** are set, as in the [Quickstart](../01-get-started/03-quickstart.md).
- **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_create_receivables`, to create) and *view receivables* (`can_view_receivables`, to read and report).
- **A webhook URL**, to receive `receivables` / `inflow`. See [Webhooks](../03-webhooks-and-emails/01-overview.md).

## How it works

```mermaid
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
```

<Steps>
<Step title="Create the receivable">

Generate a UUID for `uniqueRequestId` and store it with your invoice, then create the receivable:

```bash
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
```

```json
{
  "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": []
}
```

- `amount` is in major units (500000 is 500,000.00 NGN), stored with 2 decimal places.
- `issueDate` defaults to today (UTC). `dueDate` can't be earlier than `issueDate`.
- `uniqueRequestId` must be a **UUID** that no other receivable on Multigate has used. A reused ID is refused with `400` and the original isn't returned, so store `entityId` from the first response.
- **Store `entityId`** (here `Rc7mT2qL9xV4nB8kD1sFa`): it's the receivable's ID for every later call. Put it in `MG_RECEIVABLE_ID` for the next step.

</Step>
<Step title="Wait for the account">

Poll [Get a receivable](api:getReceivable), for example every 10 seconds for a few minutes, until `status` is `unpaid` or `failed`. No `receivables` webhook is sent for either outcome.

```bash
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_opened` event with `accountType: "receivable"` and the receivable's `uniqueRequestId`. 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 `uniqueRequestId`, or contact Multigate if it keeps failing.

</Step>
<Step title="Give the payer the account">

Once `status` is `unpaid`, send the payer the account in `fundingDetails` (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_paid`) are for NGN receivables.

</Step>
<Step title="Receive payments">

Each payment into the account sends `receivables` / `inflow`:

```json
{
  "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 `receivableDetails.entityId` or `uniqueRequestId`, and use `transactionDetails.transactionId` to ignore a repeated delivery. [Get a receivable](api:getReceivable) lists every payment in `payments`.

</Step>
</Steps>

## 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`. `paid` means the total equals `amount`; `overpaid` means 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 `paid` or `overpaid`,** 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 becomes `overpaid`, but that money stays in the receivable's own account instead of your pool account. Contact Multigate to have it moved.

## Receivable statuses

```mermaid
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 `fundingDetails`. | None (poll) |
| `partially_paid` | 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 | Email |
|---|---|---|
| The account opens after a delay | `account` / `account_opened` (`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](api:listReceivables): newest first, filtered by `status` and `currencies`. The response has `count` (items on this page) and `totalCount`, not the usual paging fields.
- [Get a receivables summary](api:getReceivablesSummary): totals invoiced (by creation date) and collected (by payment date) per currency, for all time or a date range.
- [Get receivables history](api:getReceivablesHistory): 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](api:listReceivables), 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 `uniqueRequestId`. | Check with [List receivables](api:listReceivables) before retrying with the same ID. |

## Next steps

- [Invoice collections](../05-use-cases/05-invoice-collections.md): an end-to-end example.
- [Event catalogue](../03-webhooks-and-emails/02-event-catalogue.md): the `receivables` / `inflow` payload.
- [Balances and statements](10-balances-and-statements.md): see collected money in your pool account.
