Docs/Webhooks

Webhooks

Get signed, real-time events whenever a referral changes. Covers endpoint setup per environment, the event envelope, delivery headers and best practices.

Webhooks push an event to your server every time one of your referrals changes, such as when a patient is contacted, a physiotherapist is matched, or care is completed. You don't need to poll the API.

Events

TypeWhen it fires
referral.createdA referral is created (API, referral-link lead, or portal)
referral.contactedPatient reached / intake started (incl. patient messaged Joy with the tracking code)
referral.provider_matchedA physiotherapist was assigned
referral.bookedFirst appointment confirmed (payment verified or care covered)
referral.in_treatmentTreatment under way
referral.completedCare episode completed
referral.not_suitableNot appropriate for Rehabify services
referral.cancelledCancelled / refunded
referral.closedClosed
pingTest event from the portal or POST /sandbox/trigger-webhook

The event catalog has a full example payload for each type.

Setting up an endpoint

Configure webhooks in the Partner Portal under Developers → Webhooks. Each environment has its own settings:

SandboxLive
Endpoint URLYour sandbox endpointYour live endpoint
Signing secretSandbox whsec_…Live whsec_…
ReceivesSandbox events onlyLive events only

Sandbox events go only to the sandbox endpoint, and live events go only to the live endpoint. They are never crossed. Each delivery is signed with the secret of the event's environment.

Endpoint requirements

  • The URL must use https://. Plain http:// is rejected.
  • No credentials in the URL (https://user:pass@… is rejected).
  • The host must be publicly reachable. localhost, .local and .internal hosts are rejected, as are private, loopback and link-local IP addresses. Rehabify checks the resolved address again at delivery time, so a hostname that later resolves to a private address will fail.
  • The port must be 443, 8443, or any port from 1024 upwards.
  • Redirects aren't followed. A 3xx response counts as a failed delivery.

For local development, use a tunnelling tool that gives you a public HTTPS URL.

Signing secrets

Each environment has its own signing secret: whsec_ followed by 40 alphanumeric characters. You can reveal it on demand in the portal, and rotate it at any time. After rotating, deploy the new secret to your endpoint promptly. Rehabify signs new deliveries with the new secret. To avoid rejecting deliveries during the switch, you can temporarily accept a signature that matches either the old or the new secret.

The event envelope

Every delivery is an HTTP POST with a JSON body. The body you receive is exactly the bytes that were signed.

{
  "id": "evt_01JA4T7Q9Y2V3W4X5Z6A7B8C9D",
  "object": "event",
  "type": "referral.booked",
  "api_version": "2026-10-01",
  "created_at": "2026-10-13T09:35:12.000Z",
  "environment": "live",
  "data": {
    "previous_status": "contacted",
    "sequence": 4,
    "referral": {
      "object": "referral",
      "id": "jd7f3k2m9x1q8w4e5r6t7y8u9i0o1p2a",
      "tracking_code": "RHB-4829173655",
      "partner_reference": "HMO-REF-2026-0091",
      "environment": "live",
      "status": "booked",
      "clinical": {
        "condition": "Lower back pain",
        "priority": "standard",
        "recommended_sessions": 6,
        "referring_clinician": "Dr. Okafor"
      },
      "care": { "preferred_mode": "patient_choice", "preferred_contact_channel": "whatsapp" },
      "billing_mode": "hmo_direct_billing",
      "provider": { "name": "Chiamaka Obi", "license_number": "MRTBN/PT/12345" },
      "sessions": { "completed": 0, "total": 6, "last_session_at": null, "next_session_at": "2026-10-15T10:00:00.000Z" },
      "links": {
        "tracking_url": "https://physioaroundme.com/track/RHB-4829173655",
        "whatsapp_intake_url": "https://wa.me/2348061525811?text=START%20RHB-4829173655"
      },
      "metadata": { "member_plan": "gold" },
      "created_at": "2026-10-13T09:30:00.000Z",
      "updated_at": "2026-10-13T09:35:12.000Z"
    }
  }
}
FieldDescription
idUnique event ID (evt_…). It stays the same across retries and manual resends, so deduplicate on it.
objectAlways event.
typeOne of the event types above.
api_versionThe payload format version, currently 2026-10-01.
created_atWhen the event happened (ISO-8601 UTC).
environmentsandbox or live.
data.previous_statusThe referral's status before this event, or null for referral.created. For referral.provider_matched, the status doesn't change, so this is the referral's current status.
data.sequenceIncreases by 1 for every event of the same referral. Use it to ignore stale or out-of-order events.
data.referralThe referral object, without the patient block.

Patient personal data is never sent in webhooks. If you need it, fetch GET /partners/referrals/{id}.

Delivery headers

HeaderExampleDescription
Content-Typeapplication/json
User-AgentRehabify-Webhooks/1.0
X-Rehabify-Event-Idevt_01JA4T7Q9Y2V3W4X5Z6A7B8C9DSame as the body's id. Stable across retries.
X-Rehabify-Event-Typereferral.bookedSame as the body's type.
X-Rehabify-Environmentlivesandbox or live.
X-Rehabify-Delivery-Attempt11 for the first attempt, up to 9 for the last retry.
X-Rehabify-Timestamp1791884113When this attempt was sent, in Unix seconds.
X-Rehabify-Signaturet=1791884113,v1=5f3c…HMAC-SHA256 signature. See Signature verification.

Responding

Respond with any 2xx status within 10 seconds. Anything else counts as a failure and is retried: a non-2xx status, a redirect, a timeout or a connection error. Rehabify makes 8 retries over about 44.6 hours. See the Retry policy for the schedule.

Testing your endpoint

  • From the portal: send a test event from Developers → Webhooks.
  • From the API (sandbox): call POST /sandbox/trigger-webhook with any event type or ping. Test events are signed and formatted exactly like real ones.
  • Real transitions (sandbox): refer a magic phone number, or move a referral with the advance endpoint.

A test event for a referral event type has data.previous_status: null, data.sequence: 0 and data.test: true, so you can tell it apart from real transitions. A ping has data.message and data.referral only.

Best practices

  • Verify every signature against the raw request body before you trust the payload.
  • Acknowledge fast, process later. Store the event, return 200, and do the work in a background job.
  • Deduplicate on the event id. Delivery is at-least-once, so the same event can arrive more than once.
  • Order by data.sequence. Events can arrive out of order. Ignore any event whose sequence is lower than or equal to the last one you processed for that referral.
  • Ignore unknown event types instead of failing, so new types don't break your handler.
  • Treat the payload as a notification. When in doubt, fetch the current state with GET /partners/referrals/{id}.
  • Keep secrets server-side, and rotate them in the portal if they leak.
Webhooks | Rehabify Docs | Physio Around Me by Rehabify Health