Users
A user is a member of staff with a Trialflare account. Most integrations only need GET /users/me, to find the team and permissions behind their key; the rest of these endpoints administer other users' accounts and need the team permissions noted.
Endpoints
| Method | Path | Description |
|---|---|---|
GET |
/users/me |
The account behind the key. 5 requests per minute — call it once, not before every request. |
PUT |
/users/<userId> |
Update a user's details. Your own account, or users.manage / team.manage. 10 requests per minute. |
DELETE |
/users/<userId> |
Delete a user. Needs users.delete. 5 requests per minute. |
POST |
/users/<userId>/disable |
Disable an account. Body: {"reason": "..."} (required, logged). Needs users.manage. |
POST |
/users/<userId>/enable |
Re-enable a disabled account. Needs users.manage. |
DELETE |
/users/<userId>/mfa |
Reset a user's two-factor authentication: their authenticator app and passkeys are removed and they set up a new second factor on next sign-in. Needs users.manage. 5 requests per minute. |
PUT |
/users/<userId>/permissions/<scopeId> |
Replace a user's permissions at one scope. |
PUT |
/users/<userId>/roles/<roleId> |
Give a user a trial role. |
DELETE |
/users/<userId>/roles/<roleId> |
Take a role away. |
PUT |
/users/<userId>/teams/<teamId> |
Switch which of your teams is current. Your own account only. |
Your own account
GET /users/me returns:
{
"_id": "…",
"firstName": "Ada", "lastName": "Lovelace", "email": "ada@example.org",
"team": "<current team _id>",
"teams": ["<team _id>", "…"],
"permissions": {"<teamId>": ["users.create", "trials.create"], "<trialId>": ["trial.admin"], "…": []},
"timezone": "Europe/London",
"otpEnabled": true, "hasPasskey": false, "hasPassword": true,
"mcpEnabled": false,
"avatar": "avatar_1791383694.jpg", "avatarUrl": "https://…", "avatarSource": null
}
permissions is keyed by scope _id — a team, a trial, a site or a group — and lists the permission keys held there. It is the effective set, so permissions that come from a role are included.
Updating a user
PUT /users/<userId> accepts firstName, lastName (2–30 characters), email, timezone (an IANA name such as Europe/London) and mcpEnabled (whether the user may connect an AI assistant). A team.manage holder may also set incorrectLoginAttempts — usually to 0 or null, to clear a lockout.
avatar sets your profile image. Upload the image first — see Uploading files, with forType=user and your own user _id — then send the fileName you were given. It must be a PNG, JPEG, GIF or WebP image, and only you can set your own; someone who can edit your account (users.manage or team.manage) can remove it by sending null. Someone who signs in with Google or Microsoft and has no image of their own is given their photo from that account, refreshed each time they sign in that way; avatarSource is then "google" or "microsoft" (otherwise null). Setting or removing avatar stops that for good. Wherever a user is returned alongside something they did — an assignee, an author, a team or trial user list — it carries an avatarUrl: a link to the image that expires after an hour, or null if they have none.
Disabling is preferable to deleting when someone leaves: a disabled account keeps its permissions, roles and history and is signed out everywhere; a deleted one leaves only its email address in the event log.
Permissions
PUT /users/<userId>/permissions/<scopeId> takes {"permissions": ["trial.read", "trial.readParticipantStudyData"]} and replaces the user's permissions at that scope — the trial, site, group or team whose _id you give. Send the full list you want them to end up with; an empty list removes everything at that scope. You need the ability to change permissions at that scope yourself (trial.admin on the trial, or users.manage on the team). To give someone access they must be a member of the team that owns that scope; for a trial, its sites and groups, they can instead belong to another of your teams where you can list users (users.list or users.create). Otherwise the request is refused with 403. Taking access away has no such limit, so you can tidy up after someone who has left. The change is logged with what was granted and what was revoked. Add "notify": true to email the person when this gives them their first permission on a trial, as Add personnel in the app does; it does nothing for other scopes, for later changes, for yourself, or for someone still finishing their account set-up or outside the trial's team.
Only recognised keys can be added: anything else is refused with 400. A value that isn't a list of strings is refused too, with 422 on this route. A key the person already holds at that scope can stay in the list. PUT /users/<userId> also accepts permissions, an object keyed by scope _id: it follows the same rules, except that a badly formed value gets 400, and if any scope is refused, none of them change.
The available keys are listed in Roles and permissions (trial.*, site.*, group.*) and Team permissions.
Roles
Where a trial has roles defined, PUT /users/<userId>/roles/<roleId> and DELETE /users/<userId>/roles/<roleId> add and remove them; both return the user's full roles list. Either one needs the ability to change permissions on the role's trial and on every scope the role covers, and adding a role also follows the membership rule above. Roles are created and edited under the trial: see Trials.