Skip to content
On this page

Post your first double-entry ledger transaction

Most debit and credit mistakes happen before anyone chooses a debit or a credit. They happen when an engineer starts with the operation instead of the economic event.

A customer payment is not “a debit.” A refund is not “a credit.” Each event changes several accounts, and every affected account can receive either operation. What the operation does is decided by the account’s type - which is why this guide comes after creating the ledger structure and modeling normal debit and credit accounts. With those two in place, posting a transaction stops being vocabulary and becomes a repeatable process, and this guide is that process, run end to end on one real order.

A posting is an operation on an account

We finally have enough context to define every term precisely.

  • A debit entry is an entry on the debit side of an account. A credit entry is an entry on the credit side. They are not synonyms for plus and minus, and they do not mean money in and money out.
  • A journal entry is what an accountant calls the complete record of one event - all of its entries together, as a unit.
  • Posting is the act of writing those entries to the ledger.
  • Double-entry is the method that requires the debit and credit totals of every transaction to match.

The complete process, from the world to the ledger:

text
Real-world event
      ↓
Which economic quantities changed?
      ↓
Which accounts measure them?
      ↓
What type is each account?
      ↓
Did each balance increase or decrease?
      ↓
Debit or credit follows from the account type
      ↓
Verify that total debits equal total credits

Notice where debit and credit appear: second to last. They are derived, not chosen. By the time you reach that step there is no decision left to make.

Build the transaction from the event, not from a mnemonic

Take the order that runs through this whole series. A buyer pays $1,000 on a marketplace charging a 10% commission, Stripe’s fee on the charge is $29.30, and $900 belongs to the seller.

Start with economic facts, not operations:

text
Stripe balance increases                    $1,000.00
Amount owed to the seller increases           $900.00
Marketplace commission earned increases       $100.00
Processing expense increases                    $29.30
Stripe balance decreases                        $29.30

Now classify the accounts that measure them:

AccountTypeNormal sideChange
assets:stripe-balanceAssetDebit+$1,000.00, then -$29.30
liabilities:payables-sellers/9001LiabilityCredit+$900.00
income:commissionIncomeCredit+$100.00
expenses:processing-feesExpenseDebit+$29.30

Only now translate the changes into entries. The buyer pays. Cash arrives, most of it spoken for:

yaml
transaction
  description: "Order 4417 paid"
  entries:
    - account: assets:stripe-balance                 debit   USD:1000.00
    - account: liabilities:payables-sellers/9001     credit  USD:900.00
    - account: income:commission                     credit  USD:100.00

Debits: $1,000. Credits: $900 + $100 = $1,000. Balanced. The asset went up because a debit increases a normal debit account, and both the obligation to the seller and your commission went up because a credit increases a normal credit account. One transaction, three facts about the same event: the platform controls $1,000 more at Stripe, $900 of it is owed to the seller, and the remaining $100 was earned.

Stripe takes its fee. This one is a genuine cost, not a share of anything:

yaml
transaction
  description: "Stripe processing fee, order 4417"
  entries:
    - account: expenses:processing-fees              debit   USD:29.30
    - account: assets:stripe-balance                 credit  USD:29.30

The expense rose (debit, and expenses are normal debit) and the cash fell (credit against a normal debit asset).

Pay the seller and read the books

You pay the seller. Note what this is not: it is not a cost. You are settling a debt you already recorded.

yaml
transaction
  description: "Payout to seller 9001"
  entries:
    - account: liabilities:payables-sellers/9001     debit   USD:900.00
    - account: assets:stripe-balance                 credit  USD:900.00

A debit against the normal credit liability reduces it, from $900 to zero, and the cash falls by the same amount. Nothing touches income or expenses, because nothing was earned or consumed here. Money moved from one pocket to another.

Add up every account and see what the books say:

AccountCategoryBalance
assets:stripe-balanceAsset$70.70
liabilities:payables-sellers/9001Liability$0.00
income:commissionIncome$100.00
expenses:processing-feesExpense$29.30

Profit is $100.00 - $29.30 = $70.70, and the cash left over is $70.70. Those two numbers agree because they were never computed separately. Check the equation: assets $70.70 = liabilities $0.00 + equity $70.70. The books close on themselves, and no query reconstructed anything. Compare that to the payments-table version, where “what did we earn” was a case expression and “what do we owe” was four conditions that silently returned zero for three of the five stages.

More than two legs

“Double” in double-entry means two sides, not two rows. The first transaction above already had three. Nothing caps it.

Suppose you add a referral programme paying 2% of the order value, taken out of your own commission:

yaml
transaction
  description: "Order 4417 paid, referred"
  entries:
    - account: assets:stripe-balance                 debit   USD:1000.00
    - account: liabilities:payables-sellers/9001     credit  USD:900.00
    - account: liabilities:payables-referrers/312    credit  USD:20.00
    - account: income:commission                     credit  USD:80.00

Credits: $900 + $20 + $80 = $1,000. Still balanced, and the change cost one line. A money event is a variable-length list of participants, and this is the data structure that matches it: a new party is a new entry, and the reports that were already correct stay correct because they read accounts rather than columns. The rule is only that the two sides are equal, per currency. How many accounts participate is up to the event.

The same process for less obvious events

Once account types are clear, unusual-looking entries stop being special cases. Each of these trips up the “debit means money in” intuition, and each is just the pipeline again.

Collect a receivable

The company already recognized income when it delivered the work. The customer now pays $100.

text
DEBIT   assets:cash                  USD:100.00
CREDIT  assets:accounts-receivable   USD:100.00

Both accounts are normal debit. Cash increases with a debit entry. The receivable decreases with a credit entry. No income appears, because the company did not earn the same $100 twice - an existing asset changed form, from a right to collect into cash.

Receive a customer prepayment

The customer pays before the company earns the income:

text
DEBIT   assets:cash                  USD:100.00
CREDIT  liabilities:deferred-income  USD:100.00

Cash increases, and so does the obligation to deliver work - the deferred income liability. When the work is complete:

text
DEBIT   liabilities:deferred-income  USD:100.00
CREDIT  income:services              USD:100.00

The liability decreases and earned income increases. Cash does not move at all in the second transaction, which is the point: earning and collecting are separate events, and the ledger records each when it happens.

Repay a loan

The company returns $100 to the bank:

text
DEBIT   liabilities:bank-loan  USD:100.00
CREDIT  assets:cash            USD:100.00

The liability and the asset both decrease. The value does not land in another asset or expense. It leaves the company while settling an obligation - the same shape as paying the seller above.

Receive an expense refund

A provider returns $20 of a hosting charge:

text
DEBIT   assets:cash       USD:20.00
CREDIT  expenses:hosting  USD:20.00

Cash increases and the previously recognized expense decreases. The expense account is normal debit, but it receives a credit entry because this event reduces it. These examples are why “debit = money in, credit = money out” cannot work, and why “normal debit account” and “debit entry” must stay separate concepts.

Why the two sides must be equal

Every transaction in a double-entry ledger has equal total debits and credits. It is worth being precise about what that rule is not. It is not conservation of cash - cash can enter or leave the company. It is not conservation of economic value either - a fire can destroy equipment, and recognizing the loss does not create a replacement resource somewhere else. The invariant is narrower and more useful:

Every recorded change must preserve the accounting equation.

The equation starts with the three position categories, Assets = Liabilities + Equity. Income increases equity through business performance and expenses decrease it, so for the purpose of seeing where every normal side comes from it expands to:

Assets + Expenses = Liabilities + Equity + Income

The categories on the left increase with debit entries. The categories on the right increase with credit entries. Post equal totals on both sides and the equation survives every write, by construction. Equal debits and credits are therefore not accounting etiquette. They are an integrity check: if one side is missing, the event is incomplete, and the ledger should reject it.

The check that runs at write time

Everything above is a data model. This is the part that makes it a correctness mechanism:

text
-- Run per transaction, per currency, before the write commits.
-- Not a nightly job. Not a test. A constraint.
sum(entries where direction = 'debit')
  = sum(entries where direction = 'credit')

Run at write time, this makes a whole class of bug unrepresentable. You cannot record cash arriving without recording where it came from. You cannot pay a seller without something absorbing the other side. A half-applied change fails loudly at the boundary instead of settling quietly into a report that somebody reads three months later.

Be equally clear about what it does not catch, because a check people overtrust is worse than one they understand:

  • Both sides wrong by the same amount. Read the processor’s amount as dollars when it is minor units and you post $10.00 where $1,000.00 belonged. It lands on every leg at once, so it balances perfectly.
  • The right amount in the wrong account. This is the big one, and the next section is about it.
  • An event you never recorded at all. A webhook that never arrived, or arrived and threw before the posting ran. A ledger can only be correct about what reaches it, which is why the delivery path deserves as much care as the ledger does.

So the check is a floor, not a guarantee. What it buys is that arithmetic errors stop being a thing you look for, which frees the attention for the errors that are actually hard.

Where enforcement has to live
This only works if the check is at the write boundary, not in the code path that happens to call it. A rule enforced by convention is enforced until the third caller. In Ledfra a transaction with unequal sides is rejected by the API, so every writer inherits it: see Transactions for the shape of a posting and the trial balance for the report that proves it across the whole ledger.

Balanced is not the same as correct

Go back to the first transaction and change one word. Credit the seller’s $900 to income:sales instead of the liability account. Debits are still $1,000, credits are still $1,000, the write succeeds, every report renders, and the trial balance ties out to the cent. And your revenue now reads $1,000 instead of $100.

That is the difference between two kinds of correctness. Arithmetic correctness - total debits equal total credits - is what double-entry gives you, enforced by the check above. Structural correctness - the entries use accounts that represent the economic event truthfully - is decided by a person, and no balance check can detect its absence. The filing rules that protect it, and the full anatomy of this exact tenfold mistake, are in create the ledger structure.

The ledger keeps history, not the money itself

One property of the finished system is worth understanding before you rely on it. An append-only ledger does not erase a transaction when the business later reverses it. It records another transaction.

If a customer pays $100 and later receives a refund, the history contains both events. The current cash effect may net to zero, but the ledger can still answer what happened and when. That does not mean economic value stays inside the company forever - loan repayments, dividends, refunds, losses, and ordinary spending all reduce its resources. The durable thing is the record:

text
Economic value can enter, change form, be consumed, or leave.
The ledger preserves the history of those changes.

This distinction matters in software. If a correction overwrites the original row, you preserve the latest state but destroy the path that produced it. If a correction is a new balanced transaction, you preserve both.

The transaction model in one screen

text
ACCOUNT TYPES

Normal debit                         Normal credit
Assets                               Liabilities
Expenses                             Equity
                                     Income


ENTRY OPERATIONS

                         Debit entry       Credit entry
Normal debit account      increase           decrease
Normal credit account     decrease           increase


BUILDING A TRANSACTION

event → accounts → types → increase/decrease → debit/credit

Do not begin by asking whether an event is a debit or a credit. An event is neither. First ask what changed in the real world and which accounts measure those changes. Once you know the account types, debit and credit stop being accounting vocabulary to memorize. They become the two operations that preserve the schema.

Where this goes next

Ledfra enforces the arithmetic boundary this guide ends on. Every transaction needs at least two entries, and total debits must equal total credits within each currency - an unbalanced write is rejected instead of becoming a discrepancy somebody has to find later. Posted history is preserved the same way this guide describes: a correction is a new reversing transaction, never an edit.

What no ledger can do is decide the accounting policy for you. Whether a marketplace amount is income or a seller liability, whether a purchase is an asset or an expense - the account model is still yours, and the economic meaning of every posting remains your responsibility. The tools for getting it right are the two guides this one builds on, plus how to design your ledger, which publishes the structural rules in full.

To post a real first transaction against a real ledger, the quickstart goes from an empty workspace to a posted API transaction in about fifteen minutes, and how to build a marketplace ledger runs this guide’s exact scenario end to end, from structure through postings to the resulting statements.