Merchant Ledger Entry
Create a debit or credit entry in a merchant's cash ledger.
/v1/route/ledger-entryCreates a single entry in the merchant ledger and returns the resulting running balance.
The request is authenticated with your API key and a per-request SHA-256 hash, exactly like the
Transaction Status API — the key and
hash travel in the request headers.
Authentication & Hash Generation
This endpoint is authenticated with your API key plus a per-request SHA-256 hash. Send the key in the
X-API-Key header and the hash in the Hash header.
Hash = SHA256(key | amount | idempotency_key | salt), joined with the | character. idempotency_key is required. The salt is your merchant secret key — it is used only to compute the hash and must never be sent in the request. See the Authentication guide.
Compute the hash using the amount string exactly as it appears in the request body (e.g. "1000.00"). A different string such as "1000.0" produces a different hash and fails validation.
Headers
| Header | Type | Required | Description |
|---|---|---|---|
| X-API-Key | string | Required | Your merchant API key. |
| Hash | string | Required | SHA-256 auth hash — SHA256(key | amount | idempotency_key | salt). |
| Content-Type | string | Required | Must be application/json. |
Request Parameters
| Field Name | Data Type | Required | Description | Allowed Values | Example |
|---|---|---|---|---|---|
| amount | Decimal | Required | Transaction amount in rupees (two-decimal precision). Included in the auth hash. | Greater than 0, ≤ 13 digits | 1000.00 |
| ledger_type | Integer | Required | Ledger entry direction. | 1 = Debit, 2 = Credit |
2 |
| bank_transaction_id | String | Optional | Optional bank transaction id. When supplied, it must be unique per merchant. | ≤ 255 chars, unique per merchant | BANKTXN1234567 |
| bank_reference_number | String | Optional | Bank reference number (e.g. UTR). | ≤ 255 chars | UTR1234567890 |
| bank_narration | String | Optional | Narration / description for the entry. | — | NEFT credit from customer |
| value_date | Date | Optional | Bank value date. | Format YYYY-MM-DD |
2026-07-13 |
| transaction_date | DateTime | Optional | Transaction date-time. | ISO 8601 | 2026-07-13T22:20:00Z |
| customer_name | String | Optional | Customer / counterparty name. | — | Jane Doe |
| customer_account_number | String | Optional | Customer account number. | — | 1234567890 |
| customer_bank_name | String | Optional | Customer bank name. | — | HDFC Bank |
| customer_ifsc | String | Optional | Customer IFSC code. | — | HDFC0000123 |
| source | String | Optional | Origin of the entry. Defaults to manual. |
transaction, email, manual, manual_cash, settlement, csv_import, swift |
manual |
| message_type | String | Optional | Message type (e.g. NEFT / IMPS / RTGS). | — | NEFT |
| idempotency_key | String | Required | Key for safe retries — included in the auth hash. Must be unique per merchant; reusing one is rejected with 409. | ≤ 128 chars, unique per merchant | CRM-1 |
| reference_type | String | Optional | Business reference type stored on the entry. | ≤ 64 chars | MANUAL_CASH_ENTRY |
| reference_id | String | Optional | Business reference id stored on the entry. | ≤ 255 chars | CRM-REF-1 |
| remarks | String | Optional | Free-text remarks / description stored on the entry. | — | Cash received through merchant CRM |
| metadata | Object | Optional | Arbitrary JSON object stored with the entry. | JSON object | {"source":"merchant_crm"} |
Sample Request
Compute the hash, then POST the JSON body:
# 1. Compute the auth hash: sha256(key|amount|idempotency_key|salt)
API_KEY="YOUR_API_KEY"
API_SALT="YOUR_API_SALT"
AMOUNT="1000.00"
IDEMPOTENCY_KEY="CRM-1"
HASH=$(printf '%s' "$API_KEY|$AMOUNT|$IDEMPOTENCY_KEY|$API_SALT" | openssl dgst -sha256 | awk '{print $2}')
# 2. Call the API (key + hash travel in the request headers)
curl -X POST "https://api.growthbnk.com/v1/route/ledger-entry" \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-H "Hash: $HASH" \
-d '{
"amount": "'"$AMOUNT"'",
"idempotency_key": "'"$IDEMPOTENCY_KEY"'",
"ledger_type": 2,
"bank_reference_number": "UTR1234567890",
"remarks": "Cash received through merchant CRM",
"source": "manual"
}'import hashlib
import requests
API_KEY = "YOUR_API_KEY"
API_SALT = "YOUR_API_SALT"
BASE_URL = "https://api.growthbnk.com"
amount = "1000.00"
idempotency_key = "CRM-1"
# Auth hash: sha256(key|amount|idempotency_key|salt)
signing = f"{API_KEY}|{amount}|{idempotency_key}|{API_SALT}"
auth_hash = hashlib.sha256(signing.encode("utf-8")).hexdigest()
payload = {
"amount": amount,
"idempotency_key": idempotency_key,
"ledger_type": 2, # 1 = Debit, 2 = Credit
"bank_reference_number": "UTR1234567890",
"remarks": "Cash received through merchant CRM",
"source": "manual",
}
response = requests.post(
f"{BASE_URL}/v1/route/ledger-entry",
json=payload,
headers={
"Content-Type": "application/json",
"X-API-Key": API_KEY,
"Hash": auth_hash,
},
timeout=30,
)
print(response.status_code, response.json())<?php
$apiKey = 'YOUR_API_KEY';
$apiSalt = 'YOUR_API_SALT';
$baseUrl = 'https://api.growthbnk.com';
$amount = '1000.00';
$idempotencyKey = 'CRM-1';
// Auth hash: sha256(key|amount|idempotency_key|salt)
$signing = "{$apiKey}|{$amount}|{$idempotencyKey}|{$apiSalt}";
$authHash = hash('sha256', $signing);
$payload = [
'amount' => $amount,
'idempotency_key' => $idempotencyKey,
'ledger_type' => 2, // 1 = Debit, 2 = Credit
'bank_reference_number' => 'UTR1234567890',
'remarks' => 'Cash received through merchant CRM',
'source' => 'manual',
];
$ch = curl_init("{$baseUrl}/v1/route/ledger-entry");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
"X-API-Key: {$apiKey}",
"Hash: {$authHash}",
],
CURLOPT_POSTFIELDS => json_encode($payload),
]);
$response = curl_exec($ch);
echo $response;Request body
{
"amount": "1000.00",
"bank_transaction_id": "BANKTXN1234567",
"ledger_type": 2,
"bank_reference_number": "UTR1234567890",
"bank_narration": "NEFT credit from customer",
"value_date": "2026-07-13",
"transaction_date": "2026-07-13T22:20:00Z",
"customer_name": "Jane Doe",
"customer_account_number": "1234567890",
"customer_bank_name": "HDFC Bank",
"customer_ifsc": "HDFC0000123",
"source": "manual",
"message_type": "NEFT",
"idempotency_key": "CRM-1",
"reference_type": "MANUAL_CASH_ENTRY",
"reference_id": "CRM-REF-1",
"remarks": "Cash received through merchant CRM",
"metadata": {
"source": "merchant_crm"
}
}Response Format
A successful call returns HTTP 201 Created with the created entry and its running balance.
| Field | Type | Description |
|---|---|---|
| success | boolean | Always true on success. |
| message | string | Human-readable status message. |
| data.ledger_entry_id | string | Unique id (UUID) of the created ledger entry. |
| data.ledger_type | integer | 1 = Debit, 2 = Credit. |
| data.amount | string | Entry amount in rupees (two decimals). |
| data.opening_balance | string | Merchant balance before this entry. |
| data.closing_balance | string | Merchant balance after this entry. |
| data.bank_transaction_id | string | Echo of the supplied bank transaction id. |
| data.source | string | Source of the entry. |
| data.created_at | string | ISO-8601 creation timestamp. |
Sample Success Response
{
"success": true,
"message": "Ledger entry created successfully",
"data": {
"ledger_entry_id": "b3f1c2a4-8d7e-4f2b-9a1c-6e5d4c3b2a10",
"ledger_type": 2,
"amount": "1000.00",
"opening_balance": "5000.00",
"closing_balance": "6000.00",
"bank_transaction_id": "BANKTXN1234567",
"source": "manual",
"created_at": "2026-07-13T22:20:00Z"
}
}Error Responses
{
"detail": "Invalid authentication hash."
}{
"detail": "Unauthorized."
}{
"detail": "Ledger entry with this idempotency_key already exists."
}{
"success": false,
"message": {
"ledger_type": [
"\"5\" is not a valid choice."
]
}
}{
"success": false,
"message": "bank_transaction_id already exists"
}{
"success": false,
"message": "Internal server error"
}Status Codes
| Status | Meaning |
|---|---|
| 201 Created | Ledger entry created successfully. |
| 400 Bad Request | Missing or invalid parameters (missing amount/idempotency_key or a serializer validation error). |
| 401 Unauthorized | Missing/invalid API key or mismatched authentication hash. |
| 409 Conflict | The idempotency_key (or a supplied bank_transaction_id) already exists for the merchant. |
| 500 Internal Server Error | Unexpected server error. |
Error Codes
| Status | Message | Reason |
|---|---|---|
| 401 | Invalid authentication hash. | Computed hash does not match key|amount|idempotency_key|salt. |
| 401 | Unauthorized. | No merchant is registered for the supplied API key. |
| 400 | amount and idempotency_key are required. | A required auth/hash field is missing from the body. |
| 409 | Ledger entry with this idempotency_key already exists. | The idempotency_key was already used by this merchant — the entry is not created again. |
| 409 | bank_transaction_id already exists | A supplied bank_transaction_id already exists for this merchant. |
| 400 | Validation error | A serializer field has the wrong type / choice. |
| 500 | Internal server error | An unexpected error occurred while creating the entry. |
Notes
- The hash is computed over
key | amount | idempotency_key | salt— send theamountexactly as you hashed it. - The
saltis your server-side secret; never expose it to the browser or mobile app, and never send it in the request. idempotency_keyis required and must be unique per merchant. Reusing one returns409 Ledger entry with this idempotency_key already exists— the entry is not created again, which makes retries safe.bank_transaction_idis optional. When supplied it must be unique per merchant; reusing one returns409 bank_transaction_id already exists. When omitted it is left empty.- Remaining ledger fields (running balances, currency, reconciliation status, fingerprint) are populated automatically by GrowthBnk.