Documentation Index
Fetch the complete documentation index at: https://docs.wellapp.ai/llms.txt
Use this file to discover all available pages before exploring further.
A LedgerAccount represents a single line in a workspace’s chart of accounts (CoA), classified by accounting type (ASSET, LIABILITY, EQUITY, REVENUE, EXPENSE) and grouped into account classes 1–9. It is the foundational node of the accounting graph: invoice items, journal entry lines, and workspace posting mappings all FK into it. Ledger accounts may be arranged in a parent–child hierarchy (one self-referential ManyToOne), and they can be marked as auxiliary (linked to a customer, supplier, or employee counterparty dimension). The connector sync pipeline writes ledger accounts from external accounting tools (Xero, Pennylane, etc.) and stamps each row with source_workspace_connector_pk for provenance tracking.
| Naming | Value |
|---|
| Object | Ledger Account |
Resource type (JSON:API type) | ledger_account |
| Collection / records root | ledger_accounts |
| REST base | /v1/ledger-accounts |
| Entity class | LedgerAccount |
API operations
| Operation | Method & path | Status |
|---|
| List | GET /v1/ledger-accounts | ✅ Implemented |
| Retrieve | GET /v1/ledger-accounts/{id} | ✅ Implemented |
| Create | POST /v1/ledger-accounts | 🟡 Planned |
| Update | PATCH /v1/ledger-accounts/{id} | 🟡 Planned |
| Delete | DELETE /v1/ledger-accounts/{id} | 🟡 Planned |
Data model
Attributes
| Field | Type | Required | Constraints | Allowed values | Description |
|---|
| ledger_account_id | string, UUID, 🔒 system | ✅ Yes | unique; generated by gen_random_uuid() on INSERT | — | Public stable identifier for the ledger account. Use this in all API references; the internal pk is never exposed. |
| account_number | string | ✅ Yes | max length 20; PARTIAL UNIQUE on (workspace_pk, account_number) WHERE deleted_at IS NULL (index idx_ledger_accounts_workspace_account_number_active_unique); the legacy non-partial UNIQUE constraint was replaced by Migration20260529100000 | — | The chart-of-accounts code for this account within the workspace, e.g. ‘411000’ (PCG), ‘1200’ (IFRS). Uniqueness is enforced only among active (non-deleted) rows to allow re-creation after a soft-delete. |
| name | string | ✅ Yes | max length 255 | — | Human-readable display name for the ledger account. |
| account_type | enum (ledger_account_type_enum) | ✅ Yes | non-nullable; native PostgreSQL enum | ASSET | LIABILITY | EQUITY | REVENUE | EXPENSE | Broad accounting classification per the double-entry model. Drives debit/credit sign convention in journal entries and determines which financial statement section the account appears in. |
| account_class | integer | ✅ Yes | CHECK (account_class >= 1 AND account_class <= 9); composite index (workspace_pk, account_class) | 1 – 9 | Numeric account class (plan comptable class digit). Used to group accounts in chart-of-accounts views and as a fast filter for trial-balance queries. Indexed together with workspace_pk. |
| is_auxiliary | boolean | ✅ Yes | default false | true | false | Indicates whether this account carries an auxiliary (counterparty) dimension. When true, auxiliary_type must be set and journal entry lines on this account must reference a counterparty company. |
| auxiliary_type | enum (auxiliary_type_enum) | ⚪ No | nullable; native PostgreSQL enum; required in practice when is_auxiliary = true | CUSTOMER | SUPPLIER | EMPLOYEE | The counterparty dimension type for auxiliary accounts. CUSTOMER = accounts receivable style, SUPPLIER = accounts payable style, EMPLOYEE = payroll/expense style. |
| is_active | boolean | ✅ Yes | default true | true | false | Whether the account is currently open for posting. Inactive accounts remain in the chart for historical reporting but should be excluded from new posting selection lists. |
| description | string | ⚪ No | nullable; TEXT (unbounded) | — | Optional free-text description or instructions for use. Populated by the connector sync when the source accounting tool provides an account note. |
| created_at | Date, 🔒 system | ✅ Yes | set once on INSERT via MikroORM onCreate hook; TIMESTAMPTZ NOT NULL DEFAULT now() | — | Timestamp of row creation. Never modified after insert. |
| updated_at | Date, 🔒 system | ✅ Yes | TIMESTAMPTZ NOT NULL DEFAULT now() per migration; set on INSERT and on every UPDATE via MikroORM onUpdate hook | — | Timestamp of last modification. The database column is NOT NULL (baseline migration defines it as TIMESTAMPTZ NOT NULL DEFAULT now()). Updated automatically on every PATCH/flush. |
| deleted_at | Date | null, 🔒 system | ⚪ No | nullable; soft-delete sentinel; all queries must filter WHERE deleted_at IS NULL | — | Soft-delete timestamp. When non-null the row is logically deleted. Hard deletes are never performed. The partial unique index on (workspace_pk, account_number) ignores rows where deleted_at IS NOT NULL, so a previously deleted account number can be re-created. |
Relationships
| Name | Type | Required | Description |
|---|
| workspace | to-one (workspace) | ✅ Yes | The workspace that owns this ledger account. Enforces tenant isolation. All queries must scope to workspace_pk. Indexed via the composite (workspace_pk, account_class) and (workspace_pk, parent_account_pk) indexes. |
| parent_account | to-one (ledger_account) | ⚪ No | Self-referential parent in the chart-of-accounts hierarchy. NULL for top-level accounts (e.g. class root ‘400000’). Indexed on (workspace_pk, parent_account_pk) — index idx_ledger_accounts_workspace_parent created by Migration20260306100000 — to support efficient subtree traversal. |
| child_accounts | to-many (ledger_account) | — | Inverse of parent_account. Collection of all direct child accounts in the hierarchy. Lazy-loaded via MikroORM Collection. |
| source_workspace_connector | to-one (workspace_connector) | ⚪ No | The WorkspaceConnector instance (e.g. a Xero or Pennylane sync) that last wrote or created this ledger account row. NULL for manually created accounts. Added by Migration20260406100000_expand_mcp_sync_models (that migration covers ledger_accounts; Migration20260331100000 only added the column to companies and peoples). ON DELETE SET NULL so connector removal does not cascade-delete ledger accounts. Partial index idx_ledger_accounts_source_wc (WHERE source_workspace_connector_pk IS NOT NULL) added by the same migration. |
System-computed
- ledger_account_id is generated by PostgreSQL gen_random_uuid() on INSERT and also seeded in-process via randomUUID() from Node crypto as the MikroORM default; it is unique and immutable.
- created_at is set once via MikroORM onCreate lifecycle hook (new Date()); never written again.
- updated_at is set on CREATE and on every UPDATE via MikroORM onUpdate lifecycle hook. The database column is NOT NULL (TIMESTAMPTZ NOT NULL DEFAULT now() per Migration20260306100000); the MikroORM entity declares it optional (updated_at?: Date) but that only affects TypeScript nullability — the DB enforces NOT NULL.
- deleted_at is null on creation; set to now() by the application (never by the database) to perform a soft-delete. The partial unique index idx_ledger_accounts_workspace_account_number_active_unique (WHERE deleted_at IS NULL) ensures uniqueness only among active rows — soft-deleted rows are excluded, allowing re-insertion of the same (workspace_pk, account_number) pair after a soft-delete.
- The non-partial UNIQUE(workspace_pk, account_number) constraint from Migration20260306100000 was dropped by Migration20260529100000_ledger_accounts_dedup_unique_idx. That migration also deduped existing active duplicates (keeping the earliest pk) before building the partial index.
- source_workspace_connector_pk is set by the connector sync pipeline (MCP / Xero / Pennylane / etc.) to record provenance. It is NULL for manually created accounts and is SET NULL (not cascade-deleted) when the workspace_connector row is removed. Column and partial index added by Migration20260406100000_expand_mcp_sync_models.
- account_class is a human-supplied classification digit (1–9) enforced by a CHECK constraint in the database; it is not derived.
- is_auxiliary defaults to false; auxiliary_type defaults to NULL. The pipeline or user must explicitly set both when creating an auxiliary account.
- is_active defaults to true; setting it to false deactivates the account without soft-deleting it, preserving historical journal entry lines.
- The (workspace_pk, account_class) composite index (idx_ledger_accounts_workspace_class) and the (workspace_pk, parent_account_pk) composite index (idx_ledger_accounts_workspace_parent) are created by Migration20260306100000 and are transparent to the application layer.
Example
{
"data": {
"type": "ledger_account",
"id": "e3a4c9f0-12b7-4d88-9031-cc52a7d1e205",
"attributes": {
"ledger_account_id": "e3a4c9f0-12b7-4d88-9031-cc52a7d1e205",
"account_number": "411000",
"name": "Clients — France",
"account_type": "ASSET",
"account_class": 4,
"is_auxiliary": true,
"auxiliary_type": "CUSTOMER",
"is_active": true,
"description": "Comptes clients — marché domestique France",
"created_at": "2026-03-10T09:14:22.000Z",
"updated_at": "2026-05-15T16:03:08.000Z",
"deleted_at": null
},
"relationships": {
"workspace": {
"data": { "type": "workspace", "id": "a1b2c3d4-0000-4000-8000-111111111111" }
},
"parent_account": {
"data": { "type": "ledger_account", "id": "b9d00001-aaaa-4bbb-cccc-000000000001" }
},
"source_workspace_connector": {
"data": { "type": "workspace_connector", "id": "f5e60000-dead-beef-cafe-123456789abc" }
}
}
}
}
Source: apps/api/src/database/entities/LedgerAccount.ts · domain: financial-graph · tier: Main