When we designed Ledgerline's data model, we made exactly one decision that mattered more than the rest: every state change emits an immutable event. An invoice is created — that's an event. A charge succeeds — event. A payment fails — event. The events are append-only and named resource.action: invoice.paid, payment_link.created, charge.succeeded.
That one choice pays for itself three times over.
1. Webhooks are free
Once every change is already an event, delivering those events to your endpoint is a transport detail, not a feature you design separately. Subscribe in-process with ll.events.subscribe(), or receive them over HTTPS with a signed Ledgerline-Signature header. Same events, different pipe.
2. The audit trail is free
An append-only log of every state change, each carrying a full snapshot of the object at that moment, is an audit trail. You don't build a separate history table — you query events. Reconciliation, debugging, and "what happened to this invoice?" are all the same list, filtered.
3. The free tier is free
Because test and live are the same code path keyed off your key, the sandbox costs us almost nothing to run. Events settle instantly and never touch a banking network. So we can give test mode away forever — and it doubles as the demo, since the feed is genuinely live.
Good architecture isn't the features you add. It's the features you don't have to.
See the model in action on the Events & Webhooks page.
Ledgerline is a cshell reference venture. Product behavior described here is real in test mode; no real money moves in this build.