When to use this endpoint

Create the definition before saving per-user routes or starting a run. Editing a definition does not change the Visit limit already copied into an existing run.

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

ParameterTypeRequiredDescription
idInteger-compatibleNoZero/omitted creates. Existing ID must be owned by you, or a default definition edited by a client administrator.
nameStringYesNonblank trimmed name, max 128 characters; no name uniqueness check.
visitsPositive integer or digit stringYesPlanned Visits per run, 1–366.
active0/1 or booleanNoDefaults to 1, including on updates when omitted.
is_default0/1 or booleanNoCreate only; defaults to 0. Only client administrators can create defaults. Ignored on updates.
revisionInteger-compatibleFor updatesCurrent revision from the read; stale versions are rejected.

Example request

$result = FM_api('ContractProgramme_saveProgramme', [
	'data' => json_encode([
		'name' => 'North programme',
		'visits' => 12,
		'active' => 1,
	]),
]);

Example response

{
  "ok": 1,
  "id": 410
}

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 changed. Reload before saving."}Update revision is stale.
{"error":"Only the programme creator can change its definition"}Actor does not own the definition and cannot edit a default.
{"error":"At most 366 visits per run"}Visit count exceeds the limit.

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

Behaviour and edge cases

  • Private definitions are creator-owned even against other administrators. Defaults are readable by other actors, but their routes and runs remain independently owned.
  • Update requires the current revision and increments it; response returns only id, so reload before another edit. Existing runs keep their saved name/Visit count.
  • This endpoint saves no route and publishes no Jobs. Deactivating a definition blocks later route saves/run starts/preview without undoing published history.

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.
  • Audits programme_saved. New definitions store the actor as owner.

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 → Programme Settings → Create/Edit Programme. Filter for ContractProgramme_saveProgramme. Duplicate creates a new definition with copied name/Visit count, not copied routes.

Related endpoints