curl --request POST \
--url https://sandbox-payment-b2b.singapay.id/api/v1.0/virtual-accounts/{account_id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'X-PARTNER-ID: <api-key>' \
--data '
{
"bank_code": "BRI",
"kind": "temporary",
"amount_type": "closed",
"name": "Bakso Enak- John Doe",
"merchant_reff_no": "INV-2026-001",
"expired_at": "1774000000000",
"max_usage": 1,
"amount": 100000,
"min_amount": 10000,
"max_amount": 100000
}
'import requests
url = "https://sandbox-payment-b2b.singapay.id/api/v1.0/virtual-accounts/{account_id}"
payload = {
"bank_code": "BRI",
"kind": "temporary",
"amount_type": "closed",
"name": "Bakso Enak- John Doe",
"merchant_reff_no": "INV-2026-001",
"expired_at": "1774000000000",
"max_usage": 1,
"amount": 100000,
"min_amount": 10000,
"max_amount": 100000
}
headers = {
"Authorization": "Bearer <token>",
"X-PARTNER-ID": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
Authorization: 'Bearer <token>',
'X-PARTNER-ID': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
bank_code: 'BRI',
kind: 'temporary',
amount_type: 'closed',
name: 'Bakso Enak- John Doe',
merchant_reff_no: 'INV-2026-001',
expired_at: '1774000000000',
max_usage: 1,
amount: 100000,
min_amount: 10000,
max_amount: 100000
})
};
fetch('https://sandbox-payment-b2b.singapay.id/api/v1.0/virtual-accounts/{account_id}', 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://sandbox-payment-b2b.singapay.id/api/v1.0/virtual-accounts/{account_id}",
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([
'bank_code' => 'BRI',
'kind' => 'temporary',
'amount_type' => 'closed',
'name' => 'Bakso Enak- John Doe',
'merchant_reff_no' => 'INV-2026-001',
'expired_at' => '1774000000000',
'max_usage' => 1,
'amount' => 100000,
'min_amount' => 10000,
'max_amount' => 100000
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"X-PARTNER-ID: <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://sandbox-payment-b2b.singapay.id/api/v1.0/virtual-accounts/{account_id}"
payload := strings.NewReader("{\n \"bank_code\": \"BRI\",\n \"kind\": \"temporary\",\n \"amount_type\": \"closed\",\n \"name\": \"Bakso Enak- John Doe\",\n \"merchant_reff_no\": \"INV-2026-001\",\n \"expired_at\": \"1774000000000\",\n \"max_usage\": 1,\n \"amount\": 100000,\n \"min_amount\": 10000,\n \"max_amount\": 100000\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("X-PARTNER-ID", "<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://sandbox-payment-b2b.singapay.id/api/v1.0/virtual-accounts/{account_id}")
.header("Authorization", "Bearer <token>")
.header("X-PARTNER-ID", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"bank_code\": \"BRI\",\n \"kind\": \"temporary\",\n \"amount_type\": \"closed\",\n \"name\": \"Bakso Enak- John Doe\",\n \"merchant_reff_no\": \"INV-2026-001\",\n \"expired_at\": \"1774000000000\",\n \"max_usage\": 1,\n \"amount\": 100000,\n \"min_amount\": 10000,\n \"max_amount\": 100000\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://sandbox-payment-b2b.singapay.id/api/v1.0/virtual-accounts/{account_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["X-PARTNER-ID"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"bank_code\": \"BRI\",\n \"kind\": \"temporary\",\n \"amount_type\": \"closed\",\n \"name\": \"Bakso Enak- John Doe\",\n \"merchant_reff_no\": \"INV-2026-001\",\n \"expired_at\": \"1774000000000\",\n \"max_usage\": 1,\n \"amount\": 100000,\n \"min_amount\": 10000,\n \"max_amount\": 100000\n}"
response = http.request(request)
puts response.read_body{
"status": 200,
"success": true,
"data": {
"id": "01K946KF851RK7FX075GJHBVKF",
"number": "9090583126022726",
"code": "VA_BNI",
"bank": {
"short_name": "BRI",
"number": "002",
"swift_code": "BRINIDJA"
},
"amount": {
"value": "1234.56",
"currency": "IDR"
},
"amount_type": "closed",
"status": "active",
"kind": "temporary",
"current_usage": 0,
"expired_at": 1774000000000,
"name": "Bakso Enak- John Doe",
"merchant_reff_no": "INV-2026-001",
"min_amount": {
"value": "1234.56",
"currency": "IDR"
},
"max_amount": {
"value": "1234.56",
"currency": "IDR"
}
}
}{
"status": 400,
"success": false,
"error": {
"code": 400,
"message": "Bad Request."
}
}{
"status": 401,
"success": false,
"error": {
"code": 401,
"message": "Unauthorized merchant, please sign in"
}
}{
"status": 404,
"success": false,
"error": {
"code": 404,
"message": "Account not found."
}
}{
"status": 405,
"success": false,
"error": {
"code": 4059901,
"message": "The PATCH method is not supported for this route. Supported methods: GET, HEAD, POST."
}
}{
"status": 422,
"success": false,
"error": {
"code": 422,
"message": "Validation error.",
"errors": {
"field": [
"error message"
]
}
},
"data": {
"errors": {
"field": [
"error message"
]
}
}
}Create
Creates a new virtual account for an active account. Amount rules depend on amount type (open or closed), kind (temporary or permanent), and merchant configuration limits.
curl --request POST \
--url https://sandbox-payment-b2b.singapay.id/api/v1.0/virtual-accounts/{account_id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'X-PARTNER-ID: <api-key>' \
--data '
{
"bank_code": "BRI",
"kind": "temporary",
"amount_type": "closed",
"name": "Bakso Enak- John Doe",
"merchant_reff_no": "INV-2026-001",
"expired_at": "1774000000000",
"max_usage": 1,
"amount": 100000,
"min_amount": 10000,
"max_amount": 100000
}
'import requests
url = "https://sandbox-payment-b2b.singapay.id/api/v1.0/virtual-accounts/{account_id}"
payload = {
"bank_code": "BRI",
"kind": "temporary",
"amount_type": "closed",
"name": "Bakso Enak- John Doe",
"merchant_reff_no": "INV-2026-001",
"expired_at": "1774000000000",
"max_usage": 1,
"amount": 100000,
"min_amount": 10000,
"max_amount": 100000
}
headers = {
"Authorization": "Bearer <token>",
"X-PARTNER-ID": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
Authorization: 'Bearer <token>',
'X-PARTNER-ID': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
bank_code: 'BRI',
kind: 'temporary',
amount_type: 'closed',
name: 'Bakso Enak- John Doe',
merchant_reff_no: 'INV-2026-001',
expired_at: '1774000000000',
max_usage: 1,
amount: 100000,
min_amount: 10000,
max_amount: 100000
})
};
fetch('https://sandbox-payment-b2b.singapay.id/api/v1.0/virtual-accounts/{account_id}', 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://sandbox-payment-b2b.singapay.id/api/v1.0/virtual-accounts/{account_id}",
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([
'bank_code' => 'BRI',
'kind' => 'temporary',
'amount_type' => 'closed',
'name' => 'Bakso Enak- John Doe',
'merchant_reff_no' => 'INV-2026-001',
'expired_at' => '1774000000000',
'max_usage' => 1,
'amount' => 100000,
'min_amount' => 10000,
'max_amount' => 100000
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"X-PARTNER-ID: <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://sandbox-payment-b2b.singapay.id/api/v1.0/virtual-accounts/{account_id}"
payload := strings.NewReader("{\n \"bank_code\": \"BRI\",\n \"kind\": \"temporary\",\n \"amount_type\": \"closed\",\n \"name\": \"Bakso Enak- John Doe\",\n \"merchant_reff_no\": \"INV-2026-001\",\n \"expired_at\": \"1774000000000\",\n \"max_usage\": 1,\n \"amount\": 100000,\n \"min_amount\": 10000,\n \"max_amount\": 100000\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("X-PARTNER-ID", "<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://sandbox-payment-b2b.singapay.id/api/v1.0/virtual-accounts/{account_id}")
.header("Authorization", "Bearer <token>")
.header("X-PARTNER-ID", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"bank_code\": \"BRI\",\n \"kind\": \"temporary\",\n \"amount_type\": \"closed\",\n \"name\": \"Bakso Enak- John Doe\",\n \"merchant_reff_no\": \"INV-2026-001\",\n \"expired_at\": \"1774000000000\",\n \"max_usage\": 1,\n \"amount\": 100000,\n \"min_amount\": 10000,\n \"max_amount\": 100000\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://sandbox-payment-b2b.singapay.id/api/v1.0/virtual-accounts/{account_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["X-PARTNER-ID"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"bank_code\": \"BRI\",\n \"kind\": \"temporary\",\n \"amount_type\": \"closed\",\n \"name\": \"Bakso Enak- John Doe\",\n \"merchant_reff_no\": \"INV-2026-001\",\n \"expired_at\": \"1774000000000\",\n \"max_usage\": 1,\n \"amount\": 100000,\n \"min_amount\": 10000,\n \"max_amount\": 100000\n}"
response = http.request(request)
puts response.read_body{
"status": 200,
"success": true,
"data": {
"id": "01K946KF851RK7FX075GJHBVKF",
"number": "9090583126022726",
"code": "VA_BNI",
"bank": {
"short_name": "BRI",
"number": "002",
"swift_code": "BRINIDJA"
},
"amount": {
"value": "1234.56",
"currency": "IDR"
},
"amount_type": "closed",
"status": "active",
"kind": "temporary",
"current_usage": 0,
"expired_at": 1774000000000,
"name": "Bakso Enak- John Doe",
"merchant_reff_no": "INV-2026-001",
"min_amount": {
"value": "1234.56",
"currency": "IDR"
},
"max_amount": {
"value": "1234.56",
"currency": "IDR"
}
}
}{
"status": 400,
"success": false,
"error": {
"code": 400,
"message": "Bad Request."
}
}{
"status": 401,
"success": false,
"error": {
"code": 401,
"message": "Unauthorized merchant, please sign in"
}
}{
"status": 404,
"success": false,
"error": {
"code": 404,
"message": "Account not found."
}
}{
"status": 405,
"success": false,
"error": {
"code": 4059901,
"message": "The PATCH method is not supported for this route. Supported methods: GET, HEAD, POST."
}
}{
"status": 422,
"success": false,
"error": {
"code": 422,
"message": "Validation error.",
"errors": {
"field": [
"error message"
]
}
},
"data": {
"errors": {
"field": [
"error message"
]
}
}
}Authorizations
JWT issued by POST /api/v1.1/access-token/b2b. Send Authorization: Bearer <token>.
Merchant API key (Credential.api_key). Required on every request.
Path Parameters
Account identifier in ULID format.
Body
Issuing bank for the virtual account.
BCA, BNI, BRI, MANDIRI, PERMATA, MAYBANK, CIMB, BSI, MUAMALAT, BNC, OCBC, DANAMON "BRI"
Virtual account duration type.
temporary, permanent "temporary"
Amount model: closed (fixed amount) or open (min/max range). Defaults to closed when omitted.
open, closed "closed"
Display name for the virtual account.
200"Bakso Enak- John Doe"
Merchant reference number for this virtual account.
255"INV-2026-001"
Expiration time as a Unix timestamp in milliseconds (13 digits). Required when kind is temporary.
^\d{13}$"1774000000000"
Maximum number of successful payments allowed. Required when kind is temporary.
1 <= x <= 2551
Fixed payment amount. Required for closed virtual accounts. Minimum and maximum limits depend on merchant configuration.
100000
Minimum payment amount. Required for open virtual accounts.
10000
Maximum payment amount. Required for open virtual accounts and must be greater than or equal to min_amount.
100000
