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

ParameterTypeRequiredDescription
programme_idPositive integer or digit stringYesLocal 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_idPositive integer or digit stringYesActive 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

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":"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