When to use this endpoint

Call preview and review its complete impact first. Reuse the same scheduling fields and returned revision. Generate one request_key per intended publication and keep the exact request for uncertain-response retries.

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.contractProgrammePublishing. 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.

Non-administrators also need Jobs Add and Edit. The legacy Job edit restriction (permissions bit 128) removes Edit; TeamAccess field roles cannot add/edit through this workflow.

For newly created work, the configured CRM Job customer/Contract eligibility restriction is checked independently. Publication revalidates it before changing prior work; adopted reactive Jobs use their existing identity.

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
run_idPositive integer or digit stringYesActive actor-owned run whose Programme is active.
user_idPositive integer or digit stringYesActive local assignee within your permitted team; use the workspace’s board user choices.
start_dateYYYY-MM-DDYesReal tenant-local calendar date. No future-only restriction.
visitsInteger-compatibleNoDefaults to 1; cast to integer and must be 1 or 2, within remaining run Visit count.
saturday, sunday, bank_holidays0/1 or booleanNoEach defaults to 0. Setting 1 includes those calendar dates; 0 skips them.
revisionSHA-256 stringYesExact preview revision; publication recomputes the payload before writes.
request_keyStringYesNonblank trimmed key, max 64 characters. Actor and full input participate in replay hash. Preserve identical field values/types/order on retry.
confirm_archive0/1 or booleanNoRequired as 1 when the preview lists prior incomplete work; defaults to 0.

Example request

$result = FM_api('ContractProgramme_publish', [
	'data' => json_encode([
		'run_id' => 510,
		'user_id' => 104,
		'start_date' => '2026-11-09',
		'visits' => 1,
		'revision' => 'replace-with-preview-revision',
		'request_key' => 'north-2026-11-09-1',
		'confirm_archive' => 1,
	]),
]);

Example response

{
  "ok": 1,
  "visit_ids": [
    710
  ],
  "job_ids": [
    810,
    811
  ],
  "archived": 2
}

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":"The preview changed. Review the updated dates and prior work before publishing."}Route, settings, current work or proposed payload changed.
{"error":"Confirm archiving the prior incomplete work"}Archive impact exists without confirm_archive=1.
{"error":"Request key has already been used for different data"}Key reused with a different request or actor.

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

Behaviour and edge cases

  • Exact completed retries return the stored result before preview revalidation; permissions/feature gates still apply. Same key with different data or actor fails. There is one durable batch acknowledgement, and transaction locking/occurrence uniqueness prevents duplicate publication.
  • Archives prior incomplete Jobs from the preview as Cancelled (is_complete=5), retaining metadata/evidence. Previous live Visits for this Programme/user become archived. Completed Jobs are not changed to Cancelled.
  • New Service Jobs get active Form, per-Form report number, ordinary configured Job reference (including deliberately blank references), Site snapshot, skills, checklist and immutable Programme provenance. Work is immediately released, including future-dated Jobs.
  • Reactive entries adopt the original Job once, preserving its reference/Form/evidence; previous assignment is retained. They are not copied again on later Visits or a new run.
  • No standalone Tasks are created from checklist definitions. The shared materializer writes directly; this route does not invoke the ordinary Job_new event/email workflow.
  • Archiving removes live mobile visibility but retains late offline completion evidence. Disabling authoring later does not undo released Jobs or history. Compatible mobile download retention is required for pending evidence.
  • New Jobs receive meeting_time at their scheduled start, due_date using that scheduled date, and an initial date_completed/epoch at the estimated end; they remain incomplete (is_complete=0). A populated estimate is not proof of completion.

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.
  • Commits Visits, Jobs/adoptions, provenance, archive metadata, linked Contract rows, Job and visits_published audits, mobile sync markers and replay result together. Generated linked Tasks from older data are marked archived only when their Programme metadata matches the Job. No files/evidence are removed.

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 Job Progress → Publish Jobs, Preview, review prior work, acknowledge archive impact and Publish. Filter for ContractProgramme_publish; the UI keeps one generated key for the reviewed attempt.

Related endpoints