Capturing events from your org
The ledger fills from your Salesforce org through one of two ingestion modes, configured per tenant. Both feed the same event store; the difference is what lives in your org and how quickly changes appear.
Real-time capture (outbox mode)
A small trigger stack in your org classifies every financial save as it happens and writes one row per event to an outbox object (LedgerEvent__c). The Edge service polls the outbox every 30 seconds on a watermark and ingests new rows.
- Captures inserts and mutations — including cancellations, which produce
C-indicator events at the moment they happen. - The outbox is a queue, not a history: rows can be purged after ingestion. The ledger holds the history.
- Classification runs in the trigger using the same mapping documented in your
txh/event-definitions.yaml, so what the org publishes and what the ledger expects stay in lockstep. - Each outbox row carries the human references (booking number, trip title, record label), the row-level currency, and a source-context payload.
The trigger stack is deliberately thin: classify, write outbox row, done. It never blocks the user's save — a publisher failure is logged, not thrown. A companion pattern stamps a Last Financial Change datetime field on the booking on every financial save, giving your org an indexed, SOQL-queryable answer to "what changed in the last N hours" without touching the API.
Read-only sync (transaction_sync mode)
For tenants that have not deployed (or cannot yet deploy) the trigger stack, the Edge service polls the source records themselves — payments and items — on a created-date watermark and classifies them on ingestion.
- Requires no metadata in your org — read-only API access only.
- Captures inserts only: a record mutated after capture is not re-read, so cancellations modelled as record updates are out of scope until the outbox phase.
- Typically used during evaluation and migration; tenants graduate to outbox mode for real-time, mutation-aware capture. The switch is a one-line configuration change on the Edge side — no redeploy.
Historical backfill
Either mode starts capturing from "now". History is loaded explicitly through a windowed, idempotent backfill that scans the org's existing records, classifies them with the same maps, and dedupes against anything already captured — safe to re-run, safe to overlap with live capture. Backfills are operated by Kaptio and always dry-run first, reporting per-code counts for sign-off before any write.
Classification is configuration
The payment-method and item-type maps live in your tenant configuration (txh/event-definitions.yaml), reviewed like any other config change:
payment_method_map:
Card: { positive: { tr_code: CCR, event_type: CC_PAYMENT_RECEIVED },
negative: { tr_code: REC, event_type: CC_REFUND_ISSUED } }
Bank Transfer: { positive: { tr_code: CSH, event_type: CASH_PAYMENT_RECEIVED },
negative: { tr_code: REF, event_type: CASH_REFUND_ISSUED } }
item_record_type_map:
Package_Item: { tr_code: CST, event_type_add: TOUR_COST_ADDED,
event_type_cancel: TOUR_COST_OFFSET }
Excursion: { tr_code: OPT, event_type_add: OPTION_ADDED,
event_type_cancel: OPTION_REMOVED }
A method missing from the map does not lose data — it produces an UNK suspense event, and extending the map plus replaying the GL rules brings the history into line.