Customer invoice dispute
Invoice disputes are the most common high-friction inbound. They're also one of the easiest workflows to mishandle — Mike's firsthand pain after the May 6 Dave Jordan $786K alignment call was the trigger for making this a first-class workflow.
The cascade does the unglamorous work: pull the invoice, pull the customer's recent history, classify the reason (pricing / qty / quality / delivery / other), draft a response Mike can polish, route to a sales rep if escalation is needed, and stage an AR hold-or-continue review if the customer has prior disputes.
Risk level 3 (medium). Most rows write to proposed_actions and ar_disputes — read-only from NS's perspective unless escalated.
Trigger conditions
- Email triage classifies an inbound as
invoice_disputeand routes here. - A customer mentions an invoice number in a phone call and Mike opens the dispute manually.
- Sales rep flags an invoice during AR review and triggers the workflow.
- A persistent customer email thread references "invoice" and the AI classifier scores it as a dispute.
prior_disputes >= 2 conditionally stages an ar_hold_review proposed action. This catches the pattern of repeat-dispute customers who tend to use disputes as a payment delay tactic.
The 3 beats
-
01
Draft response to the customer
AI drafts a response email addressing the specific dispute reason, citing the invoice ref, and offering a next step (credit, copy of paperwork, schedule a call). Status=pending_review in
outbound_email_log. -
02
Escalate to sales rep (HITL)
A
proposed_actionsrow stages withaction_type=customer_escalationassigned to the customer's sales rep. Mike taps approve to route. -
03
AR hold-or-continue review (conditional)
If
prior_disputes >= 2, anar_hold_reviewrow stages so AR can decide whether to keep shipping. This is the chokepoint that prevents repeat-dispute customers from accumulating uncollectible AR.
What's different after the workflow runs
ar_disputeshas a row capturing the customer, invoice, reason, and timestamps.- Mike has a draft customer response waiting for approval.
- The sales rep has an escalation in their queue.
- If pattern repeats, AR has a hold-or-continue decision queued.
- reflexion_log carries an
invoice_disputetagged row with customer_id.
What can go wrong and how to recover
Precondition warns. The response draft can still proceed using context but flags "no matching invoice found." Often the customer cites a PO number rather than an invoice number — the trick of leading-% on memo search applies.
The conditional AR hold review is skipped. Single-dispute customers don't auto-flag — they get the friendly path.
Escalation has no target. Default routing falls back to Mike's queue. Cleanup task: populate customers.sales_rep.
Outcome: a credit memo proposed_action stages from the response. That feeds the standard credit-memo workflow, not this one.
Adjacent workflows + diagrams
Code paths + invariants
| Concern | Where |
|---|---|
| Workflow contract | workflow_definitions WHERE workflow_type='customer_invoice_dispute' |
| Dispute table | ar_disputes |
| Classifier | AI classification in inbound_email_triage |
| Memo-field trick | leading '%' on tranid → memo search for PO numbers |
| Reflexion tag | invoice_dispute,customer:<id> |
| Risk level | 3 |
| Expected duration | ~20 min |
| Trigger | event · inbound_email_triage_classification |