Invoices
Update mapped invoice data
/a/Invoice_updateData
Update a CRM2 mapped invoice form, normalize accounting and row data, and run recurrence, notification, job-total, Workflow, and snapshot effects.
When to use this endpoint
Use this as the save endpoint for configurable Invoice Update layouts. Narrow legacy edits may use a specific setter instead.
Authentication and permissions
The request must use a logged-in CRM session or valid signed API credentials and is scoped to that tenant.
Users whose legacy crm_access mask contains bit 64 receive {"error":"no permission to access api"}. No record-owner check is made.
The handler requires edit permission for pageInvoices. A legacy CRM1 account with no role row is allowed by the compatibility fallback; an equivalent CRM2 request is denied until a role grants the permission.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | Positive integer | Yes | Active invoice ID consumed by the shared mapped updater. |
Mapped invoice fields | Mixed | No | Any writable field from the invoice field map, including customer, dates, notes, number, form, custom values, tax and nominal selections. |
widgetTableInvoice | Widget object | No | Complete CRM2 row widget; normalizes rows, derives missing amounts/tax/item codes, calculates totals, and updates linked jobs. |
widgetInvoiceRecurrence / widgetFormField | Widget objects | No | Save recurrence configuration and custom invoice-form values. |
status | Integer-compatible | No | When Workflow is enabled, removed from the generic write and delegated to Invoice_setStatus. Status 4 is rejected when real credit notes are enabled. |
Example request
$result = FM_api('Invoice_updateData', [
'id' => 981,
'notes' => 'Updated payment terms',
'cdate' => '2026-10-02',
]);
Example response
{
"ok": 1
}
Errors and unusual responses
| Response | Condition |
|---|---|
{"error":"not logged in"} | No authenticated tenant context is available. Invalid signed credentials can be rejected by the dispatcher before the handler runs. |
{"error":"missing or invalid parameter: id"} | No positive ID is supplied. |
{"error":"...refer to different accounts..."} | Header or row nominal identifiers conflict. |
{"error":"Credit Notes are enabled. Use the Credit Notes page instead of invoice status Credit Note."} | Legacy status 4 is requested while credit notes are enabled. |
Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.
Behaviour and edge cases
- Row saves derive totals and may update each job represented by line items. Final
total_netis recalculated from activeinvoices_rows. - Recurrence saving occurs before the main mapped update and is not transactionally rolled back if a later step fails.
- Customer details refresh when the date or target changes, or repairs a blank legacy snapshot; historical address data is used at the invoice date.
- Header-only mapped edits do not reject Paid invoices. A
widgetTableInvoicereplacement does reject Paid status through the row-storage helper.
Side effects
- Can rewrite rows and recurrence data, update job prices, send mention notices and update notifications, store row-table data, refresh the customer snapshot, run a status transition, and fire
Invoice_edit.
See it in Fieldmotion
CRM1
No direct production CRM1 string caller was found; CRM1 uses the specific setter routes.
CRM2
Editing an invoice through layoutInvoiceUpdate submits this route through the shared configurable-layout save helper.
Related endpoints
Invoices_getDataTablesupplies paginated CRM2 invoice rows and IDs.Invoices_getDTis the CRM1 invoice-list counterpart.