Products
Customers
Onboard the businesses you serve, so you can open accounts, convert and pay out for them. The API calls a customer a sub-client. Multigate checks each customer (KYB, know your business: verifying the company, its owners and its documents) before it can transact.
When to use it
Use customers when you hold money or make payments for other businesses: a fintech's or marketplace's merchants, a remittance company's business clients, a bank's corporate clients, or a corporate's subsidiaries. If you only move your own money, you don't need customers.
There are two types:
subClientType |
For | Review |
|---|---|---|
client |
An external business you serve. | Approved in your business, then Multigate screens the business name (checks it automatically against sanctions and watch lists). A possible match goes to Multigate's compliance team. |
subsidiary |
A company in your own group. | Approved in your business only. Multigate doesn't screen it. |
Before you start
- You've completed the Quickstart environment setup:
MG_BASE_URL,MG_API_KEYandMG_CLIENT_ID(your client ID). - Your own business must be
activeand approved on Multigate. - Your API user needs the manage customers permission. To skip the second approver, it also needs the approve customers permission (see the submit step below).
- Your webhook URL is registered, so you receive the review outcome (
client/subclient_).approved - Have the customer's registration details, its directors and beneficial owners (people who own or control it, usually 25% or more), and its KYB documents as PDF or image files.
IDs you'll use
| ID | Where you get it | Where you send it |
|---|---|---|
Your client ID (MG_CLIENT_ID) |
current in Get the current user |
The {clientId} part of every customer URL. |
| Customer ID | entityId returned by Create a customer |
The {id} part of customer URLs; onBehalfOf for accounts, payouts and conversions; subClientId when you save a beneficiary; client to list its beneficiaries. |
| Person ID | entityId returned by Add a director or owner |
business when you upload or list that person's documents; {share in Update a director or owner. |
Every example below uses customer ID Kx81m and person ID Hq3v: replace them with yours.
How onboarding works
sequenceDiagram
autonumber
participant You as Your backend
participant MG as Multigate
participant AP as Your approver
participant CO as Multigate compliance
You->>MG: POST create customer
MG-->>You: entityId, status draft
You->>MG: POST add each director and owner
You->>MG: POST upload each document
You->>MG: POST submit for review
MG-->>You: approval request, customer now processing
alt Your API user can't approve customers
MG->>AP: Email "Sub-client Creation Request for Approval"
AP->>MG: Approve in the dashboard
end
MG->>MG: Screen the business name
alt Nothing found
MG-)You: Webhook client / subclient_approved, status active
else Possible match
MG->>CO: Review
CO->>MG: Approve or decline
MG-)You: Webhook client / subclient_approved, status active or rejected
end
A subsidiary skips screening: it becomes active as soon as it's approved in your business, and no webhook is sent.
curl -X POST "$MG_BASE_URL/client-service/api/v1/clients/$MG_CLIENT_ID/sub-clients" \
-H "mp-api-key: $MG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"subClientType": "client",
"businessName": "Kola Stores Ltd",
"businessType": "Limited Liability Company",
"customerType": "corporate",
"businessAddressLine1": "4 Allen Avenue",
"city": "Ikeja",
"state": "Lagos",
"countryOfRegistration": "NGA",
"operatingCountries": ["NGA"],
"businessContactEmail": "finance@kolastores.example",
"registrationNumber": "RC1234567"
}'
{
"message": "Sub-Client information saved successfully",
"entityId": "Kx81mQ2pZr4tV9wYa3Bc5",
"businessName": "Kola Stores Ltd",
"subClientType": "client",
"status": "draft",
"approvalStatus": "pending",
"stagesRemaining": ["documentation", "director", "owner"],
"parentClientId": "Ab3kP9xQ2mN7vR1sT4uV6"
}
Store the returned entityId: it's the customer ID. The customer is draft: nothing is reviewed until you submit it.
Required fields: subClientType, businessName, businessType (Public Limited Company, Limited Liability Company, Ordinary Business Registration or Government Agency), business, city, state, country, operating and business. Countries are upper-case ISO 3166-1 alpha-3 codes (NGA, GHA). customerType is corporate or financial. See Create a customer for every optional field.
businessmust not be used by any other customer on Multigate, including declined ones. A duplicate returnsContact Email 409.- Give each customer a distinct
businessName. Two customers with the same name can't both be used for payouts and conversions. - Creating isn't idempotent. If the call times out, look for the customer with List customers (
businessNamefilter) before you retry.
Add each person with Add a director or owner, one call per person. Use designation to say what they are.
curl -X POST "$MG_BASE_URL/client-service/api/v1/clients/$MG_CLIENT_ID/sub-clients/Kx81mQ2pZr4tV9wYa3Bc5/business-users" \
-H "mp-api-key: $MG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Mrs", "firstName": "Adaeze", "lastName": "Kola", "gender": "female",
"occupation": "Director", "dateOfBirth": "1980-04-12",
"identificationType": "PASSPORT", "identificationNumber": "A01234567",
"issuedDate": "2022-01-10", "expiryDate": "2032-01-09",
"countryIso3Code": "NGA", "country": "Nigeria",
"address": "4 Allen Avenue", "city": "Ikeja", "state": "Lagos", "postalCode": "100001",
"designation": "BeneficialOwnerDirector", "shareholderPercentage": 60
}'
The response is the person, with their entityId: store it as the person ID. Dates are YYYY-MM-DD; the identity document's issuedDate can't be in the future and its expiryDate can't be in the past.
stages is a checklist for you; Multigate doesn't enforce it:
designation |
Removes from stages |
|---|---|
Beneficial, Beneficial, Beneficial, Beneficial |
owner |
Director, Director |
director |
Signatory |
nothing |
A Beneficial clears owner only. Uploading documents never clears documentation.
Upload each file with Upload a customer document as multipart/form-data, one file per call. Company documents go on the customer. A person's documents go on the customer too, with the person ID in business.
A company document:
curl -X POST "$MG_BASE_URL/client-service/api/v1/clients/$MG_CLIENT_ID/sub-clients/Kx81mQ2pZr4tV9wYa3Bc5/upload-kyc" \
-H "mp-api-key: $MG_API_KEY" \
-F "file=@certificate.pdf" \
-F "documentType=CERTIFICATE_OF_INCORPORATION" \
-F "issueDate=2019-03-14"
A director's passport:
curl -X POST "$MG_BASE_URL/client-service/api/v1/clients/$MG_CLIENT_ID/sub-clients/Kx81mQ2pZr4tV9wYa3Bc5/upload-kyc" \
-H "mp-api-key: $MG_API_KEY" \
-F "file=@adaeze-passport.pdf" \
-F "documentType=PASSPORT" \
-F "businessUserId=Hq3vT8nLm2Xc5RzW" \
-F "issueDate=2022-01-10" \
-F "expiryDate=2032-01-09"
Pick the documentType that matches each file:
| Document | documentType |
Uploaded on |
|---|---|---|
| Certificate of incorporation or registration | CERTIFICATE_ or REGISTRATION_ |
The customer |
| Memorandum and articles of association | ARTICLE_ |
The customer |
| List of directors, list of shareholders | LIST_, LIST_ |
The customer |
| Ownership chart | SHAREHOLDING_ |
The customer |
| Business proof of address (for example a utility bill) | PROOF_ |
The customer |
| Tax registration, business or regulatory licence | TAX_, BUSINESS_, REGULATORY_ |
The customer |
| Board resolution | BOARD_ |
The customer |
| Recent bank statement | RECENT_BANK |
The customer |
| Passport, national ID card, NIN slip, driver's licence, voter's card | PASSPORT, IDCARD, NIN, DRIVERS_, VOTERS_CARD |
The person (business) |
| Personal proof of address | PROOF_ |
The person (business) |
Check what's uploaded with List customer documents (add business for a person's documents).
- Send PDF or image files. Plain-text files (
.txt,.csv) fail with500. - A second file with the same
documentTypeis added to the same document. Files can't be removed through the API. - The API doesn't say which documents are required, and submission doesn't check them. Your Multigate contact tells you which documents your market requires; a customer without them can be declined.
curl -X POST "$MG_BASE_URL/client-service/api/v1/clients/$MG_CLIENT_ID/sub-clients/Kx81mQ2pZr4tV9wYa3Bc5/submit-for-approval" \
-H "mp-api-key: $MG_API_KEY"
The response is the approval request (type creation), not the customer. What happens next depends on your API user:
- It has both the manage customers and approve customers permissions: the customer is approved in your business at once. A
clientbecomesprocessingand is screened; asubsidiarybecomesactive. - It doesn't: the customer becomes
processingwithapprovalStatus pending. Your approvers receive a "Sub-client Creation Request for Approval" email and approve or decline in the dashboard. If they decline, the customer is removed: it disappears from lists, its email can't be reused, and no webhook is sent.
Submit only draft customers, and only once. The endpoint doesn't check the status: submitting again moves an active or rejected customer back to processing, even when the call then fails with 409. Use Resubmit a declined customer for declined customers.
- Nothing found by screening: the customer becomes
active, usually within minutes, and you receiveclient/subclient_.approved - Possible match: Multigate compliance reviews it. The customer stays
processingwith no webhook until they decide; this can take days if they need more information.
When subclient_ arrives, check status: active means approved, rejected means declined. The same event is used for both.
If declined, read approval, fix what was asked (update the customer, add people or documents), then call Resubmit a declined customer. Compliance reviews it again and you receive subclient_ with the new outcome.
If you miss the webhook, poll Get a customer no more than every few minutes.
Customer statuses
stateDiagram-v2
direction LR
[*] --> draft: created
draft --> processing: submitted
draft --> active: subsidiary approved at once
processing --> active: screening clear, compliance approves, or subsidiary approved
processing --> rejected: compliance declines
processing --> inactive: your approver declines, customer removed
rejected --> processing: resubmitted
active --> inactive: deactivated
inactive --> active: reactivated
active --> deleted: deleted
active --> blocked: blocked by Multigate
blocked --> active: unblocked by Multigate
status |
Meaning | Can you open accounts, pay out or convert for it? | What to do |
|---|---|---|---|
draft |
Created, not submitted. | No | Add people and documents, then submit. |
processing |
Submitted. Waiting for your approver, for screening, or for Multigate compliance. | No | Wait for subclient_. |
active |
Approved. | Yes | Open accounts (Accounts). |
rejected |
Declined by Multigate compliance. | No | Read approval, fix, then resubmit. |
inactive |
Deactivated in your business. Customers declined by your own approver are also set to inactive, but they're removed, so you won't see them. |
No | Reactivate it in the dashboard. |
deleted |
Deleted in your business. | No | Create a new customer (with a new email). |
blocked |
Blocked by Multigate compliance. | No | Contact Multigate. |
Deactivating, reactivating and deleting customers are dashboard actions that go through your approvers. Accounts are opened only for active customers ("Sub-Client not active" otherwise).
approval shows approval inside your business only: pending (created, or waiting for your approver), approved (approved in your business; Multigate's review may still be running) or declined (declined by your approver). It stays approved when Multigate compliance declines the customer. Always use status for the outcome.
The subclient_approved webhook
The body is the customer, as returned by Get a customer: entityId, businessName, status, approval, parent (your client ID) and the other customer fields.
- Sent: for a
clientcustomer, when screening finds nothing (statusactive) or when Multigate compliance decides (activeorrejected), including after a resubmission. - Not sent: for subsidiaries, when your approver declines, while compliance is still reviewing, or for creating, updating, uploading or submitting.
approvalandComments approvalDateappear only after a compliance decision.approvalDateisn't ISO 8601 (it looks likeSat Oct 10 2026 14:20:00 GMT+0000 (Coordinated Universal Time)).- De-duplicate by
entityIdandstatus: the same outcome can arrive more than once.
Things to know
- Active customers become usable for payouts and conversions after a short delay (up to a few minutes) while the change reaches those services. Until then, payouts for them fail with "Invalid sub onBehalfOf".
- Updates aren't reviewed. Update a customer works in any status and applies at once. It doesn't check country codes, prohibited countries or email uniqueness, so send the same kind of values you'd send on create.
- Countries. A customer registered or operating in a country Multigate prohibits is refused when you create it ("The selected country or countries are prohibited"). Use upper-case ISO 3166-1 alpha-3 codes.
- Email domains. Production refuses disposable email domains (yopmail.com, mailinator.com and similar) for the customer and its people. The sandbox accepts them.
- Emails go to your business. Approval requests go to your approvers, and approval results ("Subclient Creation Request Approved", "Sub-client Creation Request Declined") go to the user who submitted, which is your API user. Customers never receive emails from these steps.
In the sandbox, a submitted client customer can stay processing for days, because screening results are collected far less often than in production. Until it's active you can't open accounts for it. Ask your Multigate contact to complete the review for your test customers. See Sandbox vs production.
Errors
| Message | Cause | Fix |
|---|---|---|
Client not found (400) |
The clientId in the path isn't your client ID, or your business isn't active. |
Use current from Get the current user. |
Client is inactive (400) |
Your own business isn't active and approved yet. | Contact Multigate. |
Sub-Client not found (400) |
The customer doesn't exist, isn't yours, or was declined by your approver and removed. | Check the entityId. |
Resource not found (404) |
Your API user lacks the permission. | Ask your administrator for the manage customers permission. |
Sub-Client already exists (409) |
The contact email is already used by a customer on Multigate. | Use the customer's own unique email. |
The selected country or countries are prohibited (400) |
A country Multigate can't serve. | Remove it. |
Country not found (404) |
An unknown country code. | Use upper-case ISO 3166-1 alpha-3 codes. |
Email domain is not allowed (400) |
A disposable email domain, in production. | Use a real business domain. |
ShareHolder not found (404) |
The business isn't a person on this customer. |
Use the entityId from Add a director or owner. |
Pending approval request for creation already exists (409) |
A submission is still open. | Wait for the outcome. Don't submit again. |
Sub-Client not eligible for resubmission (400) |
Only rejected customers can be resubmitted. |
Check status. |
Sub-Client in review (400) |
The customer is already being reviewed. | Wait for the outcome. |
Next
Wallets and virtual accounts with onBehalfOf.
How onBehalfOf works across the API.