Knowing when a carrier invoice is processed
After you submit a carrier invoice, Upwell works on it asynchronously. This page explains how to learn when that work is done.There’s no single “processed” flag. You read the existing fields on the invoice — its
status, its exceptions, and the matched shipmentId — plus the type on each attached document. You can read them by polling the GET endpoints or by receiving webhooks. Most production integrations use webhooks for timeliness and polling as a reconciliation backstop.Two phases of processing
It helps to separate two things:- Ingestion (automatic, seconds after create). Upwell stamps provenance (
source_system = "API"), matches the invoice to a shipment/carrier/bill from your references, and runs the exception audit. Thestatususually staysRECEIVEDthrough this phase — ingestion populatesshipmentId/billId/carrierIdand updatesexceptions, it doesn’t move the status. - Review & approval (later; may involve your rules or a human). The invoice moves toward
APPROVEDor an exception state. This is wherestatuschanges.
The signals
| Signal | Where | Meaning |
|---|---|---|
shipmentId populated | GET /carrier_invoices/{id} | Ingestion matched the invoice to a shipment. |
updatedAt > createdAt | GET /carrier_invoices/{id} | Ingestion has run (Upwell touched the row after you created it). |
exceptions (string) | GET /carrier_invoices/{id} | Comma-joined open audit findings, e.g. "INVOICE AMOUNT MISMATCH". Empty = clean. |
status | GET /carrier_invoices/{id} | The review/approval lifecycle (see table below). |
document type | GET /carrier_invoices/{id}/documents | A document is parsed once type is no longer "UNKNOWN". |
Status values
| Phase | Statuses | Meaning |
|---|---|---|
| In-flight | RECEIVED, PROCESSING, UNDER_REVIEW, AWAITING_INVOICE, AWAITING_CARRIER_RESPONSE, CARRIER_RESPONDED | Submitted / under review. |
| Done — clear | APPROVED, PART_PAID, PAID | Approved and progressing to / through payment. |
| Needs attention | EXCEPTION, CARRIER_EXCEPTION, DISPUTED, REJECTED | A carrier/audit issue needs resolving. |
Approach 1 — Polling
Read the invoice and its documents on an interval. Wait for ingestion (the row’supdatedAt advances past createdAt), then read the outcome.
Approach 2 — Webhooks
Subscribe once and let Upwell push the changes to you. Configure subscriptions in the Upwell dashboard (see Outbound webhooks for the model, payload shape, and auth).| Trigger | Fires when |
|---|---|
create.carrier_invoice | The invoice is created. |
update.carrier_invoice | Any field/status change — fires when ingestion enriches the row. |
update.carrier_invoice.shipment_updated | The invoice was matched/linked to a shipment. |
update.carrier_invoice.status.APPROVED | The invoice reached APPROVED (review lifecycle). |
update.carrier_invoice.status.EXCEPTION | The invoice hit an exception status (review lifecycle). |
update.carrier_document / create.carrier_document | A document was (re)classified or attached. |
Which should I use?
Webhooks
Best when you can host an HTTPS endpoint. Near-real-time, no polling load.
Polling
Best when you can’t receive inbound requests, or as a backstop. Read
GET /carrier_invoices/{id} (+ /documents) with backoff.Webhook delivery retries on
5xx and can be delayed under load. A robust integration uses webhooks for timeliness and reconciles with a periodic poll so a missed delivery never leaves an invoice stuck in your system. The demo TMS app implements both.
