Skip to content

Invoice Inbox

The invoice inbox receives vendor invoices as PDF uploads, extracts key fields (vendor, amounts, dates, line items), and provides a review workflow before converting to bills.

StatusMeaning
receivedPDF uploaded, not yet processed
extractingExtraction in progress
needs_reviewExtracted but missing required fields (vendor name or total)
readyAll required fields present, ready for conversion
convertedConverted to a bill — read-only
errorExtraction failed

GET /api/books/invoice_documents

Scope: read

ParameterTypeDescription
statusstringFilter by status

Includes 30-day inbox stats and failure reason counts:

{
"invoice_documents": [
{
"id": 7,
"source_type": "upload",
"status": "ready",
"filename": "invoice-march.pdf",
"file_size_bytes": 245000,
"sha256": "a1b2c3d4...",
"extracted_vendor_name": "Acme Materials",
"extracted_invoice_number": "INV-2026-0042",
"extracted_invoice_date": "2026-03-01",
"extracted_due_date": "2026-04-01",
"extracted_total_cents": 109500,
"extracted_tax_cents": 0,
"extracted_subtotal_cents": 109500,
"extracted_currency": "USD",
"extraction_method": "pdf_text",
"duplicate_of_bill_id": null,
"bill_id": null,
"commitment_review_status": "unmatched",
"converted_at": null,
"created_at": "2026-03-10T12:00:00Z",
"updated_at": "2026-03-10T12:00:00Z"
}
],
"stats": {
"total": 45,
"ready": 12,
"needs_review": 3,
"error": 1,
"converted": 29,
"failure_reasons": {
"missing_vendor_name": 2,
"missing_total": 1,
"duplicate_flagged": 3
}
}
}
FieldTypeDescription
idintegerUnique identifier
source_typestringupload or email
statusstringSee lifecycle above
filenamestringOriginal filename
file_size_bytesintegerPDF file size
sha256stringSHA-256 hash of the file (used for deduplication)
extracted_vendor_namestringVendor name from extraction
extracted_invoice_numberstringInvoice number from extraction
extracted_invoice_datedateInvoice date from extraction
extracted_due_datedateDue date from extraction
extracted_total_centsintegerTotal amount from extraction
extracted_tax_centsintegerTax amount from extraction
extracted_subtotal_centsintegerSubtotal from extraction
extracted_currencystringCurrency code
extraction_methodstringMethod used (pdf_text, ocr, etc.)
duplicate_of_bill_idintegerIf flagged as duplicate, the existing bill ID
bill_idintegerBill created from this document (after conversion)
commitment_review_statusstringunmatched, pending, or approved
converted_attimestampWhen converted to a bill

POST /api/books/invoice_documents
Content-Type: multipart/form-data

Scope: write

Upload a vendor invoice PDF. The system extracts fields automatically.

FieldTypeRequiredDescription
filefileyesPDF file (max 20 MB)
{
"invoice_document": { ... },
"pdf_url": "https://...",
"existing": false
}

If the same file (by SHA-256) was already uploaded, returns the existing document with "existing": true and status 200 instead of 201.

Returns: 201 Created (new) or 200 OK (duplicate)


GET /api/books/invoice_documents/:id

Scope: read

Returns full detail including extraction results, line items, and vendor suggestion:

{
"invoice_document": {
"id": 7,
"status": "ready",
"extracted_vendor_name": "Acme Materials",
"extracted_total_cents": 109500,
"pdf_page_count": 2,
"extraction_result": {
"vendor_name": { "value": "Acme Materials", "confidence": "high", "source": "header" },
"total": { "value": 109500, "confidence": "high", "source": "footer" }
},
"extraction_version": 3,
"extracted_line_items": [
{ "description": "Excavator rental", "quantity": 1, "unit_price_cents": 109500, "amount_cents": 109500 }
],
"extraction_success": true,
"extraction_duration_ms": 1250,
"missing_required_fields": []
},
"pdf_url": "https://...",
"suggestion": {
"contact_id": 42,
"contact_name": "Acme Materials",
"confidence": 0.95
}
}

PATCH /api/books/invoice_documents/:id

Scope: write

Correct extracted fields before conversion. Cannot update converted documents.

{
"extracted_vendor_name": "Acme Materials Inc.",
"extracted_total_cents": 109500,
"extracted_invoice_date": "2026-03-01"
}
FieldTypeDescription
extracted_vendor_namestringVendor name
extracted_invoice_numberstringInvoice number
extracted_invoice_datedateInvoice date
extracted_due_datedateDue date
extracted_total_centsintegerTotal in cents
extracted_tax_centsintegerTax in cents
extracted_subtotal_centsintegerSubtotal in cents
extracted_currencystringCurrency code

Returns 409 Conflict if the document is already converted.


POST /api/books/invoice_documents/:id/convert

Scope: write

Converts an invoice document directly to a draft bill without commitment matching. Use this for invoices that don’t correspond to any commitment.

{
"contact_id": 42
}
FieldTypeRequiredDescription
contact_idintegernoVendor contact to use on the bill

Returns the created bill and updated document.


POST /api/books/invoice_documents/:id/reextract

Scope: write

Re-runs PDF extraction with the latest extraction logic. Useful after extraction improvements. Cannot re-extract converted documents.


POST /api/books/invoice_documents/bulk_convert

Scope: write

Convert multiple ready documents to bills in one call.

Preview what would happen:

{
"ids": [1, 2, 3],
"dry_run": true
}

Returns will_convert and will_fail arrays with reasons.

{
"ids": [1, 2, 3]
}

Returns converted and failed arrays.


DELETE /api/books/invoice_documents/:id

Scope: write

Permanently deletes a document. Cannot delete converted documents.

Returns 409 Conflict if converted.