Deze referentie is alleen in het Engels. Naar het Nederlandse REST API-overzicht

Diese Referenz gibt es nur auf Englisch. Zur deutschen REST-API-Übersicht

Cette référence existe uniquement en anglais. Aller à la présentation de l'API REST en français

Esta referencia solo está en inglés. Ir a la introducción de la API REST en español

PATCH/sales-invoices/{sales_invoice}

Update a sales invoice

PATCHhttps://<your-url>/api/v2/sales-invoices/{sales_invoice}

MCP id sales-invoices.updateAlso on this path: GET Read a sales invoice, DELETE Delete a sales invoice

View as Markdown openapi.json

Partially updates the invoice header: absent keys are untouched, and the line rows are edited through their own endpoints.

Access

Token scope
api:write:sales_invoices

Without it: 403 insufficient-token-scope.

Permission
sales_invoices::update

Without it: 403 insufficient-permission.

Feature
Needs the billing feature on your organisation.

Without it: 404 endpoint-not-found.

Rate cost
1 unit, write bucket. A write costs the same whatever view you ask for.
Also
Accepts If-Match. Never cached: private, no-store. Unknown body keys are refused, never dropped.

Both are checked, scope first. A token never does more than its user. How access is checked

Send If-Match with the ETag from your last read to detect a concurrent edit, which answers 412. A legacy invoice cannot be changed at all and answers 409 state-conflict with reason locked_by_legacy. An invoice that has already synced to the accounting system can still be edited here, which goes further than plnnrly: plnnrly closes its edit form once an invoice is synced, and the change will not be pushed to the accounting system again. status is never writable: it is stripped from the payload, and any update by a caller without sales_invoices::finalize additionally returns the invoice to pending_approval, so an already-approved invoice cannot be changed and synced without a finalizer re-checking it.

Path parameters

sales_invoiceinteger or stringRequired

The SalesInvoice's numeric id. Or its invoice_number, matching [A-Z]+-[0-9]{2}-[0-9]{5}|L-[0-9]+. Accepts a soft-deleted record.

Query parameters

Unknown parameters are refused with 400, naming the parameter.

viewstring

Which field set to render.

summarydetaildefault

trashedstring

Whether soft-deleted rows are included.

withoutdefaultwithonly

Request headers

X-Request-Idstringpattern

A correlation id of your own. It is recorded in our logs next to the server's request id but never returned: the response header always carries the server's id. A value that does not match the pattern is discarded silently, never rejected.

If-Matchstring

The ETag a write is conditional on. A stale validator is 412 precondition-failed.

Request body

Unknown keys are refused with 422, naming the key.

customer_idinteger → Customer.id

Customer this invoice is billed to.

project_idinteger or null → Project.id

Project this invoice was raised for, when it was raised from one.

customer_subscription_idinteger or null → CustomerSubscription.id

Customer subscription this invoice was generated from, when it was generated from a renewal.

invoice_datedate (YYYY-MM-DD)

Invoice date, a zone-free calendar date. Newest first is the default sort.

due_datedate (YYYY-MM-DD) or null

Date payment is due, a zone-free calendar date. Null when no term was applied.

Must be on or after invoice_date.

referencestring or null≤ 255

The customer's own reference, typically a purchase-order number, reproduced on the invoice.

descriptionstring or null≤ 255

One-line description of what was invoiced.

internal_notesstring or null

Notes for colleagues. Never rendered on the invoice the customer receives.

should_emailboolean

Whether the invoice is emailed to the customer when it syncs. The email is sent through the accounting integration, so it is forced to false in an organisation with no integration configured. Writing it requires sales_invoices::finalize.

Response 200

Returns a SalesInvoice in the detail view.

Headers

ETag
A strong validator of exactly this representation.
RateLimit
The IETF httpapi-ratelimit-headers form of your remaining allowance on the tightest bucket this request touched.
RateLimit-Policy
The ceiling and window of that bucket, as <limit>;w=<seconds>.
X-Request-Id
The server-generated ULID correlating this response with our logs.

Every response also carries Cache-Control, Vary, X-Content-Type-Options, X-Plnnrly-Api-Version, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset. Headers and versioning

Errors

7 errors every operation can return: 400, 401, 404, 406, 429, 500, 503

This path allows GET, PATCH, DELETE. Anything else answers 405.