Invoice Processing Workflow
Business8 nodes · 8 edgesbusiness
Visual
ex-invoice-processing.osop.yaml
# Invoice Processing Workflow
# Receive invoices, extract data via OCR, match to POs, route for approval, process payment
osop_version: "2.0"
id: invoice-processing
name: Invoice Processing Workflow
nodes:
- id: receive_invoice
type: api
purpose: Accept invoice via email attachment, upload, or EDI and normalize to PDF
runtime:
endpoint: /api/v1/invoices/ingest
method: POST
url: https://ap-service.internal
outputs: [invoice_id, source_channel, raw_document_url]
timeout_sec: 30
- id: ocr_extract
type: agent
purpose: Extract structured data from invoice PDF using document AI
runtime:
provider: google
model: document-ai-invoice-parser
config:
output_format: json
fields: [vendor_name, invoice_number, date, due_date, line_items, subtotal, tax, total, currency, bank_details]
inputs: [raw_document_url]
outputs: [extracted_data, confidence_scores, extraction_warnings]
timeout_sec: 60
retry_policy:
max_retries: 2
backoff_sec: 5
explain: |
Uses Google Document AI for structured extraction. Fields with
confidence below 0.85 are flagged for manual review.
- id: validate_data
type: cli
purpose: Validate extracted fields against business rules and check for duplicates
runtime:
command: |
python validate_invoice.py \
--invoice-id ${invoice_id} \
--check-duplicate \
--check-vendor-exists \
--check-amounts \
--check-dates
inputs: [extracted_data, invoice_id]
outputs: [validation_passed, validation_errors, duplicate_flag]
timeout_sec: 30
- id: match_purchase_order
type: db
purpose: Match invoice line items to open purchase orders using fuzzy matching
runtime:
engine: postgresql
connection: postgresql://erp:5432/procurement
inputs: [extracted_data]
outputs: [matched_po_ids, match_confidence, unmatched_lines]
timeout_sec: 15
explain: |
Three-way match: invoice amount, PO amount, and goods receipt.
Tolerance of 2% on amounts. Unmatched lines require manual review.
- id: route_approval
type: agent
purpose: Determine approval routing based on amount thresholds and department policy
runtime:
provider: internal
model: approval-rules-engine
config:
thresholds:
- { max: 1000, approvers: 1, role: "team_lead" }
- { max: 10000, approvers: 1, role: "department_head" }
- { max: 100000, approvers: 2, role: "vp_finance" }
inputs: [extracted_data, matched_po_ids]
outputs: [approval_route, required_approvers]
- id: manager_approval
type: human
purpose: Designated approver reviews invoice details, PO match, and approves payment
role: approver
inputs: [extracted_data, matched_po_ids, match_confidence, unmatched_lines]
outputs: [approval_decision, rejection_reason]
approval_gate:
required_approvers: 1
timeout_min: 4320
explain: |
Approver sees side-by-side view of invoice and matched PO.
Can approve, reject, or request additional information from vendor.
- id: process_payment
type: api
purpose: Submit approved invoice for payment via ERP payment batch
runtime:
endpoint: /api/v1/payments/schedule
method: POST
url: https://erp.internal
inputs: [invoice_id, extracted_data, approval_decision]
outputs: [payment_id, scheduled_date, payment_method]
security:
auth: oauth2
secret_ref: ERP_SERVICE_ACCOUNT
timeout_sec: 30
- id: update_ledger
type: db
purpose: Post accounting entries to general ledger and update AP aging
runtime:
engine: postgresql
connection: postgresql://erp:5432/accounting
inputs: [payment_id, extracted_data]
outputs: [journal_entry_id, gl_posted]
timeout_sec: 15
edges:
- from: receive_invoice
to: ocr_extract
mode: sequential
- from: ocr_extract
to: validate_data
mode: sequential
- from: validate_data
to: match_purchase_order
mode: conditional
condition: "validation_passed == true && duplicate_flag == false"
- from: match_purchase_order
to: route_approval
mode: sequential
- from: route_approval
to: manager_approval
mode: sequential
- from: manager_approval
to: process_payment
mode: conditional
condition: "approval_decision == 'approved'"
- from: process_payment
to: update_ledger
mode: sequential
- from: validate_data
to: receive_invoice
mode: error
condition: "duplicate_flag == true"
label: "Duplicate detected, reject and notify sender"