Skip to main content

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.
NamingValue
ObjectLedger Account
Resource type (JSON:API type)ledger_account
Collection / records rootledger_accounts
REST base/v1/ledger-accounts
Entity classLedgerAccount

API operations

OperationMethod & pathStatus
ListGET /v1/ledger-accounts✅ Implemented
RetrieveGET /v1/ledger-accounts/{id}✅ Implemented
CreatePOST /v1/ledger-accounts🟡 Planned
UpdatePATCH /v1/ledger-accounts/{id}🟡 Planned
DeleteDELETE /v1/ledger-accounts/{id}🟡 Planned

Data model

Attributes

FieldTypeRequiredConstraintsAllowed valuesDescription
ledger_account_idstring, UUID, 🔒 system✅ Yesunique; generated by gen_random_uuid() on INSERTPublic stable identifier for the ledger account. Use this in all API references; the internal pk is never exposed.
account_numberstring✅ Yesmax 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 Migration20260529100000The 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.
namestring✅ Yesmax length 255Human-readable display name for the ledger account.
account_typeenum (ledger_account_type_enum)✅ Yesnon-nullable; native PostgreSQL enumASSET | LIABILITY | EQUITY | REVENUE | EXPENSEBroad 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_classinteger✅ YesCHECK (account_class >= 1 AND account_class <= 9); composite index (workspace_pk, account_class)1 – 9Numeric 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_auxiliaryboolean✅ Yesdefault falsetrue | falseIndicates 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_typeenum (auxiliary_type_enum)⚪ Nonullable; native PostgreSQL enum; required in practice when is_auxiliary = trueCUSTOMER | SUPPLIER | EMPLOYEEThe counterparty dimension type for auxiliary accounts. CUSTOMER = accounts receivable style, SUPPLIER = accounts payable style, EMPLOYEE = payroll/expense style.
is_activeboolean✅ Yesdefault truetrue | falseWhether 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.
descriptionstring⚪ Nonullable; 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_atDate, 🔒 system✅ Yesset once on INSERT via MikroORM onCreate hook; TIMESTAMPTZ NOT NULL DEFAULT now()Timestamp of row creation. Never modified after insert.
updated_atDate, 🔒 system✅ YesTIMESTAMPTZ NOT NULL DEFAULT now() per migration; set on INSERT and on every UPDATE via MikroORM onUpdate hookTimestamp 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_atDate | null, 🔒 system⚪ Nonullable; soft-delete sentinel; all queries must filter WHERE deleted_at IS NULLSoft-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

NameTypeRequiredDescription
workspaceto-one (workspace)✅ YesThe 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_accountto-one (ledger_account)⚪ NoSelf-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_accountsto-many (ledger_account)Inverse of parent_account. Collection of all direct child accounts in the hierarchy. Lazy-loaded via MikroORM Collection.
source_workspace_connectorto-one (workspace_connector)⚪ NoThe 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