How to post first transaction
A transaction is derived from an event, not chosen from vocabulary. A payment is not "a debit" and a refund is not "a credit": each event changes several accounts, and what each entry does is decided by the account it lands in. Start from what happened, and the entries write themselves.
This page is the short version - the full teaching, with the misconception drills and the derivations, is the guide post your first double-entry ledger transaction. It assumes your accounts already exist. If they do not, how to design your ledger comes first.
Derive the entries from the event
Every posting follows the same pipeline, and debit or credit is the second-to-last step:
Real-world event
↓
Which economic quantities changed?
↓
Which accounts measure them?
↓
Did each balance increase or decrease?
↓
Debit or credit follows from the account type
↓
Total debits must equal total creditsThe only table you need on the way: assets and expenses increase on a debit, liabilities, equity and income increase on a credit. Why that is - and not something to memorize - is model normal debit and credit accounts.
Run it on one real event
A guest pays Firecnc $230. The host is owed $200, your service fee is $30. The facts: your Stripe balance goes up $230, your debt to the host goes up $200, your earned income goes up $30. Translate:
transaction
description: "Booking 4417 paid"
entries:
- account: assets:stripe debit USD:230.00
- account: liabilities:hosts/42 credit USD:200.00
- account: income:service-fees credit USD:30.00Debits $230, credits $200 + $30 = $230. Balanced. Note the $200 is a liability, not income - it was never yours, only held by you. Stripe's fee is its own event, a genuine cost:
transaction
description: "Stripe processing fee, booking 4417"
entries:
- account: expenses:processing-fees debit USD:7.00
- account: assets:stripe credit USD:7.00Paying the host later settles the debt. Nothing is earned or consumed - money just moves:
transaction
description: "Payout to host 42"
entries:
- account: liabilities:hosts/42 debit USD:200.00
- account: assets:stripe credit USD:200.00After all three: Stripe holds $23, the host is owed $0, income shows $30, expenses $7. Profit $23, cash left $23 - the same number, because they were never computed separately.
The check that runs at posting
Ledfra rejects any transaction whose debit and credit totals differ, per currency, at write time. A half-recorded event fails loudly at the API boundary instead of becoming a three-cent discrepancy somebody hunts down in a quarter-end report. The shape of the request is in Transactions, and writes are idempotent, so a retry returns the original transaction instead of double-posting.
Common shapes that trip people up
Every one of these follows the same pipeline. None of them fits "debit means money in", which is why that mnemonic fails:
| Event | Debit | Credit | Note |
|---|---|---|---|
| Customer pays an existing invoice | assets:cash | assets:receivable | No income - it was earned when invoiced |
| Customer prepays for future work | assets:cash | liabilities:deferred-income | Cash without income - you owe the work |
| The prepaid work is delivered | liabilities:deferred-income | income:services | Income without cash moving |
| Loan repayment | liabilities:bank-loan | assets:cash | Both sides shrink - nothing is consumed |
| Vendor refunds an expense | assets:cash | expenses:hosting | An expense account taking a credit |
Corrections work the same way: a reversal is a new balanced transaction, never an edit. History is append-only, so the books can always answer what happened and when.
Where to go next
- Quickstart - post this transaction against a real ledger, through the app and the API.
- How to build a marketplace ledger - the full Firecnc build, from structure to statements.
- Guide: Post your first double-entry ledger transaction - the long version, with the derivations and the drills.
- What is Ledfra? - where posting fits in the whole model.