# Collection programme API

Source: https://govform.com/resources/documentation/article/33-13-api-response-hub
Topic: Govform API & MCP

Use the Collection programme API to coordinate expected, time-bounded Collections, manage respondent assignments and work with submitted responses and reviews.

The API is hosted by Govform Builder. It is separate from the APIs hosted by QA and Production services because Builder securely routes each request to the collection environment you specify.

## Before you start

Create an API key from **Library settings → Govform API keys**. Give the key only the permissions it needs:

| Permission | Capability |
| --- | --- |
| `apiCollectionProgrammeRead` | Read collection programmes, collections, respondent organisations and contacts, assignments, submitted answer versions, reviews, communications and audit events. |
| `apiCollectionProgrammeWrite` | Create and update collection programmes and collections; manage lifecycle, cohorts, assignments, rosters and reviews. Write permission also permits reads. |
| `apiFormRead` | Additionally required to read saved Collection review settings; apiFormWrite also permits this read. |
| `apiFormWrite` | Additionally required to create a Collection or save its managed form review settings. |
| `apiFormDeploy` | Additionally required to prepare the latest saved collection form for use in QA, Staging or Production. |

This separation lets you create one integration that works with form designs and another that works with Collection programme data. A key with form permissions does not receive Collection programme data unless it also has `apiCollectionProgrammeRead` or `apiCollectionProgrammeWrite`.

Collection programme response data can contain personal or otherwise sensitive information. Use IP allowlists, store the key in a secrets manager and grant the narrowest permission possible.

## Base URL and authentication

All Collection programme API paths use this Builder base URL:

```text
https://app.govform.com/builder/libraries/{libraryId}/api/response-hub
```

Send the API key in the `Authorization` header. HMAC authentication is recommended; the legacy raw-key form is also supported:

```http
Authorization: GovformsApiKey KEY_ID:SECRET_VALUE
Content-Type: application/json
```

You may send `X-On-Behalf-Of` with a user or integration identity to make audit records more descriptive. If it is omitted, the API key ID is used.

## Common identifiers

| Name | Meaning |
| --- | --- |
| `libraryId` | The Govform library ID. Every operation is restricted to this tenant. |
| `collectionProgrammeId` | The stable ID of a collection programme. |
| `environment` | `qa`, `staging` where enabled, or `production`. |
| `collectionId` | The immutable ID of one collection period in one environment. |
| `assignmentId` | A respondent organisation's assignment in a collection. |
| `responseId` | The response linked to an assignment. |
| `workItemId` | A review item for one immutable submitted response version. |

Dates are ISO 8601 strings. Collection timetable dates may be supplied as `YYYY-MM-DD`. Opening and scheduled review dates resolve to the start of that day in the library's service time zone; response deadlines (including assignment and CSV deadlines) include the whole day in that zone. Datetimes without an offset also use service time. Explicit timestamps with `Z` or an offset retain their supplied instant. Values are stored as absolute timestamps; saving region settings does not rewrite existing programme dates.

List endpoints accept `page` and `pageSize`; page size is capped at 100. Relevant endpoints also accept `search`, `sort`, `direction=asc|desc`, and assignment filters. These endpoints retain their page-based defaults and response wrappers. The response-summary, response-version, review-comment and export-operation lists use cursor pagination by default.

For integrations that need a reliable traversal of a larger dataset, snapshot cursor access is available to every API key with the relevant existing Response Hub permission. For lists with an existing page-based contract, only `pagination=cursor` selects snapshot cursor access. A `cursor` parameter alone or another `pagination` value retains page-based behaviour. Response-summary, response-version, review-comment and export-operation lists are cursor-first.

### Snapshot cursor lists

Start a traversal with `pagination=cursor`. Cursor mode accepts `pageSize` from 1 to 100 and defaults to 100. It returns a fixed point-in-time view and does not calculate an exact total:

```json
{
  "items": [],
  "pageSize": 100,
  "hasMore": true,
  "nextCursor": "opaque-value",
  "snapshotAt": "2026-08-08T10:15:30.000Z",
  "snapshotExpiresAt": "2026-08-08T10:17:30.000Z"
}
```

Pass only `pagination=cursor&cursor=opaque-value` for the next page. You may repeat `pageSize`, the canonical `sort`, `direction` or `expand`, but repeated values must match the cursor. Do not use `page`, `search` or filters in cursor mode. Cursors are bound to the key, library, environment, collection and normalized query, and cannot be transferred to another scope.

A completed traversal returns every matching record from one fixed snapshot exactly once. Process a page durably before committing its `nextCursor`. Retrying the same cursor can return the same page. Snapshot cursors are intentionally short-lived; if a cursor expires, the API returns `410 CURSOR_EXPIRED` with `restartRequired: true` and the traversal must restart.

Assignments support `expand=organisation,memberships,response,activeReview`; reviews support `expand=organisation,memberships,response,assignment`. Without `expand`, assignment items contain only `{ "assignment": ... }` and review items contain only `{ "workItem": ... }`; selected relations are added beside that base record. Original page-mode enrichment is unchanged.

## Endpoints

All paths below follow `/builder/libraries/{libraryId}/api/response-hub`.

### Collection programmes

| Method and path | Capability |
| --- | --- |
| `GET /collection-programmes` | List collection programmes. |
| `POST /collection-programmes` | Create a collection programme. |
| `GET /collection-programmes/{collectionProgrammeId}` | Get programme defaults and message templates. |
| `PATCH /collection-programmes/{collectionProgrammeId}` | Update the programme name, Purpose (`description`), defaults and message templates used by future collections. |

Create a Collection programme:

```json
{
  "collectionProgrammeId": "annual-returns",
  "name": "Annual returns",
  "description": "Collect annual assurance evidence from partner organisations.",
  "defaults": {
    "retentionDays": 2555,
    "defaultPolicy": {
      "openingDate": "2026-09-01",
      "effectiveDeadline": "2026-10-01",
      "reminderOffsetDays": 7,
      "reviewStartMode": "onSubmission",
      "amendmentWindow": "never",
      "reopeningReasonPolicy": "required"
    }
  }
}
```

The optional `description` is the programme's **Purpose**, limited to 500 characters. Omit it on update to preserve the current purpose; send an empty string to clear it. Partial `defaults.messages` updates preserve unspecified templates. Programme defaults apply to future Collections; they do not rewrite existing Collection settings.

### Collections and form application

| Method and path | Capability |
| --- | --- |
| `GET /collection-programmes/{collectionProgrammeId}/collections/{environment}` | List collections in an environment. |
| `POST /collection-programmes/{collectionProgrammeId}/collections` | Create a Collection and its dedicated form from a saved source form version. |
| `GET /collection-programmes/{collectionProgrammeId}/collections/{environment}/{collectionId}` | Get one collection. |
| `PATCH /collection-programmes/{collectionProgrammeId}/collections/{environment}/{collectionId}` | Update a draft collection's name, timetable, policy and retention. |
| `POST /collection-programmes/{collectionProgrammeId}/collections/{environment}/{collectionId}/transition/{action}` | `open`, `close` or `reopen` a collection. Reopening requires a reason. |
| `GET /collection-programmes/{collectionProgrammeId}/collections/{environment}/{collectionId}/review-settings` | Read saved review settings and revision metadata; also requires form-read or form-write access. |
| `PATCH /collection-programmes/{collectionProgrammeId}/collections/{environment}/{collectionId}/review-settings` | Save supported review and answer-history settings on a draft/open Collection form; also requires form-write access. |
| `POST /collection-programmes/{collectionProgrammeId}/collections/{environment}/{collectionId}/apply-form` | Apply the latest saved collection form. |
| `GET /collection-programmes/{collectionProgrammeId}/collections/{environment}/{collectionId}/form-revisions` | List applied revision metadata. Form definitions are intentionally omitted. |

Create a collection:

```json
{
  "environment": "qa",
  "sourceFormId": "my-library-source-return",
  "sourceSsId": "saved-version-id",
  "name": "2026 annual return",
  "policy": {
    "openingDate": "2026-09-01",
    "effectiveDeadline": "2026-10-01"
  }
}
```

Applying a form in a remote environment prepares a deployment and returns an `approvalUrl`. Open that URL as an authorised user to confirm the change. This preserves the existing human deployment boundary. If the saved form is already active, the API returns `status: "upToDate"`.

Renaming through `PATCH .../{collectionId}` changes `Collection.name` only. It does not rename the managed Collection form (`Form.formName`), save a new form revision or rename an applied/deployed form. Clients should treat the Collection name and form name as separate values. The same behaviour applies to MCP `update_programme_collection`.

### Collection review settings

Ordinary form APIs still exclude managed Collection forms. Use the Collection-specific `review-settings` endpoint to read and change **Require review in Response Hub**, **Require claiming before review**, **Allow changes during review** and **Answer change history**. The read returns no answers, pages, actions or credentials. It requires Collection-read (or write) and form-read (or write) permissions; saving requires both Collection-write and form-write.

1.  `GET .../{collectionId}/review-settings` returns `formSsId`, `formVersion`, the saved review and answer-history settings, `appliedFormSsId`, `appliedReviewClaimRequired` and `hasUnappliedChanges`.
2.  `PATCH .../{collectionId}/review-settings` requires that `formSsId` as `expectedSsId` and at least one setting. Omitted settings are preserved; unrelated form properties are rejected. A stale revision or a closed/purged Collection returns `409`.
3.  Saving returns `status: "saved"` and the new settings/revision metadata. It does not apply the form or change the runtime requirement. Apply the saved form separately with `apply-form`, completing its normal deployment confirmation.

```json
{
  "expectedSsId": "saved-version-id-from-get",
  "responseHubReviewRequired": "true",
  "responseHubReviewClaimRequired": "false"
}
```

MCP uses `get_programme_review_settings` and `update_programme_review_settings` with the same inputs and results, then `apply_programme_form`. OAuth requires form-design and Collection data access in the same library. Writes respect approval requirements from both grants. These operations do not grant general editing access to managed forms or change ownership.

### Cohorts, assignments and roster imports

| Method and path | Capability |
| --- | --- |
| `GET .../{collectionId}/cohorts` | List cohorts. |
| `POST .../{collectionId}/cohorts` | Create a cohort with optional policy overrides. |
| `PATCH .../{collectionId}/cohorts/{cohortId}` | Update a non-default cohort before opening. |
| `GET .../{collectionId}/assignments` | List assignments with organisation, contact, response and review context. |
| `POST .../{collectionId}/assignments` | Create or update an assignment using a stable external organisation ID. |
| `POST .../{collectionId}/assignments/{assignmentId}/cancel` | Cancel an assignment while preserving response history. |
| `POST .../{collectionId}/roster` | Stage and validate a base64-encoded CSV roster. |
| `POST .../{collectionId}/roster/{rosterJobId}/apply` | Apply a staged roster with `mode` set to `merge` or `replace`. |

Upsert an assignment:

```json
{
  "externalOrganisationId": "ORG-1042",
  "organisationName": "Example authority",
  "userEmail": "returns@example.gov.uk",
  "userDisplayName": "Returns team",
  "cohortId": "optional-cohort-id",
  "dueAt": "2026-10-01T23:59:59Z"
}
```

Roster staging does not change respondents. It returns a preview containing creates, updates, unchanged assignments and omissions. Apply that preview separately after checking it.

### Responses, reviews, communications and audit

| Method and path | Capability |
| --- | --- |
| `GET .../{collectionId}/responses/{responseId}` | Get a response, immutable submitted answer versions, active review and comments. |
| `GET .../{collectionId}/responses` | Cursor-first list of response summaries. |
| `GET .../{collectionId}/responses/{responseId}/versions` | Cursor-first list of immutable response versions. |
| `GET .../{collectionId}/reviews` | List available and claimed review work. |
| `GET .../{collectionId}/reviews/{workItemId}` | Get one review and its submitted answer version. |
| `GET .../{collectionId}/reviews/{workItemId}/issues` | Cursor-first list of review comments. |
| `POST .../{collectionId}/reviews/{workItemId}/{action}` | `claim`, `accept`, `return`, `release`, `reassign` or `addIssue`. |
| `GET .../{collectionId}/communications` | List queued, sent, failed and cancelled respondent messages. |
| `GET .../{collectionId}/audit` | List append-only collection audit events. |

Review commands accept an optional `commandId` for safe retry. Returning, releasing and reassigning require a reason. `addIssue` accepts a target for a field, bulk-data source row or exact bulk-data cell.

Review and claiming are separate managed-form settings. `Form.responseHubReviewRequired` is a string boolean, default `"false"`, controlling reviews for new submissions. `Form.responseHubReviewClaimRequired` is a string boolean, default `"true"`. Collection read results expose the applied `reviewClaimRequired` boolean (missing means `true`). When it is `false`, `accept`, `return` and `addIssue` work on available reviews without a claim. Claims remain optional and reserve the review for that reviewer. Other reviewers' claims, held-review eligibility and normal access checks still apply. Changing the claiming requirement also affects existing available reviews after form application.

```json
{
  "commandId": "integration-command-2026-00042",
  "reason": "The totals do not reconcile with the uploaded schedule."
}
```

Submitted versions are immutable. The API returns the answer snapshot associated with that version, rather than regenerating it from a later form design.

### Bulk exports

Use an asynchronous export to download selected collection data in compressed parts, for example after a collection has finished. Export operations require Response Hub read access and do not change collection business data.

Each batch reads the data available when that batch is processed. An export does not represent a single point in time: changes during processing may be reflected in later batches, and additions/deletions may affect which records appear. Related records can therefore reflect different moments. Export after collection work has settled when a stable dataset is wanted. Stored response versions remain immutable.

The manifest declares `consistency: "read-as-processed"` and records `startedAt` and `completedAt`; it does not return `snapshotAt`. Batches use stable ordering so edits to names, review state or schedules do not move existing records between pages. Exports do not depend on MongoDB retaining a historical view for the job's duration.

| Method and path | Capability |
| --- | --- |
| `POST .../{collectionId}/exports` | Start or safely retry an export; returns `202`, `Location` and `Retry-After`. |
| `GET .../{collectionId}/exports` | Cursor-first list of export operation metadata. |
| `GET .../{collectionId}/exports/{exportId}` | Poll status and progress; supports `ETag` and `If-None-Match`. |
| `DELETE .../{collectionId}/exports/{exportId}` | Request cancellation. Read-only access can cancel only an export started by the same API key. |
| `GET .../{collectionId}/exports/{exportId}/manifest` | Get the manifest after successful completion. |
| `GET .../{collectionId}/exports/{exportId}/parts/{partId}` | Download one authenticated gzip-compressed result part. |

Start an export with a caller-chosen `requestId` and the normalized resources you need:

```json
{
  "requestId": "warehouse-annual-2026-08-08",
  "resourceTypes": ["cohorts", "assignments", "responses", "responseVersions", "reviewWorkItems", "reviewIssues"],
  "format": "ndjson",
  "compression": "gzip",
  "includeBulkArtifacts": false,
  "includeAnswerFiles": false
}
```

Retrying the same authorized request returns the original operation. Reusing `requestId` with a different selection returns `409 EXPORT_CONFLICT`. Resources are normalized into independent parts. Organisation and membership output contains only records reached through the collection's respondents. Form revision output contains metadata, never form definitions.

The manifest is published only after every part is complete. Each entry includes its record count, compressed and uncompressed byte sizes, SHA-256 checksum and authenticated download URL. Data-resource parts are NDJSON. `includeBulkArtifacts` and `includeAnswerFiles` are deprecated compatibility properties and do not create copied artifact parts. Response-version values contain allowlisted metadata and authorised Govform download links instead; uploaded files and bulk datasets are never placed inside an export. Internal storage references are never returned. Downloads include `ETag` and `Repr-Digest`; verify the exact compressed representation and manifest totals before committing an import.

Exports expire no later than 24 hours or collection retention, whichever is earlier. Retention and purge always win: expired or purging results return `410` and their artifacts are removed.

## Errors and safe retries

Errors use an HTTP status and JSON body:

```json
{
  "error": {
    "message": "Collection not found"
  }
}
```

Typical statuses are:

-   `400` for missing or invalid input;
-   `403` for an invalid key, IP restriction or missing permission;
-   `404` when a tenant- and collection-scoped record is not found;
-   `409` when the requested lifecycle change is not valid in the current state.

Cursor and export errors keep the same outer shape and add a stable `code`. Cursor expiry also includes `restartRequired`; a temporarily unavailable snapshot or not-yet-ready export may include `Retry-After`. Cursor and export operations return these additional fields. Other errors use `{ "error": { "message": "..." } }`.

Read operations are safe to retry. Review commands should reuse their `commandId`. Roster import is deliberately staged and applied in two calls. Collection IDs, response versions and audit history are never replaced by update requests.

## Download submitted evidence

Use the same Builder host and Collection programme read or write permission. These are authenticated byte streams, not Base64 JSON responses. Start from a submitted response version's returned download links, which identify the retained evidence.

All paths below follow /builder/libraries/{libraryId}/api/response-hub/collection-programmes/{collectionProgrammeId}/collections/{environment}/{collectionId}.

| Method and path | Capability |
| --- | --- |
| GET /responses/{responseId}/versions/{versionNumber}/files/{fieldId}/{fileIndex} | Stream one ordinary uploaded file from a submitted version. |
| GET /responses/{responseId}/versions/{versionNumber}/bulk/{fieldId}/{artifact} | Stream a retained Bulk Data artifact from a submitted version. |

versionNumber is a positive submitted-response version number; fileIndex is zero-based. For repeating fields, supply iteration as a zero-based query parameter. Bulk artifact values are canonical (confirmed CSV), source (untouched upload) or errors (retained validation-errors CSV). These Collection programme values differ from the ordinary submitted-data endpoint's current artifact selector.

Downloads remain subject to the response's retention and availability. They never fall back to a different submitted version. Invalid path selectors or unavailable evidence return 404. Responses include the content type and an attachment filename where available, and may include content length. Stream files in the client.

## API units and related guides

Collection programme list and ordinary change calls generally use 5 API units, single status or manifest reads use 1, and exports, roster operations and Bulk Data downloads use 25. Ordinary response-file download paths follow the 5-unit class. Consult the API-unit response headers for the charged class and follow [API units and limits](<https://govform.com/resources/documentation/article/33-govforms-api#api-units-and-limits>).

See the [endpoint index](<https://govform.com/resources/documentation/article/33-10-api-endpoint-reference>), [service discovery and schemas](<https://govform.com/resources/documentation/article/33-09-api-list-services-and-form-schemas>) and [ordinary submission file downloads](<https://govform.com/resources/documentation/article/33-07-api-list-download-uploaded-files>).

## Live review and answer change history

Managed-form review settings also accept `responseHubReviewAllowChanges` (string true/false, default false) and `responseHubAnswerChangeHistory` (`off`, `fromStart`, `afterSubmission`, default off). Supply `expectedSsId` from the settings read. Saving requires form-design write access as well as Collection programme access; apply the form separately. Both options apply to new response identities.

For live reviews, the review read includes `currentAnswers` (confirmed spreadsheets carry authenticated `sourceDownloadUrl` and `currentCsvDownloadUrl` links under `reviews/{workItemId}/bulk/{fieldId}/{source|canonical}`), `currentAnswersComplete`, `latestSavedAt`, `acceptanceFailure` and `expectedAnswers`. Pass that `expectedAnswers` object, containing `answersHash`, unchanged in an `accept` request. Refresh after a conflicting save. Acceptance validates current answers, closes editing and starts configured closure workflows. If the response needs a caseworker form, accept is refused until it has been submitted; during live review one submission before acceptance is enough. Acceptance retains separate accepted evidence without changing submitted-version numbers. `return` is unavailable in live mode.

Use review action `editIssue` with `issueId`, `expectedUpdatedAt` (the comment’s `updatedAt`, or `createdAt` if never edited), `respondentMessage` and optional `internalNote`. Existing claim rules apply. Additions and edits are audited. Public feedback appears on the next respondent request; no feedback email is sent.

Under the same Collection API base path, `GET /responses/{responseId}/changes` returns at most 25 newest-first entries. Omit `page` for the newest page and request `page-1` for older entries. `GET /responses/{responseId}/changes/{changeId}?page=N` reads one committed checkpoint. Follow its authenticated file links at `/responses/{responseId}/changes/{changeId}/files/{fileIndex}?page=N`. These reads use existing Collection programme read/write permissions and retention. Internal storage keys are not download links.

For live or accepted-review files, follow the authenticated links returned by the review read at `GET /reviews/{workItemId}/files/{fieldId}/{fileIndex}?iteration=N`. The optional zero-based `iteration` selects a repeated answer. Accepted-file links use retained evidence; initial submitted files keep their submitted-version URLs.

## Comment on the whole form or an individual field

On the review screen, use **Comment on the whole form** for the overall comment sent with **Return for changes**. It is required when returning a response, but not when accepting it. The rich editor opens in **Text** mode by default. Use its toolbar for formatted text; the comment is stored as Markdown and displayed with formatting to staff and the respondent.

For a particular answer, go to **Field comments**, expand **Comment on an individual field**, choose the **Answer**, write your comment and select **Add field comment**. Repeat for other fields. Field comments are plain text and appear beside the relevant answers and in the summary.

The whole-form return comment and field comments are shared with the respondent. Internal notes are visible only to staff. In standard review, the respondent sees the comments when you return the response. Ordinary Form responses send no automatic return email; Collections send their return email.

With **Allow changes during review**, staff can add or edit field comments and respondents see them on their next visit, without an email. Return for changes and its whole-form comment are unavailable in this mode. Ordinary live-review field comments also support replies. Standard reviews and Collection programmes have no replies.

Existing REST/MCP names remain unchanged for compatibility: `addIssue`, `editIssue`, `issueId`, `issues`, `list_programme_review_issues` and the `/issues` endpoint refer to comments. Use `reason` for the whole-form return comment in Markdown (up to 5,000 characters); `target` and `respondentMessage` identify a plain-text field, bulk-data row or cell comment. `internalNote` stays private. Each `respondentMessage` and `internalNote` can be up to 5,000 characters. These Collection tools do not operate ordinary Form responses; ordinary casework uses the browser.

## Ordinary response comment conversations

Ordinary Form responses with **Allow changes during review** support field comment history and staff/respondent replies in the browser. Standard reviews and Collection programmes have no replies. Public REST, MCP and Darcy have no ordinary-response reply/history operation, and Collection review endpoints cannot operate ordinary responses. See [field comment history and replies](<https://govform.com/resources/documentation/article/21-11-service-response-review#field-comment-history-and-replies>) for the workflow, statuses and permissions. Review and answer-history settings still use their existing authoring APIs and separate save/deploy or save/apply steps.
