← Back to Cookbook

Invoice Processor

Extracts line items and totals from invoices, flags anomalies, and stores results.

aiswitch

Source

/**
 * Processes invoice documents. Extracts line items and totals,
 * flags anomalies, and stores results.
 */

webhook invoice_received {
  label: "Invoice Received"
  schema: @json {
    {
      "type": "object",
      "required": ["vendor", "invoice_text", "amount"],
      "properties": {
        "vendor": { "type": "string" },
        "invoice_text": { "type": "string" },
        "amount": { "type": "number" },
        "due_date": { "type": "string" }
      }
    }
  }
}

graph process_invoice {
  label: "Process Invoice"

  persistence {
    enabled: true
    condition: @ts { return true }
    name: "invoices"
  }

  root {
    type: code
    label: "Extract invoice"
    code: @ts { return context.nodes.root.input }
    outputSchema: @json {
      {
        "type": "object",
        "properties": {
          "vendor": { "type": "string" },
          "invoice_text": { "type": "string" },
          "amount": { "type": "number" },
          "due_date": { "type": "string" }
        }
      }
    }
  }

  node extract_details {
    type: ai
    label: "Extract line items"
    kind: object
    model: "google/gemini-2.5-flash"
    prompt: @ts {
      return "Extract structured line items from this invoice.\n\nVendor: " + context.nodes.root.output.vendor + "\nTotal: $" + context.nodes.root.output.amount + "\n\n" + context.nodes.root.output.invoice_text
    }
    schema: @json {
      {
        "type": "object",
        "required": ["line_items", "total_matches"],
        "properties": {
          "line_items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "description": { "type": "string" },
                "quantity": { "type": "number" },
                "unit_price": { "type": "number" },
                "total": { "type": "number" }
              }
            }
          },
          "total_matches": { "type": "boolean" },
          "discrepancy_note": { "type": "string" }
        }
      }
    }
  }

  node check_anomaly {
    type: switch
    label: "Check for anomalies"
    cases: ["ok", "flagged"]
    router: @ts {
      if (context.nodes.extract_details.output.total_matches) return "ok"
      return "flagged"
    }
  }

  node approve {
    type: code
    label: "Auto-approve"
    code: @ts {
      return {
        status: "approved",
        vendor: context.nodes.root.output.vendor,
        amount: context.nodes.root.output.amount,
        line_items: context.nodes.extract_details.output.line_items.length
      }
    }
  }

  node flag {
    type: code
    label: "Flag for review"
    code: @ts {
      return {
        status: "flagged",
        vendor: context.nodes.root.output.vendor,
        amount: context.nodes.root.output.amount,
        note: context.nodes.extract_details.output.discrepancy_note
      }
    }
  }

  flow {
    root -> extract_details
    extract_details -> check_anomaly
    check_anomaly -["ok"]-> approve
    check_anomaly -["flagged"]-> flag
  }
}

trigger on_invoice {
  webhook:invoice_received -> process_invoice
  enabled: true
}

Flow

Trigger → graph

Graph nodes