When to use this endpoint

Use ScheduleOfRate_updateData for CRM2 edits, archive changes, and newer structured labour/metadata values. It accepts the same camel-case map as ScheduleOfRate_createData.

Authentication and permissions

The request must authenticate with signed API-key fields or a logged-in CRM session. The handler calls the normal login guard but performs no additional page or role-permission check.

All reads and writes use the authenticated tenant. The CRM screens apply client-side Schedule of Rates permissions, but those controls are not repeated by this endpoint.

Parameters

ParameterTypeRequiredDescription
idInteger string or native arrayYesTarget ID or decoded list of IDs. Scalar mode returns row-specific warnings. Array mode processes sequentially, discards per-row results, and returns {"ok":1}.
Mapped updatesMixedNoAny writable key from name, code, stockUsed, cost, lastEdited, description, meta, metaNominal, metaNominalID, metaNominalCode, metaTaxTypeID, assetTypes, labourUsed, priceBands, archived, and metaCustomerCosts. Values completely replace their column/nested key; native arrays or objects in text/JSON fields are JSON-encoded.
taxTypeNative objectNoComposite picker input with id and nominalID. Integrations can instead send metaTaxTypeID, metaNominalID, metaNominalCode, and metaNominal explicitly.
metaCustomerCostsNative object, string, or blankNoCustomer-ID-to-price map in meta.customerCosts. Exact blank input triggers a preliminary JSON-key removal; other supplied values go through the normal nested-map update.
noRecord / noRecordCustomNative arraysNoInternal generic-history controls. They do not suppress the write or the Schedule of Rates event.

Example request

$result = FM_api('ScheduleOfRate_updateData', [
	'id' => '430001',
	'name' => 'Annual service plus',
	'cost' => 165,
	'archived' => 0,
]);

Example response

{
  "ok": 1
}

A scalar no-op returns {"warning":"No Change."}; a missing target returns {"warning":"No data."}; only unknown update keys return {"warning":"no updated fields."}. Supplying only id returns success.

Errors and unusual responses

ResponseCondition
{"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"}The id key is absent.
Database errorAn invalid value is assigned to the JSON price_bands column.

Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.

Behaviour and edge cases

  • Unknown keys are ignored. IDs are integer-converted; there is no explicit positive-ID validation.
  • An actual mapped change advances last_edited to current server time. Supplying lastEdited does not override this: the helper emits a second current-time assignment after the requested value. No-op and missing-row paths do not update the row.
  • Blank metaCustomerCosts removes only meta.customerCosts, preserves other metadata, and then removes that key from the mapped request.
  • These endpoints store the base price in cost; they do not calculate a customer’s final price. Job/quote consumers can apply meta.customerCosts, the tenant setting sor-child-gets-parent-price, and the customer’s price band later.

Side effects

  • Changed fields advance the schedule_of_rates cache marker and write generic field-level history.
  • After any nonzero integer-converted request ID, fires the tenant-configurable SOR_edit event with the rate ID. Any configured listeners run synchronously and can add integration-specific side effects. This also happens after no-op or missing-row warnings; a nonempty bulk-ID array incorrectly emits ID 1 instead of its members.

See it in Fieldmotion

CRM1

CRM1 edits the legacy table through focused ScheduleOfRates_set* routes.

CRM2

Open Stock → Schedule of Rates and edit a row, its archive state, tax picker, stock, labour, or customer prices. The shared widget calls this route.

Related endpoints