Customers
Export customer data
/a/Customers_getExportData
Run the legacy two-stage customer export: first select matching IDs, then stream requested fields for explicit ID batches.
When to use this endpoint
Use Customers_getExportData only for the Fieldmotion customer CSV exporter. The first request uses _ids=1 to select IDs from filters; later requests send a JSON ID batch plus the chosen field list.
For ordinary integration reads, use Customers_list and Customers_getById; their responses use named fields rather than positional f0, f1 keys.
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 |
|---|---|---|---|
_ids | String | Yes | Exact 1 starts the ID-selection phase. Any other value must be a JSON array of IDs for the data phase. |
fields | JSON array string | Data phase | Requested field names. Response key f0 corresponds to the first item, f1 to the second, and so on; base id is always included separately. |
search | String | No | ID phase general substring, applied only when search_fields decodes to a non-empty array. |
search_fields | JSON array string | Conditional | General-search fields: all, contact_name, name, notes, street_address_1, street_address_2, account_number, town, county, postcode, landline, mobile, email, or custom-value keys. |
id | Integer string | No | ID phase exact nonzero customer ID. |
type_id | Integer string | No | Exact nonzero customer-type ID. |
lead_acc | Integer string | No | Exact nonzero stored lead/account state. Unlike the table endpoint, no grouped meaning is applied. |
department_id | Integer string | No | Exact nonzero department ID. |
accountmanager_id | Integer string | No | Exact nonzero account-manager ID. |
is_active | Integer string | No | Exact active flag; blank omits the filter and 0 selects inactive rows. |
priceband | Integer string | No | Positive price-band ID; -1 selects stored ID 0. |
name | String | No | Finds non-deleted name matches, then includes each match and its direct non-deleted children in the export ID set. |
notes / county / postcode / landline / mobile | String | No | Legacy substring filters. County also searches town; postcode also searches both street lines. |
parent_id | Integer string | No | Exact parent ID when the converted value is greater than -1; 0 selects no parent. |
has_sites | Integer string | No | Truthy selects IDs referenced as a parent by a non-deleted customer; zero selects IDs not referenced. |
has_jobs | Integer string | No | Truthy requires either stored job counter; zero requires both to be zero. |
contactName / town / city / street / email / accountNumber | String | No | Additional CRM2 export substring filters. street searches either street line. |
country | Array | No | Country-code values passed to the shared export IN-filter builder. Invalid values can return a builder error. |
marketingSource | Array | No | Numeric marketing-source IDs matched against meta.marketing_source through the shared export filter builder. |
callbackStatus | Array | No | OR-combined callback states -2 overdue, -1 yesterday, 0 today, 1 tomorrow, and 2 future, based on the PHP server date. |
dateFromFilter / dateToFilter | Date values | No | When both are present and referenceDateField is set, build inclusive date filters with the shared export helper. |
referenceDateField | String | Conditional | Allowed values are callback, cdate, or last_edited. |
isDateUnix | Boolean-like | No | Passed to the shared date-filter helper to interpret supplied date values as Unix values where applicable. |
Example request
// Phase 1: select matching IDs.
$selection = FM_api('Customers_getExportData', [
'_ids' => '1',
'is_active' => '1',
'country' => ['IRL'],
]);
// Phase 2: retrieve a retryable ID batch.
$rows = FM_api('Customers_getExportData', [
'_ids' => json_encode([1042, 1088]),
'fields' => json_encode(['id', 'name', 'parent_id', 'last_job']),
]);
Example response
// Phase 1
{
"ids": [1042, 1088]
}
// Phase 2
[
{
"id": "1042",
"f0": "1042",
"f1": "Northwind Service Centre",
"f2": "1001",
"f3": "2026-09-23 15:30:00"
}
]
Phase-one IDs are emitted as JSON numbers. Phase-two database values are normally strings/null, and each fN position follows the caller’s fields order.
Accepted stored/metadata fields are account_manager, account_number, account_number_sage, accounts_name, accounts_number, appointments_closed, appointments_open, callback, callback_notes, cdate, city, contact_name, country, county, created_by, credit_terms, custom_values, default_nominal, default_taxcode, department_id, email, email_accounting, email_extra, id, industry, is_active, landline, last_edited, last_job, latitude, lead_acc, longitude, meta_default_invoice_due_date, meta_discount, mobile, name, notes, parent_id, postcode, price_band, state, static, street_address1, street_address2, town, type, xero_id, marketing_source, and last_edit_user.
Dynamic fields are custom_values.<key>, last_edited.<path>, and customer_table:<table-id>_<field-id>.
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: _ids"} | _ids is absent. |
{"error":"_ids should be a JSON array of numbers"} | Data-phase _ids is empty, malformed, or does not decode to a non-empty array. |
{"error":"fields should be an array of requested field names"} | Data-phase fields is absent, malformed, empty, or not an array. |
{"error":"unknown field name: ..."} | A non-dynamic field is outside the allowlist. Colon-containing names that are not a valid customer_table token are silently skipped instead. |
{"ids":[]} | The ID-selection query fails or no non-deleted customer matches. A general search with missing/invalid search_fields also returns this response. |
[] | A valid data-phase ID array matches no customer rows. |
Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.
Behaviour and edge cases
- Phase one always adds
deleted=0. Phase two does not: an explicitly supplied ID can export a soft-deleted or inactive customer. Phase two also does not verify that IDs came from phase one. - All phase-one filters are AND-combined except the values within country, marketing-source, and callback-status groups. Neither phase establishes a stable row order.
- IDs and fields are integer-converted/allowlisted before the data query. Duplicate IDs collapse in SQL; duplicate field requests produce multiple
fNvalues. last_jobuses the maximum stored job end epoch for matching numericcustomer_idand converts it to tenant-local time. The derived query does not filter soft-deleted jobs or constrain customer type.xero_iduses only Xero customer links for the currentxero_token.tenantIdand formats the stored 32-character remote value as a hyphenated GUID.default_nominal,default_taxcode, andmarketing_sourcetranslate stored IDs to current display strings; missing references become blank.last_edit_userreturns the user ID from the newest customer-change row.customer_tablefields return a JSON-encoded array of all matching row values, or a single-space string when no value exists.custom_valuesand other stored JSON values remain encoded strings.staticis always blank.- The handler streams both phases directly with invalid UTF-8 substitution and provides no built-in page size. The UI is responsible for sending retryable ID batches.
See it in Fieldmotion
CRM1
On Customers, pass the export-password dialog and configure a CSV export. The export widget first calls Customers_getExportData with _ids=1 and the current CRM1 table filters, then requests selected fields in ID batches.
CRM2
On Customers, choose Export. CRM2 first gathers filtered IDs through Customers_getDataTable, then its export widget calls Customers_getExportData in data phase for selected fields. It does not use this endpoint’s phase-one filters in that path.
Related endpoints
Customers_exportCheckPassis the UI’s separate password preflight.Customers_getDataTablesupplies CRM2’s filtered ID set.Customers_listreturns named fields for integration reads.