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

# JournalEntryPostingAttempt

> JournalEntryPostingAttempt is an append-only audit ledger that records every attempt to post a double-entry journal entry from a source document (invoice, invoi

JournalEntryPostingAttempt is an append-only audit ledger that records every attempt to post a double-entry journal entry from a source document (invoice, invoice\_transaction, or bank transaction). One row is written per attempt — persisted or halted — so that human-driven retries never overwrite prior halt reasons and the full posting history is preserved. It is workspace-scoped and carries actor metadata (system vs user) together with a nullable reference to the People who triggered a manual retry. The table is the authority for why a journal entry was or was not created for a given source document.

| Naming                          | Value                                |
| ------------------------------- | ------------------------------------ |
| Object                          | JournalEntryPostingAttempt           |
| Resource type (JSON:API `type`) | `journal_entry_posting_attempt`      |
| Collection / records root       | — <sub>(not a records root)</sub>    |
| REST base                       | `/v1/journal-entry-posting-attempts` |
| Entity class                    | `JournalEntryPostingAttempt`         |

<Note>
  **Internal object.** Not currently exposed on the public REST API. The operations below describe the intended contract.
</Note>

## API operations

| Operation | Method & path                                    | Status     |
| --------- | ------------------------------------------------ | ---------- |
| List      | `GET /v1/journal-entry-posting-attempts`         | 🟡 Planned |
| Retrieve  | `GET /v1/journal-entry-posting-attempts/{id}`    | 🟡 Planned |
| Create    | `POST /v1/journal-entry-posting-attempts`        | 🟡 Planned |
| Update    | `PATCH /v1/journal-entry-posting-attempts/{id}`  | 🟡 Planned |
| Delete    | `DELETE /v1/journal-entry-posting-attempts/{id}` | 🟡 Planned |

## Data model

### Attributes

| Field                                | Type                                            | Required | Constraints                                                                                            | Allowed values                                 | Description                                                                                                                                                                                                                                                            |
| ------------------------------------ | ----------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------ | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| journal\_entry\_posting\_attempt\_id | string (UUID) 🔒 system                         | ✅ Yes    | UNIQUE                                                                                                 | —                                              | Public UUID identifier for this posting attempt. Generated by gen\_random\_uuid() on insert. This is the value exposed as the JSON:API resource id.                                                                                                                    |
| source\_kind                         | enum (JournalEntryPostingAttemptSourceKindEnum) | ✅ Yes    | Native Postgres enum: journal\_entry\_posting\_attempt\_source\_kind\_enum. NOT NULL.                  | invoice \| invoice\_transaction \| transaction | Identifies the type of source document that triggered the posting attempt. 'invoice' = invoice-driven JE; 'invoice\_transaction' = legacy per-bridge-row trigger (D9, preserved for backward compat); 'transaction' = D9.1 per-bank-transaction payment-settlement JE. |
| source\_id                           | string                                          | ✅ Yes    | VARCHAR(64), NOT NULL                                                                                  | —                                              | Public UUID string of the source row (invoice\_id, invoice\_transaction\_id, or transaction\_id). Stored as VARCHAR(64) rather than a typed FK so new source kinds do not require schema changes.                                                                      |
| source\_pk                           | integer                                         | ⚪ No     | INTEGER, NULLABLE                                                                                      | —                                              | Internal integer PK of the source row for fast joins when the source kind is known. Nullable because legacy attempt rows pre-dating the D9.1 migration may not have this populated.                                                                                    |
| status                               | enum (JournalEntryPostingAttemptStatusEnum)     | ✅ Yes    | Native Postgres enum: journal\_entry\_posting\_attempt\_status\_enum. NOT NULL.                        | persisted \| halt                              | Outcome of this posting attempt. 'persisted' = a journal entry was written; 'halt' = posting was blocked (see reason/details). Halts never roll back the source document.                                                                                              |
| reason                               | string                                          | ⚪ No     | VARCHAR(100), NULLABLE                                                                                 | —                                              | Short machine-readable reason code for a halt (e.g. 'missing\_mapping', 'polarity\_conflict'). Free-string rather than enum because halt reasons span multiple subsystems and are expected to grow. NULL on status=persisted.                                          |
| details                              | string                                          | ⚪ No     | VARCHAR(500), NULLABLE                                                                                 | —                                              | Human-readable explanation of the halt reason, providing context for the operator reviewing the audit trail. NULL on status=persisted.                                                                                                                                 |
| idempotency\_key                     | string                                          | ⚪ No     | VARCHAR(160), NULLABLE                                                                                 | —                                              | Idempotency key passed to the journal entry persister. Set on status=persisted; null on status=halt. Allows the persister to detect duplicate attempts and avoid double-writing JEs.                                                                                   |
| line\_count                          | integer                                         | ⚪ No     | INTEGER, NULLABLE                                                                                      | —                                              | Number of journal entry lines written when status=persisted. Null on status=halt. Useful for sanity-checking multi-line JEs (e.g. AR/AP lines per counterparty).                                                                                                       |
| created                              | boolean                                         | ⚪ No     | BOOLEAN, NULLABLE                                                                                      | —                                              | True when a new journal entry was created; false when an existing one was updated/reused via the idempotency key. Null on status=halt.                                                                                                                                 |
| attempted\_at                        | Date (timestamptz)                              | ✅ Yes    | TIMESTAMPTZ, NOT NULL                                                                                  | —                                              | Wall-clock timestamp at which the posting was attempted. Used as the DESC sort key in the latest-per-source and timeline indexes. Stored as TIMESTAMPTZ.                                                                                                               |
| attempted\_by\_kind                  | enum (JournalEntryPostingAttemptActorKindEnum)  | ✅ Yes    | Native Postgres enum: journal\_entry\_posting\_attempt\_actor\_kind\_enum. NOT NULL. DEFAULT 'system'. | system \| user                                 | Whether the attempt was triggered by an automated pipeline ('system') or by an explicit human action such as a manual retry after fixing a classification ('user'). Defaults to 'system'.                                                                              |
| created\_at                          | Date 🔒 system                                  | ✅ Yes    | TIMESTAMP, NOT NULL, DEFAULT now()                                                                     | —                                              | Row creation timestamp. Set by the onCreate hook; never user-editable.                                                                                                                                                                                                 |
| updated\_at                          | Date 🔒 system                                  | ⚪ No     | TIMESTAMP, NULLABLE                                                                                    | —                                              | Last modification timestamp. Set by onUpdate hook. Nullable per entity declaration.                                                                                                                                                                                    |
| deleted\_at                          | Date 🔒 system                                  | ⚪ No     | TIMESTAMP, NULLABLE                                                                                    | —                                              | Soft-delete timestamp. NULL means the row is active. Used in the workspace-status composite index to keep halted-attempt dashboard queries efficient.                                                                                                                  |

### Relationships

| Name           | Type                         | Required | Description                                                                                                                                                           |
| -------------- | ---------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| workspace      | to-one (ManyToOne)           | ✅ Yes    | The workspace this posting attempt belongs to. All queries must filter by workspace\_pk for tenant isolation. References core\_api.workspaces(pk).                    |
| journal\_entry | to-one (ManyToOne, nullable) | ⚪ No     | The journal entry that was written on a successful (persisted) attempt. NULL on status=halt. References core\_api.journal\_entries(pk).                               |
| attempted\_by  | to-one (ManyToOne, nullable) | ⚪ No     | The People record of the user who triggered a manual retry when attempted\_by\_kind='user'. NULL when attempted\_by\_kind='system'. References core\_api.peoples(pk). |

### System-computed

* journal\_entry\_posting\_attempt\_id — generated by gen\_random\_uuid() on insert; never user-supplied
* created\_at — set by MikroORM onCreate hook (new Date()); immutable after creation
* updated\_at — set by MikroORM onUpdate hook on every flush
* deleted\_at — soft-delete; set by repository-layer soft-delete helpers; not set by the application layer on normal posting flows
* attempted\_by\_kind defaults to JournalEntryPostingAttemptActorKindEnum.SYSTEM ('system') when not explicitly provided
* source\_kind native enum extended via Migration20260526030000 to include 'transaction' for D9.1 payment-settlement attempts; 'invoice\_transaction' retained for backward compat with pre-D9.1 rows

## Example

```json theme={null}
{
  "data": {
    "type": "journal_entry_posting_attempt",
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "attributes": {
      "source_kind": "invoice",
      "source_id": "inv_7e2f3a1b-4c5d-6e7f-8a9b-0c1d2e3f4a5b",
      "source_pk": 10482,
      "status": "halt",
      "reason": "missing_mapping",
      "details": "No ledger account mapping found for category 'operating_expense' in chart of accounts version 3.",
      "idempotency_key": "inv_7e2f3a1b-4c5d-6e7f-8a9b-0c1d2e3f4a5b:attempt:3",
      "line_count": null,
      "created": null,
      "attempted_at": "2026-05-26T14:23:11.000Z",
      "attempted_by_kind": "system",
      "created_at": "2026-05-26T14:23:11.000Z",
      "updated_at": "2026-05-26T14:23:11.000Z",
      "deleted_at": null
    },
    "relationships": {
      "workspace": {
        "data": { "type": "workspace", "id": "ws_00000000-0000-0000-0000-000000000001" }
      },
      "journal_entry": {
        "data": null
      },
      "attempted_by": {
        "data": null
      }
    }
  }
}
```

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