# Ledger

Ledgers let you track the balance of any amount by posting transactions to them. A ledger can represent a bank account, a digital wallet, an inventory product — anything whose balance you need to keep track of.

Each LedgerTransaction moves balance in or out of a ledger and returns the resulting running balance, so your books always reflect the latest state. Rules let you cap a ledger's balance.

NOTE: Read Core Concepts before continuing this guide.

**RESOURCE SUMMARY**

LedgerTrack the balance of an amount.Ledger RuleBound a ledger's balance with a minimum or maximum.Ledger TransactionMove balance in or out of a ledger; the response returns the new running balance.

## Setup

For each environment (Sandbox or Production):

1. Create an account at Stark Bank.

2. Get in touch with your account manager to enable the Ledger product to your account.

## Ledger Overview

Here we show you how to create a ledger and keep its tags, metadata and rules up to date.

### Creating a Ledger

Create a ledger with a unique externalId of your own — use it to reference the ledger from your system. You can also attach tags for searching and a free-form metadata object.

You can create up to 100 ledgers in a single request.

**Request**

```python
import starkinfra

ledgers = starkinfra.ledger.create([
    starkinfra.Ledger(
        external_id="my-internal-id-123456",
        tags=["account/123", "savings"],
        metadata={"accountId": "123"}
    )
])

for ledger in ledgers:
    print(ledger)
```

**Response**

```python
[
    Ledger(
        id=5656565656565656,
        external_id=my-internal-id-123456,
        rules=[],
        tags=['account/123', 'savings'],
        metadata={'accountId': '123'},
        created=2022-01-01 00:00:00,
        updated=2022-01-01 00:00:00
    )
]
```

### Updating a Ledger

Update an existing ledger by its id. You can change its tags, metadata or rules — the example below replaces the ledger's tags.

**Request**

```python
import starkinfra

ledger = starkinfra.ledger.update(
    "5656565656565656",
    tags=["account/123", "closed"]
)

print(ledger)
```

**Response**

```python
Ledger(
    id=5656565656565656,
    external_id=my-internal-id-123456,
    rules=[],
    tags=['account/123', 'closed'],
    metadata={'accountId': '123'},
    created=2022-01-01 00:00:00,
    updated=2022-06-01 00:00:00
)
```

## Ledger Rule Overview

Rules bound the balance a ledger can reach and are validated on every transaction. Here we show you how to restrict a ledger's minimum and maximum balance.

### Restricting a ledger minimum balance

Add a minimumBalance rule to stop the balance from dropping below a floor. Setting it to 0, for example, prevents the ledger from ever going negative.

The example below sets rules at creation time; you can also modify them later with an update.

**Request**

```python
import starkinfra

ledgers = starkinfra.ledger.create([
    starkinfra.Ledger(
        external_id="my-internal-id-123456",
        rules=[
            starkinfra.ledger.Rule(
                key="minimumBalance",
                value=0
            )
        ]
    )
])

for ledger in ledgers:
    print(ledger)
```

**Response**

```python
[
    Ledger(
        id=5656565656565656,
        external_id=my-internal-id-123456,
        rules=[
            Rule(
                key=minimumBalance,
                value=0
            )
        ],
        tags=[],
        metadata={},
        created=2022-01-01 00:00:00,
        updated=2022-01-01 00:00:00
    )
]
```

### Restricting a ledger maximum balance

Add a maximumBalance rule to cap how high the balance can go. Any transaction that would exceed the cap is rejected.

The example below adds the rule to an existing ledger with an update.

**Request**

```python
import starkinfra

ledger = starkinfra.ledger.update(
    "5656565656565656",
    rules=[
        starkinfra.ledger.Rule(
            key="maximumBalance",
            value=10000000
        )
    ]
)

print(ledger)
```

**Response**

```python
Ledger(
    id=5656565656565656,
    external_id=my-internal-id-123456,
    rules=[
        Rule(
            key=maximumBalance,
            value=10000000
        )
    ],
    tags=[],
    metadata={},
    created=2022-01-01 00:00:00,
    updated=2022-06-01 00:00:00
)
```

## Ledger Transaction Overview

A LedgerTransaction moves balance in or out of a ledger and returns the ledger's new running balance. Here we show you how to post transactions, move funds between ledgers, read balances, import history and override rules.

### Creating a transaction

Post a transaction to move balance into a ledger. Provide the amount (in cents), the ledgerId it belongs to, a unique externalId and a source describing where the movement came from.

The response returns the resulting balance of the ledger after the transaction is applied.

**Request**

```python
import starkinfra

transactions = starkinfra.ledgertransaction.create([
    starkinfra.LedgerTransaction(
        amount=11234,
        ledger_id="5656565656565656",
        external_id="my-internal-id-123456",
        source="bank-transfer/123"
    )
])

for transaction in transactions:
    print(transaction)
```

**Response**

```python
[
    LedgerTransaction(
        id=5656565656565656,
        ledger_id=5656565656565656,
        external_id=my-internal-id-123456,
        source=bank-transfer/123,
        amount=11234,
        fee=0,
        balance=11234,
        tags=[],
        metadata={},
        created=2022-01-01 00:00:00
    )
]
```

### Creating transactions in multiple ledgers

A single batch can carry transactions for different ledgers. This is the recommended way to move balance from one ledger to another while keeping your overall balance consistent: send one negative transaction debiting the source ledger and one positive transaction crediting the destination ledger, both with the same amount.

Because the two amounts net to zero, the total balance across your ledgers is preserved, and either both transactions are accepted or the whole request fails.

**Request**

```python
import starkinfra

transactions = starkinfra.ledgertransaction.create([
    starkinfra.LedgerTransaction(
        amount=-5000,
        ledger_id="5656565656565656",
        external_id="transfer-001-out",
        source="internal-transfer/789"
    ),
    starkinfra.LedgerTransaction(
        amount=5000,
        ledger_id="4545454545454545",
        external_id="transfer-001-in",
        source="internal-transfer/789"
    )
])

for transaction in transactions:
    print(transaction)
```

**Response**

```python
[
    LedgerTransaction(
        id=5656565656565656,
        ledger_id=5656565656565656,
        external_id=transfer-001-out,
        source=internal-transfer/789,
        amount=-5000,
        fee=0,
        balance=6234,
        tags=[],
        metadata={},
        created=2022-01-01 00:00:00
    ),
    LedgerTransaction(
        id=4545454545454545,
        ledger_id=4545454545454545,
        external_id=transfer-001-in,
        source=internal-transfer/789,
        amount=5000,
        fee=0,
        balance=5000,
        tags=[],
        metadata={},
        created=2022-01-01 00:00:00
    )
]
```

### Checking a ledger current balance

A ledger does not expose a balance field of its own — the current balance is simply the balance of its most recent transaction.

Query the transactions filtered by ledgerId with limit=1 to retrieve the latest one, and read its balance to get the ledger's current balance.

**Request**

```python
import starkinfra

transactions = starkinfra.ledgertransaction.query(
    ledger_id="5656565656565656",
    limit=1
)

latest = next(transactions, None)
current_balance = latest.balance if latest else 0
print(current_balance)
```

**Response**

```python
6234
```

### Exporting transaction history

When migrating from a legacy system you can rebuild a ledger's history by posting past transactions with a created datetime.

created must be in ISO format and cannot be in the future. When sending several transactions in the same request, their created datetimes must be in chronological order so the running balance is computed correctly.

**Request**

```python
import starkinfra

transactions = starkinfra.ledgertransaction.create([
    starkinfra.LedgerTransaction(
        amount=10000,
        ledger_id="5656565656565656",
        external_id="legacy-001",
        source="legacy-import/2021",
        created="2021-01-01T00:00:00.000000+00:00"
    ),
    starkinfra.LedgerTransaction(
        amount=-2500,
        ledger_id="5656565656565656",
        external_id="legacy-002",
        source="legacy-import/2021",
        created="2021-02-01T00:00:00.000000+00:00"
    )
])

for transaction in transactions:
    print(transaction)
```

**Response**

```python
[
    LedgerTransaction(
        id=5656565656565656,
        ledger_id=5656565656565656,
        external_id=legacy-001,
        source=legacy-import/2021,
        amount=10000,
        fee=0,
        balance=10000,
        tags=[],
        metadata={},
        created=2021-01-01 00:00:00
    ),
    LedgerTransaction(
        id=4545454545454545,
        ledger_id=5656565656565656,
        external_id=legacy-002,
        source=legacy-import/2021,
        amount=-2500,
        fee=0,
        balance=7500,
        tags=[],
        metadata={},
        created=2021-02-01 00:00:00
    )
]
```

### Overwriting ledger rules

A transaction can carry its own rules, which momentarily overwrite the ledger's rules for that single transaction — the ledger's own rules stay unchanged for every other transaction.

This is handy when you need to force a one-off movement that the ledger's rules would otherwise forbid. For example, to push a balance negative even though the ledger has a minimumBalance of 0, override minimumBalance with a lower value on the transaction itself.

**Request**

```python
import starkinfra

transactions = starkinfra.ledgertransaction.create([
    starkinfra.LedgerTransaction(
        amount=-15000,
        ledger_id="5656565656565656",
        external_id="forced-debit-001",
        source="manual-adjustment/42",
        rules=[
            starkinfra.ledger.Rule(
                key="minimumBalance",
                value=-100000000
            )
        ]
    )
])

for transaction in transactions:
    print(transaction)
```

**Response**

```python
[
    LedgerTransaction(
        id=5656565656565656,
        ledger_id=5656565656565656,
        external_id=forced-debit-001,
        source=manual-adjustment/42,
        amount=-15000,
        fee=0,
        balance=-8766,
        tags=[],
        metadata={},
        created=2022-01-01 00:00:00
    )
]
```

---

## Other Languages

- [Python](https://docs.starkinfra.com/get-started/ledger-python.md)
- [Node.js](https://docs.starkinfra.com/get-started/ledger-node.md)
- [PHP](https://docs.starkinfra.com/get-started/ledger-php.md)
- [Java](https://docs.starkinfra.com/get-started/ledger-java.md)
- [Ruby](https://docs.starkinfra.com/get-started/ledger-ruby.md)
- [Elixir](https://docs.starkinfra.com/get-started/ledger-elixir.md)
- [.NET](https://docs.starkinfra.com/get-started/ledger-dotnet.md)
- [Go](https://docs.starkinfra.com/get-started/ledger-go.md)
- [Clojure](https://docs.starkinfra.com/get-started/ledger-clojure.md)
- [cURL](https://docs.starkinfra.com/get-started/ledger-curl.md)
