Trials

A trial (or study) holds everything else: its data types, stages, participants, sites, groups, files and responses. Trials belong to a team; creating one needs the team permission trials.create, and everything after that is governed by the trial permissions you hold on it — trial.read to see its configuration, trial.write to change it, trial.admin (or the team-wide trials.manage) to administer it.

Endpoints

Method Path Description
GET /teams/<teamId>/trials The trials in your team that you can access. Add ?includeArchived=true to include archived ones. 10 requests per minute.
POST /teams/<teamId>/trials Create a trial. 5 requests per minute.
GET /trials/<trialId> One trial's configuration and counts.
PUT /trials/<trialId> Update a trial's details and settings.
DELETE /trials/<trialId> Delete a trial and everything in it. Needs trial.admin, a reason and a fresh confirmation of identity; refused while the database is locked. See Locking.
GET /trials/<trialId>/licence The trial's licence and the changes open to it.
PUT /trials/<trialId>/licence Upgrade a sandbox to a licence. Needs trials.manage on the team.
GET /teams/<teamId>/licences Your team's sandbox usage and licence allocations. Which trials used each licence is included for those with team.manage.
GET /trials/<trialId>/overview The overview's figures and charts; ?site= or ?group= for one site or group. See Overview and recruitment.
PUT /trials/<trialId>/recruitmentTargets Set the trial's, sites' and groups' recruitment targets.
GET /trials/<trialId>/users The trial's personnel: every user with permissions or a role on it.
POST /trials/<trialId>/personnel/invitations Invite someone to the trial by email. 10 requests per minute.
GET /trials/<trialId>/roles The roles defined on the trial.
POST /trials/<trialId>/roles Create a role.
GET PUT DELETE /roles/<roleId> Read, update or delete a role.
GET /trials/<trialId>/logs The trial event log.
GET /trials/<trialId>/logs/communication Messages sent to participants by SMS, WhatsApp and email, with delivery status. Needs trial.admin.
GET /trials/<trialId>/configuration-versions Configuration snapshots.
POST /trials/<trialId>/configuration-versions Take a snapshot now.
GET /trials/<trialId>/configuration-versions/<number> One snapshot, optionally compared with another.
POST /trials/<trialId>/study-record Build the study record archive as a job.
GET /trials/<trialId>/locked-datasets The study records taken at each database lock.
GET /trials/<trialId>/schedule Upcoming meetings and events across participants.
GET /trials/<trialId>/wallet/balance The wallet balance and transactions.

Listing trials

Each trial in GET /teams/<teamId>/trials is a summary — _id, name, createdAt, isArchived — with participantCount, dataTypeCount, stageCount and responseCount, and hasUnreadMessages: true where participants have written in. Use GET /trials/<trialId> for the full configuration.

Creating and updating a trial

POST /teams/<teamId>/trials creates a trial from its name, description and restrictRegistration (which defaults to true), gives you trial.admin on it, and returns it. Everything else — including the code participants will join with — is set afterwards with PUT /trials/<trialId>, which takes:

Field Type Notes
name string 2–500 characters.
code string 2–100 characters. The code participants enter to join. Unique across Trialflare; stored lower-case.
scientificName, studyAcronym, sponsor, description, conditions string Descriptive details.
sponsorLogo string Stored file name of a logo, from an upload.
eudractNumber, ctisNumber, clinicalTrialsGovNumber, protocolNumber string Registry identifiers.
approvalNeeded boolean Whether the trial tracks ethics/regulatory approvals.
approvals list Each with id, type, status, submissionDate, approvalDate, reference, address and contact fields.
password string A trial-wide password participants must enter in addition to their own. null to remove.
defaultNotificationTitle string Up to 50 characters. The title on push notifications.
restrictRegistration boolean Only participants registered by staff can join.
recruitmentBasis "automatic", "added", "joined", "consented" or "manual" What counts as recruited on the overview and in the participants' recruited filter. automatic (the default) is joined where anyone has, otherwise added; manual counts only participants marked as recruited, who count from that date whatever this is.
enableLookback boolean Show participants their previous answer to look-back fields.
signOffNoteRequired boolean Require a note when staff edit a response.
eConsentEnabled, eConsentClosed boolean Whether eConsent is on, and whether it is closed to new signers.
responseSigningEnabled, signaturesRequiredForLock, signatureMeanings boolean, boolean, list of strings Casebook signature settings.
fitbitEnabled, garminEnabled, dexcomEnabled boolean Wearable and device integrations.
garminGroups list of group _ids Restrict Garmin to these groups; null for the whole trial.
garminConnectionReminders boolean Remind participants who have not connected, and (daily, for up to two weeks) those whose Garmin has not synced for two days.
foodDiariesEnabled, foodDiaryDataSource boolean, "myfood24" Food diaries.
smartAutomaticQueriesEnabled, smartAutomaticQueryAssignees, smartAutomaticQueryInstructions boolean, list of user _ids, string AI-raised queries.
participantInsightsEnabled, trialInsightsEnabled boolean AI insights.
queryTasksEnabled, siteMonitoringTasksEnabled boolean Add a task to the board when a query (with assignees) or a site monitoring issue is raised. Off unless switched on.
isArchived boolean Archive or restore the trial.

Two extra fields on creation: copyFrom (a trial _id) creates a copy of an existing trial's configuration instead of a blank one, and ?creationType=demo on the query string builds the Demo Study, ignoring the body.

Licences

Every trial is a sandbox, academic, commercial or general trial, and each trial in GET /teams/<teamId>/trials carries its licence.type. A new trial is a sandbox unless you pass licenceType on creation, which needs trials.manage on the team; licenceAllocation (an allocation _id from GET /teams/<teamId>/licences) chooses which of your allocations it uses, and without it the one ending soonest is used. A sandbox counts towards your team's sandbox limit; any other trial uses a licence from an allocation instead, which is not returned if the trial is deleted. Copies, the Demo Study and built trials are always sandboxes.

GET /trials/<trialId> includes licence (its type) and enabledFeatures — your team's features plus any the trial's licence adds.

PUT /trials/<trialId>/licence upgrades a sandbox: it takes type (academic, commercial or general) and an optional allocation. Each allocation has its type and may have a name, which is what the web app shows. A trial that already has a licence can't be changed through the API; contact us. GET /trials/<trialId>/licence returns the current type, its allocation, participantCap and participantCount, the features the trial has, canUpgrade, and under options your allocations of each type that are valid today — those with seatsLeft first, then any that are used up, which can't be chosen. GET /teams/<teamId>/licences returns the same options for a new trial.

The trial builder — ?creationType=build with a prompt and uploaded files — is described in Building a trial with Scorch; it returns a job whose result is the new trial.

Locking

isLocked (with a lockJustification, 10–200 characters) locks the trial's configuration; isDatabaseLocked (with databaseLockJustification) locks its data. Both need a fresh confirmation of identity at the moment of locking or unlocking, which an API key cannot give — see Authenticating requests. Every other field can be changed through the API as normal, subject to the locks themselves: a locked trial refuses configuration changes, and a locked database refuses new or edited responses and participants.

Deleting a trial is held to the same standard as locking it. DELETE /trials/<trialId> takes {"reason": "..."} (at least four characters), which is recorded in the trial and team logs, needs the same fresh confirmation of identity (so an API key cannot delete a trial), and is refused while the trial's database is locked: unlock it first.

Overview and recruitment

GET /trials/<trialId>/overview returns what the overview page charts, for the whole trial (needs trial.read) or, with ?site=<_id> or ?group=<_id>, for one site or group (open to whoever can see its participants). Figures are only ever read from a cache, recounted several times a day and shortly after a change to what counts as recruited or a participant being marked; computedAt says when. Where there is no cache yet the response is {"pending": true} while the figures are counted in the background: ask again in a few seconds. It carries start and end (UTC days), series (one count per day from start to end for registered, joined, withdrawn, consents, responses and recruited), totals for each (total, last7Days, previous7Days, last30Days), screening (per screening questionnaire, with passed, unsuccessful and pending), consent.byDocument, progress and daily snapshots. recruitment is null unless the scope has a target or the trial has chosen a recruitmentBasis; otherwise it gives target, basis, current, remaining, perWeek, reachedOn and estimatedDate (at the pace of the last four weeks).

Recruitment targets are read on the documents they belong to: recruitmentTarget on the trial (GET /trials/<trialId>), on each site (the trial's sites list) and on each group (its groups list), absent where there is none. PUT /trials/<trialId>/recruitmentTargets sets any of them at once: {"trial": 250, "sites": {"<_id>": 120}, "groups": {"<_id>": null}}, each a whole number up to 1,000,000, with null or 0 removing a target. It returns the targets as now set, in the same shape. Changing targets needs trial.write (or trial.admin) and is refused while the trial is locked; if any site or group isn't the trial's, nothing is changed. Each change is logged as updateRecruitmentTargets.

Personnel

GET /trials/<trialId>/users lists everyone who can work on the trial — users with permissions on it, users holding one of its roles, and team members with trials.manage — as {"users": [...]}, each with their permissions on the trial and memberOfSites, the ids of the trial's sites they are a member of (site.access, held directly or through a role). Needs trial.read or trial.readUsers.

Without those, site.readUsers on one or more of the trial's sites lists just the members of those sites, each with only their permissions at those sites and memberOfSites limited to them.

POST /trials/<trialId>/personnel/invitations invites someone to the trial by email: {"email": "sam@example.org", "roles": ["<roleId>"]}, where roles (optional) are the _ids of some of the trial's roles. It returns {"outcome": ..., "email": ...}. outcome is added for someone already in your team, who is given trial.access and the roles straight away; invitedNewAccount for someone new, who is given them on a new account in your team and emailed a link to set it up; or invitedExistingAccount for someone with an account in another team, who is emailed an invitation to join yours and is given them when they accept. For added and invitedNewAccount, user is the person as the personnel list shows them. Needs trial administrator access and users.invite or users.create at team level. Someone already on the trial (holding permissions on it, or one of its roles), a disabled account, or a role from another trial is refused with a 400. Each invitation is logged on the trial as invitePersonnel, and the grants are logged like any other change to someone's access.

To add someone, give them permissions or a role with the Users endpoints. To remove them, replace their permissions on the trial with an empty list and take away any roles.

Roles

A role bundles trial, site and group permissions under a name so they can be given to a user in one step. Creating, editing and deleting roles needs trial.admin.

Field Type Notes
name string 2–100 characters.
description string Up to 2000 characters.
permissions object Keyed by scope _id (the trial, a site or a group), each a list of permission keys: {"<trialId>": ["trial.read"], "<siteId>": ["site.readParticipants"]}. Unrecognised keys are refused with 400, unless the role already holds them at that scope; a value that isn't a list of strings gets 422.

Assign a role with PUT /users/<userId>/roles/<roleId>.

The trial event log

GET /trials/<trialId>/logs returns the audit trail shown on the trial's Settings → Audit → Logs tab, newest first, 30 to a page. Needs trial.readLogs (or trial.admin).

Parameter Default Notes
page 1
userType user user for staff actions, participant for participants' own actions.
type — One event type, e.g. createParticipant, exportResponse. The types are those offered by the filter in the web app.
format json csv returns every matching entry as a CSV string in {"csv": "..."}; limit caps the rows.

Entries carry createdAt, userEmail, type, event, client, the participantId acted on where there was one, and apiToken where the action came through an API key. Participants' personal information within events is hidden from viewers without the permission to see it.

Configuration versions

A snapshot of the trial's configuration — data types, stages, consent forms and settings — is taken automatically at each lock, and on demand with POST /trials/<trialId>/configuration-versions (body: {"note": "..."}, optional; needs trial.admin). If nothing has changed since the last snapshot, that one is returned with unchanged: true rather than a duplicate being made.

GET /trials/<trialId>/configuration-versions lists them as {"versions": [...]} — number, createdAt, who took it, the trigger (lock, databaseLock or manual) and the note — and GET /trials/<trialId>/configuration-versions/<number> returns one with its full snapshot. Add ?against=<number> to also get the changes between the two. Reading versions needs the same permission as the log.

Study record and locked datasets

POST /trials/<trialId>/study-record builds a zip of the trial's complete record — configuration, responses, consents, files, log — as a job. When done, the job's result has a request download URL (valid for five days), a fileName, and counts of what was included. Needs trial.admin and the export permission.

GET /trials/<trialId>/locked-datasets lists the study records taken automatically at each database lock, newest first, each with a fresh one-hour request URL.

Schedule

GET /trials/<trialId>/schedule?from=<datetime>&to=<datetime>&type=<meeting|event> lists the meetings and events booked for the trial's participants in a window, as {"schedule": [...]}. The window defaults to the coming seven days and may be at most 92. 30 requests per minute.

Wallet

GET /trials/<trialId>/wallet/balance?page=1&perPage=30&category= returns the trial's wallet balance and a page of its transactions. Needs trial.manageWallet or trial.admin. Topping up goes through a checkout page and is done in the web app.