Skip to content
MultigateAPI
Support Staff

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.accountId from Create an account or List accounts. Get an account by ID calls it entityId. Get an account by ID, conversions (sourceAccountId, targetAccountId), Get a dynamic account's details.
Wallet number 1029384756 account.accountNumber from Get an account by ID, or accountBalance.accountNumber from Get an account balance. Payouts (sourceAccountNumber), balances, statements. It's a Multigate ledger number, not a bank account: payers can't send money to it.
Virtual account number 9912345678 fundingDetails[].accountNumber Give it to payers. It arrives as receivingAccountNumber 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) Kx81mQ2pZr4tV9wYa3Bc5 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.
`entityId` means two things

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 account numbers are test numbers

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 active and you have its entityId (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
uniqueRequestId 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.
currencyIso3Code 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.
countryIso3Code 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.accountId, your uniqueRequestId, the account holder (onBehalfOf, or your own business) and every fundingDetails[].accountNumber. You need the virtual account numbers to tell which customer an inflow belongs to (see Collections).

Is it ready? If status is active and fundingDetails 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 fundingDetails; give payers any of them.
  • Two names. The wallet's accountName is the name you sent. Virtual accounts are opened at the bank in the account holder's registered business name, which is what payers see (fundingDetails[].accountName).
  • The wallet's own number isn't in this response. Get it from Get an account by ID (account.accountNumber) when you need it for payouts.
  • XOF: send countryIso3Code with the account's UEMOA (West African Economic and Monetary Union) country, for example SEN; the default is CIV.
  • ETB: payers pay into a shared account and must quote the fundingReference shown in fundingDetails.
  • Wallets without virtual accounts. Some foreign-currency wallets are opened without any virtual account: fundingDetails stays empty, and you fund them by conversion. The exception is a USD wallet with a USD bank account.

What to give payers

From one entry of fundingDetails (for a dynamic account, from data.fundingDetails 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 bankOrSortCode if their bank asks for a code)
Reference (ETB only) fundingReference: 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

uniqueRequestId 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 onBehalfOf for a customer) and look for your uniqueRequestId. Only if it isn't there, wait a minute, look again, then resend with the same uniqueRequestId: if the first attempt landed meanwhile you get 400 "uniqueRequestId has already been used" and no second account.
  • Never send two requests with the same uniqueRequestId at the same time: both can open an account.
  • 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 uniqueRequestId. After a timeout, look for that wallet in List accounts, not for your new uniqueRequestId.

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 fundingDetails. 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_opened.
profiling Waiting for the bank to approve your business profile (virtual accounts only). Wait for account_opened; 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 uniqueRequestId, 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 fundingDetails. Use accountId and status to recognise it.

Wait for the account / account_opened webhook, whose fundingDetails lists the new account numbers. You can receive it more than once for one account, as each partner bank activates.

No webhook when an account fails later

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 }
  1. Call Get a dynamic account's details with the returned accountId, and give your payer fundingDetails.accountNumber: that is the single-payment account.
  2. When exactly that amount arrives and clears screening, the money is credited to your pool account (sweepsTo), and you receive the account / inflow webhook. Its receivingAccountNumber is the dynamic account; its accountId is the pool account's.
  3. The dynamic account then closes: status becomes disabled, and receivedAmount, reference and transactionDate are filled in. No separate webhook or email announces the closure; read the details endpoint.
  4. 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 uniqueRequestId: 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_opened for the USD wallet, once, with the new entry in fundingDetails. It also appears in List accounts and Create an account responses from then on.

What to give payers from that fundingDetails 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 bankOrSortCode 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.

No signal while it's under review, or if it's refused

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_opened 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)
Pick the bank

List banks you can link, optionally filtered by countryIso3Code or bankName. Note the bank's bankId.

Prepare the bank's documents

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).

Submit the request
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" } ]
  }'
  • clientUUId is your client ID (MG_CLIENT_ID), or a customer's entityId to link the customer's account.
  • bankId comes from step 1; each templateId and blobName from step 2.
  • countryCode (ISO alpha-3) and accountCurrency are only checked at approval, so send valid codes.
  • 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 accountLinking.entityId identifies the request; its ApprovalStatus is PENDING. Several accounts at one bank? Use Submit several linking requests.

Approve it

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 approvalStatus = PENDING, APPROVED or DECLINED), and find the new account with List accounts (accountTypes=linked).

Upload bank credentials (local accounts)

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):

  1. List bank credential slots and find your bank (isProfiled shows whether credentials are in).
  2. Download a bank credentials template, fill one row per account with the credentials from your bank.
  3. 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 sourceAccountNumber 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.
Payouts from local linked accounts can wait silently

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_opened event for the new linked account. Approvers are notified by email.

Next

What happens when money arrives in a virtual account.

Reconcile every account.