Ledger Statement
The Ledger Statement provides a bank-style account statement showing all movements that affected your virtual account balance over a specified period.
What is a Ledger Statement?
Think of it like a bank statement for your virtual account:
- Opening Balance: Your balance at the start of the period
- Movements: Every debit and credit that occurred
- Running Balance: Balance after each movement
- Closing Balance: Your balance at the end of the period
Key Concepts
Movement Types
| Type | Description | Effect on Balance |
|---|---|---|
| credit | Funds added to account | Increases balance |
| debit | Funds removed from account | Decreases balance |
Movement Categories
| Category | Description |
|---|---|
PAYIN | Customer payment received |
PAYOUT | Provider payment sent |
BALANCE_TRANSFER | Currency exchange between accounts |
SETTLEMENT | Automatic withdrawal to bank |
ADJUSTMENT | Manual balance correction |
FEE | Service fee charged |
TAX | Tax withholding |
ONRAMP | Fiat used to buy crypto |
OFFRAMP | Crypto sold for fiat |
REVERSE | Transaction reversal |
OTHER | Other movement types |
API Endpoint
GET /api/v1/organizations/{organizationId}/merchants/{merchantId}/accounts/{accountId}/reports/ledger-statementPath Parameters
| Parameter | Required | Description |
|---|---|---|
organizationId | Yes | Organization ID |
merchantId | Yes | Merchant ID |
accountId | Yes | Virtual account ID |
Query Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
from | Yes | - | Start date (YYYY-MM-DD) |
to | Yes | - | End date (YYYY-MM-DD) |
granularity | No | daily | Date cutoff: daily or monthly |
cursor | No | - | Pagination cursor from previous response |
limit | No | 50 | Items per page (1-100) |
Quick Example
const response = await axios.get(
`https://api.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/accounts/${accountId}/reports/ledger-statement`,
{
params: {
from: '2025-01-01',
to: '2025-01-31'
},
headers: { 'Authorization': `Bearer ${token}` }
}
);
console.log('Opening Balance:', response.data.openingBalance);
console.log('Closing Balance:', response.data.closingBalance);
console.log('Movements:', response.data.movements.length);response = requests.get(
f'https://api.koywe.com/api/v1/organizations/{org_id}/merchants/{merchant_id}/accounts/{account_id}/reports/ledger-statement',
params={
'from': '2025-01-01',
'to': '2025-01-31'
},
headers={'Authorization': f'Bearer {token}'}
)
data = response.json()
print(f"Opening Balance: {data['openingBalance']}")
print(f"Closing Balance: {data['closingBalance']}")
print(f"Movements: {len(data['movements'])}")curl -X GET 'https://api.koywe.com/api/v1/organizations/org3_xxx/merchants/mrc_xxx/accounts/acc_xxx/reports/ledger-statement?from=2025-01-01&to=2025-01-31' \
-H 'Authorization: Bearer YOUR_TOKEN'Understanding the Response
{
"accountId": "acc_0031c537-2301-40ab-9153-0f7c48505350",
"currency": "CLP",
"merchantId": "mrc_2e8f96ab-dbd5-45f9-b4b6-645945daf340",
"periodStart": "2025-01-01T00:00:00.000Z",
"periodEnd": "2025-01-31T23:59:59.999Z",
"openingBalance": "4000000.00",
"closingBalance": "5500000.00",
"movements": [
{
"ledgerEntryId": "137",
"postedAt": "2025-01-15T10:30:00.000Z",
"type": "credit",
"amount": "1000000.00",
"currency": "CLP",
"runningBalance": "5000000.00",
"description": "PAYIN from Juan Pérez - Bank transfer received",
"orderId": "ord_abc123",
"category": "PAYIN"
},
{
"ledgerEntryId": "142",
"postedAt": "2025-01-20T14:15:00.000Z",
"type": "debit",
"amount": "500000.00",
"currency": "CLP",
"runningBalance": "4500000.00",
"description": "PAYOUT to Supplier Co - Invoice payment",
"orderId": "ord_def456",
"category": "PAYOUT"
}
],
"pagination": {
"cursor": "eyJpZCI6IjE0MiIsImRhdGUiOiIyMDI1LTAxLTIwVDE0OjE1OjAwLjAwMFoifQ==",
"hasMore": true,
"limit": 50
},
"generatedAt": "2025-01-31T12:00:00.000Z"
}Response Fields
| Field | Description |
|---|---|
openingBalance | Balance at period start |
closingBalance | Balance at period end |
movements[] | Array of individual movements |
movements[].ledgerEntryId | Unique ID (use for detailed receipt) |
movements[].runningBalance | Balance after this movement |
movements[].type | credit or debit |
movements[].category | Movement category (PAYIN, PAYOUT, etc.) |
pagination.cursor | Use for next page request |
pagination.hasMore | Whether more pages exist |
Pagination
The ledger statement uses cursor-based pagination. To retrieve all movements:
async function getAllMovements(orgId, merchantId, accountId, from, to, token) {
const allMovements = [];
let cursor = null;
do {
const response = await axios.get(
`https://api.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/accounts/${accountId}/reports/ledger-statement`,
{
params: {
from,
to,
limit: 100,
...(cursor && { cursor })
},
headers: { 'Authorization': `Bearer ${token}` }
}
);
const data = response.data;
allMovements.push(...data.movements);
cursor = data.pagination.hasMore ? data.pagination.cursor : null;
console.log(`Fetched ${data.movements.length} movements, total: ${allMovements.length}`);
} while (cursor);
return allMovements;
}
// Usage
const movements = await getAllMovements(orgId, merchantId, accountId, '2025-01-01', '2025-01-31', token);
console.log('Total movements:', movements.length);def get_all_movements(org_id, merchant_id, account_id, from_date, to_date, token):
all_movements = []
cursor = None
while True:
params = {
'from': from_date,
'to': to_date,
'limit': 100
}
if cursor:
params['cursor'] = cursor
response = requests.get(
f'https://api.koywe.com/api/v1/organizations/{org_id}/merchants/{merchant_id}/accounts/{account_id}/reports/ledger-statement',
params=params,
headers={'Authorization': f'Bearer {token}'}
)
data = response.json()
all_movements.extend(data['movements'])
print(f"Fetched {len(data['movements'])} movements, total: {len(all_movements)}")
if not data['pagination']['hasMore']:
break
cursor = data['pagination']['cursor']
return all_movements
# Usage
movements = get_all_movements(org_id, merchant_id, account_id, '2025-01-01', '2025-01-31', token)
print(f'Total movements: {len(movements)}')Granularity Options
The granularity parameter controls how date boundaries are applied:
| Granularity | Period Start | Period End |
|---|---|---|
daily | Start of day (00:00:00) | End of day (23:59:59) |
monthly | First day of month | Last day of month |
Use monthly granularity for month-end reconciliation reports to ensure consistent period boundaries.
Complete Integration Example
async function generateMonthlyStatement(orgId, merchantId, accountId, year, month, token) {
// Calculate date range for the month
const from = `${year}-${String(month).padStart(2, '0')}-01`;
const lastDay = new Date(year, month, 0).getDate();
const to = `${year}-${String(month).padStart(2, '0')}-${lastDay}`;
console.log(`Generating statement for ${from} to ${to}`);
// Fetch first page
const response = await axios.get(
`https://api.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/accounts/${accountId}/reports/ledger-statement`,
{
params: {
from,
to,
granularity: 'monthly',
limit: 100
},
headers: { 'Authorization': `Bearer ${token}` }
}
);
const statement = response.data;
// Summary
console.log('='.repeat(50));
console.log('MONTHLY STATEMENT');
console.log('='.repeat(50));
console.log(`Account: ${statement.accountId}`);
console.log(`Currency: ${statement.currency}`);
console.log(`Period: ${statement.periodStart} to ${statement.periodEnd}`);
console.log('-'.repeat(50));
console.log(`Opening Balance: ${statement.openingBalance}`);
console.log(`Closing Balance: ${statement.closingBalance}`);
console.log('-'.repeat(50));
// Calculate totals
let totalCredits = 0;
let totalDebits = 0;
statement.movements.forEach(m => {
const amount = parseFloat(m.amount);
if (m.type === 'credit') {
totalCredits += amount;
} else {
totalDebits += amount;
}
console.log(`${m.postedAt} | ${m.type.toUpperCase().padEnd(6)} | ${m.amount.padStart(15)} | ${m.category} | ${m.description.substring(0, 30)}`);
});
console.log('-'.repeat(50));
console.log(`Total Credits: ${totalCredits.toFixed(2)}`);
console.log(`Total Debits: ${totalDebits.toFixed(2)}`);
console.log(`Net Change: ${(totalCredits - totalDebits).toFixed(2)}`);
console.log('='.repeat(50));
return statement;
}
// Usage: Generate January 2025 statement
const statement = await generateMonthlyStatement(
'org3_xxx',
'mrc_xxx',
'acc_xxx',
2025,
1,
token
);Next Steps
Last updated on