# Update an invoice

Operation ID: `updateInvoice`

Group: Accounting › Invoices

[Open in the interactive reference](https://docs.api.lensing.ai/lensing/v1/endpoints/updateInvoice)

`PATCH /accounting/invoices/{id}`

Retry-safe: A write that accepts Idempotency-Key: repeating a request with the same key replays the stored answer instead of executing twice.

Updates an invoice with the fields you send. A field you leave out keeps its current value.

Use the invoice's id, not its invoiceNumber.

You can change invoiceDate, paymentTerm, customerMessage, and purchaseOrderOverride on the invoice, and description and dutiesAndCustomsAmount on each line. Amounts, tax, the set of lines, deliveryStatus, the addresses and invoiceEmail can't be changed.

To clear purchaseOrderOverride, send an empty string, and the sales order's PO references apply again. An empty paymentTerm, customerMessage, or invoiceDate is ignored, so customerMessage can't be cleared.

Each item in updateLines changes one existing line of this invoice, identified by its id. Lines you leave out don't change, and you can't add or remove lines.

The response contains only the invoice's id. To see the recalculated totals, use GET accounting/invoices/:id.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Api-Version` | header | string · v1 | yes | The API version this request targets, named by its public identifier. Required on every request; omitting it, or naming a version this company does not have, is answered with 400. This document describes version `v1`. |
| `Idempotency-Key` | header | string | no | An opaque caller-chosen string (1-255 characters) that makes this request safe to retry. Repeating the same key with an identical request body replays the original response instead of re-executing - but only once that original request succeeded; a failed attempt re-executes on retry. Repeating the key with a different body is answered with 422. A key still in flight is answered with 409 - as is a key whose original request was interrupted before it finished, for a few minutes afterwards, until that attempt is presumed dead. A completed response stays replayable for 24 hours. The key is scoped to the caller, not to the action: reusing it for a different action is answered with 409 or 422 rather than treated independently. On read actions this header is ignored. On write actions that cannot offer the guarantee it is answered with `400 IDEMPOTENCY_NOT_SUPPORTED` rather than silently ignored. |
| `id` | path | string | yes | The unique identifier of the invoice you want to update. |

## Request body

Required, `application/json`: updateInvoiceInput.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `invoiceDate` | date | no | The date the invoice was issued. Format YYYY-MM-DD. Omit to leave unchanged. Changing it re-derives the due date from the payment term and moves which tax period the invoice falls in. Example: `"2026-09-05"`. |
| `paymentTerm` | string | no | The payment term governing when payment falls due, as the term's configured code. Omit to leave unchanged. An empty string is ignored rather than clearing it. This field is write-only: the read endpoints publish the resolved paymentTermDays instead of the term itself. |
| `customerMessage` | string | no | A free-form message printed on the invoice for the customer. Omit to leave unchanged. It cannot be cleared through this endpoint. An empty string is ignored, not stored. Example: `"Thank you for your business."`. |
| `purchaseOrderOverride` | string | no | A manually entered PO number that overrides the references derived from the sales order. Omit to leave unchanged. Unlike the other fields here, pass an empty string to clear it and fall back to the sales order's own PO references. |
| `updateLines` | array<object> | no | The line edits to apply, each addressing one existing line of this invoice by its id. Omit to leave the lines untouched. Lines cannot be added or removed here, because the set of lines comes from the sales order's invoice plan. Example: `[{"id":"5e2a7c81-9d34-4b06-8f19-2c7e4a0d3b58","description":"Platform subscription — September 2026","dutiesAndCustomsAmount":"45.00"}]`. |
| `updateLines[].id` | string | yes | The unique identifier for the invoice line to edit, as returned by GET accounting/invoices/:id. It must be a line of this invoice, and may appear at most once in the request. |
| `updateLines[].description` | string | no | The new line description, as shown to the customer. Omit to leave unchanged. |
| `updateLines[].dutiesAndCustomsAmount` | decimal | no | The duties and customs charged on this line, in the invoice's currency. Requires the entity's Duties and Customs feature to be enabled, and setting it recalculates the invoice's journal entry. Omit to leave unchanged. |

```json
{
  "invoiceDate": "2026-09-05",
  "paymentTerm": "<paymentTerm>",
  "customerMessage": "Thank you for your business.",
  "purchaseOrderOverride": "<purchaseOrderOverride>",
  "updateLines": [
    {
      "id": "5e2a7c81-9d34-4b06-8f19-2c7e4a0d3b58",
      "description": "Platform subscription — September 2026",
      "dutiesAndCustomsAmount": "45.00"
    }
  ]
}
```

## Response

`200` Success

Under `data` (updateInvoiceOutput):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | The unique identifier for the updated invoice. Read the invoice back with GET accounting/invoices/:id to see the recalculated totals. Example: `"f3b9c1d4-2a68-4e07-8c15-9d0b7e5a3f62"`. |

```json
{
  "data": {
    "id": "f3b9c1d4-2a68-4e07-8c15-9d0b7e5a3f62"
  },
  "diagnostics": [
    {
      "code": "<code>",
      "message": "<message>",
      "severity": "error",
      "path": [
        "<path>"
      ],
      "details": "<details>"
    }
  ]
}
```

## Response headers

- `Idempotency-Replayed` — Present and `true` only when this response replays a previously stored one for the same Idempotency-Key, rather than reflecting a live execution. Absent on a live response - never sent as `false`.

## Errors

| Code | Status |
| --- | --- |
| `INVOICE_NOT_FOUND` | 400 |
| `LINE_NOT_ON_INVOICE` | 400 |
| `DUPLICATE_LINE` | 400 |
| `IDEMPOTENCY_KEY_CONFLICT` | 409 |
| `IDEMPOTENCY_PAYLOAD_MISMATCH` | 422 |

## Statuses

- `400` — Invalid request or business failure. Codes: INVOICE_NOT_FOUND, LINE_NOT_ON_INVOICE, DUPLICATE_LINE
- `401` — The request carries no valid access token.
- `403` — Not authorized
- `404` — Not found
- `409` — The Idempotency-Key is still being processed by another request.
- `422` — The Idempotency-Key was already used with a different request payload.
- `429` — Too many requests from this client address. The response has no body and no `Retry-After` header.
- `500` — Internal server error
