Skip to content
Last updated: Sep 25, 2026

Run the Cash Application Engine ​

OwnerClassificationVersionEffectiveNext reviewStatus
Platform EngineeringInternal0.32026-09-042026-12-04Draft

Purpose. Operating guide for the cash-application engine — how to trigger it, what it processes, how to confirm a run, and the prerequisites that silently disable it. Rule-level detail (the CID rules, the 24 match + 12 remittance rules, the Layer-2 judge internals) is reference material and is not repeated here.

When to run it ​

The engine runs after a bank-item import to identify payers and apply cash, or on demand to re-process the unresolved set. It is the cashapplication Temporal workflow: customer identification (CID) → invoice matching → straight-through clearing (STP).

Two execution paths (bulk vs live) ​

CID runs on two substrates by design, for performance. Both read the same rule catalog and decision thresholds, so a payment the live path resolves lands the same way in a bulk run — only the execution mechanics differ.

PathTriggerSubstrateLatency
BulkRun Engine / scheduleSet-based over the whole eligible populationMinutes (full dataset)
LiveUser clicks Smart ID on a single paymentPer-item, on demandSub-second

The two paths are not symmetrical, and that is intentional. The live path is tuned for sub-second response and therefore runs a high-precision subset of the rules — it omits the set-based, scan-heavy rules (lockbox resolution, cross-subsidiary amount disambiguation, fuzzy token overlap) that only make sense in a batch pass. So the correct expectation is: a payment live identifies, bulk also identifies; bulk resolves more. If a payment live couldn't place but the next bulk run does, that is by design — not a stale config. For the exact rule-by-rule split, see the Cash Application Intelligence reference.

Trigger ​

MethodDetail
In-app (bulk)The Run Engine action — post-import CTA on analyst/payments, and on admin/data-import.
In-app (live)The Smart ID action on a single payment — runs the per-item CID path and returns in under a second.
APIPOST /api/o/finance/cash/v1/workflows/runengine (optional body { "batchSize": <n> }). Permission: cashapp:workflow:execute.

The bulk methods async-start the same workflow. The workflow worker (wfw) and Temporal must be running for the bulk run to execute; the trigger returns a handle immediately, not a result. The live Smart ID path is synchronous and does not depend on wfw.

Stages ​

  1. CID — identify the payer from bank-item text against customers, aliases, and open invoices.
  2. Remittance-driven CID — identify from a linked remittance's invoice references.
  3. Invoice matching — match identified payments to open invoices (1:1, N:1, partial, variance).
  4. Conflict detection — downgrade invoices claimed by multiple payments to suggestions.
  5. Approval routing / STP — auto-clear clean matches; route the rest for approval.

Rule definitions live in the cash-application intelligence reference (matching engine, CID rules, Layer-2 judge, trace format). This guide covers operation only.

CID behaviors a support engineer sees ​

Full rule detail is in the intelligence reference; these two rules drive most "why did/didn't it identify this?" questions.

Alias identification (CID-004) ​

The engine matches known customer aliases against the payment's name and narrative fields, in two ways:

  • Exact field match — an alias equals the name/narrative field. Scores 95.
  • Narrative substring — an alias of 5 or more characters appears as a substring within the narrative. Scores 75 — a suggestion only, below the auto_match_threshold (85), so it surfaces for review rather than auto-identifying.

Example: a payment narrated ACH CREDIT ACME CORP PMT is matched to Acme Corporation via the ACME CORP alias (substring, score 75). The 5-character minimum keeps short aliases from firing on incidental text.

Amount ambiguity (CID-008) ​

When a payment's amount matches open invoices for more than one customer, the engine does not guess. It surfaces every candidate customer (up to amount_max_customers, default 3) and escalates the ambiguity to the Layer-2 AI evaluator, which disambiguates using context such as a prior-payment relationship. If the amount maps to more customers than the cap, it is treated as noise and suppressed rather than surfaced.

Configuration knobs ​

Thresholds and caps a support engineer can tune live in finbase.matching_config on the CASH-CID row (shared by both execution paths). Scoring constants are code-level.

KnobDefaultWhere it livesEffect
auto_match_threshold85matching_config column (CASH-CID row)Score at/above which CID auto-identifies
suggest_threshold50matching_config column (CASH-CID row)Score at/above which CID surfaces a suggestion
amount_max_customers3matching_config.parameters (JSON); env CID_AMOUNT_MAX_CUSTOMERS as fallbackCID-008 fan-out cap — above this the amount is suppressed as noise
ambiguity_gap15code-level constantMinimum score gap between top two candidates to treat the top as unambiguous
short_circuit95code-level constantScore at which CID stops evaluating further rules (an exact-alias-grade hit)
multi_rule_bonus10code-level constantScore added when multiple rules independently agree on the same customer

Code-level constants change only via release; the matching_config values are data and can be tuned per tenant without a deploy. Because both execution paths read the same CASH-CID row, a threshold change applies to bulk and live alike.

What a run touches (blast radius) ​

A full run is scoped by column state, not by a global rebuild — curated and resolved data is left alone.

SetProcessed?Basis
Items with no customer (New or Unidentified)🟢CID work-set is customernumber IS NULL / '' / '000000'
Items already carrying a customer⚪skipped by CID — identified payers are never downgraded
Identified-but-unapplied items🟢matched — matchingstatus NOT IN ('Matched','Cleared')
Matched / Cleared items⚪skipped by matching
Manual / Assisted matches⚪protected; the engine learns aliases from them rather than overwriting

The CID work-set is keyed on customernumber, not on the status literal — so both New and Unidentified items are eligible.

Idempotency ​

Re-running is safe:

  • Recommendations are written delete-then-insert, scoped by masterbankitemsid — no duplicates.
  • intelligencelog is append-only by design (a per-run audit trail). Readers take the latest row (createdon DESC), so a re-run adds history without corrupting state. A reset clears the log to return to a clean baseline.

Status lifecycle ​

New → Identified            → Matched → Cleared
    → Unidentified (ambiguous / no candidate)
                            → Suggestions Available / Partial Match

The front-end CID action renders only for status='Unidentified'. A freshly seeded or imported item at status='New' shows no CID control until the engine flips an ambiguous item to Unidentified. This is the most common "the button is missing" report — the engine has not run.

Prerequisites that silently disable the engine ​

These fail green: balance checks stay valid while the engine produces nothing.

RequirementSymptom if missing
matching_config + matching_rule populated (data, not schema)Engine runs and matches nothing; no error
CID rules (CASH-CID) + thresholds (auto_match_threshold, suggest_threshold; see Configuration knobs)No identification or no auto-apply
amountaspervendor populated on AR itemsPayment silently drops out of CEI/DSO metrics

Run verify-engine-readiness.sql before relying on a run — it is the check that catches the silent engine-death.

Verify a run ​

sql
-- status distribution moved as expected
SELECT status, count(*) FROM cash.masterbankitems GROUP BY status ORDER BY 2 DESC;

-- decisions were logged (latest per item)
SELECT processtype, count(*) FROM finbase.intelligencelog
WHERE sourceobject = 'masterbankitems' GROUP BY processtype;

Then confirm engine readiness and FIG alignment:

bash
psql < scripts/demo/verify-engine-readiness.sql   # engine alive
psql < scripts/demo/verify-fig-alignment.sql      # balances + JV→recon tie

Layer-2 judge and Finni ​

  • Layer-2 judge — LLM disambiguation for an ambiguous CID (e.g. the CID-008 multi-customer fan-out above) or match. It is not part of the bulk run; it fires on demand when an analyst requests it, and requires the agents service (:10013) and a reachable model. The bulk engine only flags ambiguity and stores the candidate customers for the judge to disambiguate.
  • Finni — the cash-application assistant surface (recommendations, natural-language payment queries, identify/match actions). It invokes the same identification and matching logic on demand.

Both are LLM features; their model, tokenization, and data-handling posture is governed under AI Governance.

Operational constraints ​

  • The wfw worker must be running. In development it bundles workflows from source on respawn; in production a changed cashwf workflow requires build:workflow before deploy.
  • Temporal latency: a full-dataset run is minutes-long. For demonstrations, run the engine ahead of time and ship the materialised snapshot rather than running live. A scoped re-run after a reset processes only the unresolved set (guards skip everything resolved) and completes in seconds.
  • The Layer-2 judge is on demand and depends on the agents service and model availability.

Deployment (Railway) ​

RequirementStatus
wfw deployed and running; build:workflow run for changed cashwf workflows⚪ verify
matching_config + matching_rule seeded⚪ verify
verify-engine-readiness.sql all-green on the target⚪ verify
Agents service reachable for Layer-2 (if the demo exercises it)⚪ verify
DEMO_MODE=true on the API service (for demo reset endpoints)⚪ verify

Revision history ​

VersionDateAuthorChange
0.12026-08-29Platform EngineeringInitial draft — trigger, stages, blast radius, idempotency, lifecycle, prerequisites, verification, L2/Finni, constraints, Railway checklist.
0.22026-09-01Platform EngineeringDocumented the bulk vs live (Smart ID) execution paths, CID-004 alias identification and CID-008 amount-ambiguity behaviors, and the configuration knobs (thresholds, fan-out cap, scoring constants).
0.32026-09-04Platform EngineeringCorrected the bulk-vs-live framing: the paths are intentionally asymmetric (live is a high-precision, low-latency subset; bulk runs the set-based rules too), not guaranteed-identical. Cross-linked the new Cash Application Intelligence reference for the rule-by-rule split.

Finaisse Internal — Confidential. Access-restricted; not for external distribution.