# Treasury with linked accounts

See balances and statements across the bank accounts you already hold, and make centralised payments from any of them, through one API. This is the API side of Multigate's Automated Treasury Management and Multi-Bank Connector.

**Who it's for:** corporate enterprises (CFOs, treasurers and finance teams), and banks or financial institutions.

A **linked account** is a bank account your business already holds at another bank, connected to Multigate. The money stays at your bank: Multigate reads its balance and statements and sends payments from it through your bank.

## Architecture

```mermaid
flowchart LR
    api[Your treasury system] --> mg[Multigate API]
    mg -->|bank API credentials| z[(Linked local account<br/>Bank A, NGN)]
    mg -->|bank API credentials| s[(Linked local account<br/>Bank B, NGN)]
    mg -->|SWIFT from your BIC| c[(Linked SWIFT account<br/>Bank C, USD)]
    z --> out([Beneficiaries])
    s --> out
    c --> out
    c -.->|MT940 statements| mg
```

- **Local linked accounts** (for example NGN) need API credentials that your bank issues to you. Multigate uses them to pay out, read the live balance and pull transactions.
- **SWIFT linked accounts** send payments as SWIFT messages from your bank's BIC (its international bank code). Statements arrive as MT940 files (the standard SWIFT statement format).

## Before you start

- [ ] A sandbox API key, and `MG_BASE_URL`, `MG_API_KEY` and `MG_CLIENT_ID` set as in the [Quickstart](../01-get-started/03-quickstart.md).
- [ ] **Two people.** Linking is maker-checker: one user submits (*initiate account linking* permission) and a **different** user approves (*approve account linking*). Your approver can use the dashboard.
- [ ] Your API user also needs *manage account credentials* (to upload bank credentials), *view accounts* and *initiate payment*.
- [ ] For each local account: the bank's signed linking documents (such as an account mandate) and the API credentials your bank issued you.
- [ ] For SWIFT accounts: Multigate has registered your BIC, and the SWIFT relationship (RMA) between your BIC and the bank is active.

## Build plan

<Steps>
<Step title="Submit a linking request per account">

Follow [Accounts › Linked accounts](../04-products/02-accounts.md#linked-accounts):

1. [List banks you can link](api:listLinkingBanks) and note the bank's `bankId`.
2. Local accounts: [List a bank's linking documents](api:listBankTemplates), download each template with [Download a linking document](api:downloadLinkingDocument), sign it, and upload it with [Upload a linking document](api:uploadLinkingDocument). Keep each `blobName` with its `templateId`.
3. [Submit a linking request](api:submitAccountLinking) with `clientUUId` = your client ID. Store `accountLinking.entityId`: it identifies the request.

</Step>
<Step title="Approve it with a second user">

A different user approves the request in the dashboard or with [Approve or decline a linking request](api:updateAccountLinkingStatus):

```bash
curl -X PATCH "$MG_BASE_URL/client-service/api/v1/account-linking/Lk3mP8qR2sT6vW9x/status" \
  -H "mp-api-key: $APPROVER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status": "APPROVED" }'
```

`APPROVER_API_KEY` is an API key that belongs to the approving API user, not to your submitting API user ("You cannot approve a request that you have raised yourself." otherwise). Choose that user when you generate the key. Your business can have only 2 active keys in all, so approving in the dashboard is often simpler.

Linking sends **no webhooks**. Track requests with [Search linking requests](api:searchAccountLinking) (`approvalStatus` = `PENDING`, `APPROVED` or `DECLINED`), then find the new account with [List accounts](api:listAccounts) `?accountTypes=linked`. Its `accountNumber` is your bank account number. If a request is `APPROVED` but no linked account appears, contact Multigate.

</Step>
<Step title="Upload bank credentials (local accounts)">

1. [List bank credential slots](api:listBankCredentials) with your client ID, and find your bank (`isProfiled` shows whether credentials are in).
2. [Download a bank credentials template](api:downloadBankCredentialsTemplate) and fill one row per account (10-digit account numbers).
3. [Upload bank credentials](api:uploadBankCredentials) and check `failedUploads`.

This works for your own business's accounts only. Without credentials, a local linked account's payouts wait and its balance shows `0`.

</Step>
<Step title="See your positions">

- Total per currency, with each account's balance: [Get balances across accounts](api:getConsolidatedBalances) with your client ID. `totalBalance` is a JSON number: round it to the currency's decimals.
- One account's live balance: [Get an account balance](api:getAccountBalance).

Linked balances are fetched live from your bank. `"0"` can mean the bank couldn't be reached: retry before you act on it. SWIFT linked accounts always count as `0` in the consolidated view.

</Step>
<Step title="Pay out from a linked account">

Call [Create a transfer](api:createSingleTransfer) (or [Bulk payouts](../04-products/06-bulk-payouts.md)) with the linked account number as `sourceAccountNumber`, a new `uniqueRequestId`, and the beneficiary. Your approval rules apply (`pending_approval` while an approver is needed). Linked-account payouts aren't screened, so otherwise they start at `processing`.

- Local: paid through your bank's API.
- SWIFT: sent as a SWIFT payment from your BIC; set `chargeType` (see [Payouts › Which rail is used](../04-products/05-payouts.md#which-rail-is-used)).

A local payout waits in `processing`, retried about every 5 minutes, while your bank balance is too low or credentials are missing. No webhook tells you why: keep the account funded and alert on payouts that stay `processing`.

</Step>
<Step title="Pull statements">

Use [Get account transactions](api:getTransactionHistory) (dates in `from` and `to`) or [Download a statement](api:downloadAccountStatement) (dates in `startDate` and `endDate`), set to yesterday. Without dates, local linked accounts return **today only**. See [Balances and statements › Linked accounts](../04-products/10-balances-and-statements.md#linked-accounts).

</Step>
</Steps>

## Data to store

| Field | From | Used for |
|---|---|---|
| `bankId`, `templateId` and `blobName` | linking bank and document endpoints | Submitting the request |
| Linking request `entityId` and `ApprovalStatus` | [Submit a linking request](api:submitAccountLinking) | Approval and tracking |
| Linked `accountId` and `accountNumber` | [List accounts](api:listAccounts) `?accountTypes=linked` | Balances, payouts, statements |
| Credential slot `entityId` (`credentialId`) | [List bank credential slots](api:listBankCredentials) | Uploading credentials |
| Payout `uniqueRequestId` (stored before the call) and `transactionId` | your own, then the transfer response | Matching webhooks and statements |

## Webhooks to handle

| Event | What to do |
|---|---|
| [`transfer` / `transfer_completed`](webhook:transfer.transfer_completed) | Mark the payout paid. |
| [`transfer` / `transfer_failed`](webhook:transfer.transfer_failed) | Mark it failed; show `responseMessage`. |

Linking requests, approvals, new linked accounts and inflows into linked accounts send **no** webhooks. Poll as described above. Approvers receive a "Bank Account Linking Request" email when a request is submitted.

## Reconciliation

- **Local linked accounts:** pull yesterday's [Get account transactions](api:getTransactionHistory) (the whole day comes back in one list, without paging) and match payouts by amount and reference. [Get an account statement](api:getAccountStatement) also works, but its opening and closing balances are `0`: check closing positions against your bank statement.
- **SWIFT linked accounts:** use [Download a statement](api:downloadAccountStatement) or [Get account transactions](api:getTransactionHistory) (one row per MT940 statement received). [Get an account statement](api:getAccountStatement) doesn't support them.
- Payouts that stay `processing` are the usual unmatched items: check the bank balance and credentials.

## Things that trip people up

- A declined account can't be requested again at the same bank: contact Multigate.
- Money paid into a linked account stays at your bank; Multigate processes no inflows for it.
- Bank credentials can't be uploaded for customers' linked accounts today.
- `Get account transactions` answers `200` with an empty list when transactions can't be loaded: retry before concluding there were none.

## Go-live notes

- Re-link and re-upload credentials in production: sandbox links and credentials don't carry over.
- For SWIFT, confirm with Multigate that your BIC is registered and each RMA is active before submitting.
- Set alerts on linked-account payouts that stay `processing`, and on balances of `0`.
- Work through the [go-live checklist](../01-get-started/07-go-live-checklist.md).
