Customers
Export customer change history
/a/Customer_exportChanges
Download normalized customer change history as an unpaged CSV with old and new values.
When to use this endpoint
Use Customer_exportChanges from the customer Changes tab after Customer_getChangesDT has normalized any legacy embedded history.
The endpoint streams CSV and exits. FM_api() returns the raw CSV text rather than decoded JSON.
Authentication and permissions
The request must authenticate with signed API-key fields or a logged-in CRM session. The handler performs no additional page or role-permission check.
The lookup and any write use the authenticated tenant; an ID from another tenant cannot be addressed through this call.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
customer_id | Integer string | Yes | Positive customer ID. Customer existence is not checked. |
f-field | String | No | Case-insensitive substring filter on the change label. Unlike Customer_getChangesDT.field, this is not an exact match. Blank presence produces a match-all substring. |
Example request
$csv = FM_api('Customer_exportChanges', [
'customer_id' => '1088',
'f-field' => 'Email',
]);
file_put_contents('customer-changes.csv', $csv);
Example response
Date,User,Field,"Change From","Change To"
2026-09-24,"Aoife Murphy",Email,old@example.test,service@example.test
The response is text/csv with attachment filename customer-changes.csv. Rows are ordered by change epoch descending.
The Date column is rendered as YYYY-MM-DD in the PHP server timezone, not converted through the tenant-time helper.
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: customer_id"} | customer_id is absent, zero, negative, or non-numeric. |
Header-only CSV | No normalized history row matches, including for a nonexistent positive customer ID. |
Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.
Behaviour and edge cases
- Array-valued old or new values are JSON-encoded into a CSV cell. Scalar values are written directly.
- There is no pagination or maximum-row limit; the complete match set is loaded before the response is streamed.
- The endpoint does not run the legacy-history migration. A customer whose changes still exist only in
customers.changescan export an incomplete or header-only file until the table endpoint is called.
Side effects
- Writes
/tmp/<tenant ID>/tmp/customer_changes.csv, creating the tenant directory with mode0777when needed, then streams that file. - The fixed per-tenant filename is overwritten and left on disk. Concurrent exports in the same tenant can interfere with one another.
See it in Fieldmotion
CRM1
Open a customer’s Changes tab and choose Export. CRM1 submits a form to Customer_exportChanges with the customer ID and current field filter, causing a browser download.
CRM2
A source search found no CRM2 caller for this CSV endpoint.
Related endpoints
Customer_getChangesDTdisplays, filters exactly, and migrates legacy history before export.Customer_getAddressHistoryDTreturns stored address snapshots that are separate from these audit rows.