Programme Contracts
Save a reusable per-user route
/a/ContractProgramme_saveRoute
Save an ordered route revision without scheduling live Jobs.
When to use this endpoint
Load ContractProgramme_route first. Submit the complete ordered days and items with the current revision; old revisions remain available to previously published Visits.
Send operation fields inside one JSON-encoded data object. When data is present it replaces top-level operation fields; otherwise the handler accepts the top-level request. String data is limited to 2 MB. Reserved transport/internal keys are removed. Malformed or non-array JSON becomes an empty input, so validate JSON before sending.
Authentication and permissions
Authenticate with a signed API key or logged-in CRM session; session requests follow dispatcher CSRF checks. API-key requests use the key owner’s identity and permissions.
Requires the Contracts entitlement (features & 64), features2.contractProgrammes and Contracts View. Also requires features2.contractProgrammePlanning. Requires Contracts Edit.
All storage belongs to the authenticated tenant. Contract operations follow the actor’s recursive HR manager/team scope, or the configured TeamAccess Contract/Department policy. Client administrators have broad Contract scope; private Programme and route ownership still applies. Active assignees must be within the permitted team.
The tenant must have the Programme storage upgrade (foundation 803; independent route ownership 804 before planning). Requests never create or repair tables. A missing/incompatible storage failure can return an initialization/storage error or Contracts operation failed. Check the saved state before retrying.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
programme_id | Positive integer or digit string | Yes | Local Programme definition ID. It must be active for writes that start runs or save routes, and owned by you or marked as a default. |
user_id | Positive integer or digit string | Yes | Active local assignee within your permitted team; use the workspace’s board user choices. |
revision | Integer-compatible | Yes for existing routes | Current route revision; zero/omitted for a new route. |
days | Array or JSON list | Yes | 1–366 ordered day objects; array position supplies day_number, so supplied day numbers are ignored. |
days[].capacity_minutes | Positive integer | No | Defaults to account work-day duration (fallback 480); max 1440. Advisory capacity, not a sum-of-items limit. |
days[].items | Array | No | Defaults to []. At most 5000 items across route. Each needs contract_id and site_id; order determines position. Contract must be active, Site active and linked. |
days[].items[].job_id | Integer-compatible | No | Zero/omitted means recurring Site service. Positive value adopts an existing incomplete ordinary customer Job matching Contract/Site, local user_type=0, unclaimed by Programme provenance or another current route; no duplicate reactive Job in this route. |
days[].items[].duration_minutes | Positive integer or digit string | No | 1–1440 minutes; defaults to Contract default_duration. |
Example request
$result = FM_api('ContractProgramme_saveRoute', [
'data' => json_encode([
'programme_id' => 410,
'user_id' => 104,
'revision' => 0,
'days' => [
[
'capacity_minutes' => 480,
'items' => [
[
'contract_id' => 210,
'site_id' => 1043,
'duration_minutes' => 60,
],
],
],
],
]),
]);
Example response
{
"ok": 1,
"id": 610,
"revision": 1
}
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":"Programme Contracts is not enabled"} | Workspace flag or Contracts entitlement is disabled. |
{"error":"Permission denied"} | Contracts View or the required action permission is unavailable. |
{"error":"This Contracts capability is not enabled"} | The operation’s additional feature flag is disabled. |
{"error":"Route changed. Reload before saving."} | Revision differs from existing route. |
{"error":"A reactive Job can occur only once on a route"} | Repeated reactive Job ID. |
{"error":"Reactive Job is already on another route"} | Another current route contains this Job. |
Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.
Behaviour and edge cases
- Each successful save increments route revision, inserts new days/items and retains old revisions. Other actors using the same default Programme/user have independent route records.
- Recurring Site entries may repeat. Reactive Jobs are one-off and may not exist on another current route, including another creator’s route.
- Capacity does not reject an overloaded day. Saving neither allocates calendar dates nor creates/adopts Jobs yet. Empty days are allowed; an empty entire route is rejected.
- An inactive Programme or Contract blocks saving; Site must be non-deleted, active, a child Customer and linked to the Contract. Publication revalidates references.
Side effects
- The service serializes writes on the tenant owner in a transaction. Validation, persistence, audit or TeamAccess outstanding-Job access failures roll back that transaction. Successful changed writes refresh linked Contract/Customer sync timestamps and shared mobile update markers, then invalidate CRM caches after success. These operations do not create schema or enable account features.
- Writes route revision/day/item rows and route_saved audit. Previous publications continue using their original route revision.
See it in Fieldmotion
CRM1
No direct caller was found in CRM1 source. This workflow is implemented by the opted-in CRM2 Programme Contracts workspace.
CRM2
In Planning Board drag Sites/reactive Jobs into days, reorder them, then Save Route. Filter for ContractProgramme_saveRoute; the UI sends the whole draft and current revision.
Related endpoints
ContractProgramme_route— Read current route/revision.ContractProgramme_preview— Map the saved route to calendar dates.ContractProgramme_publish— Release reviewed work.