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>"}, …]}.

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.