How the work gets done

Document Extractor

Turns an invoice, packing list, or bill of lading into structured, reviewable line items.

3 min readUpdated Jul 17, 2026

Single-purpose LLM agent (vision). Turns a PDF or image of a customs document into structured fields, ready for downstream agents.

What it does

Reads a commercial invoice, packing list, bill of lading, or PO image and returns a strict Zod-validated structure: supplier, importer, Incoterms, ports, dates, line items (description, qty, unit price, COO, HS hint), totals. This is the input every other agent depends on — if extraction fails, nothing downstream runs.

Inputs

{
  filename: string;                 // "INV-22041.pdf"
  mimeType: string;                 // "application/pdf" | "image/png" | …
  bytes: Buffer;                    // raw file
  source?: 'upload' | 'inbox' | 'po-portal';
}

The model (Claude Sonnet, vision) receives the file directly via document/image content blocks — no OCR pre-processing step. Claude handles the vision natively, reading printed layout, tables, and stamps straight from the PDF or image.

Outputs

{
  meta: {
    documentType: 'commercial_invoice' | 'packing_list' | 'bill_of_lading' | 'po',
    issueDate: '2026-05-24',
    invoiceNumber: 'INV-22041',
    incoterms: 'FOB',
    currency: 'USD',
  },
  parties: {
    supplier: { name: 'Hangzhou Lanyu Textile Co., Ltd.', country: 'CN', city: 'Hangzhou' },
    importer: { name: 'Northwell Outfitters', country: 'US' },
    consignee: { name: 'Northwell Outfitters', country: 'US' },
  },
  shipment: {
    originPort: 'CNNGB',           // Ningbo
    destinationPort: 'USLAX',
    mode: 'ocean_fcl',
    eta: '2026-06-15',
  },
  lines: [
    {
      lineNumber: 1,
      description: "Men's 100% cotton crew-neck T-shirts, knit, size M",
      quantity: 12000,
      unitOfMeasure: 'EA',
      unitPriceCents: 100,         // $1.00 / unit
      valueCents: 1_200_000,
      countryOfOrigin: 'CN',
      htsHint: '6109.10',          // if printed on the doc; classifier still re-runs
    },
    // … more lines
  ],
  totals: {
    subtotalCents: 12_000_000,
    freightCents: 380_000,
    insuranceCents: 60_000,
    invoiceTotalCents: 12_440_000,
  },
}

Schema validation. Every field is optional in the extraction schema, by design: because nothing is required, the model is never forced to fill a field it can't actually read, so it never invents a value to satisfy the structure. It returns what it can read and leaves the rest empty. Every extracted field is broker-reviewable in the drawer, and missing fields are flagged by downstream services so the broker knows what to ask the supplier for.

When it runs

  • Drag-drop upload on Dashboard / Simulation / New entry wizard (upload source) → fires immediately, surfaces the structured result in the inbox row.
  • Email inbox parse — when a packet arrives, every attachment runs through the extractor on receipt.
  • Re-extract on demand — broker clicks "Re-parse" in the drawer when the source PDF is updated.

Dependencies

  • Vision-capable model (Claude Sonnet) that accepts PDF and image input directly.
  • No retrieval, no DB lookups at extract time. Pure vision-to-structure. The HTS hint surfaced from the document is only a hint; the classifier re-runs from the description regardless.

What success looks like

High-accuracy structured extraction across the fields a broker needs to open an entry — document type, parties, ports, dates, line items with quantities and values, and totals — from real commercial invoices, packing lists, bills of lading, and POs. Every field is surfaced for review rather than trusted blindly: we do not auto-file based on extracted data alone. The broker reviews every field in the drawer before transmit.

What it does NOT do

  • Does not invent values. Empty fields stay empty; the schema makes this explicit (every leaf is optional). The drawer shows missing fields to the broker as "Missing — broker to confirm".
  • Does not classify HTS codes. It surfaces any HS hint printed on the document, but the HTS Classifier re-runs from the description for every line regardless.
  • Does not translate documents. Foreign-language invoices are extracted in the original language; translation is a separate concern.
  • Does not read handwriting reliably. Scanned and printed text only. Handwritten amendments are flagged for broker review.
  • Does not stitch multi-document packets (invoice + packing list + BoL) into a single record. Each document is extracted independently; the customs-clearance module reconciles them at the entry level.

Roadmap

  • Q3 2026 — Multi-document packet reconciliation: when invoice + packing list + BoL arrive together, merge into one entry draft so the broker isn't reconciling line numbers manually.
  • Q3 2026 — Handle handwritten amendments by surfacing the raw region to the broker instead of guessing.
  • Q4 2026 — Auto-detect when a document is a revised version of one already in the inbox (same supplier + invoice number, different totals) and prompt the broker to diff.
  • 2027 — Language hints: detect non-English documents and surface a "translate" affordance in the drawer for the broker.
Ready to see it live?

Put your own book of entries through Commers.