Products
Accounts
Accounts hold, receive and send money. This page covers every account type, how to open each one, and how to use it.
Account types
| Type | accountType |
What it is | Use it to |
|---|---|---|---|
| Wallet | wallet |
A balance in one currency on the Multigate ledger, for you or a customer. | Hold funds, pay out, convert. |
| Virtual account | viban |
A dedicated bank account number in the account holder's name, issued by a partner bank and attached to a wallet. Money paid to it lands in the wallet. | Give payers an account number to send money to. |
| Dynamic virtual account | wallet with isDynamic: true |
An NGN virtual account that accepts one payment of an exact amount, then closes. | Collect a specific invoice or order. |
| Linked account | linked |
A bank account your business already holds at another bank, connected to Multigate. The money stays at your bank. | Pay out from your existing bank accounts and see their balances and statements through the API. |
| Receivable account | receivable |
An account opened for one invoice. | Match payments to invoices automatically (see Receivables). |
flowchart LR
subgraph Multigate ledger
wallet[(Wallet NGN)]
end
va1[Virtual account<br/>Partner bank A] --> wallet
va2[Virtual account<br/>Partner bank B] --> wallet
payer([Payer]) -->|bank transfer| va1
wallet -->|payouts, conversions| out([Beneficiaries])
subgraph Your bank
linked[(Linked account)]
end
linked -->|payouts via your bank| out
IDs and numbers you'll use
An account has an ID and up to three kinds of number. Mixing them up is the most common integration mistake.
| Name | Example | Where you get it | Use it for |
|---|---|---|---|
Account ID (accountId) |
4b8e1f20-… |
account. from Create an account or List accounts. Get an account by ID calls it entityId. |
Get an account by ID, conversions (source, target), Get a dynamic account's details. |
| Wallet number | 1029384756 |
account. from Get an account by ID, or account from Get an account balance. |
Payouts (source), balances, statements. It's a Multigate ledger number, not a bank account: payers can't send money to it. |
| Virtual account number | 9912345678 |
funding |
Give it to payers. It arrives as receiving in the inflow webhook. Balances and statements also accept it and return the wallet's. |
| Linked account number | 1011223344 |
Your own bank account number. | Payouts, balances and statements for a linked account. |
Account holder ID (clientId, and entityId in account lists) |
Kx81m |
The same account responses. | Tells you whose account it is: your client ID, or the customer's entityId. It isn't the account's ID. |
In Create an account, List accounts and Get an account, entityId is the account holder's ID. In Get an account by ID, entityId is the account's ID (the same value as accountId). Store accountId and the holder's ID in separate fields.
Wallets and virtual accounts
Sandbox virtual accounts come from bank test systems; real transfers to them never arrive, and some show test bank codes such as test-bank. Fund them with Simulate an inflow, and always read bank names and codes from the API. See Sandbox vs production.
Before you open one
- You've completed the Quickstart environment setup (
MG_BASE_URL,MG_API_KEY). - Your business is active. To open an account for a customer, the customer is
activeand you have itsentityId(see Customers). - Multigate has set up at least one partner bank (a bank that issues virtual accounts for Multigate) for you in the currency. Otherwise the request fails with
404"Partner/Client mapping not found". - You have accepted that partner bank's terms of service in the dashboard. Otherwise:
400"One or more required terms of service have not been accepted for …". - Your API user has the manage accounts permission.
You don't send KYC details: Multigate uses the account holder's profile on file (registered business name, contact email and phone).
Open an account
| Field | Required | What to send |
|---|---|---|
unique |
Yes | A new ID of your own for this request (an idempotency key: it stops a retry from opening a second account). See Retries and duplicates. |
currency |
Yes | Upper-case ISO 4217 code, for example NGN. |
accountName |
Yes | Your name for the wallet, up to 100 characters: letters, digits, spaces and & ' ( ) * + , - . / :. |
onBehalfOf |
For a customer | The customer's entityId. Leave it out for your own business. |
country |
XOF only | The account's country, for example SEN. |
partnerCodes |
No | Only open virtual accounts at these partner banks, for example ["214", "057"]. See Choosing the partner banks. |
amount |
Dynamic accounts only | See Dynamic accounts. |
curl -X POST "$MG_BASE_URL/client-service/api/v1/accounts" \
-H "mp-api-key: $MG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"uniqueRequestId": "acct_20261010_0001",
"currencyIso3Code": "NGN",
"accountName": "Kola Stores Collections",
"onBehalfOf": "Kx81mQ2pZr4tV9wYa3Bc5"
}'
{
"message": "account created",
"account": {
"accountId": "Ac4mW8pZ2kR6tY1nQ7xVb",
"clientId": "Kx81mQ2pZr4tV9wYa3Bc5",
"entityId": "Kx81mQ2pZr4tV9wYa3Bc5",
"uniqueRequestId": "acct_20261010_0001",
"currencyIso3Code": "NGN",
"accountName": "Kola Stores Collections",
"status": "active",
"accountType": "wallet",
"isDynamic": false,
"fundingDetails": [
{ "accountName": "Kola Stores Ltd", "accountNumber": "9912345678", "bankName": "Partner Bank", "bankOrSortCode": "000017", "bankCountry": "NGA", "countryIso3Code": "NGA" }
]
}
}
Store account., your unique, the account holder (onBehalfOf, or your own business) and every funding. You need the virtual account numbers to tell which customer an inflow belongs to (see Collections).
Is it ready? If status is active and funding lists at least one account number, payers can pay into it now. If status is processing or pending, see When the account isn't active yet. If the call returned 400 account failed, no bank opened it.
- One wallet, possibly several virtual accounts. Multigate requests a virtual account at each partner bank configured for you in that currency. Each active one appears in
funding; give payers any of them.Details - Two names. The wallet's
accountNameis the name you sent. Virtual accounts are opened at the bank in the account holder's registered business name, which is what payers see (funding).Details[]. account Name - The wallet's own number isn't in this response. Get it from Get an account by ID (
account.) when you need it for payouts.account Number - XOF: send
countrywith the account's UEMOA (West African Economic and Monetary Union) country, for exampleIso3 Code SEN; the default isCIV. - ETB: payers pay into a shared account and must quote the
fundingshown inReference funding.Details - Wallets without virtual accounts. Some foreign-currency wallets are opened without any virtual account:
fundingstays empty, and you fund them by conversion. The exception is a USD wallet with a USD bank account.Details
What to give payers
From one entry of funding (for a dynamic account, from data. in Get a dynamic account's details):
| Tell the payer | Field |
|---|---|
| Account number | accountNumber |
| Account name | accountName (the registered business name, not your wallet name) |
| Bank | bankName (and bank if their bank asks for a code) |
| Reference (ETB only) | funding: the payer must quote it, or the money can't be matched |
| Exact amount (dynamic accounts only) | The amount you opened it with. Any other amount isn't credited. |
Always read these values from the API; don't hard-code bank names or codes.
Retries and duplicates
unique must be new for each account holder (your business, or each customer).
- Reusing it is refused with
400"uniqueRequestId has already been used", even if the earlier attempt failed. The earlier account isn't returned. - If a request times out, don't resend it straight away: call List accounts (with
onBehalfOffor a customer) and look for yourunique. Only if it isn't there, wait a minute, look again, then resend with the sameRequest Id unique: if the first attempt landed meanwhile you getRequest Id 400"uniqueRequestId has already been used" and no second account. - Never send two requests with the same
uniqueat the same time: both can open an account.Request Id - Some partner banks allow only one account per account holder in a currency. A second local-currency account then skips that bank; a second foreign-currency account is refused with
409"account already exist" when every such bank already has one. - Foreign currencies: if the account holder already has a wallet in that currency and Multigate has set up a bank that issues virtual accounts in it, the new virtual accounts are added to that wallet, and the response is that wallet with its original
unique. After a timeout, look for that wallet in List accounts, not for your newRequest Id unique.Request Id
When the account isn't active yet
Local-currency virtual accounts (NGN, GHS, ETB, XOF) are requested during the call. Some partner banks confirm later, and some foreign-currency banks first review your business profile. The account then comes back processing (or pending) with an empty funding. There's no fixed waiting time: it depends on the partner bank. Don't give payers anything until the account is active.
stateDiagram-v2
direction LR
[*] --> processing: requested
[*] --> active: a bank opened it straight away
[*] --> failed: every bank refused (400 account failed)
processing --> active: a partner bank confirms
processing --> failed: every partner bank refused
active --> disabled: dynamic account paid, or business suspended
| Status | Meaning | What to do |
|---|---|---|
processing, pending |
Waiting for a partner bank. | Wait for account_. |
profiling |
Waiting for the bank to approve your business profile (virtual accounts only). | Wait for account_; it's often not sent on this path, so also poll Get an account by ID. |
active |
At least one virtual account is open (or the wallet needs none). | Use it. |
failed |
Every bank refused. | Open a new account with a new unique, or contact support. |
inactive, disabled |
Closed or blocked. | It can't receive or send money. |
enabled |
Re-enabled after being disabled. Today it can't send money until Multigate sets it back to active. |
Contact support. |
In Create an account, List accounts and Get an account, an account in any status other than active comes back without the holder's entityId and with an empty funding. Use accountId and status to recognise it.
Wait for the account / account_ webhook, whose funding lists the new account numbers. You can receive it more than once for one account, as each partner bank activates.
If every bank refuses during the call, you get 400 account failed straight away. If a bank refuses later, no webhook is sent: the account turns failed silently, or, for some foreign-currency banks, stays processing. Poll Get an account by ID for accounts that stay processing (for example hourly), and alert after a day.
Choosing the partner banks
By default, Multigate opens a virtual account at every partner bank set up for you in that currency, so your payers can choose between several account numbers. To control which banks your customers' account numbers come from, list them in partnerCodes, using the partner bank codes Multigate gives you when it sets you up:
{ "uniqueRequestId": "acct_kola_ngn_214", "currencyIso3Code": "NGN", "accountName": "Kola Stores", "onBehalfOf": "Kx81mQ2pZr4tV9wYa3Bc5", "partnerCodes": ["214"] }
Multigate then requests virtual accounts from those banks only. The wallet starts processing as usual.
| Response | Meaning |
|---|---|
404 "Partner not found" |
One of the codes isn't a partner bank code Multigate knows. |
404 "Partner(s) not allowed 057" |
A code is a real partner bank, but it isn't set up for this account holder in this currency. The message lists the codes that aren't allowed. Remove them, or ask Multigate to add that bank to your setup. |
Dynamic accounts
A dynamic account is an NGN virtual account for one payment, for example one order. Payments into it are swept (moved automatically) to your pool account: one wallet Multigate sets up to collect money paid into many dynamic accounts.
Use a dynamic account when each payment must match what you're owed. A reusable account accepts any amount, so a payer can send too little or too much and you have to chase the difference or refund it. A dynamic account only accepts the exact amount, and only once: the payer must pay the full amount in one transfer. A part payment isn't credited, and the payer can't top it up later.
Add amount (NGN only, in naira, at most 2 decimals) to open one:
{ "uniqueRequestId": "order_5521", "currencyIso3Code": "NGN", "accountName": "Order 5521", "amount": 125000 }
- Call Get a dynamic account's details with the returned
accountId, and give your payerfunding: that is the single-payment account.Details. account Number - When exactly that amount arrives and clears screening, the money is credited to your pool account (
sweepsTo), and you receive theaccount/inflowwebhook. Itsreceivingis the dynamic account; itsAccount Number accountIdis the pool account's. - The dynamic account then closes:
statusbecomesdisabled, andreceived,Amount referenceandtransactionare filled in. No separate webhook or email announces the closure; read the details endpoint.Date - Give your payer a receipt with Download a dynamic account receipt.
Dynamic accounts need a pool account and a partner bank that supports them, both set up by Multigate. Otherwise the request fails with 400 and a message saying which is missing.
A payment of any other amount isn't credited and sends no webhook. Depending on the bank, it is refused before it reaches Multigate, or it is rejected and your business contact receives a "Rejected Inflow Transaction" email (whose text says the account is disabled). Ask the payer to send the exact amount; contact support to recover a wrong payment.
expiryDate is passed to the bank, but most banks ignore it: don't rely on it to close the account.
USD bank account (Veem)
If Multigate has set your business up for it, your USD wallet can get its own USD bank account: an account in your business's name, provided by Multigate's partner Veem, that payers anywhere can send USD to by international (SWIFT) transfer. Without one, a USD wallet can only be funded by conversion. Money paid into it lands in your USD wallet, ready to pay out or convert.
sequenceDiagram
autonumber
participant You
participant MG as Multigate
participant Veem
participant Payer
You->>MG: POST /accounts (USD)
MG->>Veem: Application for a USD bank account
MG-->>You: USD wallet (processing, or your existing wallet)
Note over Veem: Veem reviews the application
Veem-)MG: Approved
MG-)You: account / account_opened, with fundingDetails
Payer->>Veem: USD transfer to your account details
Veem-)MG: Payment received
Note over MG: Screened like any inflow
MG-)You: account / inflow, USD wallet credited
Who can have one: your own business only, not your customers, and only in USD. Multigate turns it on for you; before it can, your business must be approved by Veem (Multigate shares your onboarding details with them) and someone in your business must have accepted Veem's terms of service in the dashboard.
Open it with Create an account in USD, without onBehalfOf:
{ "uniqueRequestId": "acct_usd_vba_0001", "currencyIso3Code": "USD", "accountName": "Kola Stores USD" }
Multigate sends Veem an application using the business details from your onboarding (registered name and number, incorporation date, the countries you operate in and pay to, and your website). You don't send them again. Then:
| Situation | What the response shows | What happens next |
|---|---|---|
| You have no USD wallet yet | A new USD wallet, status: processing, fundingDetails: []. |
The wallet becomes active when Veem approves. |
| You already have a USD wallet | Your existing wallet, unchanged (its accountId), still active. |
The bank account is added to it when Veem approves. Your balance isn't affected. |
| The application can't be sent (for example, Veem hasn't approved your business yet) | A new wallet comes back with status: failed; an existing wallet comes back unchanged. |
Ask Multigate to complete your Veem setup, then send a new request with a new unique: Multigate retries the same application. |
| A USD bank account is already open or under review | 409 "account already exist". |
Nothing to do: use the one you have. |
If Multigate sets this up after you already hold a USD wallet, it applies for you automatically: you don't need to call anything.
When Veem approves (this can take days; Veem may ask Multigate for more information), you receive account / account_ for the USD wallet, once, with the new entry in funding. It also appears in List accounts and Create an account responses from then on.
What to give payers from that funding entry:
| Payer's form asks for | Use |
|---|---|
| Account name | accountName (your registered business name) |
| Account number or IBAN | accountNumber (an IBAN) |
| SWIFT / BIC code | bic |
| Bank name and address | bankName, bankAddress |
| Bank country | bankCountry |
Ignore bank on this entry: it holds the internal value veem, not a bank code. Only these international (SWIFT) details are shown today; US domestic details (ACH or wire routing numbers) aren't available through the API.
Payments in are handled like any other inflow: screened, credited to your USD wallet, then announced by account / inflow. Only completed payments are credited.
While Veem reviews the application, nothing in the API shows its progress: a USD wallet's details don't list pending bank accounts. If Veem refuses it, no webhook or email is sent and the wallet keeps its previous status (a new wallet stays processing). If you're waiting for the account and account_ hasn't arrived, ask Multigate for the application's status.
Find your accounts
| You have | Use |
|---|---|
| Nothing; want them all | List accounts (filter by currencies, statuses, accountTypes). Without onBehalfOf it lists your business's accounts only; pass a customer's ID for theirs. |
| A wallet, virtual or linked account number | Get an account, which includes the balance |
An accountId |
Get an account by ID, which shows the wallet's own number and, for local currencies, every virtual account in any status |
Linked accounts
A linked account connects a bank account your business already holds, at your local or international bank, to Multigate. The money stays at your bank. This is the API side of Multigate's Multi-Bank Connector: see balances, pull statements and make centralised payments from any of your banks through one integration.
How linking works
sequenceDiagram
autonumber
participant Maker as Your API user
participant MG as Multigate
participant Checker as Your approver
participant Bank as Your bank
Maker->>MG: GET banks you can link
Maker->>MG: GET the bank's documents, download templates
Maker->>MG: POST upload signed documents
Maker->>MG: POST submit linking request
MG-->>Checker: Email: Bank Account Linking Request
Checker->>MG: PATCH approve
MG->>Bank: Checks the account (local currencies)
MG-->>Bank: Email: request approved, documents attached
Note over MG: Linked account created, status active
Maker->>MG: Upload bank API credentials (local accounts)
stateDiagram-v2
direction LR
[*] --> PENDING: submitted
PENDING --> APPROVED: another user approves (linked account created)
PENDING --> DECLINED: another user declines
PENDING --> PENDING: approval failed (fix and approve again)
List banks you can link, optionally filtered by country or bankName. Note the bank's bankId.
Local accounts need the documents the bank asks for, such as a signed mandate. List a bank's linking documents, download each blank template with Download a linking document, complete and sign it, then upload it with Upload a linking document (PDF, DOC, DOCX, XLS or XLSX, up to 2 MB). Keep each returned blobName with its templateId.
SWIFT accounts need no documents (next step).
curl -X POST "$MG_BASE_URL/client-service/api/v1/account-linking/submit" \
-H "mp-api-key: $MG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"clientUUId": "Ab3kP9xQ2mN7vR1sT4uV6",
"bankId": "Hq4nT8vW2xZ6cB1m",
"countryCode": "NGA",
"accountConfigurationTypes": ["Local/Domestic"],
"bankAccountNumber": "1011223344",
"accountName": "Acme Payments Ltd",
"accountCurrency": "NGN",
"accountOfficerEmail": "officer@bank.example",
"documents": [ { "templateId": "Tm5pQ8rS2uV4wX7y", "blobName": "Lp2q8RnW4xY7zA-account-mandate-signed.pdf" } ]
}'
clientUUIdis your client ID (MG_CLIENT_ID), or a customer'sentityIdto link the customer's account.bankIdcomes from step 1; eachtemplateIdandblobNamefrom step 2.countryCode(ISO alpha-3) andaccountare only checked at approval, so send valid codes.Currency - For a SWIFT account, send
"accountConfigurationTypes": ["SWIFT"]and no documents. Multigate must have registered your BIC (your bank's 8- or 11-character SWIFT code), and the RMA (Relationship Management Application: the SWIFT setting that lets two banks exchange messages) between your BIC and the bank must be active before the request can be approved or declined.
The response's account identifies the request; its Approval is PENDING. Several accounts at one bank? Use Submit several linking requests.
A different user in your business, with the approve account linking permission, approves the request with Approve or decline a linking request or in the dashboard.
Approval creates the linked account straight away (accountType linked, status active, accountNumber = your bank account number) and emails your bank officer and the bank's linking contacts. For local currencies Multigate first checks the account with your bank. If anything fails, the request stays PENDING; fix the cause and approve again. For some banks, approval marks the request APPROVED without creating the account: if no linked account appears in List accounts, contact Multigate.
Track requests with Search linking requests (filter approval = PENDING, APPROVED or DECLINED), and find the new account with List accounts (account).
To pay out from a local linked account and see its live balance and statements, Multigate connects to your bank directly with API credentials your bank issues to you. This is available for your own business's accounts (pass your own client ID):
- List bank credential slots and find your bank (
isProfiledshows whether credentials are in). - Download a bank credentials template, fill one row per account with the credentials from your bank.
- Upload bank credentials. Check
failedUploads; account numbers must be 10 digits. Uploading a row again replaces its credentials.
People in your business who manage credentials receive an "Account Activation Confirmation" email.
Using a linked account
| You want to | How | Notes |
|---|---|---|
| Pay out | Create a transfer or bulk, with the linked account as source |
Local accounts pay through your bank's API; SWIFT accounts send a SWIFT payment from your BIC. Approval rules apply. |
| See the balance | Get an account balance | Fetched live from your bank. Returns "0" if the bank can't be reached. |
| See transactions | Get account transactions or Download a statement | Live from your bank (SWIFT: from MT940 statements, the standard SWIFT statement message). Get an account statement doesn't support SWIFT accounts. |
| Receive money | — | Payers pay your bank account directly; Multigate doesn't process inflows to linked accounts. |
Multigate checks your bank balance before sending. If the balance is too low, or no credentials are uploaded, the transfer stays processing and is retried about every 5 minutes. No webhook tells you why. Keep the bank account funded and credentials uploaded, and alert on linked-account transfers that stay processing.
Linking rules and errors
| Message | Cause | What to do |
|---|---|---|
| The associated entity does not belong to this organisation | clientUUId isn't your client ID or an active customer's. |
Use the right ID. |
| Bank account to link already exist | This account was already requested at this bank, whatever happened to that request. | Search your requests. A declined account can't be requested again: contact Multigate. |
| The required document is missing. / Template not found. | A required template has no document, or a templateId isn't this bank's. |
Upload and send every required document. |
| Too many documents uploaded… | More documents than the bank has templates. | Send one document per template. |
| Client not mapped to bic | SWIFT linking before Multigate registered your BIC. | Contact Multigate. |
| You cannot approve a request that you have raised yourself. | The approver is the submitter. | Approve with another user. |
| RMA Setup not completed. | SWIFT relationship with the bank isn't active yet. | Wait for the RMA, then approve. |
| account verification with third party failed | Your bank didn't confirm the account at approval. | Check the account number, then approve again. |
| Account linking already processed | The request was already approved or declined. | None. |
Linking sends no webhooks, and no account_ event for the new linked account. Approvers are notified by email.
Next
What happens when money arrives in a virtual account.
Reconcile every account.