> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zuba.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Ledger System

> Double-entry bookkeeping and transaction management

## Overview

The Zuba ledger system implements a robust double-entry bookkeeping system that tracks all financial movements across the platform. Every transaction is recorded as transfers between named accounts, providing complete audit trails and real-time balance tracking.

## Core Concepts

### Accounts

Accounts represent financial entities in the system and are identified by unique names with currency support. When querying your balances, you'll see:

```javascript Account Balance Response theme={"dark"}
{
  "currency": "EUR",
  "balance": 1250.50,
  "pendingIn": 100.00,
  "pendingOut": 50.00,
  "totalBalance": 1300.50,
  "isActive": true,
  "lastTransactionAt": "2026-01-12T12:00:00Z"
}
```

| Field               | Description                           |
| ------------------- | ------------------------------------- |
| `currency`          | ISO 4217 currency code                |
| `balance`           | Available balance (funds you can use) |
| `pendingIn`         | Incoming funds being processed        |
| `pendingOut`        | Outgoing funds being processed        |
| `totalBalance`      | Total balance including pending       |
| `isActive`          | Whether the account is active         |
| `lastTransactionAt` | Timestamp of most recent transaction  |

### Transactions

All financial movements are recorded as double-entry transactions. When querying transactions, you'll see them from your account's perspective as debits (money out) or credits (money in):

```javascript Transaction Structure theme={"dark"}
{
  "id": "572e779e-6d71-4bd7-91f1-57109c562b56",
  "type": "debit",
  "amount": 0.95411814,
  "currency": "EUR",
  "balanceAfter": 87.57104914,
  "counterparty": "ext:****3000",
  "status": "confirmed",
  "createdAt": "2026-01-12T12:50:48.792Z",
  "confirmedAt": "2026-01-12T12:50:48.791Z"
}
```

| Field          | Description                                                                                                                                                          |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`           | Unique transaction identifier                                                                                                                                        |
| `type`         | `debit` (money out) or `credit` (money in)                                                                                                                           |
| `amount`       | Transaction amount                                                                                                                                                   |
| `currency`     | ISO 4217 currency code                                                                                                                                               |
| `balanceAfter` | Your account balance after this transaction                                                                                                                          |
| `counterparty` | The external counterparty, masked (e.g. `ext:****3000`). Internal platform accounts are omitted, so this field is absent on internal transfers (such as fees and FX) |
| `isFee`        | `true` when the entry is a Zuba service fee; lets you label fee rows without inspecting account names                                                                |
| `status`       | Transaction status (`pending`, `confirmed`, `failed`, `cancelled`)                                                                                                   |
| `createdAt`    | When the transaction was created                                                                                                                                     |
| `confirmedAt`  | When the transaction was confirmed                                                                                                                                   |
| `payoutId`     | Present on payout entries — the originating payout's id                                                                                                              |
| `payoutStatus` | Present on payout entries — the payout's delivery status (`created`, `queued`, `processing`, `paid`, `failed`, `cancelled`)                                          |
| `txHash`       | Present on crypto entries — the on-chain transaction hash, for looking the transfer up on a block explorer                                                           |

<Note>
  A payout's ledger transaction is `confirmed` as soon as the funds are debited
  from your balance — it does not mean the beneficiary has been paid. Slow
  rails (e.g. SWIFT) can remain in flight for days after the debit confirms.
  Use `payoutStatus` to track delivery; `paid` means the funds arrived.
</Note>

## Transaction States

```mermaid theme={"dark"}
stateDiagram-v2
    [*] --> pending
    pending --> confirmed
    pending --> failed
    pending --> cancelled
    confirmed --> [*]
    failed --> [*]
    cancelled --> [*]
```

| Status      | Description                                | Actions Available  |
| ----------- | ------------------------------------------ | ------------------ |
| `pending`   | Transaction created, awaiting confirmation | Confirm, Cancel    |
| `confirmed` | Transaction successfully processed         | View only          |
| `failed`    | Transaction failed during processing       | Retry, Investigate |
| `cancelled` | Transaction cancelled before processing    | View only          |

## Balance Management

### Real-time Balance Calculation

Account balances are calculated in real-time from confirmed transactions:

```javascript theme={"dark"}
// Simplified balance calculation
const balance = confirmedCredits - confirmedDebits;
```

### Multi-Currency Support

Each account maintains balances in a single currency. When querying all balances, you'll receive a list of accounts for each currency:

```javascript Multi-Currency Response theme={"dark"}
{
  "userId": "auth0|123456",
  "clientId": "cc82fa1d-fc7a-478c-a734-d3bce40464e7",
  "balances": [
    { "currency": "EUR", "balance": 1000.00, "pendingIn": 0, "pendingOut": 0, "totalBalance": 1000.00 },
    { "currency": "USD", "balance": 1200.00, "pendingIn": 50.00, "pendingOut": 0, "totalBalance": 1250.00 }
  ],
  "totalValueEur": 2100.00,
  "primaryCurrency": "EUR"
}
```

## Payment Flow Integration

<Note>
  Ledger transactions are created automatically by the platform when processing deposits and payouts. You cannot create transactions directly - they are generated as a result of your payment operations.
</Note>

### Payin Transaction Flow

When a customer makes a deposit, the platform settles the funds internally and credits your account automatically. From your perspective it lands as a single credit:

```mermaid theme={"dark"}
flowchart LR
    A[External Provider] --> B[Platform Account]
    B --> C[Customer Account]
```

You'll see the credit appear in your transaction history. The internal account that funded it is omitted, so no `counterparty` is present:

```javascript Credit Transaction theme={"dark"}
{
  "id": "572e779e-6d71-4bd7-91f1-57109c562b56",
  "type": "credit",
  "amount": 100.00,
  "currency": "EUR",
  "balanceAfter": 1100.00,
  "status": "confirmed",
  "createdAt": "2026-01-12T12:00:00.000Z",
  "confirmedAt": "2026-01-12T12:00:00.000Z"
}
```

### Payout Transaction Flow

For payouts, the system creates transaction chains including FX conversion when needed:

```mermaid theme={"dark"}
sequenceDiagram
    participant Customer
    participant Platform
    participant FX
    participant Provider

    Customer->>Platform: Debit funds
    Platform->>FX: Convert currency (if needed)
    FX->>Provider: Send converted amount
    Provider-->>External: Final payout
```

You'll see the debit in your transaction history. The internal accounts used to route and convert the payout are omitted, so no `counterparty` is present:

```javascript Debit Transaction theme={"dark"}
{
  "id": "683f880f-7e82-5ce8-b202-68210d673c67",
  "type": "debit",
  "amount": 100.00,
  "currency": "EUR",
  "balanceAfter": 900.00,
  "status": "confirmed",
  "createdAt": "2026-01-12T14:30:00.000Z",
  "confirmedAt": "2026-01-12T14:30:00.000Z"
}
```

## Querying the Ledger

### Account Balance

<CodeGroup>
  ```bash curl theme={"dark"}
  curl -X GET "https://api.sandbox.zuba.com/v1/ledger/balances/EUR" \
    -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
  ```

  ```javascript JavaScript theme={"dark"}
  const balance = await fetch('https://api.sandbox.zuba.com/v1/ledger/balances/EUR', {
    headers: {
      'Authorization': 'Bearer YOUR_ACCESS_TOKEN'
    }
  });

  const data = await balance.json();
  console.log(`Balance: ${data.balance} ${data.currency}`);
  ```

  ```python Python theme={"dark"}
  import requests

  balance = requests.get(
      'https://api.sandbox.zuba.com/v1/ledger/balances/EUR',
      headers={
          'Authorization': 'Bearer YOUR_ACCESS_TOKEN'
      }
  )

  data = balance.json()
  print(f"Balance: {data['balance']} {data['currency']}")
  ```

  ```java Java theme={"dark"}
  HttpClient client = HttpClient.newHttpClient();
  HttpRequest request = HttpRequest.newBuilder()
      .uri(URI.create("https://api.sandbox.zuba.com/v1/ledger/balances/EUR"))
      .header("Authorization", "Bearer YOUR_ACCESS_TOKEN")
      .GET()
      .build();

  HttpResponse<String> response = client.send(request,
      HttpResponse.BodyHandlers.ofString());

  JsonObject data = new Gson().fromJson(response.body(), JsonObject.class);
  System.out.println("Balance: " + data.get("balance").getAsString() +
      " " + data.get("currency").getAsString());
  ```
</CodeGroup>

### Transaction History

<CodeGroup>
  ```bash curl theme={"dark"}
  curl -X GET "https://api.sandbox.zuba.com/v1/ledger/transactions?currency=EUR&limit=50" \
    -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
  ```

  ```javascript JavaScript theme={"dark"}
  const response = await fetch('https://api.sandbox.zuba.com/v1/ledger/transactions?currency=EUR&limit=50', {
    headers: {
      'Authorization': 'Bearer YOUR_ACCESS_TOKEN'
    }
  });

  const data = await response.json();
  data.transactions.forEach(tx => {
    const direction = tx.type === 'credit' ? '+' : '-';
    console.log(`${direction}${tx.amount} ${tx.currency} → Balance: ${tx.balanceAfter}`);
  });
  ```

  ```python Python theme={"dark"}
  import requests

  response = requests.get(
      'https://api.sandbox.zuba.com/v1/ledger/transactions',
      params={'currency': 'EUR', 'limit': 50},
      headers={
          'Authorization': 'Bearer YOUR_ACCESS_TOKEN'
      }
  )

  data = response.json()
  for tx in data['transactions']:
      direction = '+' if tx['type'] == 'credit' else '-'
      print(f"{direction}{tx['amount']} {tx['currency']} → Balance: {tx['balanceAfter']}")
  ```

  ```java Java theme={"dark"}
  HttpClient client = HttpClient.newHttpClient();
  HttpRequest request = HttpRequest.newBuilder()
      .uri(URI.create("https://api.sandbox.zuba.com/v1/ledger/transactions?currency=EUR&limit=50"))
      .header("Authorization", "Bearer YOUR_ACCESS_TOKEN")
      .GET()
      .build();

  HttpResponse<String> response = client.send(request,
      HttpResponse.BodyHandlers.ofString());

  JsonObject data = new Gson().fromJson(response.body(), JsonObject.class);
  JsonArray transactions = data.getAsJsonArray("transactions");
  transactions.forEach(tx -> {
      JsonObject transaction = tx.getAsJsonObject();
      String direction = transaction.get("type").getAsString().equals("credit") ? "+" : "-";
      System.out.println(direction + transaction.get("amount").getAsString() + " " +
          transaction.get("currency").getAsString() + " → Balance: " +
          transaction.get("balanceAfter").getAsString());
  });
  ```
</CodeGroup>

## Audit & Compliance

### Audit Trail

Every transaction maintains a complete audit trail:

* **Immutable Records**: Transactions cannot be modified once confirmed
* **Timestamps**: All operations are timestamped with precision
* **Metadata**: Additional context stored with each transaction
* **Reference IDs**: Link to external provider transactions

### Compliance Features

<AccordionGroup>
  <Accordion title="Financial Reconciliation">
    * Daily balance reconciliation across all accounts
    * Provider settlement matching and verification
    * Automated discrepancy detection and alerts
    * Historical balance reconstruction capabilities
  </Accordion>

  <Accordion title="Regulatory Reporting">
    * Transaction history export for audits
    * Real-time balance monitoring and limits
    * Suspicious activity pattern detection
    * Automated regulatory filing support
  </Accordion>

  <Accordion title="Data Integrity">
    * Double-entry validation on every transaction
    * Balance consistency checks across all accounts
    * Automated reconciliation with external providers
    * Immutable transaction records with cryptographic hashing
  </Accordion>
</AccordionGroup>

## Advanced Features

### Currency Conversion Tracking

When a payout converts currency, you still see a single debit on your account. It carries both the amount debited from your balance and the amount sent to the beneficiary, via the `sourceAmount`/`sourceCurrency` and `payoutAmount`/`payoutCurrency` fields. Internal FX accounts are not surfaced, so no `counterparty` is present:

```javascript FX Payout Debit theme={"dark"}
{
  "id": "572e779e-6d71-4bd7-91f1-57109c562b56",
  "type": "debit",
  "amount": 100.00,
  "currency": "EUR",
  "balanceAfter": 900.00,
  "sourceAmount": 100.00,
  "sourceCurrency": "EUR",
  "payoutAmount": 108.00,
  "payoutCurrency": "USD",
  "status": "confirmed",
  "createdAt": "2026-01-12T12:50:48.792Z",
  "confirmedAt": "2026-01-12T12:50:48.791Z"
}
```

The FX rate applied to your transaction is visible in the payout details, not the ledger transaction itself.

### Account Statements

For detailed reconciliation, you can generate account statements for a specific period:

<CodeGroup>
  ```bash curl theme={"dark"}
  curl -X GET "https://api.sandbox.zuba.com/v1/ledger/statement/EUR?startDate=2026-01-01&endDate=2026-01-31" \
    -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
  ```

  ```javascript JavaScript theme={"dark"}
  const response = await fetch(
    'https://api.sandbox.zuba.com/v1/ledger/statement/EUR?startDate=2026-01-01&endDate=2026-01-31',
    {
      headers: {
        'Authorization': 'Bearer YOUR_ACCESS_TOKEN'
      }
    }
  );

  const statement = await response.json();
  console.log(`Opening balance: ${statement.openingBalance}`);
  console.log(`Closing balance: ${statement.closingBalance}`);
  console.log(`Total credits: ${statement.totalCredits}`);
  console.log(`Total debits: ${statement.totalDebits}`);
  ```
</CodeGroup>

**Statement Response:**

```javascript Account Statement theme={"dark"}
{
  "accountId": "123e4567-e89b-12d3-a456-426614174000",
  "accountName": "user:cc82fa1d-fc7a-478c-a734-d3bce40464e7",
  "currency": "EUR",
  "periodStart": "2026-01-01",
  "periodEnd": "2026-01-31",
  "openingBalance": "1000.00",
  "closingBalance": "1500.50",
  "totalCredits": "600.50",
  "totalDebits": "100.00",
  "transactions": [
    {
      "date": "2026-01-15T10:00:00Z",
      "credit": "500.00",
      "balanceAfter": "1500.00",
      "reference": "DEPOSIT-001",
      "transactionId": "abc123..."
    }
  ],
  "generatedAt": "2026-01-31T12:00:00Z"
}
```

## Error Handling

### Transaction Failures

When a payout or deposit fails, you may see a failed transaction in your history:

```javascript Failed Transaction theme={"dark"}
{
  "id": "572e779e-6d71-4bd7-91f1-57109c562b56",
  "type": "debit",
  "amount": 100.00,
  "currency": "EUR",
  "balanceAfter": 1000.00,
  "status": "failed",
  "createdAt": "2026-01-12T12:50:48.792Z"
}
```

Failed transactions do not affect your available balance. The platform automatically handles any necessary reversals.

### Common Error Scenarios

| Error               | Description                           | Resolution                     |
| ------------------- | ------------------------------------- | ------------------------------ |
| Insufficient funds  | Not enough balance to complete payout | Add funds to your account      |
| Invalid beneficiary | Beneficiary details are incorrect     | Update beneficiary information |
| Provider error      | External provider issue               | Retry or contact support       |

## Integration Guidelines

<Note>
  Ledger transactions are created automatically by the platform when you create payouts or receive deposits. You cannot create transactions directly - use the [Payouts API](/concepts/payouts) to initiate transfers.
</Note>

### Best Practices

<AccordionGroup>
  <Accordion title="Balance Monitoring">
    * Check balances before initiating payouts to avoid failures
    * Set up [webhooks](/guides/webhooks) for real-time balance notifications
    * Use the statement endpoint for reconciliation
    * Monitor pending amounts for cash flow planning
  </Accordion>

  <Accordion title="Reconciliation">
    * Generate statements periodically for your records
    * Match ledger transactions with your internal systems
    * Keep track of pending transactions for accurate cash positions
    * Export transaction history for accounting integration
  </Accordion>

  <Accordion title="API Usage">
    * Use pagination (`limit` and `offset`) for large transaction histories
    * Filter by currency when querying specific account activity
    * Cache balance responses where real-time accuracy isn't required
    * Handle rate limits gracefully with exponential backoff
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="First Payout Guide" icon="rocket" href="/guides/first-payout">
    Send your first payout with the Zuba API
  </Card>

  <Card title="Webhooks" icon="webhook" href="/guides/webhooks">
    Set up real-time notifications for payment events
  </Card>

  <Card title="Batch Payouts" icon="layer-group" href="/guides/batch-payouts">
    Process multiple payouts efficiently
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/introduction">
    Complete API documentation
  </Card>
</CardGroup>
