Appearance
Subject Forms V2 APIs
These APIs are only applicable for AireForms deployments with associated persistence.
Query form instances for subjects
Route: POST or QUERY /api/v2.0/subjectforms (Requires authentication) Permission: ListPatientFormsDescription: Combinable, sorted, cursor-paged querying of a subject's form instances. Every filter can be combined, results can be sorted, and responses are always paged.
Both POST and the QUERY verb are accepted; the request body is identical.
subjectIds or instanceGuids is required. Every other filter narrows within that set — this API cannot be used to list every instance of a form across an organisation. Use the Extract V2 APIs for that.
Request
json
{
"subjectIds": ["a4847fd9-7da1-49da-b5f4-be278dba0730"],
"formKeys": [{ "key": "end-of-life-care", "versions": [1, 2] }],
"formMetadata": [{ "key": "speciality", "value": "cardiology" }],
"selection": "latestCompleted",
"correlationId": "corr-123",
"includeWithdrawn": false,
"sort": { "field": "created", "direction": "desc" },
"pageSize": 100,
"cursor": null,
"direction": "forward"
}All fields other than subjectIds/instanceGuids are optional, and all supplied filters are combined with AND.
| Field | Description |
|---|---|
subjectIds | Subject identifiers to search within. Maximum 100. |
instanceGuids | Instance identifiers to search within. Maximum 100. |
formKeys | Form keys to match, each with optional versions. An empty or omitted versions matches every version of that key. |
formMetadata | Form metadata key/value pairs. Matched exactly and case-insensitively, and AND'd across entries. |
selection | One of latest, completed, latestCompleted. Omit for all instances. These are mutually exclusive by construction. latest is the most recently created instance per subject per form key, across versions. latestCompleted is the latest instance that is complete, not the latest instance if it happens to be complete. latest/latestCompleted is evaluated after every other filter (subjectIds/instanceGuids, formKeys, correlationId, includeWithdrawn, and, for latestCompleted, the completed check) has narrowed the set — it picks the latest instance among those already matching your other filters, not the latest instance overall with filters applied afterwards. For example, selection: "latest" combined with a formKeys filter returns the latest instance of that specific form key, not the latest instance of any form key. |
correlationId | Matches the instance's correlation id. Where no correlation id was supplied at creation it defaults to the instance guid, and this filter matches that fallback too. |
includeWithdrawn | Defaults to false. |
sort | field is created (default) or lastModified; direction is desc (default) or asc. Instances sharing a timestamp are ordered deterministically. |
pageSize | Defaults to 100, maximum 200. |
cursor | An endCursor/startCursor from a previous response. |
direction | forward (default) or backward, relative to the supplied cursor. backward requires a cursor to be supplied. |
Response
json
{
"items": [
{
"subjectIdentifier": "a4847fd9-7da1-49da-b5f4-be278dba0730",
"instanceGuid": "816eb663-cc1f-478d-8efe-5c068ec434f2",
"title": "End of Life Care Plan",
"createDateTimeUtc": "2026-01-09T16:41:34.813",
"createdByUserIdentifier": "user-123",
"createdByUserIdentifierType": "user",
"lastModifiedDateTimeUtc": "2026-01-09T16:41:34.813",
"lastModifiedByUserIdentifier": "user-123",
"lastModifiedByUserIdentifierType": "user",
"status": "Draft",
"canEdit": true,
"isAmendable": true,
"key": "end-of-life-care",
"version": 1,
"correlationId": "corr-123"
}
],
"pageInfo": {
"startCursor": "MXwzZjJhYmMxMnwyMDI2...",
"endCursor": "MXwzZjJhYmMxMnwyMDI2...",
"hasNextPage": true,
"hasPreviousPage": false
},
"totalCount": 137
}Paging
Paging always applies. Omitting pageSize uses the default of 100 rather than returning everything, so hasNextPage and endCursor tell you whether more results exist.
To walk forward, send the previous response's endCursor as cursor with direction: "forward"; to walk back, send its startCursor with direction: "backward".
Cursors are opaque and encode the filters and sort they were produced under. Changing any filter or the sort while reusing a cursor is rejected with a 400 — request a fresh first page instead.
totalCount counts matching instances in the database. On the rare occasions where an instance's form definition cannot be found, that instance is counted but omitted from items, so totalCount can exceed the number of rows returned.
Error handling and standard HTTP response codes
HTTP Response Codes are used per standard REST API design. The codes returned by this API are listed below.
| Code | Description |
|---|---|
200 | Success - The query succeeded. An empty items array with a totalCount of 0 is a success, not an error. |
400 | Bad Request - Neither subjectIds nor instanceGuids was supplied; a limit was exceeded; direction is backward without a cursor; or a cursor does not match the supplied filters. |
400 | Bad Request - Error code SubjectFormsQueryTooBroad. The filters match more instances than the server will consider. Supply fewer subject identifiers, or narrow the filters. |
403 | Forbidden - The user does not have the ListPatientForms permission, or is not authenticated. |
500 | Internal Server Error - An error has occurred on the server. |
503 | Service Unavailable - The server might be re-starting, or a proxy server cannot contact the service. |
