curl --request POST \
--url https://api-staging.rolla.xyz/api/v1/external/wallet/virtual-accounts/simulate-deposit \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"customer_identifier": "user-12345",
"amount": "2000",
"sender_name": "John Sender"
}
'import requests
url = "https://api-staging.rolla.xyz/api/v1/external/wallet/virtual-accounts/simulate-deposit"
payload = {
"customer_identifier": "user-12345",
"amount": "2000",
"sender_name": "John Sender"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({customer_identifier: 'user-12345', amount: '2000', sender_name: 'John Sender'})
};
fetch('https://api-staging.rolla.xyz/api/v1/external/wallet/virtual-accounts/simulate-deposit', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api-staging.rolla.xyz/api/v1/external/wallet/virtual-accounts/simulate-deposit",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'customer_identifier' => 'user-12345',
'amount' => '2000',
'sender_name' => 'John Sender'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api-staging.rolla.xyz/api/v1/external/wallet/virtual-accounts/simulate-deposit"
payload := strings.NewReader("{\n \"customer_identifier\": \"user-12345\",\n \"amount\": \"2000\",\n \"sender_name\": \"John Sender\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api-staging.rolla.xyz/api/v1/external/wallet/virtual-accounts/simulate-deposit")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"customer_identifier\": \"user-12345\",\n \"amount\": \"2000\",\n \"sender_name\": \"John Sender\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-staging.rolla.xyz/api/v1/external/wallet/virtual-accounts/simulate-deposit")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"customer_identifier\": \"user-12345\",\n \"amount\": \"2000\",\n \"sender_name\": \"John Sender\"\n}"
response = http.request(request)
puts response.read_body{
"status": 201,
"message": "Simulated deposit created",
"success": true,
"data": {
"transactionId": "8f2a1d64-9c11-4b7e-b3a0-2b5f0c9a77e1",
"externalReference": "sim_2f1c9e3a-4d6b-4f2a-9c88-7a1e5b3d0c42",
"status": "completed",
"account_number": "4382272943",
"customer_identifier": "user-12345",
"amount": "2000"
}
}{
"status": 400,
"message": "Deposit amount is too small — it must be greater than the payin fee configured on this account. Try a larger amount."
}{
"status": 400,
"message": "Validation failed",
"code": "ACCOUNT_RESTRICTED",
"errors": [
{
"path": [
"amount"
],
"message": "amount must be a whole number of smallest currency units (e.g. cents)"
}
]
}{
"status": 404,
"message": "No active virtual account found for account_number \"9999999999\" under this business"
}Simulate a Deposit (Sandbox)
Sandbox only: injects a deposit into one of your own virtual accounts. The wallet is credited and the deposit webhook is delivered, without a real bank transfer. The route does not exist in production, where it returns 404. Provide exactly one of customer_identifier or account_number.
curl --request POST \
--url https://api-staging.rolla.xyz/api/v1/external/wallet/virtual-accounts/simulate-deposit \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"customer_identifier": "user-12345",
"amount": "2000",
"sender_name": "John Sender"
}
'import requests
url = "https://api-staging.rolla.xyz/api/v1/external/wallet/virtual-accounts/simulate-deposit"
payload = {
"customer_identifier": "user-12345",
"amount": "2000",
"sender_name": "John Sender"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({customer_identifier: 'user-12345', amount: '2000', sender_name: 'John Sender'})
};
fetch('https://api-staging.rolla.xyz/api/v1/external/wallet/virtual-accounts/simulate-deposit', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api-staging.rolla.xyz/api/v1/external/wallet/virtual-accounts/simulate-deposit",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'customer_identifier' => 'user-12345',
'amount' => '2000',
'sender_name' => 'John Sender'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api-staging.rolla.xyz/api/v1/external/wallet/virtual-accounts/simulate-deposit"
payload := strings.NewReader("{\n \"customer_identifier\": \"user-12345\",\n \"amount\": \"2000\",\n \"sender_name\": \"John Sender\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api-staging.rolla.xyz/api/v1/external/wallet/virtual-accounts/simulate-deposit")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"customer_identifier\": \"user-12345\",\n \"amount\": \"2000\",\n \"sender_name\": \"John Sender\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-staging.rolla.xyz/api/v1/external/wallet/virtual-accounts/simulate-deposit")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"customer_identifier\": \"user-12345\",\n \"amount\": \"2000\",\n \"sender_name\": \"John Sender\"\n}"
response = http.request(request)
puts response.read_body{
"status": 201,
"message": "Simulated deposit created",
"success": true,
"data": {
"transactionId": "8f2a1d64-9c11-4b7e-b3a0-2b5f0c9a77e1",
"externalReference": "sim_2f1c9e3a-4d6b-4f2a-9c88-7a1e5b3d0c42",
"status": "completed",
"account_number": "4382272943",
"customer_identifier": "user-12345",
"amount": "2000"
}
}{
"status": 400,
"message": "Deposit amount is too small — it must be greater than the payin fee configured on this account. Try a larger amount."
}{
"status": 400,
"message": "Validation failed",
"code": "ACCOUNT_RESTRICTED",
"errors": [
{
"path": [
"amount"
],
"message": "amount must be a whole number of smallest currency units (e.g. cents)"
}
]
}{
"status": 404,
"message": "No active virtual account found for account_number \"9999999999\" under this business"
}transaction.completed webhook and attributes it to the right end-user.
404 Not Found. Use it against your sandbox environment only.Targeting an account
Send exactly one of the following. The account must belong to your business.customer_identifier— the id you used when creating a customer virtual account. Best when testing per-end-user deposits.account_number— the number of any of your virtual accounts (static, dynamic, or customer).
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
customer_identifier | string | Conditional | The customer id of one of your customer virtual accounts. Provide this or account_number, not both. |
account_number | string | Conditional | The account number of any of your virtual accounts. Provide this or customer_identifier, not both. |
amount | string | Yes | Deposit amount in major units of the account currency (e.g. Naira). Must be greater than the pay-in fee on the account, otherwise the request is rejected. |
sender_name | string | No | Name to record as the sender on the transaction (appears in the webhook’s sender snapshot). |
provider | string | No | Sandbox only. Overrides which payment rail the simulated deposit is tagged against: one of HABARI, NOMBA, VFD, NETMFB. Omit to inherit the target account’s own rail — recommended, and the closest match to a real deposit. |
Example Request — by customer_identifier
curl -X POST "https://api-staging.rolla.xyz/api/v1/external/wallet/virtual-accounts/simulate-deposit" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"customer_identifier": "user-12345",
"amount": "2000",
"sender_name": "John Sender"
}'
Example Request — by account_number
curl -X POST "https://api-staging.rolla.xyz/api/v1/external/wallet/virtual-accounts/simulate-deposit" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"account_number": "4382272943",
"amount": "2000"
}'
Example Response
{
"status": 201,
"message": "Simulated deposit created",
"success": true,
"data": {
"transactionId": "8f2a1d64-9c11-4b7e-b3a0-2b5f0c9a77e1",
"externalReference": "sim_2f1c9e3a-4d6b-4f2a-9c88-7a1e5b3d0c42",
"status": "completed",
"account_number": "4382272943",
"customer_identifier": "user-12345",
"amount": "2000"
}
}
customer_identifier is echoed only when you targeted the account that way; a request by account_number omits it. status is the transaction’s own status, completed once the deposit has been posted.
An account number or customer identifier that does not match an active virtual account of yours returns 404, rather than crediting anything:
{
"status": 404,
"message": "No active virtual account found for account_number \"9999999999\" under this business"
}
amount, returns 400 with "message": "Validation failed" and an errors array.
What happens
- The deposit is routed through the same crediting path a real provider deposit uses — your wallet is credited (net of the pay-in fee) and the deposit is tagged with the account’s provider.
- The transaction is completed, emitting the deposit webhook to your configured endpoint: a
transaction.pendingevent, followed bytransaction.completed. - For a customer virtual account, the webhook payload includes the
customer_identifier, so you can attribute the deposit to the right end-user. See Webhook Payloads.
amount must be greater than the pay-in fee on the account. A smaller amount is rejected with 400 and the message Deposit amount is too small — it must be greater than the payin fee configured on this account. Try a larger amount.Authorizations
Your Rolla API key
Body
Deposit amount in major units of the account currency (e.g. Naira). Must be greater than the pay-in fee on the account.
^\d+(\.\d{1,2})?$"2000"
Customer id of one of your customer virtual accounts. Provide this or account_number, not both.
1 - 100"user-12345"
Account number of any of your virtual accounts. Provide this or customer_identifier, not both.
1 - 50"4382272943"
Name to record as the sender on the transaction.
1 - 120"John Sender"
Force the provider tag on the deposit. Omit to inherit the target account's own provider.
HABARI, NOMBA, VFD, NETMFB