Skip to content

Schema overview + ERD

The entire data layer is Supabase (Postgres). The DDL across these pages is a reference starting point, not final migrations — adjust types/indexes during implementation, but keep the keys, constraints, and the idempotency / double-entry guarantees.

The two threads

Everything is keyed on two ids:

  • correlation_id — the one-ID-per-transaction thread that ties a rail notification → transactionsjournalsledger_entries → settlement.
  • process_id — the transaction / settlement id used for idempotency together with category (unique (process_id, category)).

Two sources of truth, never confused

journals = source of truth for money (only successful, captured financial events). transactions = source of truth for attempts/analytics (every outcome, incl. declined / failed / pending / expired). Joined on correlation_id. See Ingestion & analytics.

Entity-relationship diagram

Table map

GroupTablesPage
Ledger coreaccounts, journals, ledger_entriesLedger tables
Configfee_rules, finances_config, balance_snapshotsConfig tables
Ingestion & analyticstransaction_events, transactionsIngestion & analytics
Operator / registrymerchants, rails, webhooks, webhook_deliveries, operators, audit_logOperator tables
Settlementsettlements, settlement_runs, settlement_merchant_configSettlement reports · orchestration
Checkoutcheckout_configHosted Checkout

Idempotency is enforced structurally — see the idempotency model.

Vecnet — Build Spec v0.2 · Obsidian Terminal