Simulate domestic payments
Learn how to test your complete domestic payment flow in our sandbox environment without moving real money. Use this guide to understand how to validate your integration by simulating various payment outcomes.
Domestic payout or transfer out via virtual account is currently only enabled for Singapore-issued virtual accounts. Only bank-to-bank transfers to other local banks participating in the FAST (Fast And Secure Transfers) network are available.
The user in this document pertains to both the individual and business user types.
Use Cases
- You want to test a domestic payment transaction using the provisioned user virtual account.
- You want to test various domestic payment transaction outcome scenarios.
- You want to simulate sending funds to an external bank account without spending real money.
Prerequisites
- The user profile risk assessment status must not be
risk_flagged. - The user profile must be in an
activestatus. - The user must have an
approvedKYC or KYB status - The user account or wallet must be in an
activestatus. - The user's virtual account must be in an
activestatus. - The user account's current
deduct_limitsandtransfer_out_limitsmust have enough buffer to accommodate the incoming debit amount. - The user account must have a balance that is equal to or higher than the payment amount.
- A valid domestic recipient must have already been defined and must be in an
activestatus.
API Workflow
1
Retrieve the list of local banks available for domestic payout
Call the Domestic Transfer Agents Enumerations (GET /users/wallets/payouts/domestic/enumerations/transfer_agents) to retrieve the list of available local banks that can receive a domestic transfer. It is important to filter only the active banks during a particular timeframe, as receiving banks may go on maintenance and be unavailable for certain periods of time.
In the below example, the status=active filter is added to only retrieve currently available banks for transfer.
Please note that the value of the bank code will be agent_code returned in the response.
Domestic Transfer Agents Enumerations request
curl --location 'https://{{payouts_base_url}}/{{program_code}}/v1/users/wallets/payouts/domestic/enumerations/transfer_agents?status=active' \
--header 'Authorization: ••••••'*Please note that this is a special API that needs to be called in admin scope, without passing X-Auth-User-ID.
Domestic Transfer Agents Enumerations response
{
"total_records": 37,
"records_per_page": 37,
"total_pages": 1,
"page": 1,
"links": [
{
"rel": "self",
"href": "https://{{server}}/{{program_code}}/v1/users/wallets/payouts/domestic/enumerations/transfer_agents?page=1&records_per_page=10&return_all=true&status=active",
"method": "GET"
}
],
"data": [
{
"agent_code": "ANZBSGS0XXX",
"agent_logo": "https://assets.mmvpay.com/sg/domestic_transfer_agents/ANZ_BANK.png",
"mode": "bank",
"name": "ANZ Bank",
"status": "active",
"bic_code": "ANZBSGS0XXX",
"fees": {
"fixed": "0.000000",
"percentage": "0.000000",
"variable": "",
"fee_calculation_type": ""
},
"transfer_details": [
{
"channel_id": "382dd1f29f9411ec875a02b5044dcedc",
"channel": "FAST",
"min_transfer_amount": "0.01",
"max_transfer_amount": "200000.00",
"transfer_fulfillment_time": "Instant"
}
],
"account_number_regex": "^[0-9]*$",
"account_number_min_length": "5",
"account_number_max_length": "34"
},
{
"agent_code": "BKCHSGS0XXX",
"agent_logo": "https://assets.mmvpay.com/sg/domestic_transfer_agents/BANK_OF_CHINA_CREDIT_CARD.png",
"mode": "bank",
"name": "Bank of China Credit Card",
"status": "active",
"bic_code": "BKCHSGS0XXX",
"fees": {
"fixed": "0.000000",
"percentage": "0.000000",
"variable": "",
"fee_calculation_type": ""
},
"transfer_details": [
{
"channel_id": "382dd1f29f9411ec875a02b5044dcedc",
"channel": "FAST",
"min_transfer_amount": "0.01",
"max_transfer_amount": "200000.00",
"transfer_fulfillment_time": "Instant"
}
],
"account_number_regex": "^[0-9]*$",
"account_number_min_length": "5",
"account_number_max_length": "34"
},
... more data here ...
]
}2
Create a beneficiary using a naming notation
To simulate an intended simulation outcome, we will need to create beneficiaries using the following notation:
Naming notation
Bankholder Name = First Name + Simulation Outcome
e.g.
If the intended simulation outcome is to get a successful transaction, the beneficiary bank_holder_name can be John Success
Unavailable | Will simulate a Receiving party unavailable error |
Success | Will simulate a successful transaction |
Invalid | Will simulate an invalid receiving account error, and will automatically do an auto debit reversal |
Other | Will simulate other related errors and will automatically do an auto debit reversal |
Closed | Will simulate a beneficiary account closed error, and will automatically do an auto debit reversal |
Stopped | Will simulate a beneficiary account stopped error and will automatically do an auto debit reversal |
Blocked | Will simulate a beneficiary account blocked error, and will automatically do an auto debit reversal |
Call Create New Beneficiary Bank Account (POST /users/wallets/payouts/domestic/bank_accounts) passing the beneficiary details, including the bankholder name, following the naming notation.
Create New Beneficiary Bank Account request (success outcome)
curl --location 'https://{{payouts_base_url}}/{{program_code}}/v1/users/wallets/payouts/domestic/bank_accounts' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: : ••••••' \
--header 'X-Auth-User-ID: {{user_id}}' \
--data-urlencode 'bank_account_number=123456' \
--data-urlencode 'bank_holder_name=John Success' \
--data-urlencode 'bank_code={{agent_code}}'Create New Beneficiary Bank Account response (success outcome)
{
"id": "f9ce81d6-47d8-467e-ba74-9c17f92e479f",
"bank_account_number": "123456",
"bank_holder_name": "John Success",
"bank_code": "ANZBSGS0XXX",
"bank_name": "ANZ Bank",
"mode": "bank",
"is_active": 1,
"created_at": "2023-07-27T20:06:41+08:00",
"updated_at": null
}Please take note of the bank account ID of the beneficiary because this will be required on the Simulate a domestic payout step.
Otherwise, you can retrieve the same bank account ID by following the Retrieve the bank account ID of the intended beneficiary step.
3
Retrieve the bank account ID of the intended beneficiary
Call the Get Beneficiary Bank Accounts (GET /users/wallets/bank_accounts) to list down all the defined beneficiaries associated with the active user (sender).
Get Beneficiary Bank Accounts request
curl --location 'https://{{payouts_base_url}}/{{program_code}}/v1/users/wallets/bank_accounts' \
--header 'Authorization: ••••••' \
--header 'X-Auth-User-ID: <user_id>'Get Beneficiary Bank Accounts response
{
"total_records": 6,
"records_per_page": 10,
"total_pages": 1,
"page": 1,
"links": [
{
"rel": "self",
"href": "https://{{server}}/{{program_code}}/v1/users/wallets/bank_accounts?page=1&records_per_page=10&return_all=false",
"method": "GET"
}
],
"data": [
{
"id": "f9ce81d6-47d8-467e-ba74-9c17f92e479f",
"bank_account_number": "123456",
"bank_holder_name": "John Success",
"bank_code": "ANZBSGS0XXX",
"bank_name": "ANZ Bank",
"mode": "bank",
"is_active": 1,
"created_at": "2023-07-27T20:06:41+08:00",
"updated_at": null
}
... more data here ...
]
}Please take note of the bank account ID of the beneficiary, as this will be required during the Simulate a domestic payout step.
4
Simulate a domestic payout
Call the Credit Bank Account (POST /v1/users/wallets/payouts/domestic/fund_transfers/credit) to simulate a domestic payout transaction.
Credit Bank Account request
curl --location --request POST 'https://{{payouts_base_url}}/{{program_code}}/v1/users/wallets/payouts/domestic/fund_transfers/credit' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'X-Auth-User-ID: {{user_id}}' \
--header 'Authorization: : ••••••' \
--data-urlencode 'amount=10' \
--data-urlencode 'bank_account_id=f9ce81d6-47d8-467e-ba74-9c17f92e479f' \
--data-urlencode 'client_ref_id={{transfer_comments}}' \
--data-urlencode 'currency=SGD' \
--data-urlencode 'purpose_of_transfer={{transfer_purpose}}'Credit Bank Account response
{
"id": "c9535919-cd4d-4b62-4256-a3f89559b452",
"client_ref_id": "{{transfer_comments}}",
"amount": 10,
"fee": 0,
"currency": "SGD",
"unique_payment_id": "20251208MAYPSGS0BRT1548907",
"payment_reference_id": "DEBIT-c9535919-cd4d-4b62-4256-a3f89559b452",
"ref_id": "000002F7000000000026000000000018",
"purpose_of_transfer": "{{transfer_purpose}}",
"transfer_type": "transfer_out",
"status": "pending",
"description": null,
"bank_account": {
"id": "f9ce81d6-47d8-467e-ba74-9c17f92e479f",
"bank_code": "ANZBSGS0XXX",
"bank_name": "ANZ Bank",
"bank_account_number": "123456",
"bank_holder_name": "John Success"
},
"created_at": "2025-12-08T13:20:31+08:00",
"updated_at": null
}Domestic Payout Webhooks
Partners have the option to subscribe to webhook events associated with domestic payout activities.
CONSUMER_ACCOUNT.BANK_TRANSFER_DEBIT.SUCCESS BUSINESS_ACCOUNT.BANK_TRANSFER_DEBIT.SUCCESS | Notification is sent if the transfer is completed and has been acknowledged by the destination bank | No action required |
CONSUMER_ACCOUNT.BANK_TRANSFER_DEBIT.FAILED BUSINESS_ACCOUNT.BANK_TRANSFER_DEBIT.FAILED | Notification is sent for any failure cases on the bank transfer | No action required. The user can retry the bank transfer |
CONSUMER_ACCOUNT.BANK_TRANSFER_DEBIT_REVERSAL.SUCCESS BUSINESS_ACCOUNT.BANK_TRANSFER_DEBIT_REVERSAL.SUCCESS | Notification is sent if the transfer has been rejected by the destination bank The system will perform a reversal | No action required |
CONSUMER_ACCOUNT.BANK_TRANSFER_DEBIT_REVERSAL.FAILED BUSINESS_ACCOUNT.BANK_TRANSFER_DEBIT_REVERSAL.FAILED | Notification is sent if the transfer has been rejected by the destination bank A reversal performed by the system has failed. | No action required. Matchmove will auto-retry the reversal until it is successful |
Related Links
On this page
- Simulate domestic payments