---
name: invoice-inbox-processing
description: Extract every invoice field twice, reconcile the two passes, and queue only the mismatches. Use this skill when: New invoice in the inbox — Watches the AP inbox and the drive drop folder, running in the cloud as long as the backlog takes.
---

# Turn invoice PDFs into ledger entries

Goal: extract every invoice with two independent passes and post only what reconciles clean.

Connected tools this skill uses: gmail, drive, googlesheets, slack.

## When to use

- New invoice in the inbox — Watches the AP inbox and the drive drop folder, running in the cloud as long as the backlog takes.
- Or when the user asks directly to extract every invoice with two independent passes and post only what reconciles clean.

## Method

Process the invoice inbox for {{invoice_inbox}} and {{drive_folder}}.

1. List every invoice file received since the last run. Check each against
   the processed-invoice log first, and skip anything already posted or
   queued, so a re-sent PDF is never double-posted.
2. For each new invoice, parse the PDF to raw text and line items with a
   shell step.
3. Run two independent extraction sub-agents against the invoice field
   schema in memory: vendor, amount, date, PO number, line items. The two
   sub-agents work from the parsed text only, without seeing each other's
   output.
4. Validation: reconcile the two passes field by field. Where both agree,
   accept the value; where they disagree, do not guess — flag the field as
   a mismatch instead.
5. For every invoice with zero mismatches, match it against the open PO in
   {{ledger_sheet}}: quantity, price, and vendor must all agree within
   {{tolerance_pct}}.
6. Post only fully-reconciled, PO-matched invoices to the ledger sheet, and
   log each processed invoice, posted or queued, to the processed-invoice
   log.
7. Queue every invoice with a mismatch or PO gap to {{review_channel}} with
   both extracted values side by side, so the reviewer sees exactly where
   the passes disagreed.

This can run as long as the inbox backlog takes — let it work the full
queue in the cloud rather than stopping partway.

Never post an invoice to the ledger on a single unreconciled pass, and
never approve a PO variance yourself — queue it for sign-off instead.

## Invoice field schema

Define the exact fields every invoice extraction must produce, so both
passes and the ledger post agree on structure.

## Method
1. List the required fields: vendor name, invoice amount, invoice
   date, PO number, line items (description, quantity, unit price).
2. Specify the expected type and format for each field (currency, ISO
   date, string).
3. Mark which fields are required versus optional per invoice type.
4. Reject any extraction pass missing a required field — do not fill
   it with a guess.
5. Version the schema so a schema change doesn't silently break past
   comparisons.

## Rules
- Every field a downstream step depends on (amount, PO number) is
  required, not optional.
- Never invent a value for a missing required field — flag the
  invoice as incomplete instead.
- Field list and formats (set on first run, then stored in memory).
- Normalize currency and date formats before any comparison; never
  compare raw, unformatted text.

## Output
A field spec — `{vendor, amount, date, po_number, line_items[]}` —
that every extraction pass and the ledger post conform to.

## Dual-pass reconciliation

Accept a value only when two independent extraction passes agree on
it, field by field.

## Method
1. Run two extraction sub-agents against the same parsed invoice text,
   each without seeing the other's output.
2. Compare the two passes field by field against the invoice field
   schema.
3. Where both passes agree exactly, after normalizing format, accept
   the value.
4. Where the passes disagree on any field, do not guess or average —
   flag that field as a mismatch.
5. Roll mismatched fields up to an invoice-level status; a clean
   invoice has zero mismatched fields.
6. Build the mismatch queue entry: field name, pass-1 value, pass-2
   value, side by side.

## Rules
- Never accept a value when the two passes disagree — hold it, do not
  pick one arbitrarily.
- Never post an invoice to the ledger on a single unreconciled pass.
- Run the two passes independently; one must not see the other's
  extraction before finishing.
- Field-normalization rules for the comparison, currency and date
  format (set on first run, then stored in memory).

## Output
A result per invoice: accepted `{field: value}` pairs, plus a
mismatch list `{field, pass_1_value, pass_2_value}`, queued to
`{{review_channel}}` when non-empty.

## PO matching

Match a fully-reconciled invoice against its open purchase order and
flag any quantity or price gap.

## Method
1. Take only invoices with zero mismatches from the reconciliation
   step.
2. Look up the PO number in `{{ledger_sheet}}` and pull its quantity,
   price, and vendor.
3. Recompute the invoice total from its line items independently,
   rather than trusting the invoice's stated total.
4. Compare quantity and price against the PO's figures.
5. Flag any variance outside `{{tolerance_pct}}` as a PO gap.
6. Confirm the vendor name matches exactly, not fuzzily.

## Rules
- Never post an invoice whose recomputed total doesn't tie to the
  invoice's stated total — hold it instead.
- Never approve a PO variance yourself — queue every gap for
  sign-off.
- PO-match tolerance (set on first run, then stored in memory).
- A vendor-name mismatch always fails the match, even when amount and
  quantity agree.

## Output
A match result per invoice: `{matched: true/false, variance_pct,
gap_reason}`, feeding the post-or-queue decision.

## Recurring jobs

This skill pairs with scheduled jobs. Offer to set each one up; only create the ones the user approves:

- AP inbox sweep (Every 30 minutes on weekdays) — https://eluu.ai/cookbook/invoice-inbox-processing/jobs-setup/ap-inbox-sweep.md
