Invoices
Save an invoice recurrence
/a/InvoiceRecurring_save
Create an invoice recurrence from an invoice or edit an existing recurrence.
When to use this endpoint
Use this for the legacy recurrence editor. Creation copies the invoice’s customer target; editing preserves the recurrence’s existing customer target.
Authentication and permissions
The request must authenticate with signed API-key fields or a logged-in CRM session and is scoped to the authenticated tenant.
The handler performs no additional invoice page, record-owner, or department permission check unless stated below.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | Positive integer | For editing | Existing recurrence ID. Takes precedence over invoice_id. |
invoice_id | Positive integer | For creation | Existing invoice to attach and use as the recurrence source. |
name | String, 1–255 bytes after trimming | Yes | Recurrence name. The stored value itself is not trimmed. |
every | Integer 0–6 | Yes | Schedule kind: weekday, month days, year days, or day/week/month/year interval respectively. |
on | JSON value | Yes | For kinds 0–2, normally a JSON array of numeric offsets; for 3–6, a positive numeric JSON scalar. A decoded string is split on commas. |
expiry | YYYY-MM-DD | No | Expiry date stored as noon Unix time. Blank or 0000-00-00 stores zero. |
active | Integer-compatible | No | Active flag; defaults to zero. |
notes | String | No | Replaces notes when present. Omission preserves notes on edit and leaves them unset on creation. |
copy_items | Integer-compatible | No | Stored in recurrence metadata; defaults to zero. |
job_recurrence_id | Integer-compatible | No | Links the invoice recurrence to a job recurrence; defaults to zero. |
Example request
$result = FM_api('InvoiceRecurring_save', [
'invoice_id' => '981',
'name' => 'Monthly service invoice',
'every' => '1',
'on' => '[0]',
'active' => '1',
]);
Example response
{
"ok": 1,
"id": 42
}
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: name"} | Name is absent, blank after trimming, or longer than 255 bytes. |
{"error":"missing or invalid parameter: every"} | Schedule kind is absent or outside 0–6. |
{"error":"missing or invalid parameter: on"} | Schedule data is absent, blank, or contains a nonnumeric array item. |
{"error":"invalid parameter: edate"} | The nonblank expiry value is not in the required shape. |
{"error":"invoice recurrence does not exist","errcode":1} | Edit ID does not exist. |
{"error":"invoice does not exist"} | Creation invoice does not exist. |
{"error":"no way to identify which recurrence to edit"} | Neither a positive recurrence ID nor invoice ID is supplied. |
Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.
Behaviour and edge cases
- If the source invoice already has a recurrence, the endpoint returns
{"ok":1,"warning":"recurrence not created - already existed","id":…}without applying the submitted recurrence fields. - Edits reset
num_invoicesto 1 andndateto zero before recalculating it. Existing metadata keys other than the two controlled keys are preserved. - All array entries in
onmust be numeric, but range and combination validation is deferred to recalculation.
Side effects
- Creates or updates the recurrence, attaches a newly created recurrence to the source invoice, synchronizes the reciprocal job-recurrence metadata link, and recalculates the next run.
- Advances recurrence and invoice
last_editedtimestamps as applicable.
See it in Fieldmotion
CRM1
The invoice recurrence widget creates and edits definitions with this route.
CRM2
The recurrence editor currently loads/deletes records directly, while creation/editing is also available through the invoice module.
Related endpoints
InvoiceRecurring_recalculateNDateimplements schedule calculation.InvoiceRecurring_getreloads the stored row.