Customers
Import customer create/edit rows
/a/Customers_edit
Create or edit a batch of legacy customer-import rows and return one status per input row.
When to use this endpoint
Use Customers_edit for the CRM customer CSV import workflow after columns have been mapped to legacy customer field names.
Do not use it as a transactional bulk API. Each row independently calls Customer_edit or Customer_new, and successful earlier rows remain committed after later errors.
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 |
|---|---|---|---|
customers | Request array of objects | Yes | Nonempty native request array. Despite an unreachable JSON-decoding branch, a JSON string is rejected by the initial type check. Each object is one create/update row. |
run_triggers | Integer/boolean-like | Yes | Converted to an integer. Each row receives do_not_run_trigger = 1 - run_triggers; values outside 0/1 produce unusual trigger-control integers. |
customers[].id | Integer string | No | Truthy positive ID selects Customer_edit; otherwise the row is passed to Customer_new. |
customers[].parent | String or number | No | A truthy nonnumeric value is resolved by exact customer name to parent_id; a missing match returns a row error. Numeric values are left for the child endpoint’s normal parameters. |
customers[].account_manager | String or number | No | A truthy nonnumeric value is resolved by exact user name; a missing match returns a row error. |
customers[].department | String or number | No | A truthy nonnumeric name is resolved exactly. If absent, a new department is created and its ID is used. |
customers[].type | String or number | No | A truthy nonnumeric value is resolved by exact customer-type name; a missing match returns a row error. |
customers[].country | String | No | Values longer than three characters are uppercased and looked up in the static country-name map. Unknown names can become an unset/null value; no explicit row error is generated here. |
customers[].callback | String | No | Falsy/non-numeric-leading values are removed. Otherwise the time is stripped, and DD/MM/YYYY is converted to YYYY-MM-DD. |
customers[].notes | String | No | Legacy text beginning [{' has single quotes replaced by double quotes. |
customers[].<other fields> | Legacy customer values | No | Remaining keys pass to Customer_edit or Customer_new; use the target endpoint’s snake-case parameter contract. |
Example request
$result = FM_api('Customers_edit', [
'run_triggers' => '0',
'customers' => [
['id' => '1042', 'name' => 'Northwind Belfast'],
['name' => 'Northwind Cork', 'country' => 'Ireland'],
],
]);
Example response
{
"statuses": [
{"ok": 1, "id": 1042, "errors": [], "updates": {"name": "\"Northwind Belfast\""}},
{"ok": 1, "id": 1088}
]
}
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":"customers parameter not supplied or empty"} | customers is absent, not a native array, or empty. |
{"error":"run_triggers parameter not supplied or empty"} | The run_triggers key is absent. A supplied zero is accepted despite the message wording. |
statuses[n]: {"error":"parent does not exist"} | A named parent cannot be resolved. |
statuses[n]: {"error":"account_manager does not exist"} | A named account manager cannot be resolved. |
statuses[n]: {"error":"department does not exist"} | Department resolution/creation returns strict false. |
statuses[n]: {"error":"type does not exist"} | A named customer type cannot be resolved. |
Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.
Behaviour and edge cases
- Name-to-ID caches are reused within the batch. Lookups are exact database comparisons and do not explicitly exclude inactive users/types/departments.
- Missing named departments are created as a side effect, unlike missing parent, manager, or type records.
- Rows are processed in request order. The handler removes the HTTP referrer before child calls to avoid legacy redirects.
- Callback normalization cannot clear a callback because a falsy value is removed before the child call.
- There is no pagination or batch-size limit.
Side effects
- Combines all side effects of
Customer_newandCustomer_edit, including geocoding, audit/history rows, events, trigger emails, and notifications where their child routes apply. - May create departments while processing import rows.
See it in Fieldmotion
CRM1
The customer import dialog sends mapped rows to Customers_edit. Inspect the final import request and its per-row statuses.
CRM2
The CRM2 customer import module calls the same route and shows row-level outcomes; filter Network requests for Customers_edit.
Related endpoints
Customer_editdefines update-row behavior.Customer_newdefines create-row behavior.Customers_checkDatapreviews normalization/matching before import.