Get started
Sandbox vs production
The sandbox runs the same API as production, with the same endpoints, fields, statuses, webhooks and error formats. But a few behaviours are deliberately different, and a few checks are switched off in the sandbox. Read this page before you test. It will save you from a test suite that passes in the sandbox and then fails on your first day in production.
- Converting without funds. In the sandbox, an FX lock (accepting a quoted exchange rate so the currency conversion goes ahead) succeeds even when the source account can't cover it. Production refuses it with
402. - Saving an invalid Nigerian account. In the sandbox, any NGN account number can be saved as a beneficiary. Production refuses an account the bank doesn't recognise with
400"Invalid beneficiary account number". - Disposable email addresses. In the sandbox, addresses like
test@yopmail.orcom x@mailinator.are accepted. Production refuses them with "Email domain is not allowed".com - Simulated inflows. Simulate an inflow funds sandbox accounts. In production it answers success but does nothing: no money arrives and no webhook follows.
- Speed. Payout results, customer reviews and some beneficiary checks are much slower in the sandbox (minutes to days, not seconds). Don't size your production timeouts on sandbox timings.
At a glance
| Area | Production | Sandbox | What to do |
|---|---|---|---|
| FX: balance check on lock | Refused with 402 "Insufficient balance" if the source account can't cover the conversion. 402 "The settlement has failed…" if the debit fails. |
Not checked. The lock succeeds without funds and the conversion goes ahead. | Check the balance yourself before locking, and handle 402. |
| NGN beneficiary account check | Saving refuses an account the bank can't find: 400 "Invalid beneficiary account number". |
Any account number is accepted when saving or updating. | Run Validate a beneficiary account (NGN) first in both environments. |
| Name enquiry results | The account holder's real name, from their bank. | From test banks. The name may be a fixed test name that doesn't match your input. | Don't assert specific names in tests. |
| Email addresses | Disposable domains (yopmail.com, mailinator.com, temp-mail.org, guerrillamail.com, 10minutemail.com and others) are refused. | Accepted. | Use real domains in test data. |
| Funding accounts and inflows | Real bank transfers to your virtual accounts. The simulate endpoint does nothing. | Only Simulate an inflow. Sandbox account numbers can't receive real transfers. | Never call the simulate endpoint from production code. |
| Payout status updates | Usually seconds after the bank answers. | Can take 15 minutes or more. | Wait for the webhook; poll at most every few minutes. |
| Payout outcomes | The bank's real answer. | From test banks and simulated partners. Some outcomes are random (completed or failed for the same input). | Test that you handle every outcome, not that you get one. |
| Customer onboarding review | Results arrive within minutes of compliance completing. | A customer can stay processing for days. |
Ask your Multigate contact to complete a sandbox review. |
| International payout-channel registrations | Finalised within minutes. | Can stay processing for up to a week, and the transfer waits. |
Don't block sandbox tests on these; ask your contact if needed. |
| Webhook delivery | Dedicated delivery service. | Shared test infrastructure: occasionally delayed or briefly unavailable. Retries work the same. | Keep the status fallback you need in production anyway. |
| Emails | Sent to the addresses you entered. | Also real emails to the addresses you entered, with links to the sandbox web app. | Use addresses you control. |
| Bank codes on some virtual accounts | Real bank codes. | Can show test values such as test-bank, and some non-NGN test accounts show NGN-style details. |
Never hard-code bank codes or names; read them from the API. |
| Charges | Your agreed pricing. | May record per-payout charge entries that production doesn't. | Don't build reconciliation rules on sandbox fee amounts. |
| Capacity and releases | Scaled for production traffic. | Smaller, and can run a newer or older release than production for a while. | Don't load test the sandbox. Ignore fields you don't recognise. |
How to think about it
Your code should be identical in both environments. Only the base URL, the credentials (API key and client secret) and the webhook URL change. What sits behind the API is different:
flowchart LR
A["Your server<br/>(same code)"] -->|"MG_BASE_URL"| S["Sandbox API"]
A -->|"MG_BASE_URL"| P["Production API"]
S --> SB["Test banks and simulated partners<br/>Simulated inflows<br/>Some checks off, slower jobs"]
P --> PB["Real banks and partners<br/>Real inflows<br/>All checks on"]
Avoid if (sandbox) … else … in your integration, apart from configuration. The one exception is a test-only helper that calls Simulate an inflow. Keep it out of your production code paths entirely.
FX conversions
Production: when you lock a quote, Multigate checks that the source account can cover the conversion (unless your business is post-funded: allowed by agreement to pay for conversions afterwards), then debits it.
- Not enough available balance:
402"Insufficient balance". Nothing is booked. - The debit fails:
402"The settlement has failed due to an issue with our banking partners." No conversion is recorded. Usually nothing was debited, but check the balance before retrying: a debit that timed out inside Multigate can still complete.
Sandbox: neither check stops the conversion. A lock on an empty account succeeds, and the conversion moves on to processing and completed as if it had been funded.
What to do:
- Fetch the source account's balance (Get an account balance) before you lock, and stop if it's short.
- Handle
402from the lock as "not converted": tell your user, fund the account, then lock again (the quote stays usable untilendTime) or request a new quote. - Don't rely on a sandbox test to prove your insufficient-funds handling. Unit-test it with the
402response shown above.
See FX conversions.
Beneficiaries and name enquiry
Production: when you save an NGN beneficiary (a person or business you pay), Multigate asks the beneficiary's bank whether the account exists. This lookup is called a name enquiry. If the bank can't find it, the request fails with 400 "Invalid beneficiary account number". The check doesn't compare the name you send, and updating a beneficiary doesn't repeat it.
Sandbox: if the bank lookup fails, the beneficiary is saved anyway. A typo'd or made-up account number goes through.
Validate a beneficiary account (NGN) and Look up an account name behave the same in both environments; for example, a failed validation returns 200 with nameEnquiryResponse: null. In the sandbox, though, the answers come from test banks, so the returned name can be a fixed test value that doesn't match the account number you sent.
What to do:
- Always validate before saving or paying a new NGN account, and treat
nameEnquiryResponse: nullas "not verified". - Show the returned name to your user to confirm. Don't compare it automatically against expected names in sandbox tests.
- Expect production to refuse account numbers that the sandbox accepted.
See Beneficiaries.
Email addresses
Production refuses email addresses at disposable or throwaway domains, such as yopmail.com, mailinator.com, temp-mail.org, guerrillamail.com, throwawaymail.com, maildrop.cc and 10minutemail.com. The error is a 400 validation error, "Email domain is not allowed", on the email field.
It applies wherever you send an email address: customers and their shareholders, account requests, beneficiaries, linking requests and users.
Sandbox accepts them.
What to do: use real domains you control in test data (for example addresses on your own domain). Then the same payloads work in production, and you can read the emails Multigate sends.
Inflows and virtual accounts
Sandbox:
- The only way money arrives is Simulate an inflow. It runs the real inflow pipeline (screening, ledger credit,
inflowwebhook, "Payment Received" email). - A simulated inflow's sender name is the receiving account's own name and its sender account number is
null. Itspayment/Reference sessionIdis a generated ID that the simulate call doesn't return, and its narration looks likeMULTIGATE-<account name>-<masked account number>. Make sure your inflow handler copes withnullsender fields. - For some NGN and GHS accounts, the simulated amount is read as minor units (kobo or pesewas) and credited at one hundredth of what you sent. Check
amountin the webhook. - Virtual account numbers come from bank test systems, and some are mock responses. Real bank transfers to them never arrive.
- Some sandbox virtual accounts show test bank codes such as
test-bank, and some non-NGN test accounts carry NGN-style details.
Production:
- Money arrives from payers' banks, with the real sender details.
- Simulate an inflow returns
200"inflow initiated" but does nothing.
What to do: read bank names and codes from the API rather than hard-coding them, and treat sender fields as optional.
See Collections and Accounts.
Payout results and timing
Sandbox:
- Multigate checks pending payouts with the bank only every 15 minutes, so a payout can sit in
processingfor 15 minutes or more beforetransfer_orcompleted transfer_arrives.failed - Payouts go to test banks and simulated partners. Some simulated partners pick the outcome at random, so the same request can complete once and fail the next time. Others can stay
processing.
Production: pending payouts are checked every 15 seconds for their first 30 minutes, then every 15 minutes up to 6 hours and every 30 minutes up to 24 hours. Results usually arrive soon after the bank answers. A bank can still take longer, which is why the unknown-outcome rules apply in both environments.
What to do:
- Build on webhooks. If you also poll Find transfers (filtered by
unique), poll no more than every few minutes and stop when the status is final.Request Id - In automated sandbox tests, assert that your code handles
completed,failedand a longprocessing. Don't assert which one you get. - Set production alert thresholds from production experience, not from sandbox timings.
Customers and compliance review
Sandbox: compliance results for customers (the businesses you onboard and act for, called sub-clients in the API) are collected much less often than in production, so a submitted customer can stay processing for days. Registering a beneficiary with an international payout channel (done automatically on the first transfer through that channel) can take up to a week, and the transfer stays processing meanwhile. Until a customer is active, you can't open accounts, pay out or convert for it, so sandbox flows that need a customer wait for the review.
Production: results are collected every few minutes once compliance has decided.
What to do:
- Don't make sandbox test runs wait for
subclient_. Ask your Multigate contact to complete the review for your test customers.approved - In your product, show "under review" for
processing. In production it can also take a while when compliance needs more information.
See Customers.
API keys and IP allow-list
| Production | Sandbox | |
|---|---|---|
| API keys | Generated in the production dashboard | Generated in the sandbox dashboard |
| API key format | mgk_live_… |
mgk_sandbox_… |
| API key header and errors | Same | Same |
| IP allow-list | Enforced | Enforced |
Each environment has its own API keys: a sandbox key (mgk_sandbox_…) returns 401 in production, and a production key (mgk_live_…) returns 401 in the sandbox.
The IP allow-list is enforced in both environments. If your business has an allow-list, register your test servers' outbound IP addresses in the sandbox as well as your production addresses in production. Each environment has its own list.
See Authentication.
Webhooks and emails
- Registration is per environment. Register a sandbox webhook URL in the sandbox and a production URL in production. Signing, payload format, timeout and retry schedule are identical. Use one
modeper environment and sendwebhookUrlon every save: the last saved configuration (any mode) sets the webhook URL and IP allow-list. - Sandbox delivery runs on shared test infrastructure. Webhooks can arrive late, or not until a retry, during sandbox maintenance. Your fallback (fetch the status when no final webhook arrives) covers this, and you need that fallback in production too.
- Sandbox emails are real emails. They go to the addresses on your users, customers and beneficiaries, and their links open the sandbox web app. Use addresses you control.
See Webhooks overview and Email notifications.
Charges and reconciliation
The sandbox can record per-payout charge entries that production doesn't record in the same way. Fee amounts and charge lines on sandbox statements aren't a guide to your production pricing. Use Get a transfer fee in production for the fee you will actually pay, and agree pricing with Multigate.
Capacity and releases
- The sandbox runs on smaller infrastructure. Don't load test or performance test it. Ask Multigate if you need a performance test.
- The sandbox and production are released separately, so for short periods the sandbox can run a newer (or older) version. Build your integration to ignore response and webhook fields it doesn't recognise, so new optional fields never break it.
What's the same in both
So you don't have to second-guess everything, these behave identically:
- Endpoints, request and response fields, statuses, error formats and webhook payloads.
- API key authentication and IP allow-list enforcement.
- Validation rules (except disposable email domains, above), including the 1 MB request size limit and page size limits.
- Idempotency with
unique.Request Id - Approvals (maker-checker) and compliance screening of payouts and inflows.
- How long an FX quote stays valid before you must lock it.
- Webhook signing, the 9-second timeout and the retry schedule.
Before go-live: re-test against production rules
Once production is enabled, run these with your production credentials, ideally with small amounts on day one:
- Call Get the current user with your production API key from each production server (key and IP allow-list).
- Save a beneficiary with a deliberately wrong NGN account number and confirm you handle the
400. - Create a customer or user with your real production email domains.
- Try an FX lock larger than the available balance and confirm you handle the
402. - Receive a real inflow into a virtual account and process the
inflowwebhook, including the real sender fields. - Make a small real payout and process its webhook.
Then finish the go-live checklist.