Submitting carrier invoices via API
If you’re an API-integrated TMS, you can push carrier invoices to Upwell programmatically instead of forwarding them by email. An invoice submitted this way gets identical processing to one received over email: shipment/carrier/bill matching, the exception audit, and AI classification of any documents you attach. This guide walks the end-to-end flow. For the full request/response schema of each endpoint, see the auto-generated API Reference — this page focuses on the workflow and the things that aren’t obvious from the schema alone.This flow reuses existing endpoints — there’s no separate “carrier invoice API” to learn. The same
POST /api/rest/carrier_invoices and POST /api/rest/generate-upload-presigned-url endpoints power the structured submission and its document uploads.Prerequisites
- An API key included on every request (
Authorization: YOUR_API_KEY, noBearerprefix). See Authentication. - Optionally, the Upwell
carrierId/shipmentIdif you already know the match. You don’t need them — Upwell matches the invoice to a shipment, carrier, and bill from the references you send (carrierProNumber,billOfLadingNumber,customerPoNumber,customerReferenceNumber).
Your API key is bound to an integration. Upwell uses that binding to stamp every invoice you submit as API-sourced — you don’t (and can’t) set the source yourself. See the warning under Step 1.
The flow
Create the carrier invoice
POST /api/rest/carrier_invoices with the invoice’s structured data wrapped in input. Store the id from the response.Attach documents (optional)
For each PDF (the invoice scan, BOL, POD, …) request a presigned URL, then
PUT the bytes to it.Let it process — then read the outcome
Upwell matches the invoice and runs the exception audit asynchronously (seconds). You learn the outcome by polling or by webhook — see Knowing when a carrier invoice is processed.
Step 1 — Create the carrier invoice
You send a bare invoice: the header fields plus whatever references help Upwell match it. Upwell fills in the matched shipment/carrier/bill and the provenance during processing.Response
shipmentId / billId / carrierId are null at this instant — matching runs a moment later (see Step 3). Note that exceptions is a comma-joined string (not an array); on a brand-new row it reflects the un-matched state and settles after processing.
Fields
Send these insideinput:
| Field | Notes |
|---|---|
invoiceNumber | Required. The carrier’s invoice number. |
totalAmount | Required. Integer cents. |
balance | Required. Outstanding amount in cents (= totalAmount for a new invoice). |
status | Required. Use "RECEIVED". |
currency | ISO code, e.g. "USD". |
carrierName | Carrier name as printed — helps matching and avoids a spurious “no carrier name” finding. |
carrierProNumber, billOfLadingNumber, customerPoNumber, customerReferenceNumber | Matching references. carrierProNumber is the strongest. |
shipmentId, carrierId | Pin the match yourself if you already know it. |
loadNumber, issueDate, dueDate, totalTaxAmount, metadata, referenceNumbers | Optional. |
Re-submitting
There’s no server-side idempotency on create — re-posting the same invoice creates a new record. Store the returnedid on first submit and use PUT /api/rest/carrier_invoices/{id} for any later corrections; guard against accidental re-submits on your side.
Step 2 — Attach documents
Documents go through the standard two-step presigned-upload flow. Use it for every carrier document — the invoice scan, BOLs, PODs, and so on.Request a presigned URL
POST /api/rest/generate-upload-presigned-url with associationType: "CARRIER_INVOICE" and associationId set to the invoice id from Step 1. The response contains only uploadUrl and documentId.Document types and AI classification
If you know the document’s type, pass it (CARRIER_INVOICE, BILL_OF_LADING, PROOF_OF_DELIVERY, …) and it’s used as-is. If you don’t — or you’re uploading a combined PDF — pass documentType: "UNKNOWN"; when AI classification is enabled for your tenant, Upwell splits and classifies the upload, assigning each resulting document a real type. The document stays UNKNOWN until classification completes. See Knowing when a carrier invoice is processed.
Step 3 — Let it process
Once the invoice exists, Upwell processes it asynchronously (typically within seconds): it stampssource_system = "API", matches the invoice to a shipment/carrier/bill from your references, and runs the exception audit. You don’t call anything for this — you observe the result.
Next: Knowing when it's processed
Poll the invoice, or subscribe to webhooks, to learn when matching, the exception audit, and document classification are done.

