HITL contract
HITL = Human In The Loop. The agent has four pause points where it opens a Discord prompt and waits for your reply. Each prompt is backed by a checkpoint record in the database with a unique id; your reply lands against that id and triggers a resolver function that performs the actual write to e-Fin.
The four prompts
1. doc_match
Fires when: the deterministic matcher finds a candidate pair (receipt ↔ bank transaction) that scores in the 0.55 ≤ score < 0.85 band — confident enough to propose, not confident enough to auto-book.
You see: Discord message showing the receipt + the bank line + the score breakdown (which signals fired). Buttons: Approve / Reject.
On approve: the reconciliation row transitions to booked;
the booker daemon picks it up next tick.
On reject: the reconciliation row goes to rejected
(terminal); the document stays in the store, ready for a manual
re-match.
2. booker_approve
Fires when: an approved reconciliation is about to be written to e-Fin AND one of the gate conditions is true: amount > €500, OR the counterparty has never been booked before.
You see: Discord message: "Book recon #N (high value + new vendor)? Counterparty: X · Amount: Y · Date: Z." Buttons: Approve / Reject.
On approve: the booker writes the purchase_invoice to e-Fin with the proposed VAT rate.
On reject: the reconciliation row goes to rejected. No
e-Fin call.
3. client_create_approval
Fires when: the booker hits a vendor whose name has no match in e-Fin's client list — typical the first time you receive an invoice from a new supplier.
You see: Discord message with the proposed Client record (name + OCR-extracted country + supplier/buyer flags). Buttons: Approve & create / Reject.
On approve: the resolver calls POST /v1/clients with the
proposed payload, then immediately re-runs book_one(recon_id)
so the booking chain continues without a second click.
On reject: the reconciliation row goes to rejected. The
supplier stays absent; if you change your mind later, re-ingest
the receipt.
4. agent_ask
Fires when: the chat agent (the one that answers /ask
questions) needs to clarify before continuing — e.g. it can't
decide between two candidate documents you might be asking about.
You see: a free-text or multiple-choice prompt in the channel.
On reply: the orchestrator picks up your answer and resumes the agent run from where it paused.
What's NOT a HITL pause
These were considered and explicitly not HITL pauses:
- VAT decisions with high or medium confidence — written silently with the rule ids stamped on the booking for audit
- Bank-transaction inserts — flowing into the reconciliations table is purely descriptive, not a write to e-Fin
- Re-matching on a poll tick — promoting an unmatched recon to
proposedis still gated by thedoc_matchHITL above
The principle: anything that creates master data OR makes an irreversible write goes through HITL. Everything else is on the autonomous side of the line.