> ## Documentation Index
> Fetch the complete documentation index at: https://docs.upwell.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Managing customer invoices (AR) via API

> Create, update, and send customer invoices — the accounts-receivable counterpart to the carrier-invoice submission flow.

# Managing customer invoices

A **customer invoice** (`inv_…`) is what you bill your customer (shipper) for a shipment — the accounts-receivable side. This page covers creating invoices, adding line items and documents, and reading them back.

<Note>
  This is the **AR** (money in) side. For submitting **carrier invoices** (money out, AP), see [Submitting carrier invoices via API](/api-guides/carrier-invoice-submission). For the relationship between these objects, see [Core concepts & data model](/concepts/data-model).
</Note>

## Prerequisites

* An **API key** — see [Authentication](/authentication).
* A **customer** (`cus_…`) and a **shipment** (`shi_…`) already synced to Upwell. See [Syncing foundation entities](/api-guides/syncing-foundation-entities).

## Create an invoice

`POST /api/rest/invoices` creates a customer invoice. The invoice is tied to a shipment and a customer:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.upwell.com/api/rest/invoices \
    -H "Authorization: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "input": {
        "customerId": "cus_def456",
        "invoiceShipments": { "data": [{ "shipmentId": "shi_abc123" }] },
        "number": "INV-2026-0815",
        "totalAmount": 450000,
        "balance": 450000,
        "currency": "USD",
        "issueDate": "2026-07-10",
        "dueDate": "2026-08-09",
        "sourceSystem": "YOUR_TMS",
        "sourceSystemId": "INV-0815"
      }
    }'
  ```

  ```javascript JavaScript theme={null}
  const res = await fetch('https://api.upwell.com/api/rest/invoices', {
    method: 'POST',
    headers: { Authorization: 'YOUR_API_KEY', 'Content-Type': 'application/json' },
    body: JSON.stringify({
      input: {
        customerId: 'cus_def456',
        invoiceShipments: { data: [{ shipmentId: 'shi_abc123' }] },
        number: 'INV-2026-0815',
        totalAmount: 450000, // integer cents — $4,500.00
        balance: 450000, // required; equals totalAmount for a new invoice
        currency: 'USD',
        issueDate: '2026-07-10',
        dueDate: '2026-08-09',
        sourceSystem: 'YOUR_TMS',
        sourceSystemId: 'INV-0815',
      },
    }),
  });

  const { createInvoice } = await res.json();
  const invoiceId = createInvoice.id; // store this — e.g. "inv_xYz789"
  ```
</CodeGroup>

### Fields

| Field                                    | Required    | Notes                                                                                                                                          |
| ---------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `balance`                                | **Yes**     | Outstanding balance in **integer cents**. For a new invoice, equals `totalAmount`. `NOT NULL` with no server default — omitting it is a `400`. |
| `customerId`                             | Recommended | Links the invoice to a customer (`cus_…`).                                                                                                     |
| `invoiceShipments`                       | Recommended | Links the invoice to one or more shipments via a nested relation: `{ "data": [{ "shipmentId": "shi_…" }] }`.                                   |
| `number`                                 | Recommended | Your invoice number.                                                                                                                           |
| `totalAmount`                            | Recommended | Invoice total in **integer cents**. `$4,500.00` → `450000`.                                                                                    |
| `currency`                               | No          | ISO code, defaults to `USD`.                                                                                                                   |
| `issueDate`                              | Recommended | The date on the invoice (`YYYY-MM-DD`).                                                                                                        |
| `dueDate`                                | Recommended | Payment due date (`YYYY-MM-DD`). Drives aging and reminder scheduling.                                                                         |
| `sourceSystem` / `sourceSystemId`        | Recommended | External key pair — unique per tenant. See [Integration patterns](/integration-patterns#idempotent-upserts-via-sourcesystem--sourcesystemid).  |
| `customerPoNumber`, `billOfLadingNumber` | No          | Reference numbers.                                                                                                                             |
| `status`                                 | No          | See [Invoice lifecycle](#invoice-lifecycle) for the full list. Defaults to `CREATED` for new invoices.                                         |

<Warning>
  `POST` is **not** retry-safe for invoices — re-posting an existing `(sourceSystem, sourceSystemId)` pair returns a `400 Uniqueness violation`. Use `POST` for first sync, `PUT` for updates. See [Integration patterns](/integration-patterns#idempotent-upserts-via-sourcesystem--sourcesystemid).
</Warning>

## Add line items

`POST /api/rest/invoice_line_items` adds a line item to an existing invoice:

```bash theme={null}
curl -X POST https://api.upwell.com/api/rest/invoice_line_items \
  -H "Authorization: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input": {
      "invoiceId": "inv_xYz789",
      "lineNumber": "1",
      "item": "Line haul — Chicago to Dallas",
      "description": "Full truckload, 42,000 lbs",
      "quantity": 1,
      "unitPrice": 400000,
      "totalAmount": 400000,
      "sourceSystem": "YOUR_TMS",
      "sourceSystemId": "INV-0815-LI-1"
    }
  }'
```

| Field                             | Notes                                                          |
| --------------------------------- | -------------------------------------------------------------- |
| `invoiceId`                       | **Required.** The invoice this line item belongs to (`inv_…`). |
| `lineNumber`                      | Line sequence (string).                                        |
| `item`                            | Short description (e.g. charge type).                          |
| `description`                     | Longer description.                                            |
| `quantity`                        | Quantity (numeric).                                            |
| `unitPrice`                       | Per-unit price in **integer cents**.                           |
| `totalAmount`                     | Line total in **integer cents**.                               |
| `sourceSystem` / `sourceSystemId` | Your external key for this line item.                          |

Update with `PATCH /api/rest/invoice_line_items/{id}`. Delete with `DELETE /api/rest/invoice_line_items/{id}`.

## Attach documents to an invoice

Attach supporting documents (the invoice PDF, BOLs, PODs) to a customer invoice using the same presigned-upload flow as carrier invoices:

<Steps>
  <Step title="Request a presigned URL">
    `POST /api/rest/generate-upload-presigned-url` with `associationType: "INVOICE"` and `associationId` set to the invoice ID.
  </Step>

  <Step title="PUT the bytes">
    Upload the raw file to `uploadUrl`.
  </Step>
</Steps>

```bash theme={null}
curl -X POST https://api.upwell.com/api/rest/generate-upload-presigned-url \
  -H "Authorization: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input": {
      "associationType": "INVOICE",
      "associationId": "inv_xYz789",
      "documentType": "CUSTOMER_INVOICE",
      "fileName": "invoice-2026-0815.pdf",
      "mimeType": "application/pdf",
      "sourceSystem": "YOUR_TMS",
      "sourceSystemId": "INV-0815-DOC"
    }
  }'
```

You can also attach documents from the shipment — documents on a shipment are automatically visible on invoices linked to that shipment, so you often don't need to re-upload. See [Working with shipment documents](/api-guides/shipment-documents).

<Note>
  Alternatively, `POST /api/rest/invoices/{invoiceId}/documents` accepts base64-encoded documents directly — convenient for smaller files. For large documents, the presigned-URL flow avoids payload size limits.
</Note>

## Read invoices back

```bash theme={null}
# List invoices
curl "https://api.upwell.com/api/rest/invoices?limit=50" \
  -H "Authorization: YOUR_API_KEY"

# Get one invoice
curl https://api.upwell.com/api/rest/invoices/inv_xYz789 \
  -H "Authorization: YOUR_API_KEY"
```

### Search by source system

`POST /api/rest/invoices/search` finds invoices by their external key:

```bash theme={null}
curl -X POST https://api.upwell.com/api/rest/invoices/search \
  -H "Authorization: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "where": {
      "sourceSystem": { "_eq": "YOUR_TMS" },
      "sourceSystemId": { "_eq": "INV-0815" }
    },
    "limit": 10,
    "offset": 0
  }'
```

The endpoint accepts a Hasura-style `where` filter (plus `orderBy`, `limit`, `offset`). The response includes nested `invoiceLineItems` so you can read the full invoice in one call.

### List an invoice's documents

```bash theme={null}
curl https://api.upwell.com/api/rest/invoices/inv_xYz789/documents \
  -H "Authorization: YOUR_API_KEY"
```

## Update an invoice

`PUT /api/rest/invoices/{id}` updates by Upwell ID. `PUT /api/rest/invoices` (without an ID) updates by `sourceSystem` + `sourceSystemId` in the body.

```bash theme={null}
curl -X PUT https://api.upwell.com/api/rest/invoices/inv_xYz789 \
  -H "Authorization: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input": {
      "status": "SENT",
      "balance": 0
    }
  }'
```

## Invoice lifecycle

Invoices move through these statuses:

| Status             | Meaning                                                    |
| ------------------ | ---------------------------------------------------------- |
| `CREATED`          | Invoice record exists but hasn't been finalized.           |
| `OPEN`             | Invoice is finalized and ready for delivery.               |
| `APPROVED`         | Invoice has been approved for sending.                     |
| `PROCESSING`       | Invoice is being processed for delivery.                   |
| `SENT`             | Invoice has been delivered to the customer.                |
| `DELIVERED`        | Invoice delivery has been confirmed.                       |
| `VIEWED`           | Customer has viewed the invoice (online portal).           |
| `OVERDUE`          | Past due date with an outstanding balance.                 |
| `PART_PAID`        | Partially paid — balance is between `0` and `totalAmount`. |
| `PAID`             | Fully paid — balance is `0`.                               |
| `DISPUTED`         | Customer has raised a dispute.                             |
| `PORTAL_EXCEPTION` | The invoice hit an exception during portal delivery.       |
| `PORTAL_RESOLVED`  | A portal exception has been resolved.                      |
| `REJECTED`         | Invoice was rejected.                                      |
| `CANCELLED`        | Invoice was cancelled.                                     |
| `WRITE_OFF`        | Written off as uncollectable.                              |
| `REFUNDED`         | A refund was issued.                                       |
| `SEND_ERROR`       | Invoice delivery failed.                                   |

<Tip>
  When a [customer payment](/api-guides/customer-payments) is applied to an invoice, update the invoice's `balance` and `status` accordingly. Some TMS integrations manage this in Upwell; others update invoices from the TMS when payment is recorded there.
</Tip>

## Webhook events

Subscribe to invoice events in the Upwell dashboard to get notified of status changes:

| Trigger                                  | Fires when                            |
| ---------------------------------------- | ------------------------------------- |
| `create.invoice`                         | An invoice is created.                |
| `update.invoice`                         | Any field change on an invoice.       |
| `update.invoice.status.SENT`             | The invoice was sent to the customer. |
| `update.invoice.status.PORTAL_EXCEPTION` | The invoice hit an exception.         |

See [Outbound webhooks](/api-guides/webhooks) for the delivery model and payload format.

## What's next

<CardGroup cols={2}>
  <Card title="Customer payments" icon="money-bill" href="/api-guides/customer-payments">
    Record remittances applied to these invoices.
  </Card>

  <Card title="Statements & reminders" icon="bell" href="/accounts-receivable/reminders">
    Automated follow-up on outstanding invoices.
  </Card>

  <Card title="Receivables management" icon="chart-line" href="/accounts-receivable/receivables">
    The AR dashboard and reconciliation workflow.
  </Card>

  <Card title="Shipment documents" icon="file-pdf" href="/api-guides/shipment-documents">
    Attach supporting docs to shipments (shared with linked invoices).
  </Card>
</CardGroup>
