> ## 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.

# Ledger Account

> A LedgerAccount represents a single line in a workspace's chart of accounts (CoA), classified by accounting type (ASSET, LIABILITY, EQUITY, REVENUE, EXPENSE) an

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

```json theme={null}
{
  "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" }
      }
    }
  }
}
```

<sub>Source: `apps/api/src/database/entities/LedgerAccount.ts` · domain: financial-graph · tier: Main</sub>
