Skip to main content

Invoice Complexity Classification

Invoice Complexity Classification Overview

This page documents the current state of invoice complexity classification in BINS and the Wasteology platform, and outlines the three viable approaches for implementing easy/medium/hard classification.

No classification exists yet

As of 2026-04-16, no easy/medium/hard complexity field exists in any table, endpoint, or configuration in the BINS codebase or database schema. This page documents what does exist that can support building it.


What Currently Exists

1. ML Line Item Confidence Scores (Best ML Signal)

Every invoice that flows through the invoice-listener pipeline has its line items classified by an external ML API. The confidence scores from this API are the best existing signal for invoice processing difficulty.

API: https://service-normalization.agreeablesmoke-44fc50ff.centralus.azurecontainerapps.io/predict/service_info/

Per-line-item output stored in docai.raw_invoice_normalized_line_items:

ColumnDescription
md5Invoice identifier
labelLabel type (service_desc, size, material, container, is_valid)
valuePredicted label value
confidenceConfidence score (0.0–1.0)

Complexity proxy logic:

-- Invoices with low average confidence = harder to process
SELECT
md5,
COUNT(*) as line_item_count,
AVG(confidence) as avg_confidence,
SUM(CASE WHEN label = 'is_valid' AND value = 'false' THEN 1 ELSE 0 END) as invalid_items,
CASE
WHEN AVG(confidence) >= 0.85 AND invalid_items = 0 THEN 'Easy'
WHEN AVG(confidence) >= 0.60 THEN 'Medium'
ELSE 'Hard'
END as complexity
FROM docai.raw_invoice_normalized_line_items
GROUP BY md5

2. "Needs Coordination" Status (Existing Implicit Rule)

The only existing business rule that implicitly flags invoices as harder to process. Any invoice missing one of these fields at ingest is automatically set to "Needs Coordination":

  • service_address
  • counterparty (matched CieTrade location)
  • vendor_name
  • state
  • service_date
  • invoice_date

This is a binary flag (missing/not missing), not a 3-tier classification — but it is the existing "complexity" signal in the system.

3. Configurable Rule Engine

The BINS backend has a full rule engine built into the database. Rules fire on invoice create or update, evaluate field conditions, and execute actions — including setting arbitrary fields.

Tables:

TableDescription
trashy.invoice_rulesRule definitions (name, trigger: on_create / on_update)
trashy.rule_conditionsConditions (field, operator, value) per rule
trashy.rule_actionsActions per rule: set_status, set_team, set_field, call_ai_agent

Supported condition operators: equals, contains, greater_than, less_than, not_equals, not_contains

Supported action types: set_status, set_team, set_field (arbitrary field), call_ai_agent (stubbed — not yet implemented)

Adding a complexity rule requires:

  1. One database migration to add a complexity column to trashy.invoices (VARCHAR(10) — 'Easy'/'Medium'/'Hard')
  2. Insert rows into invoice_rules, rule_conditions, rule_actionsno code changes needed

Example rule configuration (database records):

Rule: "High Value = Hard"
Trigger: on_create
Condition: invoice_amount > 10000
Action: set_field complexity = 'Hard'

Rule: "Missing Fields = Hard"
Trigger: on_create
Condition: status equals "Needs Coordination"
Action: set_field complexity = 'Hard'

Rule: "Standard Invoice = Easy"
Trigger: on_create
Condition: invoice_amount <= 5000 AND status != "Needs Coordination"
Action: set_field complexity = 'Easy'

4. User-Defined Flags

trashy.flags + trashy.invoice_flags: named, hex-colored tags that users apply to invoices through the BINS UI. Flags are a flexible, general-purpose feature — different departments use them differently (priority, routing, review status, etc.). The 6 production flags (High Priority, Medium Priority, Low Priority, Past Due, Recycling Receipt, Stop Service) serve existing departmental workflows and should not be repurposed for complexity tiers.

To add AI-driven complexity classification via flags, new flags would be created (e.g. "Easy", "Medium", "Hard") — not the existing ones reused. No code change is required to add flags; they are database records.

Check production flags:

SELECT * FROM trashy.flags;
SELECT f.name, COUNT(if.invoice_id) as usage_count
FROM trashy.flags f
LEFT JOIN trashy.invoice_flags if ON f.id = if.flag_id
GROUP BY f.name;

Implementation Options Compared

OptionSchema ChangeCode ChangeEffortData SourceBest For
Rule engine (set_field complexity)Add complexity columnNone — DB records onlyLowInvoice fields (amount, status, vendor)Business rules driven by field conditions
ML signal (aggregate line item confidence)Add complexity columnNew dbt model or backend queryMediumdocai.raw_invoice_normalized_line_itemsData-driven, objective scoring
Manual flags (new flags)NoneNoneMinimalUser-enteredQuick start; human judgment — create new Easy/Medium/Hard flags, do not reuse existing ones
Hybrid (ML → rule engine override)Add complexity columnNone for rules; new query for MLMediumBothCombines objective + business rule overrides

The rule engine approach is most aligned with how BINS was architected. trashy.invoice_rules records fire on invoice create/update and can set arbitrary fields — no code deployment required. Steps:

  1. Check production state:

    -- Existing flags (for reference — do not reuse for complexity)
    SELECT * FROM trashy.flags;
    -- Existing rules
    SELECT * FROM trashy.invoice_rules;
    SELECT * FROM trashy.rule_conditions;
  2. If no rules exist yet, add the column:

    ALTER TABLE trashy.invoices ADD COLUMN complexity VARCHAR(10);
    -- Values: 'Easy', 'Medium', 'Hard', NULL (unclassified)
  3. Insert rule records for agreed-upon complexity criteria. No code deployment required — rules are evaluated at runtime from the DB.

  4. Optional: backfill from ML signals by querying docai.raw_invoice_normalized_line_items and computing average confidence per invoice as a seed classification.