JournalEntryPostingAttempt

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.

NamingValue
ObjectJournalEntryPostingAttempt
Resource type (JSON:API type)journal_entry_posting_attempt
Collection / records rootโ€” (not a records root)
REST base/v1/journal-entry-posting-attempts
Entity classJournalEntryPostingAttempt

Internal object. Not currently exposed on the public REST API. The operations below describe the intended contract.

API operations

OperationMethod & pathStatus
ListGET /v1/journal-entry-posting-attempts๐ŸŸก Planned
RetrieveGET /v1/journal-entry-posting-attempts/{id}๐ŸŸก Planned
CreatePOST /v1/journal-entry-posting-attempts๐ŸŸก Planned
UpdatePATCH /v1/journal-entry-posting-attempts/{id}๐ŸŸก Planned
DeleteDELETE /v1/journal-entry-posting-attempts/{id}๐ŸŸก Planned

Data model

Attributes

FieldTypeRequiredConstraintsAllowed valuesDescription
journal_entry_posting_attempt_idstring (UUID) ๐Ÿ”’ systemโœ… YesUNIQUEโ€”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_kindenum (JournalEntryPostingAttemptSourceKindEnum)โœ… YesNative Postgres enum: journal_entry_posting_attempt_source_kind_enum. NOT NULL.invoice | invoice_transaction | transactionIdentifies 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_idstringโœ… YesVARCHAR(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_pkintegerโšช NoINTEGER, 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.
statusenum (JournalEntryPostingAttemptStatusEnum)โœ… YesNative Postgres enum: journal_entry_posting_attempt_status_enum. NOT NULL.persisted | haltOutcome of this posting attempt. 'persisted' = a journal entry was written; 'halt' = posting was blocked (see reason/details). Halts never roll back the source document.
reasonstringโšช NoVARCHAR(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.
detailsstringโšช NoVARCHAR(500), NULLABLEโ€”Human-readable explanation of the halt reason, providing context for the operator reviewing the audit trail. NULL on status=persisted.
idempotency_keystringโšช NoVARCHAR(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_countintegerโšช NoINTEGER, 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).
createdbooleanโšช NoBOOLEAN, 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_atDate (timestamptz)โœ… YesTIMESTAMPTZ, 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_kindenum (JournalEntryPostingAttemptActorKindEnum)โœ… YesNative Postgres enum: journal_entry_posting_attempt_actor_kind_enum. NOT NULL. DEFAULT 'system'.system | userWhether 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_atDate ๐Ÿ”’ systemโœ… YesTIMESTAMP, NOT NULL, DEFAULT now()โ€”Row creation timestamp. Set by the onCreate hook; never user-editable.
updated_atDate ๐Ÿ”’ systemโšช NoTIMESTAMP, NULLABLEโ€”Last modification timestamp. Set by onUpdate hook. Nullable per entity declaration.
deleted_atDate ๐Ÿ”’ systemโšช NoTIMESTAMP, 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

NameTypeRequiredDescription
workspaceto-one (ManyToOne)โœ… YesThe workspace this posting attempt belongs to. All queries must filter by workspace_pk for tenant isolation. References core_api.workspaces(pk).
journal_entryto-one (ManyToOne, nullable)โšช NoThe journal entry that was written on a successful (persisted) attempt. NULL on status=halt. References core_api.journal_entries(pk).
attempted_byto-one (ManyToOne, nullable)โšช NoThe 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

{
  "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
      }
    }
  }
}
Source: apps/api/src/database/entities/JournalEntryPostingAttempt.ts ยท domain: financial-graph ยท tier: Infrastructure