Webhooks

Webhooks let RCMS push events to your system in real time — client enrolled, assessment submitted, recovery plan updated, staff deactivated. Subscribe once; RCMS calls your endpoint whenever a relevant event happens.

When to use webhooks (and when not)

Use caseUse
React to a client being enrolled or dischargedWebhook
Sync assessment scores into your system as they're finalizedWebhook
Trigger a notification when an assessment becomes overdueWebhook
Look up a client's latest score on demandJSON API
Periodic batch sync (nightly refresh, backfills)JSON API

Webhooks are for push notifications of state changes. For on-demand reads, use the API.

Register a webhook

curl -X POST https://api.measurerecovery.com/v1/webhooks \
  -H "Authorization: Bearer rcms_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://partner.example.com/hooks/rcms",
    "events": [
      "assessment.submitted",
      "assessment.scored",
      "client.enrolled",
      "client.discharged"
    ],
    "description": "Production webhook for OneStep integration"
  }'

Response:

{
  "data": {
    "id": "wh_01J8XR7K9M1N2P3Q4R5S6T7U8",
    "url": "https://partner.example.com/hooks/rcms",
    "events": ["assessment.submitted", "assessment.scored", "client.enrolled", "client.discharged"],
    "description": "Production webhook for OneStep integration",
    "signing_secret": "whsec_rK2nF9aL7pM4xQ8vR5jS3wN6yB1zT0uH",
    "status": "active",
    "created_at": "2026-04-19T14:30:00Z"
  }
}

Save the signing_secret now.

It's shown once at creation and never again. You'll need it to verify every incoming webhook signature. If lost, delete the webhook and register a new one.

Event types

Subscribe to only the events you need; ignore everything else. Specifying more events means more noise on your endpoint.

Event typeFires when
client.enrolledNew client record created
client.updatedClient profile fields change
client.dischargedClient marked as discharged
client.readmittedPreviously discharged client re-enters the program
client.intake_incompleteClient created with required intake fields still pending
assessment.scheduledNext assessment due date set
assessment.submittedClient or staff submits an assessment (scoring pending)
assessment.scoredScoring finished; domain scores available
assessment.lockedAssessment edit window closed; results immutable
assessment.due_soonAssessment approaching its due date (7 days out)
assessment.overdueAssessment past its due date, not yet submitted
staff.createdNew staff member provisioned
staff.updatedStaff role or permissions change
staff.deactivatedStaff account disabled
organization.createdNew org provisioned within a network
organization.updatedOrg profile fields change

Event payload format

Every delivery is a POST to your URL with a consistent envelope. The data field shape varies by event type.

POST /hooks/rcms HTTP/1.1
Host: partner.example.com
Content-Type: application/json
X-RCMS-Signature: t=1713546600,v1=5257a869e7ecfe04b08e4107d4e9c2d3b1c7e9f0a8e1...
X-RCMS-Event-Id: evt_01J8XS9P2Q3R4S5T6U7V8W9X0Y
User-Agent: RCMS-Webhooks/1.0

{
  "id": "evt_01J8XS9P2Q3R4S5T6U7V8W9X0Y",
  "type": "assessment.scored",
  "created_at": "2026-04-19T14:33:21Z",
  "organization_id": "org_8fKzW2",
  "data": {
    "assessment_id": "asmt_01J8WR6P5N4M3L2K1J",
    "client_id": "client_01J8W2K3L4M5N6P",
    "assessment_number": 3,
    "submitted_at": "2026-04-19T14:33:10Z",
    "positive_score": 61.8,
    "negative_score": -27.4,
    "overall_score": 17.2,
    "engagement_score": 43
  }
}

Envelope fields

FieldDescription
idUnique ID for this event. Use it for idempotency (de-duplication on retries).
typeEvent type (see list above)
created_atISO-8601 timestamp of when the event occurred
organization_idWhich org this event belongs to
dataEvent-specific payload. Shape depends on type.

Verify the signature

Every delivery carries an X-RCMS-Signature header in this format:

X-RCMS-Signature: t=1713546600,v1=5257a869e7ecfe04b08e4107d4e9c2d3b1c7e9f0a8e1...

Verification steps

  1. Parse X-RCMS-Signature into t and v1
  2. Compute the expected HMAC-SHA256 of {t}.{raw_body} using your signing secret
  3. Compare to v1 with a constant-time comparison
  4. Reject if the timestamp is older than 5 minutes (replay protection)

Node.js example

import crypto from "node:crypto";

function verifyRcmsWebhook(rawBody, signatureHeader, secret) {
  const parts = Object.fromEntries(
    signatureHeader.split(",").map((kv) => kv.split("="))
  );
  const timestamp = parseInt(parts.t, 10);
  const signature = parts.v1;

  // Reject replays older than 5 minutes
  if (Math.abs(Date.now() / 1000 - timestamp) > 300) {
    throw new Error("Webhook timestamp too old");
  }

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");

  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
    throw new Error("Webhook signature invalid");
  }
}

Python example

import hmac
import hashlib
import time

def verify_rcms_webhook(raw_body: bytes, signature_header: str, secret: str) -> None:
    parts = dict(kv.split("=") for kv in signature_header.split(","))
    timestamp = int(parts["t"])
    signature = parts["v1"]

    if abs(time.time() - timestamp) > 300:
        raise ValueError("Webhook timestamp too old")

    expected = hmac.new(
        secret.encode(),
        f"{timestamp}.{raw_body.decode()}".encode(),
        hashlib.sha256,
    ).hexdigest()

    if not hmac.compare_digest(expected, signature):
        raise ValueError("Webhook signature invalid")

Notification routing — who sends what to users

RCMS sends a few transactional messages natively (assessment reminders, follow-up nudges). When a partner integration is involved, clients often end up signed up through the partner's platform — at that point RCMS emails from an unfamiliar domain create confusion.

To avoid that, notification routing follows the auth identity owner rule. No org-level configuration; no duplicate emails:

Who created the auth identityHow reminder notifications are delivered
Your API key (partner-provisioned user)RCMS fires assessment.due_soon / assessment.overdue webhooks to your endpoint. RCMS does not send its own email.
RCMS (org admin invited the user directly)RCMS sends its native reminder email from noreply@measurerecovery.com. No webhook is fired for reminders.

If you subscribe to assessment.due_soon and assessment.overdue, you'll receive events for every client your API key created — RCMS assumes you're handling the outreach from your domain with your branding. Clients you didn't create stay on RCMS-native email delivery automatically.

Applies to reminder events only

Transactional events you want for analytics or sync (assessment.scored, client.discharged, etc.) fire for every subscribed webhook regardless of who created the auth identity. This routing rule only affects user-facing notifications where duplicate branding would cause confusion.

Delivery semantics

BehaviorDetail
At-least-once deliveryYou may receive the same event more than once. Use the id to deduplicate.
Success criteriaReturn 2xx within 30 seconds
Retry triggerNon-2xx response, timeout, or connection error
Retry scheduleExponential backoff: 30s, 2m, 10m, 30m, 1h, 2h, 6h, 12h — total window 24 hours
Dead letterAfter 24 hours of failures, delivery is marked failed. Event stays queryable via GET /v1/webhooks/:id/deliveries.
OrderingNot guaranteed. Use created_at to order.
Disabled endpointsAfter 50 consecutive failures, the webhook auto-disables. Re-enable by deleting and re-registering.

Test, inspect, and manage webhooks

Send a test event

curl -X POST https://api.measurerecovery.com/v1/webhooks/wh_01J8XR.../test \
  -H "Authorization: Bearer rcms_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Sends a synthetic webhook.test event to your URL so you can verify your endpoint accepts and signs correctly.

List recent deliveries

curl https://api.measurerecovery.com/v1/webhooks/wh_01J8XR.../deliveries \
  -H "Authorization: Bearer rcms_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Returns the last 100 deliveries with status, response code, attempt count, and timestamps. Useful for debugging partner-side issues.

Remove a webhook

curl -X DELETE https://api.measurerecovery.com/v1/webhooks/wh_01J8XR... \
  -H "Authorization: Bearer rcms_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Best practices

Next steps

Getting Started

Request an API key and make your first call.

API Reference

Full request/response documentation for every endpoint.