Samply exposes two API surfaces: a REST API for programmatic study management (the researcher API), and two integration hooks used by survey tools to signal completion and trigger ad-hoc notifications. Both run on the same host as the dashboard.
Base URL
The researcher REST API is mounted at /webapi/v1. All endpoints below are relative to that prefix. The completion and notify endpoints are mounted directly at the root and are documented separately.
Authentication
All researcher API endpoints (except the token endpoint itself) require a JWT passed in the x-auth-token request header. Obtain a token by posting your researcher credentials:
Request body
| Field | Type | Description |
|---|---|---|
email | string | Researcher account email. |
password | string | Researcher account password. |
Response
| Field | Description |
|---|---|
token | JWT valid for 14 days. Pass it as x-auth-token on subsequent requests. |
Rate limits
All API paths are subject to rate limiting. Requests that exceed a limit receive a 429 Too Many Requests response. The three tiers are:
| Limit | Paths |
|---|---|
| 20 requests / 15 min | /webapi/v1/auth, login, account-creation, and password-reset endpoints |
| 30 requests / 1 min | /api/notify |
| 100 requests / 15 min | All other /api/* and /webapi/* paths |
The active study
Most researcher API endpoints operate on the active study — a single study selected on the researcher account. Before calling participant, notification, or job endpoints, select the study you want to work with:
POST /webapi/v1/auth/select/study expects { "id": "<study_id>" } in the request body. The selection is stored on your researcher account and persists across requests until changed.
PATCH body (update study)
Only the following fields are accepted; all others are ignored:
name, description, currentlyActive, public, welcomeMessage, codeMessage, groupMessage, messageAfterJoin, completionMessage, geofencingInstruction, settings
Participants
These endpoints manage participants in the active study. Participants created via the API are initially deactivated — they receive a JWT invitation token that activates them when they open the Samply Research app.
POST body (create participant)
| Field | Required | Description |
|---|---|---|
name | yes | Display name — not shown to other participants. |
email | yes | Email used to create the Samply account. |
code | no | Participant code stored as username and available via %PARTICIPANT_CODE%. |
expiresIn | no | How long the invitation JWT remains valid (e.g. "7d"). Maximum 30 days — larger values are silently capped. |
information | no | Freeform JSON object for arbitrary participant metadata. |
Response (create participant)
| Field | Description |
|---|---|
samplyid | The auto-generated Samply ID for the new participant. |
token | JWT invitation token. Send this to the participant; the app uses it to activate their account. |
PATCH body (update participant)
| Field | Description |
|---|---|
username | Participant code / display name. |
deactivated | Boolean — set to true to stop notifications for this participant. |
group | Group assignment string. |
All other fields sent in the body are ignored.
Schedules (notifications)
The notifications endpoints manage schedule definitions — the rules that expand into queue rows. Creating a schedule via the API triggers the same queue expansion as submitting the dashboard form.
The POST body mirrors the schedule form fields. The routing key is the combination of schedule (one-time or repeat) and target (fixed-times, fixed-intervals, or user-specific), which maps to the same internal handlers used by the dashboard form.
PATCH body (update schedule)
Only the following fields are accepted; all others are ignored:
title, message, url, schedule, target, randomize, startDate, endDate, startTime, endTime, interval, intervalMax, timezone, expireIn, reminders, userid, groupid
Queue (jobs)
The jobs endpoints expose individual queue rows — the expanded sends generated from schedule definitions.
Completion callback
These endpoints are called by survey tools to register a completion event. On success, Samply marks the result as completed and cancels all pending reminders for that send. No authentication is required — the message ID serves as the shared secret.
:study- The study URL slug shown in the dashboard address bar.
:messageid- The message ID from the
%MESSAGE_ID%placeholder, passed through your survey URL to the end-of-survey redirect or webhook. See Reminders for the full setup walkthrough.
The POST endpoint returns 200 on success and 400 if no matching result record is found for the given message ID.
Notify hook
The notify hook sends an immediate ad-hoc push notification to participants in a study — without creating a schedule or queue row. Intended for event-triggered notifications from external systems (a REDCap alert, a lab system event, etc.). Authentication uses a per-study notify token rather than the researcher JWT.
Request body
| Field | Required | Description |
|---|---|---|
token | yes | Study notify token. Regenerate it from Edit study → Notify token. |
projectID | yes | The study MongoDB ID. |
title | yes | Notification title. |
message | yes | Notification body text. |
url | no | Survey URL. Supports the same %TOKEN% placeholders as scheduled notifications. |
participantID | no | Send to one specific participant (Samply ID). Omit to send to all. |
groupID | no | Send to all members of a group except the triggering participant. Typically used when one participant's action should notify their group. |
expireIn | no | Link expiry in milliseconds from send time. |
If both groupID and participantID are provided, Samply sends to all group members except the named participant. If only participantID is given, only that participant is notified. If neither is provided, all study participants receive the notification.
Error responses
| Status | Meaning |
|---|---|
200 | Success. |
400 | Bad request — missing or invalid fields, or no active study set on the account. |
401 | Missing or expired x-auth-token header. |
429 | Rate limit exceeded. Back off and retry after a short delay. |
500 | Internal server error. The response body contains the fixed string "Internal server error"; detailed diagnostics are logged server-side only. |