v1.0.0

MeasureHub API

Run MeasureHub studies inside your own platform: pull rendering-ready instrument content, submit participant responses, and read scored results. Everything the API returns respects the same licence and disclosure rules as the web app.

openapi.json ยท Create an API key

Authentication

Create a personal key in Account settings and send it as a bearer token. Keys are hashed at rest, can be revoked at any time, and carry a read and/or write scope.

curl https://YOUR-DOMAIN/api/public/v1/studies \
  -H "Authorization: Bearer mh_live_xxx"

Rate limits: 120 requests per minute and 10,000 per day per key. Exceeding a limit returns 429 rate_limited.

Endpoints
GET
/api/public/v1/studiesList your studies
read
POST
/api/public/v1/studiesCreate a study under an approved licence
write
GET
/api/public/v1/studies/{id}Retrieve one study
read
PATCH
/api/public/v1/studies/{id}Update a study or publish it
write
GET
/api/public/v1/studies/{id}/contentModules, questions, instrument items and translations, ready to render
read
GET
/api/public/v1/studies/{id}/sessionsList participant sessions
read
POST
/api/public/v1/studies/{id}/sessionsStart a session, returns a session token
write
GET
/api/public/v1/studies/{id}/resultsresponses | scores | sessions | paradata, as JSON or CSV
read
POST
/api/public/v1/sessions/{id}/responsesSubmit responses
write
POST
/api/public/v1/sessions/{id}/finishFinish the session and score it
write
GET
/api/public/v1/instrumentsBrowse the public instrument catalogue
read
GET
/api/public/v1/licensesList your instrument licences
read
POST
/api/public/v1/licensesRequest a licence
write
Delivering a study in your own app
# 1. Fetch rendering-ready content
GET  /api/public/v1/studies/{studyId}/content

# 2. Start a session for a participant
POST /api/public/v1/studies/{studyId}/sessions
  -> { "sessionId": "...", "token": "..." }

# 3. Submit answers as the participant progresses
POST /api/public/v1/sessions/{sessionId}/responses
  { "token": "...", "responses": [{ "variableCode": "PHQ1", "valueNum": 2 }] }

# 4. Finish and score
POST /api/public/v1/sessions/{sessionId}/finish
  { "token": "..." }

# 5. Pull data any time
GET  /api/public/v1/studies/{studyId}/results?dataset=scores&format=csv
Webhooks

Register endpoints in Account settings. Outgoing events are posted as JSON to your URL; incoming endpoints give you a private URL you can push data to.

Payload

POST https://your-app.example/hooks/measurehub
x-measurehub-event: session.completed
x-measurehub-delivery: 6b1f...
x-measurehub-signature: t=1767225600,v1=9f86d0...

{
  "id": "evt_...",
  "type": "session.completed",
  "created_at": "2026-08-05T10:00:00.000Z",
  "api_version": "v1",
  "data": { "sessionId": "...", "studyId": "...", "scores": [] }
}

Verifying the signature

import crypto from "node:crypto";

const [t, v1] = header.split(",").map((p) => p.split("=")[1]);
const expected = crypto
  .createHmac("sha256", process.env.MEASUREHUB_WEBHOOK_SECRET)
  .update(`${t}.${rawBody}`)
  .digest("hex");

const ok = crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));

The signed string is {timestamp}.{raw body}. Reject anything older than five minutes. Deliveries are retried up to six times (1m, 5m, 30m, 2h, 6h) and an endpoint pauses automatically after 20 consecutive failures. Respond with any 2xx within 10 seconds.

Events

session.started A participant opened a study and a session was created.

session.completed A participant finished a study; scores are included.

session.abandoned A session was started but left incomplete past the timeout.

study.published A study moved to live.

study.closed A study stopped accepting participants.

study.target_reached Completed sessions reached the target sample size.

license.requested A researcher requested a licence for an instrument.

license.approved A licence request was approved.

license.rejected A licence request was rejected.

license.expiring A licence expires within 30 days.

license.expired A licence reached its expiry date.

instrument.submitted An instrument was submitted for platform review.

instrument.approved An instrument was approved and can be used in studies.

instrument.revision_requested A reviewer asked for changes.

instrument.translation_approved A language version was approved.

Pushing data in

curl https://YOUR-DOMAIN/api/public/v1/webhooks/in/{slug} \
  -H "content-type: application/json" \
  -H "x-measurehub-signature: t=<unix>,v1=<hmac>" \
  -d '{"action":"session.create","data":{"studyId":"..."}}'

session.create Start a participant session for one of your live studies.

session.responses Submit answers for an existing session.

session.finish Complete a session and run automatic scoring.

panel.invite_status Update the status of a panel invitation you sent.

course.enrollment_upsert Add or update a student enrolment in one of your courses.

Errors

Errors return { "error": { "code", "message" } } with codes such as unauthorized, insufficient_scope, not_found, validation_failed and rate_limited.

Item wording and scoring artefacts of protected instruments are only served for studies covered by an approved, unexpired licence.