Programme Contracts
Read a private per-user route
/a/ContractProgramme_route
Read the current reusable route revision and your run history for one user.
When to use this endpoint
Load a route before editing, then send route.revision through saveRoute. An empty route means this actor has not yet saved one for this Programme/user.
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.
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. |
Example request
$result = FM_api('ContractProgramme_route', [
'data' => json_encode(['programme_id' => 1, 'user_id' => 3]),
]);
Example response
{
"ok": 1,
"route": {
"id": 1,
"deleted": 0,
"ddate": 0,
"cdate": 1792490400,
"last_edited": 1792490400,
"programme_id": 1,
"user_id": 3,
"owner_id": 1,
"revision": 1
},
"days": [
{
"id": 1,
"deleted": 0,
"ddate": 0,
"cdate": 1792490400,
"last_edited": 1792490400,
"route_id": 1,
"revision": 1,
"day_number": 1,
"capacity_minutes": 480,
"items": [
{
"id": 1,
"deleted": 0,
"ddate": 0,
"cdate": 1792490400,
"last_edited": 1792490400,
"route_id": 1,
"revision": 1,
"day_id": 1,
"position": 1,
"contract_id": 1,
"site_id": 2,
"job_id": 0,
"duration_minutes": 60,
"consumed_visit_id": 0,
"contract_name": "Grounds",
"site_name": "Site",
"manager_id": 2,
"area_id": 1
}
]
}
],
"runs": [
{
"id": 1,
"deleted": 0,
"ddate": 0,
"cdate": 1792490400,
"last_edited": 1792490400,
"programme_id": 1,
"run_number": 1,
"owner_id": 1,
"name": "Summer",
"visits": 10,
"active": 1
}
]
}
Example from the disposable regression fixture. Database-derived numeric fields can be numbers or strings depending on the database driver; explicitly cast IDs/counts remain integers.
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":"Programme is private or unavailable"} | Definition missing, deleted or private. |
{"error":"User is outside your team"} | Requested assignee is not in permitted team. |
Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.
Behaviour and edge cases
- The Programme can be your own or default; route lookup always uses the authenticated actor as owner. Administrators do not gain access to another creator’s route.
- Returns days for the current revision ordered by day_number. Items include saved row fields plus contract_name/site_name and current manager_id/area_id; filtered to non-deleted Contract/config/Site and Contract scope, ordered by position,id.
- Days/route/runs are unpaginated. runs contains your non-deleted runs for this Programme, newest run_number first, including inactive runs. An inactive Programme can still be read.
- The empty route is [] rather than null or {}. A saved route includes id, programme_id, user_id, owner_id, revision, timestamps and retained storage fields; each day includes its items array.
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
Open Planning Board and select Programme/User; filter for ContractProgramme_route. The UI inserts an empty draft day locally if no saved days are returned.
Related endpoints
ContractProgramme_saveRoute— Save the ordered days using the returned revision.ContractProgramme_newRun— Start a run for publication.