Participant management
These are the endpoints staff use to run a trial's participants: registering them, keeping their details, putting them in groups and sites, messaging them, and everything that hangs off a participant record — notes, events, meetings, packs, incentives, food diaries. The endpoints the participant app itself calls are separate: see Participant app.
Every path here is under /trials/<trialId>/participants. A participant has two identifiers: the _id used in paths, and the human-readable id your study gave them (P-0042). See Identifiers.
Permissions are granular. In outline: trial.readParticipants (or site.readParticipants on their sites) to see participants; trial.registerParticipants to create them; trial.writeParticipants to edit details; trial.tagParticipants, trial.allocateParticipants, trial.archiveParticipants, trial.withdrawParticipants and trial.deleteParticipants for those actions; trial.readParticipantPersonalInformation to see names and contact details; trial.support to message them. trial.write and trial.admin imply most of these. The Roles and permissions article has the full list.
Endpoints
| Method | Path | Description |
|---|---|---|
GET |
/trials/<trialId>/participants |
List and filter participants, 80 to a page, or export the list as CSV. |
POST |
/trials/<trialId>/participants |
Register one participant, or many. |
PATCH |
/trials/<trialId>/participants |
Update groups, sites, archival or withdrawal for many at once. |
DELETE |
/trials/<trialId>/participants |
Delete many: {"participants": ["<_id>", …], "reason": "…"}. |
GET |
/trials/<trialId>/participants/<participantId> |
One participant. Viewing is logged. |
PUT |
/trials/<trialId>/participants/<participantId> |
Update a participant. |
DELETE |
/trials/<trialId>/participants/<participantId> |
Delete a participant and all their data. Body: {"reason": "…"} (required). |
GET |
…/<participantId>/stages |
The participant's stages with their status, as the app sees them. |
POST |
…/<participantId>/stages/<stageId>/tokens |
Make a link that opens one stage for the participant without signing in. |
POST DELETE |
…/<participantId>/stageOverrides |
Move when a stage becomes available for this participant. |
GET POST |
…/<participantId>/grants |
A one-time sign-in link for the participant. |
PUT DELETE |
…/<participantId>/anchors/<anchorId> |
Set or clear an anchor date. |
GET PUT |
…/<participantId>/messages |
Read the conversation with the participant; mark it read. |
POST |
…/<participantId>/messages |
Send the participant a message. |
POST |
/trials/<trialId>/participants/notifications |
Send a push notification to many. |
GET POST |
…/<participantId>/notes |
Staff notes on a participant. |
PUT DELETE |
…/<participantId>/notes/<noteId> |
Edit or delete a note. |
GET POST |
…/<participantId>/events |
Appointments and other calendar events. |
PUT DELETE |
…/<participantId>/events/<eventId> |
Edit or delete an event. |
GET POST |
…/<participantId>/meetings |
Video meetings. |
PUT DELETE |
…/<participantId>/meetings/<meetingId> |
Edit or cancel a meeting. |
GET |
…/<participantId>/packs |
Packs assigned to the participant; ?availableOnly=true for those that could be. |
PUT DELETE |
…/<participantId>/packs/<packId> |
Assign or unassign a pack. |
GET POST |
…/<participantId>/incentives |
Gift-card incentives issued, and issue one. |
GET POST |
…/<participantId>/foodDiaryEntries |
Food diary. |
PUT DELETE |
…/<participantId>/foodDiaryEntries/<entryId> |
Edit or delete an entry. |
GET POST |
…/<participantId>/insights |
AI insights about the participant — see Dashboards and insights. |
GET |
/trials/<trialId>/participantResources |
Resources shown to participants in the app. |
POST |
/trials/<trialId>/participantResources |
Add a resource. |
PUT DELETE |
/trials/<trialId>/participantResources/<resourceId> |
Edit or remove one. |
Responses, signatures and consent records for a participant are under Responses and exports and eConsent; group and site membership can also be changed from the group and site side.
Listing participants
GET /trials/<trialId>/participants returns:
{
"participants": [...],
"total": 240,
"nextPageAvailable": true,
"prevPageAvailable": false
}
| Parameter | Notes |
|---|---|
page |
Page number; 80 participants per page. |
term |
Match on participant ID. |
sort, sortOrder |
Field to sort by (createdAt by default; id, lastSeenAt, ratioComplete) and asc or desc. |
site |
A site _id. |
groupsInclude, groupsExclude |
Comma-separated group _ids the participant must be in, or must not be in. |
groupsIncludeNone, groupsExcludeNone |
true to include, or exclude, participants in no group. |
includeArchived |
true to include archived participants. |
scheduleBehind, scheduleOnSchedule |
true to keep only participants behind schedule, or on schedule. |
allocationAllocated, allocationUnallocated |
Allocation status. |
joinedJoined, joinedNotJoined |
Whether they have signed in to the app. |
withdrawnWithdrawn, withdrawnNotWithdrawn |
Withdrawal status. |
unread |
true for participants with unread messages. |
format |
csv returns the whole filtered list as a CSV string in {"csv": "…"}, ignoring paging. |
Each participant carries _id, id, createdAt, lastSeenAt, groups, sites, isUnallocated, isArchived, isWithdrawn, progress (ratioComplete, stagesBehindSchedule), whether a password is set (passwordSet) and which channels can reach them (pushAvailable, emailAvailable, smsAvailable, whatsappAvailable). name, dob, email, phone and whatsapp are included only for viewers with the personal-information permission.
Registering participants
POST /trials/<trialId>/participants with a single participant:
| Field | Type | Notes |
|---|---|---|
participantId |
string | Required. 1–50 characters. Unique within the trial (case-insensitive). |
password |
string | Up to 200 characters. The password they sign in to the app with. Omit it for a participant who will join by link or consent flow. |
groups |
list of group _ids |
|
group |
group _id |
The same, for one group. |
isUnallocated |
boolean | Register as unallocated. |
Returns the new participant. To register many at once send {"participants": [{...}, {...}]} with the same fields per entry; the response is {"participants": [...]}. The whole request is checked before anything is written, so a duplicate ID anywhere in it means nothing is created. Registration counts against your team's participant limit, and a site-restricted registrar's participants are put in their sites automatically. A locked database refuses registration.
Updating a participant
PUT /trials/<trialId>/participants/<participantId>:
| Field | Type | Notes |
|---|---|---|
id |
string | Change the participant ID. |
password |
string | Set a new password, or "" to remove it. |
name, email, phone, whatsapp |
string | Contact details. null clears. |
dob |
date | YYYY-MM-DD. |
timezone |
string | IANA name. |
isUnallocated, allocatedReason |
boolean, string | Allocation. |
isArchived |
boolean | Archived participants are hidden and cannot submit. |
isWithdrawn |
boolean | Withdrawn participants cannot submit; staff still can on their behalf. |
Each field needs its own permission, as listed above; a request touching a field you cannot change is refused as a whole.
Updating participants in bulk
PATCH /trials/<trialId>/participants takes {"updates": [{"participant": "<_id>", "groups": [...], "sites": [...], "isArchived": true, "isWithdrawn": false}, …]}. Each entry names a participant and any of the four fields; groups and sites replace the participant's membership. Returns {"updated": [{"participant": "<_id>"}, …]}.
Stages, links and sign-in
GET …/<participantId>/stages returns the participant's timeline as the app shows it — every occurrence of every stage with enabled, completed, enableAt, expiresAt — plus their ratioComplete. Occurrences of a recurring stage have _ids of the form <stageId>::<n>; use that form as the stageId in the response endpoints.
POST …/<participantId>/stages/<stageId>/tokens with {"recurrence": 0, "expiresInDays": 30} returns a token and a url that opens that stage for the participant in the browser without a password — for sending a questionnaire link by your own channel.
POST …/<participantId>/grants with {"expiresInDays": 90} makes a one-time sign-in link (url, expiresAt) for the participant app; GET returns the current unexpired one, or nulls. Needs trial.write.
POST …/<participantId>/stageOverrides with {"stage": "<stageId>", "stageRecurrence": 0, "enableAt": "<datetime>", "applyToAllRecurrences": false} moves when a stage opens for this participant alone; DELETE with the same as query parameters removes the override. Needs trial.rescheduleParticipants or trial.write.
Anchors
PUT …/<participantId>/anchors/<anchorId> with {"datetime": "<ISO datetime>"} sets an anchor date for the participant, which schedules anchored stages and messages; DELETE clears it.
Messages and notifications
GET …/<participantId>/messages returns {"participant": {...}, "messages": [...], "automaticMessages": [...]} — the two-way conversation, and the automatic messages the participant has received. PUT on the same path marks it read.
POST …/<participantId>/messages:
| Field | Type | Notes |
|---|---|---|
message |
string | Required. 1–500 characters. |
sendVia |
list | Any of push, email, sms, whatsapp. The message always appears in the app; these send it out as well, where the participant has that channel and the trial's wallet covers it. |
attachments |
list of strings | Stored file names from an upload with forType=participant. |
Messages are delivered immediately and cannot be recalled. Needs trial.support.
POST /trials/<trialId>/participants/notifications with {"message": "…", "participants": [...], "groups": [...]} sends a push notification (up to 100 characters) to the listed participants and every member of the listed groups. Needs trial.admin.
Notes
POST …/<participantId>/notes with {"note": "…", "inRelationToDate": "<date or null>"} (up to 2000 characters) records a staff note; GET lists them. Writing needs trial.writeParticipantNotes or trial.write; reading needs trial.readParticipantNotes. A note can be edited or deleted by its author, or by a trial admin.
Events
An event is an appointment or other dated item on the participant's calendar in the app.
| Field | Type | Notes |
|---|---|---|
title |
string | 1–500 characters. |
description |
string | |
startsAt |
datetime | |
personnel |
list of user _ids |
Staff attending. |
Creating, editing and deleting events needs trial.write. A trial-wide view of events and meetings is GET /trials/<trialId>/schedule — see Trials.
Video meetings
Video meetings need the video-calls feature on your team. POST …/<participantId>/meetings takes name, startAt (datetime) and personnel (user _ids) and books a meeting room; the participant sees it in the app. See Video meetings.
Packs
GET …/<participantId>/packs lists the packs assigned to the participant; ?availableOnly=true lists the unassigned packs of the right type and site that could be given to them. PUT …/<participantId>/packs/<packId> assigns one; DELETE takes it back. Assigning needs trial.assignPacks or site.assignPacks on the pack's site.
Incentives
Where the incentives feature is on, GET /trials/<trialId>/incentives/brands lists the gift-card brands available, and POST …/<participantId>/incentives with {"brand": "<brand code>", "brandName": "…", "amount": 10, "currency": "GBP"} issues one to the participant, paid from the trial's wallet. GET lists what has been issued. Issuing needs trial.admin.
Food diary
Where food diaries are enabled on the trial, GET …/<participantId>/foodDiaryEntries?date=YYYY-MM-DD (or from and to) lists a participant's entries as {"foodDiaryEntries": [...]}. GET /trials/<trialId>/food-search?q=<term>&limit=25&offset=0 searches the food database (30 requests per minute).
POST …/<participantId>/foodDiaryEntries:
| Field | Type | Notes |
|---|---|---|
date |
date | Required. |
meal |
breakfast, lunch, dinner, snack, other |
Required. |
foodId |
integer | Required. From the search. |
portion |
object | Required. {"size": <amount>, "unit": "grams", "description": "…"}. |
quantity |
number | How many portions. Default 1. |
loggedAt |
datetime | When it was eaten. |
Participant resources
Resources are the documents, links and videos shown to participants under the app's resources tab. POST /trials/<trialId>/participantResources takes a type of file (with storedName, originalName, mimeType, size from an upload with forType=trial), link (linkUrl), youtube (youtubeUrl), video or text (html), plus a title and description. PUT changes title, description, index (position) or the content field; DELETE removes it. Needs trial.write.