DocsExtraction

Open the app →

Extraction

One resource: the invoice. Create it from a document, poll it (or receive a webhook), list and filter it. Base URL: https://nzsktgwehyzltgagloox.supabase.co/functions/v1/api

Create: POST /v1/invoices

Three input modes; PDF, JPEG, PNG, HEIC, and HTML up to 15 MB. The type comes from the part's or URL response's content type, then the file extension; set mime_type (and file_name) when neither is reliable, for example a presigned URL served as application/octet-stream.

ModeHow
multipart/form-dataA form field named file. The usual choice.
file_urlJSON body with a public https file_url, optional file_name and mime_type. Fetched with a 20s timeout and 15 MB cap.
file_base64JSON body with file_base64, file_name, and optional mime_type.
# multipart
curl -X POST 'https://nzsktgwehyzltgagloox.supabase.co/functions/v1/api/v1/invoices' \
  -H "Authorization: Bearer $INVOICEJET_API_KEY" \
  -F 'file=@invoice.pdf'

# from a URL
curl -X POST 'https://nzsktgwehyzltgagloox.supabase.co/functions/v1/api/v1/invoices' \
  -H "Authorization: Bearer $INVOICEJET_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"file_url": "https://example.com/invoices/june.pdf"}'

Batch scans: automatic splitting

A multi-page PDF holding several invoices (a batch scan, or attachments merged into one file) is detected and split automatically: each invoice becomes its own record, named original (1 of N).pdf through (N of N). The create response is the first invoice; the rest arrive as separate invoices with their own invoice.completed webhook events, so consume webhooks or poll GET /v1/invoices rather than assuming one file means one invoice. Each split invoice counts against the monthly quota. Continuation pages (line-item overflow, terms, remittance slips) stay with their invoice.

Safe retries: Idempotency-Key

Send an Idempotency-Key header (any unique string, e.g. a UUID) with the create and retries within 24 hours replay the original response instead of ingesting the document again. Details on the Errors & limits page.

Async by default, sync when you want it

The default response is 202 with the invoice in queued state; extraction runs in the background (typically 10 to 30s). Get the result by polling GET /v1/invoices/:id or via webhooks. Add ?wait=true to hold the request up to 60 seconds and receive 200 with the finished resource; if extraction outruns the wait you get 202 with "wait_timed_out": true and it finishes in the background.

Retrieve: GET /v1/invoices/:id

curl 'https://nzsktgwehyzltgagloox.supabase.co/functions/v1/api/v1/invoices/9b2f0c1e-…' \
  -H "Authorization: Bearer $INVOICEJET_API_KEY"

List: GET /v1/invoices

ParamMeaning
limit1–100, default 25.
statusqueued | processing | complete | failed | held
sourceapp | email | api
created_after / created_beforeRFC 3339 timestamps.
vendorSubstring match on vendor name.
paidtrue (paid only) or false (unpaid only).
overduetrue: complete, unpaid, past due. false: not overdue.
min_amount / max_amountAmount range.
due_before / due_afterDue-date window (ISO dates).
starting_afterInvoice id cursor: returns results strictly after it (newest first).

Responses are { "object": "list", "data": [...], "has_more": true, "next_cursor": "<id>" }. Page by passing next_cursor (the last item's id) as starting_after; next_cursor is null on the last page. Keep the other filters the same on every page.

# every unpaid invoice, 100 per page (needs jq)
cursor=""
while :; do
  page=$(curl -s "https://nzsktgwehyzltgagloox.supabase.co/functions/v1/api/v1/invoices?paid=false&limit=100${cursor:+&starting_after=$cursor}" \
    -H "Authorization: Bearer $INVOICEJET_API_KEY")
  echo "$page" | jq -c '.data[] | {id, vendor_name, amount}'
  cursor=$(echo "$page" | jq -r '.next_cursor // empty')
  [ -n "$cursor" ] || break
done

Update: PATCH /v1/invoices/:id

Two writable fields: paid (boolean; complete invoices only, otherwise 400 invoice_not_complete) and entity_tag (1-60 chars, or null to clear). Returns the updated resource; paid_at is set to now when paid, and cleared when unpaid.

curl -X PATCH 'https://nzsktgwehyzltgagloox.supabase.co/functions/v1/api/v1/invoices/9b2f0c1e-…' \
  -H "Authorization: Bearer $INVOICEJET_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"paid": true, "entity_tag": "EngCo LLC"}'

Request approval: POST /v1/invoices/:id/approval

Emails the approver a signed approve/reject link for a complete invoice (the same flow as the app). One pending request per invoice: a second one returns 400 approval_pending; a new request after a rejection overwrites it. If the email cannot be sent you get 502 approval_send_failed.

curl -X POST 'https://nzsktgwehyzltgagloox.supabase.co/functions/v1/api/v1/invoices/9b2f0c1e-…/approval' \
  -H "Authorization: Bearer $INVOICEJET_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"email": "approver@company.com"}'

Download the original: GET /v1/invoices/:id/file

Returns a signed, time-limited URL (1 hour) for the invoice's original source document (the PDF, image, or HTML that was ingested): {id, url, expires_in: 3600, file_name, mime_type}. Fetch the URL directly; no auth header is needed on the download itself.

curl 'https://nzsktgwehyzltgagloox.supabase.co/functions/v1/api/v1/invoices/9b2f0c1e-…/file' \
  -H "Authorization: Bearer $INVOICEJET_API_KEY"

# then download it
curl -o invoice.pdf "<url from the response>"

Retry extraction: POST /v1/invoices/:id/extract

Re-runs extraction on the stored document. Meant for failed invoices; pass {"force": true} to re-extract a complete one (for example after correcting the source). Returns 202 with the invoice back in queued state plus "retry_accepted": true; poll or use webhooks for the result. Retrying a failed invoice uses one extraction from the monthly quota, checked up front: over the limit you get 402 monthly_quota_reached. A processing invoice returns 400 already_processing; a complete one without force returns 400 already_complete; one with no stored document returns 404 no_document.

curl -X POST 'https://nzsktgwehyzltgagloox.supabase.co/functions/v1/api/v1/invoices/9b2f0c1e-…/extract' \
  -H "Authorization: Bearer $INVOICEJET_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"force": false}'

Vendors: GET /v1/vendors

The account's vendors with aggregates, highest spend first: {id, object: "vendor", name, invoice_count, total_billed, last_seen_at, website, email, phone}. Takes limit (1-100, default 25).

curl 'https://nzsktgwehyzltgagloox.supabase.co/functions/v1/api/v1/vendors?limit=10' \
  -H "Authorization: Bearer $INVOICEJET_API_KEY"

Export: GET /v1/exports

Renders the full filtered set into one file body, returned as a string in content — no pagination. Same filters as list (status, source, vendor, paid/overdue, amount and date windows); up to 10,000 invoices, after which truncated is true. Read-scope keys work (it's a GET).

ParamMeaning
shapeRequired. lines (one row per line item, QuickBooks bill import, with an Account column left empty for now) | xero (Xero bills import, DD/MM/YYYY dates, *AccountCode empty for now) | invoices (summary ledger) | vendors (per-vendor statement).
formatRequired. csv | json.
…list filtersEverything GET /v1/invoices accepts except limit / starting_after.

Response: { "object": "export", shape, format, filename, row_count, truncated, content }. Write content to filename and you have the file.

# unpaid invoices as a QuickBooks bill-import CSV
curl 'https://nzsktgwehyzltgagloox.supabase.co/functions/v1/api/v1/exports?shape=lines&format=csv&paid=false' \
  -H "Authorization: Bearer $INVOICEJET_API_KEY" | jq -r '.content' > bills.csv

The invoice resource

FieldNotes
statusqueued → processing → complete | failed. held: uploaded in the app or emailed in while the account was over its monthly quota; stored, not extracted until an upgrade or the reset on the 1st. Extraction fields are null until complete.
vendor_name, invoice_numberStrings.
amount, taxNumbers (invoice currency).
currencyISO 4217 (e.g. USD).
issued_at, due_atISO dates.
paid_atTimestamp the invoice was marked paid, or null. Set via PATCH paid.
entity_tagYour entity/company tag, or null. Set via PATCH.
line_items[]description, quantity, unit_price, amount (the printed line total, or quantity × unit_price), confidence, and source (the page + verbatim text it was read from).
field_confidencehigh | mid | low per header field. high means validation passed AND two independent models agreed.
citationsPer-field proof, keyed like field_confidence: { page, text } — the page number and verbatim text each field was read from. Present when status is complete.
auto_verifiedtrue when every field is high-confidence and no duplicate was flagged.
quality_score0–1 weighted confidence summary.
sourceapp | email | api: how the document arrived.
file{ name, mime_type, size_bytes } of the stored document.
created_at, updated_atRFC 3339 timestamps.