Payments
Record Offline Payment
Record a payment received outside of online channels such as bank transfer, cash, or cheque.
POST
/
v1
/
payments
/
offline
Record an offline payment
curl --request POST \
--url https://billing.example.com/v1/payments/offline \
--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",
"tds_amount": 0,
"currency": "INR",
"reference_number": "<string>",
"notes": "<string>",
"recorded_by": "<string>"
}
'import requests
url = "https://billing.example.com/v1/payments/offline"
payload = {
"customer_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"amount": 123,
"invoice_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"tds_amount": 0,
"currency": "INR",
"reference_number": "<string>",
"notes": "<string>",
"recorded_by": "<string>"
}
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',
tds_amount: 0,
currency: 'INR',
reference_number: '<string>',
notes: '<string>',
recorded_by: '<string>'
})
};
fetch('https://billing.example.com/v1/payments/offline', 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/payments/offline",
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',
'tds_amount' => 0,
'currency' => 'INR',
'reference_number' => '<string>',
'notes' => '<string>',
'recorded_by' => '<string>'
]),
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/payments/offline"
payload := strings.NewReader("{\n \"customer_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"amount\": 123,\n \"invoice_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"tds_amount\": 0,\n \"currency\": \"INR\",\n \"reference_number\": \"<string>\",\n \"notes\": \"<string>\",\n \"recorded_by\": \"<string>\"\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/payments/offline")
.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 \"tds_amount\": 0,\n \"currency\": \"INR\",\n \"reference_number\": \"<string>\",\n \"notes\": \"<string>\",\n \"recorded_by\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://billing.example.com/v1/payments/offline")
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 \"tds_amount\": 0,\n \"currency\": \"INR\",\n \"reference_number\": \"<string>\",\n \"notes\": \"<string>\",\n \"recorded_by\": \"<string>\"\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",
"payment_type": "bank_transfer",
"amount": 123,
"tds_amount": 123,
"currency": "<string>",
"reference_number": "<string>",
"notes": "<string>",
"recorded_by": "<string>",
"recorded_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 who made the payment. |
invoice_id | string | No | The invoice this payment applies to. If provided, the invoice balance is updated. |
payment_type | string | Yes | The type of offline payment. One of bank_transfer, cash, or cheque. |
amount | integer (int64) | Yes | Payment amount in smallest currency unit (paise). Must be greater than 0. |
currency | string | No | Three-letter ISO currency code (default INR). |
reference_number | string | No | External reference such as UTR number, cheque number, or receipt ID. |
notes | string | No | Free-text notes about the payment (max 500 characters). |
recorded_by | string | No | Email or user ID of the staff member recording this payment. |
Example Request
curl -X POST https://api.recurso.dev/v1/payments/offline \
-H "Authorization: Bearer rsk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cust_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"invoice_id": "inv_5d4c3b2a1f0e9876",
"payment_type": "bank_transfer",
"amount": 249900,
"currency": "INR",
"reference_number": "UTR-ICIC-20260623-001234",
"notes": "NEFT payment received from HDFC account ending 4521",
"recorded_by": "finance@acmecorp.com"
}'
Response
Returns the recorded offline payment object.{
"id": "pay_3c2b1a0f9e8d7654",
"object": "payment",
"customer_id": "cust_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"invoice_id": "inv_5d4c3b2a1f0e9876",
"payment_type": "bank_transfer",
"amount": 249900,
"currency": "INR",
"reference_number": "UTR-ICIC-20260623-001234",
"notes": "NEFT payment received from HDFC account ending 4521",
"recorded_by": "finance@acmecorp.com",
"status": "succeeded",
"channel": "offline",
"created_at": "2026-06-23T10:15:30Z",
"updated_at": "2026-06-23T10:15:30Z"
}
When an
invoice_id is provided and the payment amount matches the invoice balance,
the invoice is automatically marked as paid. Partial payments update the invoice
balance without closing it.Errors
| Status | Code | When | Fix |
|---|---|---|---|
| 400 | validation_failed | The request body is malformed or fails binding, customer_id is not a valid UUID, or invoice_id (when provided) is not a valid UUID. | Send a well-formed JSON body with valid UUIDs. |
| 401 | unauthorized | API key missing or invalid (invalid_api_key), or a live/test mode mismatch (key_mode_mismatch). | Send Authorization: Bearer <api_key> with a key for the right mode. |
| 500 | internal_error | Recording failed — e.g. the referenced customer or invoice does not exist in your tenant. | Verify the customer and invoice IDs, then retry. |
Authorizations
Tenant API key obtained from POST /auth/register or POST /v1/developer/keys.
Body
application/json
Available options:
bank_transfer, cash, cheque Amount in the lowest currency unit.
Tax deducted at source by the customer (India B2B), in the lowest currency unit. Requires invoice_id; counts toward settling the invoice and is booked to TDS Receivable in the ledger, but is not part of the cash received. The deduction cannot exceed the invoice's outstanding balance.
Required range:
x >= 0Response
Payment recorded.
Available options:
bank_transfer, cash, cheque Tax deducted at source by the customer on this receipt.
⌘I
Record an offline payment
curl --request POST \
--url https://billing.example.com/v1/payments/offline \
--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",
"tds_amount": 0,
"currency": "INR",
"reference_number": "<string>",
"notes": "<string>",
"recorded_by": "<string>"
}
'import requests
url = "https://billing.example.com/v1/payments/offline"
payload = {
"customer_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"amount": 123,
"invoice_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"tds_amount": 0,
"currency": "INR",
"reference_number": "<string>",
"notes": "<string>",
"recorded_by": "<string>"
}
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',
tds_amount: 0,
currency: 'INR',
reference_number: '<string>',
notes: '<string>',
recorded_by: '<string>'
})
};
fetch('https://billing.example.com/v1/payments/offline', 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/payments/offline",
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',
'tds_amount' => 0,
'currency' => 'INR',
'reference_number' => '<string>',
'notes' => '<string>',
'recorded_by' => '<string>'
]),
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/payments/offline"
payload := strings.NewReader("{\n \"customer_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"amount\": 123,\n \"invoice_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"tds_amount\": 0,\n \"currency\": \"INR\",\n \"reference_number\": \"<string>\",\n \"notes\": \"<string>\",\n \"recorded_by\": \"<string>\"\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/payments/offline")
.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 \"tds_amount\": 0,\n \"currency\": \"INR\",\n \"reference_number\": \"<string>\",\n \"notes\": \"<string>\",\n \"recorded_by\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://billing.example.com/v1/payments/offline")
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 \"tds_amount\": 0,\n \"currency\": \"INR\",\n \"reference_number\": \"<string>\",\n \"notes\": \"<string>\",\n \"recorded_by\": \"<string>\"\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",
"payment_type": "bank_transfer",
"amount": 123,
"tds_amount": 123,
"currency": "<string>",
"reference_number": "<string>",
"notes": "<string>",
"recorded_by": "<string>",
"recorded_at": "2023-11-07T05:31:56Z"
}{
"error": {
"code": "BAD_REQUEST",
"message": "<string>"
}
}{
"error": {
"code": "BAD_REQUEST",
"message": "<string>"
}
}