Skip to content

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 proposed is still gated by the doc_match HITL 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.