Accounts & templates
A ledger is a chart of accounts, and every transaction entry posts to one of them. Some you can plan up front: cash, revenue, processing fees. Others you cannot: a running balance for each of your users, sellers, or tenants, where there may be millions and new ones appear every day.
Ledfra handles both, and you address every account the same way, by a readable slug like income:rent or liabilities:user/123 - never a bare number like 2100 whose meaning lives in a separate lookup table. (Traditional accounting systems call that number a GL code.)
The two kinds of account
Every account is one of two kinds. The difference is simply who creates it, and when.
Control accounts
A control account (type: CONTROL) is a standalone bookkeeping account you design up front in the Ledfra app: cash, revenue, processing fees. There is a small, known set of them, you name them yourself, and the API only ever reads them. Use control accounts for the figures your whole business shares: the lines you would put on a balance sheet by name.
Template accounts
A template account (type: TEMPLATE) is a per-object subsidiary account: one wallet per user, one payable per seller, one receivable per tenant. You do not design each one. You design the template once (its currency, normal balance, and category), and Ledfra creates the individual account the first time a transaction references its slug.
That is two objects with two similar names, so it is worth pinning them down. The pattern you design once is an account template. Each account it creates is a template account, and the template account is the one that holds a balance and gets posted to. Some other ledgers use the phrase “template account” for the pattern itself, so when you are reading another API, check which of the two it means.
| Control account | Template account | |
|---|---|---|
| Who creates it | You, in the Ledfra app | Ledfra, on the first transaction that uses its slug |
| How many | A handful you design | One per object, unbounded |
| Exists from | The moment you design it | Its first transaction |
| Example slug | assets:cash, income:rent | liabilities:user/123, receivables:tenant/42 |
| Use it for | Accounts your whole business shares | One balance per external object |
Why slugs: accounts created on the fly
The obvious way to track a balance per user is to create an account row for every user before you can record their first transaction. That means a second API call, error handling for when it already exists, and a race when two events for a new user arrive at once. It is busywork, and it is easy to get wrong.
Slugs remove the step entirely. You reference an account by a stable, readable name, and posting a transaction is a transparent upsert: if the template account exists we use it, and if it does not we create it from its template and then post, all in one call. You never call a “create account” endpoint, because there isn't one.
# 'liabilities:user/123' doesn't exist yet. Ledfra creates it from the
# 'user' template, then posts the entry — one call, no pre-provisioning.
curl 'https://ledfra.com/api/ledgers/{ledgerId}/transactions' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"description": "Deposit to user wallet",
"entries": [
{ "account": "assets:cash", "operation": "DEBIT", "amount": "USD:100.00" },
{ "account": "liabilities:user/123", "operation": "CREDIT", "amount": "USD:100.00" }
]
}'liabilities:user/123 and the account is simply there. The slug doubles as an audit trail: it says exactly what the account is and which object it belongs to, in every transaction that touches it.Each account also carries a normalBalance (CREDIT or DEBIT), the side on which its balance increases, and the currency it is denominated in. You do not pick the normal balance per account: it follows the category you file the account under, which How to design your ledger covers. For how amounts are represented, see Money.
Naming and keying accounts
Two conventions are worth adopting from the start, because accounts are referenced by posted history and are not safely renamable afterwards.
- Key per-object accounts by a stable id, never a name or email.
liabilities:payables-hosts/9001survives the host changing their name.liabilities:payables-hosts/jane@example.comdoes not. - Name the leaf, not the path. The hierarchy already carries the context and the app renders the breadcrumb, so an account under Assets › Current Assets is called “Stripe”, not “Current Asset Stripe Balance”.
Listing and fetching accounts
GET /ledgers/{ledgerId}/accountslists a ledger's accounts and is how you discover valid slugs. It supports aquerysubstring filter over names and slugs, with cursor pagination.GET /ledgers/{ledgerId}/accounts/{slug}fetches a single account. A template account returns404until its first transaction brings it into existence.GET /ledgers/{ledgerId}/accounts/{slug}/balancereturns the live balance, computed from the ledger rather than cached.
Slugs may contain /, so URL-encode one when it appears in a path: receivables:tenant/123 becomes receivables:tenant%2F123.
# List accounts (optionally filter by name or slug)
curl 'https://ledfra.com/api/ledgers/{ledgerId}/accounts?query=tenant' \
-H 'Authorization: Bearer YOUR_API_KEY'
# Fetch one account — note the URL-encoded '/' in the slug
curl 'https://ledfra.com/api/ledgers/{ledgerId}/accounts/receivables:tenant%2F123' \
-H 'Authorization: Bearer YOUR_API_KEY'
# And its live balance
curl 'https://ledfra.com/api/ledgers/{ledgerId}/accounts/assets:cash/balance' \
-H 'Authorization: Bearer YOUR_API_KEY'Limitations
- A template account does not exist until its first transaction - fetching it by slug returns
404before then. - You cannot create accounts of any kind through the API. Control accounts and templates are designed in the app. Individual template accounts come into being only through the upsert above.
- The upsert only works for slugs that map to something you have already designed. A slug matching neither a control account nor a template is rejected, so you cannot invent arbitrary accounts just by posting to them.
- An account holds a single currency.
Where to go next
- How to design your ledger - deciding which accounts to create, and how to structure them.
- Transactions - how entries post against these accounts.
- Quickstart - build a small chart of accounts and use it.
- What is Ledfra? - where accounts sit in the model.
- API reference - the account endpoints in full.