Skip to main content
A subledger is a ledger-shaped table scoped to an entity and workflow run (expenses, journal staging, etc.). Rows live in the tenant ledgers schema alongside the GL — not in ingest.*. Use ntro.subledger from activities or workflow-adjacent code that already holds a data-plane connection.

Opening a handle

from uuid import UUID
from ntro.subledger import open as subledger_open

handle = subledger_open(
    name="expenses",
    entity_id=UUID("…"),
    task_id=UUID("…"),
    tenant_slug="acme",
)
rows = await handle.query(...)
Platform types register via @register_type under ntro.subledger.types. Mutation helpers for review workflows live alongside the type (e.g. edit/reject rules) so Temporal-signalled actions can apply domain logic consistently — see UI and Temporal signals.

Core concepts

SymbolPurpose
RowPydantic base for subledger rows — standard columns (id, entity_id, period, task_id, status, …).
SubledgerStatusDefault lifecycle enum; types may specialize.
SubledgerHandleLazy-bound API for query / insert / transitions for one (subledger name, entity, task).
register_typeAssociates a row model with a subledger name at import time.
Direct HTTP mutation endpoints on Ntropii Tenant may still exist for ops or legacy callers; interactive Tenant UI edits for HITL tables should go through workflow signals so Temporal stays the source of truth — same boundary as described in the signals doc.

Standard column block

Every Row subclass inherits these columns. Type-specific fields are added on top.
ColumnTypePurpose
idUUIDRow PK; auto-generated UUID4 if not supplied.
entity_idUUIDCanonical, immutable entity identifier (UUID, not slug).
periodPeriod ("YYYY-MM")Accounting period.
task_idUUIDWorkflow task that wrote the row (provenance).
statusSubledgerStatusLifecycle marker; types may override the enum.
source_refstr | NoneUpstream handle (e.g. "event:<uuid>", "doc:<uuid>").
validation_errorslist[dict] | NonePopulated on rows that failed strict-type validation (NEEDS_ATTENTION flow).
raw_payloaddict | NoneThe agent’s submitted dict, captured before validation.
created_at / updated_atdatetimeInsert / update timestamps.

Lifecycle — SubledgerStatus

Default lifecycle every platform type uses unless it overrides. Terminal states have no outgoing edges.
NEEDS_ATTENTION ─┬─▶ PENDING ─┬─▶ APPROVED ─┬─▶ POSTED   (terminal)
                 │            │             └─▶ REJECTED (terminal)
                 │            ├─▶ REJECTED              (terminal)
                 │            └─▶ EXCLUDED              (terminal)
                 └─▶ REJECTED                           (terminal)
NEEDS_ATTENTION is the entry point for rows whose payload failed type-level validation (e.g. an extraction missing vendor). HITL fixes the typed columns and transitions to PENDING. From there the row follows the normal lifecycle. transition(from_status, to_status) raises IllegalTransitionError on a disallowed move. Idempotent self-transitions (X → X) are also rejected — call sites should check row.status == target first if they want a no-op.

Platform types

Bundled subledger types ship under ntro.subledger.types and register at import time. Each carries its own typed columns, validation rules, and propose_for_gl handoff (where applicable).

expenses

ExpenseRow — single expense receipt (vendor, amount, category, VAT). Used by expense-processor.

journal_proposals

JournalProposalRow — one HITL-reviewed journal entry awaiting GL commit. Used by nav-monthly-journals.

Ingest outcomes & feedback

Earlier stage: ingest schema and feedback envelopes.

UI and Temporal signals

How row edits reach ntro.subledger via workflows.

Accounting capability

GL-facing helpers built on subledger proposals where applicable.

General ledgers

ntro.capabilities.gl — post BillProposal and other resources to the external GL.