> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vestrapay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Distribution

> Use the service keys from your dashboard to allocate each payment across services when you initialise it with the Payments API.

You configure services and splits in the merchant dashboard. **Distribution** is the bridge from that configuration to the Payments API: when your server initialises a payment, it can say how much of the payment belongs to each service. Vestrapay then divides each allocation among that service's sub-accounts according to its split shares, and settles everything you did not allocate to your primary settlement account.

If you never send a distribution, nothing changes. The whole payment settles to your primary account.

> [!NOTE]
>
> This page is about the API side. To create services, sub-accounts and splits, use the dashboard. See [Services and splits](https://docs.vestrapay.com/services/overview.md), [Sub-accounts](https://docs.vestrapay.com/services/subaccounts.md) and [Splits](https://docs.vestrapay.com/services/splits.md).

## Get the service key

Each service has a **service key**, shown on the service under **Services** in the dashboard. It is generated from the service title when you create the service: lower case, with runs of other characters replaced by a dash, so "Platform fee" becomes `platform-fee`. A numeric suffix is added if the key is already taken, as in `platform-fee-2`.

The service key is the only identifier you need. It is the key of each entry in the distribution map. Sandbox and live are separate environments, so a key must exist in the environment whose API host you call, and you should confirm the same keys on live before you launch.

## Configuration versus per-payment allocation

These two ideas are easy to mix up, so it helps to separate them.

| | Configured on a service | Sent with a payment |
| - | - | - |
| What it is | Splits: percentage shares for each sub-account | Distribution: an absolute amount per service |
| Set where | **Services** in the merchant dashboard | The initialise request, or a payment link |
| Authenticated with | Your dashboard sign-in | Secret key |
| Changes | Rarely, as the arrangement changes | On every payment |
| Answers | "How is a service divided?" | "How much of this payment goes to each service?" |

You can see the two layers as a pipeline: the payment amount is allocated to services by the distribution map, and each service's allocation is then divided by its splits.

```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
flowchart TD
  P["Payment of 1000.00"] --> D{"Distribution on initialise"}
  D -->|"tuition: 600"| T["Tuition service"]
  D -->|"insurance: 150"| I["Insurance service"]
  D -->|"not allocated: 250"| R["Primary settlement account"]
  T -->|"85 percent"| A["Federal account: 510.00"]
  T -->|"15 percent"| B["Insurer account: 90.00"]
  I -->|"100 percent"| C["Underwriter account: 150.00"]
```

## The distribution map

Add `distribution` to the body of POST `/v1/payment/initialize`. Its keys are service keys, and its values are absolute amounts in the same major unit as `amount`.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://api.sandbox.vestrapay.app/v1/payment/initialize \
  -H "x-api-key: sk_test_YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "1000.00",
    "email": "customer@email.com",
    "callbackUrl": "https://shop.example.com/pay/return",
    "distribution": {
      "tuition": 600,
      "insurance": 150
    }
  }'
```

The response is the usual initialise response, and it echoes the map back:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "status": "success",
  "data": {
    "reference": "VPY_TXN_ACME001",
    "accessCode": "a1b2c3d4e5f6a1b2",
    "checkoutUrl": "https://checkout.sandbox.vestrapay.app/a1b2c3d4e5f6a1b2",
    "amount": "1000.00",
    "cardFee": "15.00",
    "transferFee": "10.00",
    "cardTotal": "1015.00",
    "transferTotal": "1010.00",
    "currency": "NGN",
    "merchantBearsCost": false,
    "channels": ["card", "bank_transfer", "bank_payment", "ussd"],
    "status": "pending",
    "distribution": { "tuition": 600, "insurance": 150 }
  }
}
```

### Rules

* Each key must be the service key of a service that exists in this environment, is enabled, and has at least one split.
* Each value must be a positive amount, with up to two decimal places.
* **The total of all values must not exceed the payment `amount`.** You may allocate less than the whole, and often will.
* You can name any number of services. You cannot name the same service twice, because it is a map.
* The amount you do not allocate is the **remainder**, and it settles to your primary account.

Any breach returns 400 `INVALID_SPLIT_AMOUNT`, and the message names what was wrong. Nothing is created when it fails. Fix the request and send it again.

| Situation | Message gist |
| - | - |
| A value is zero, negative or not a number | The amount for that key must be a positive number. |
| The total is more than `amount` | The amounts sum to more than the payment amount. |
| A key is unknown or the service is disabled | The split group was not found or is inactive. |
| The service has no splits | No participants are configured. Add splits to the service in the dashboard. |

## How an allocation is divided

For each service in the map, Vestrapay multiplies its allocation by each entry's share and credits the result to that entry's account. Take a payment of 1000.00 with this distribution:

| Service | Allocation | Splits configured |
| - | - | - |
| tuition | 600.00 | Federal account 85 percent, insurer account 15 percent |
| insurance | 150.00 | Underwriter account 100 percent |

The result is:

| Destination | Calculation | Amount |
| - | - | - |
| Federal account | 600.00 × 85 percent | 510.00 |
| Insurer account | 600.00 × 15 percent | 90.00 |
| Underwriter account | 150.00 × 100 percent | 150.00 |
| Primary settlement account | 1000.00 − 600.00 − 150.00 | 250.00 |

A few details are worth knowing.

* **Shares apply to the allocation, not to the payment.** The 85 percent is 85 percent of 600.00, not of 1000.00.
* **A single-entry service still applies its share.** A lone participant on 100 percent receives the whole allocation. If a share is below 100, the rest of that allocation does not vanish, as explained next.
* **Anything not claimed falls to the primary account.** That includes the unallocated remainder, any part of an allocation not claimed by shares, and any fraction smaller than the smallest currency unit when a share has decimals.
* **One account, several splits.** If one account backs several splits, its legs are summed for settlement, but they are still recorded separately under their split names.

## Fees and who bears them

Allocations are decided before fees. What happens to the fee depends on the fee bearer for the payment, which comes from your payment settings and can be overridden per payment with `merchantBearsCost`.

| Fee bearer | What the customer pays | What is divided |
| - | - | - |
| Customer bears the fee | `amount` plus the fee | The full `amount`. The fee is collected on top and booked separately. |
| You bear the fee | Exactly `amount` | `amount` less the fee. |

When you bear the fee, there is usually room in the remainder to absorb it. Take the example above with a fee of 15.00. The settleable amount is 985.00, the three allocations total 750.00, and the primary account receives 235.00.

If your allocations are so large that the remainder cannot absorb the fee, the service legs are **scaled down proportionally** so that the fee can still be deducted. For example, allocating all 1000.00 of a payment where you bear a 15.00 fee leaves only 985.00 to divide, so every leg is reduced by the same proportion. To keep the exact figures you configured, leave enough remainder for the fee, or choose the customer-bears option.

## When a destination cannot be credited

Splits are resolved at initialisation, but accounts are checked at the moment the payment succeeds.

* **An account that has been deleted since the payment was initialised** is still credited, because the payment was created with it.
* **An account that is inactive, or cannot be matched**, cannot be credited. Its share is reported as a fallback and is kept in your primary account instead.

You are never short-changed: money is not held back or lost. But you should fix the cause quickly, because the primary account is not where you meant that money to go. Review the account on the **Accounts** page. See [Sub-accounts](https://docs.vestrapay.com/services/subaccounts.md).

## Reading the result on verify

Once the payment has succeeded and been allocated, verify returns a `split` object that records the outcome:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "status": "success",
  "data": {
    "reference": "VPY_TXN_ACME001",
    "amount": "1000.00",
    "status": "success",
    "split": {
      "primaryAccountAmount": "250.00",
      "currency": "NGN",
      "entries": [
        { "status": "RECONCILED", "ledgerID": "led_8c1a", "amount": "510.00" },
        { "status": "RECONCILED", "ledgerID": "led_2b7d", "amount": "90.00" },
        { "status": "RECONCILED", "ledgerID": "led_5e9f", "amount": "150.00" }
      ]
    }
  }
}
```

The example uses the initialise request above, where the customer bears the fee, so the primary amount is the full remainder of 250.00. If you had borne a 15.00 fee, it would be 235.00.

Each entry has a `status`. `RECONCILED` means the destination was credited. `FALLBACK` means it could not be. That entry carries an `amount` of 0.00 and a `message` explaining why, and the funds are counted in `primaryAccountAmount`.

The split is added shortly after the payment succeeds, so a verify call made in the first instant may not include it yet. The payment is already `success`, so it is safe to fulfil. See [Verify a payment](https://docs.vestrapay.com/payments/verify.md).

## Payment links

A payment link can carry a distribution, so that every payment made through it is divided in the same way. Add `distribution` when you create the link, or send a new map when you update it. Omitting it on update keeps the existing one.

Two details differ from a normal payment:

* The services are checked when you create or update the link, so a link cannot be published against a service that is missing, disabled or without splits. They are checked again when someone pays, so a service disabled later makes payments on the link fail with `INVALID_SPLIT_AMOUNT`.
* For a link with a variable amount, the amount the customer enters must be at least the total of the distribution.

See [Payment links](https://docs.vestrapay.com/payments/payment-links.md).

## Design guidance

* **Treat the service key as a stable identifier.** It is what your integration sends, so agree it with finance before you launch.
* **Compute the map on the server.** Never take allocation amounts from the browser, because they decide where money goes.
* **Prefer exact amounts you can reconcile.** Keep your own record of what you allocated, and compare it with the `split` on verify.
* **Plan for fallback.** Alert on `FALLBACK` entries, and review your sub-accounts on the **Accounts** page before they appear in live splits.
* **Pause with care.** Disabling a service in the dashboard makes payments that name it fail, so remove the key from your map first.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.