Virtual Accounts
Create Virtual Account
Create a virtual bank account for collecting payments via bank transfer.
POST
/
v1
/
virtual-accounts
Create a virtual account
curl --request POST \
--url https://billing.example.com/v1/virtual-accounts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"customer_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"amount": 123,
"invoice_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
'import requests
url = "https://billing.example.com/v1/virtual-accounts"
payload = {
"customer_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"amount": 123,
"invoice_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
customer_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
amount: 123,
invoice_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a'
})
};
fetch('https://billing.example.com/v1/virtual-accounts', 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://billing.example.com/v1/virtual-accounts",
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_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'amount' => 123,
'invoice_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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://billing.example.com/v1/virtual-accounts"
payload := strings.NewReader("{\n \"customer_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"amount\": 123,\n \"invoice_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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://billing.example.com/v1/virtual-accounts")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"customer_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"amount\": 123,\n \"invoice_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://billing.example.com/v1/virtual-accounts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"customer_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"amount\": 123,\n \"invoice_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}"
response = http.request(request)
puts response.read_body{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"tenant_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"customer_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"invoice_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"account_number": "<string>",
"ifsc_code": "<string>",
"bank_name": "<string>",
"beneficiary_name": "<string>",
"razorpay_va_id": "<string>",
"status": "<string>",
"amount_expected": 123,
"amount_received": 123,
"closed_at": "2023-11-07T05:31:56Z",
"created_at": "2023-11-07T05:31:56Z"
}{
"error": {
"code": "BAD_REQUEST",
"message": "<string>"
}
}{
"error": {
"code": "BAD_REQUEST",
"message": "<string>"
}
}Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
customer_id | string (UUID) | Yes | The customer this virtual account is created for. |
invoice_id | string | No | Associate the virtual account with a specific invoice for auto-reconciliation. |
amount | integer (int64) | Yes | Expected payment amount in smallest currency unit (paise). Must be greater than 0. |
Example Request
curl -X POST https://api.recurso.dev/v1/virtual-accounts \
-H "Authorization: Bearer rsk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cust_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"invoice_id": "inv_5d4c3b2a1f0e9876",
"amount": 249900
}'
Response
Returns the newly created virtual account with bank details.{
"id": "va_8e7d6c5b4a3f2109",
"object": "virtual_account",
"customer_id": "cust_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"invoice_id": "inv_5d4c3b2a1f0e9876",
"amount": 249900,
"amount_received": 0,
"account_number": "9876540012345678",
"ifsc_code": "RATN0VAAPIS",
"bank_name": "RBL Bank",
"beneficiary_name": "Recurso Payments Pvt Ltd",
"status": "active",
"expires_at": "2026-07-23T23:59:59Z",
"created_at": "2026-06-23T10:15:30Z",
"updated_at": "2026-06-23T10:15:30Z"
}
When the customer transfers the exact amount to the virtual account, the linked
invoice is automatically marked as paid. Partial payments update
amount_received
but do not close the invoice.Virtual accounts expire after 30 days by default. After expiry, any transfer to the
account will be returned to the sender.
Authorizations
Tenant API key obtained from POST /auth/register or POST /v1/developer/keys.
Body
application/json
Response
Virtual account created.
⌘I
Create a virtual account
curl --request POST \
--url https://billing.example.com/v1/virtual-accounts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"customer_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"amount": 123,
"invoice_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
'import requests
url = "https://billing.example.com/v1/virtual-accounts"
payload = {
"customer_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"amount": 123,
"invoice_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
customer_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
amount: 123,
invoice_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a'
})
};
fetch('https://billing.example.com/v1/virtual-accounts', 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://billing.example.com/v1/virtual-accounts",
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_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'amount' => 123,
'invoice_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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://billing.example.com/v1/virtual-accounts"
payload := strings.NewReader("{\n \"customer_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"amount\": 123,\n \"invoice_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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://billing.example.com/v1/virtual-accounts")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"customer_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"amount\": 123,\n \"invoice_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://billing.example.com/v1/virtual-accounts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"customer_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"amount\": 123,\n \"invoice_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}"
response = http.request(request)
puts response.read_body{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"tenant_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"customer_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"invoice_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"account_number": "<string>",
"ifsc_code": "<string>",
"bank_name": "<string>",
"beneficiary_name": "<string>",
"razorpay_va_id": "<string>",
"status": "<string>",
"amount_expected": 123,
"amount_received": 123,
"closed_at": "2023-11-07T05:31:56Z",
"created_at": "2023-11-07T05:31:56Z"
}{
"error": {
"code": "BAD_REQUEST",
"message": "<string>"
}
}{
"error": {
"code": "BAD_REQUEST",
"message": "<string>"
}
}