Quickstart: record your first transaction
Updated 14 September 20262 min read
On this page
This guide takes you from a new sandbox organization to a posted, balanced transaction in about ten minutes. You will create a ledger, open two accounts, move funds between them and read the balances back. Everything here runs against the sandbox, so no real money moves.
Before you start
You need a Ledgerline organization and a sandbox secret key. Sign in to the dashboard at https://dashboard.ledgerline.dev, open Developers, choose API keys and create a key in the Test tab. Test secret keys start with ll_test_sk_. The key is shown once, so copy it into your secrets manager straight away.
export LEDGERLINE_API_KEY="ll_test_sk_replace_me"
Every request goes to the sandbox base URL and carries the key as a bearer token:
https://sandbox.api.ledgerline.dev/v1
Authorization: Bearer $LEDGERLINE_API_KEY
Note
Live traffic uses https://api.ledgerline.dev/v1 and ll_live_ keys. Sandbox and live share no data. See Going live before you switch.
1. Create a ledger
A ledger is the container for a set of accounts that belong together, such as "Customer funds" for one product line.
curl https://sandbox.api.ledgerline.dev/v1/ledgers \
-H "Authorization: Bearer $LEDGERLINE_API_KEY" \
-H "Idempotency-Key: 6f1c0b52-6f0d-4c7e-9a52-1d2f6f1d5c01" \
-d name="Customer funds" \
-d description="Funds held on behalf of customers"
The response contains the ledger id, which starts with led_. Keep it for the next step.
2. Open two accounts
Create a customer wallet and an operating account. The wallet is credit-normal because the balance is money you owe the customer. The operating account is debit-normal because it is an asset you hold.
curl https://sandbox.api.ledgerline.dev/v1/accounts \
-H "Authorization: Bearer $LEDGERLINE_API_KEY" \
-H "Idempotency-Key: 0d4a7a3c-97d9-4a8d-8f7b-0c14a2a6a102" \
-d ledger_id=led_01J9QK3ZV8 \
-d name="Customer wallet" \
-d currency=USD \
-d normal_balance=credit
Repeat the call with name="Operating cash" and normal_balance=debit. Each response returns an account id that starts with acc_.
3. Post a transaction
A transaction is a set of entries that must balance. Debits and credits have to add up to the same amount in each currency, otherwise the request fails with entries_unbalanced.
Amounts are integers in minor units, so 250000 means $2,500.00.
{
"ledger_id": "led_01J9QK3ZV8",
"description": "Customer deposit",
"status": "posted",
"entries": [
{ "account_id": "acc_01J9QK5M2A", "direction": "debit", "amount": 250000, "currency": "USD" },
{ "account_id": "acc_01J9QK5N7C", "direction": "credit", "amount": 250000, "currency": "USD" }
]
}
Send it with POST /v1/transactions. Setting status to posted makes the entries final immediately. If you send pending instead, the entries are held until you call POST /v1/transactions/{id}/post, or release them with POST /v1/transactions/{id}/void.
4. Read the balances
curl https://sandbox.api.ledgerline.dev/v1/accounts/acc_01J9QK5N7C/balances \
-H "Authorization: Bearer $LEDGERLINE_API_KEY"
{
"account_id": "acc_01J9QK5N7C",
"currency": "USD",
"posted": 250000,
"pending": 0,
"available": 250000
}
The three balance types are explained in Account balances.
Use an SDK instead
The same flow in Node, using the official client:
import Ledgerline from "@ledgerline/node";
const ll = new Ledgerline(process.env.LEDGERLINE_API_KEY!);
const ledger = await ll.ledgers.create({ name: "Customer funds" });
const wallet = await ll.accounts.create({
ledgerId: ledger.id,
name: "Customer wallet",
currency: "USD",
normalBalance: "credit",
});
See Node SDK and Python SDK for installation and configuration.
Next steps
- Learn how the objects fit together in Core concepts.
- Make writes safe to retry with Idempotency.
- Subscribe to events so you do not have to poll: Webhooks overview.
- Send money out of the ledger with Payouts.