Quickstart
Create a sandbox referral, read it back, receive a signed webhook, simulate a full care journey, and switch to live.
This guide takes you through the whole integration in the sandbox, from first request to go-live. In sandbox, no patient is ever contacted, no emails or WhatsApp messages are sent, and no commissions are earned, so you can experiment freely.
You need:
- An approved partner account. Apply at https://partners.physioaroundme.com if you don't have one yet.
curl, Node.js 18 or later, or Python 3.8 or later withrequests(pip install requests).
1. Get a sandbox API key
- Sign in to the Partner Portal and open Developers → API keys.
- Create a key in the sandbox environment and give it a name, such as
quickstart. Sandbox keys start withsk_test_. - Copy the key immediately. It's shown only once, because Rehabify stores only a hash of it. If you lose it, create a new key and revoke the old one.
Store the key in an environment variable. The examples below read it from REHABIFY_API_KEY:
export REHABIFY_API_KEY="sk_test_xxx" # replace with your own sandbox key2. Create a referral
Send POST /partners/referrals. The patient phone number +2348000000003 is a sandbox magic number. It moves the referral through a full simulated care journey over about 35 seconds.
curl https://api.physioaroundme.com/v1/partners/referrals \
-X POST \
-H "Authorization: Bearer $REHABIFY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 9b2f6c1e-4d7a-4c3b-8e5f-2a1d0c9b8e7f" \
-d '{
"partner_reference": "QUICKSTART-001",
"patient": {
"first_name": "Adeola",
"last_name": "Balogun",
"phone": "+2348000000003",
"email": "adeola@example.com",
"city": "Lagos",
"state": "Lagos",
"area": "Ikeja",
"preferred_language": "en"
},
"clinical": {
"condition": "Lower back pain",
"notes": "Two weeks of pain after lifting. No red flags.",
"priority": "standard",
"recommended_sessions": 6,
"referring_clinician": "Dr. Okafor"
},
"care": { "preferred_mode": "patient_choice", "preferred_contact_channel": "whatsapp" },
"billing_mode": "patient_self_pay",
"consent": {
"granted": true,
"method": "electronic",
"captured_at": "2026-10-13T09:00:00.000Z",
"privacy_notice_version": "2026-09-01"
},
"metadata": { "source": "quickstart" }
}'consent.granted is required and must be true. By setting it, you confirm the patient agreed to be contacted by Rehabify. clinical.notes is write-only: Rehabify's clinicians see it, but the API and webhooks never return it.
Idempotency-Key within 24 hours replays the original response, with the header Idempotent-Replayed: true, instead of creating a second referral. To create another referral, use a new Idempotency-Key and a new partner_reference. Reusing a partner_reference returns 409 duplicate_partner_reference.3. Inspect the response
A successful call returns 201 Created with the full referral object:
{
"object": "referral",
"id": "jd7f3k2m9x1q8w4e5r6t7y8u9i0o1p2a",
"tracking_code": "RHB-4829173655",
"partner_reference": "QUICKSTART-001",
"environment": "sandbox",
"status": "received",
"patient": {
"first_name": "Adeola",
"last_name": "Balogun",
"phone": "+2348000000003",
"email": "adeola@example.com",
"city": "Lagos",
"state": "Lagos",
"area": "Ikeja",
"preferred_language": "en"
},
"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": "patient_self_pay",
"provider": null,
"sessions": { "completed": 0, "total": 6, "last_session_at": null, "next_session_at": null },
"links": {
"tracking_url": "https://physioaroundme.com/track/RHB-4829173655",
"whatsapp_intake_url": "https://wa.me/2348061525811?text=START%20RHB-4829173655"
},
"metadata": { "source": "quickstart" },
"created_at": "2026-10-13T09:30:00.000Z",
"updated_at": "2026-10-13T09:30:00.000Z"
}Store id and tracking_code next to your own record. In live, give the patient links.whatsapp_intake_url. One tap opens a WhatsApp chat with Joy that's already linked to this referral, so the patient can start right away. links.tracking_url is a public status page that shows no personal data.
4. Read the referral back
You can fetch a referral by its id, its tracking_code or your own partner_reference. Wait about 40 seconds for the simulated journey to finish, then fetch it:
curl https://api.physioaroundme.com/v1/partners/referrals/QUICKSTART-001 \
-H "Authorization: Bearer $REHABIFY_API_KEY"By now status is completed and provider holds the simulated physiotherapist:
{
"status": "completed",
"provider": { "name": "Sandbox Physiotherapist", "license_number": "SANDBOX-0001" }
}5. Receive webhooks
Webhooks push every status change to your server, so you don't have to poll.
- Expose an HTTPS endpoint. It must use
https://and be publicly reachable. Private and internal hosts are rejected. For local development, use a tunnelling tool that gives you a public HTTPS URL. - Register it. In the Partner Portal, go to Developers → Webhooks, set your sandbox endpoint URL, and copy the sandbox signing secret (
whsec_…). Sandbox and live each have their own endpoint and secret, and events are never sent across environments. - Verify signatures. Every delivery carries
X-Rehabify-Signature: t=<unix seconds>,v1=<hex>. Here's a minimal Express receiver. Signature verification has Python, PHP and Go versions.
// npm install express
import crypto from "node:crypto";
import express from "express";
const app = express();
const SECRET = process.env.REHABIFY_WEBHOOK_SECRET; // the sandbox whsec_… secret
function isValidSignature(rawBody, header) {
if (!header || !Buffer.isBuffer(rawBody)) return false;
let timestamp = null;
const signatures = [];
for (const part of header.split(",")) {
const i = part.indexOf("=");
if (i === -1) continue;
const key = part.slice(0, i).trim();
const value = part.slice(i + 1).trim();
if (key === "t") timestamp = value;
if (key === "v1") signatures.push(value);
}
if (!timestamp || !/^\d+$/.test(timestamp) || signatures.length === 0) return false;
if (Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp)) > 300) return false;
const expected = crypto.createHmac("sha256", SECRET).update(`${timestamp}.`).update(rawBody).digest();
return signatures.some((sig) => {
const received = Buffer.from(sig, "hex");
return received.length === expected.length && crypto.timingSafeEqual(received, expected);
});
}
// express.raw keeps the exact bytes that were signed. Register this route before any express.json().
app.post("/webhooks/rehabify", express.raw({ type: "application/json" }), (req, res) => {
if (!isValidSignature(req.body, req.get("X-Rehabify-Signature"))) {
return res.status(400).send("invalid signature");
}
const event = JSON.parse(req.body.toString("utf8"));
console.log(event.id, event.type, event.data.referral.tracking_code);
res.sendStatus(200); // acknowledge within 10 seconds; do slow work asynchronously
});
app.listen(3000, () => console.log("Listening on http://localhost:3000"));- Send a test event. Use the portal's test button, or call the sandbox trigger endpoint:
curl https://api.physioaroundme.com/v1/sandbox/trigger-webhook \
-X POST \
-H "Authorization: Bearer $REHABIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "type": "ping" }'{
"object": "webhook_test",
"event_id": "evt_01JA4V2K8M3N5P7Q9R1S3T5V7W",
"type": "ping",
"status": "queued",
"endpoint": "https://example.com/webhooks/rehabify"
}- Watch a real journey. Repeat step 2 with a new
partner_referenceandIdempotency-Key. Your endpoint receivesreferral.created, then an event for each status change (referral.contacted,referral.booked,referral.in_treatmentandreferral.completed), plusreferral.provider_matchedwhen the simulated physiotherapist is assigned. The event catalog shows every payload.
6. Simulate other scenarios
Each magic number triggers a different journey. Any other number stays received until you move it yourself.
| Phone | Behaviour |
|---|---|
+2348000000001 | → contacted after ~5 s |
+2348000000002 | → contacted (~5 s) → provider_matched + booked (~15 s) |
+2348000000003 | full journey: contacted (~5 s) → booked (~15 s) → in_treatment (~25 s) → completed (~35 s) |
+2348000000004 | rejected immediately: 422 patient_ineligible (no referral created) |
+2348000000005 | → not_suitable after ~10 s |
+2348000000006 | → cancelled after ~10 s |
| any other | stays received — use the advance endpoint |
To move a referral yourself, create one with any other number (for example +2348012345678), then call the sandbox-only advance endpoint. It fires the same webhooks a real transition would:
TRACKING_CODE="RHB-0123456789" # replace with the tracking code (or id) of a sandbox referral that is still "received"
curl "https://api.physioaroundme.com/v1/sandbox/referrals/$TRACKING_CODE/advance" \
-X POST \
-H "Authorization: Bearer $REHABIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "contacted" }'See Sandbox & testing for the full rules.
7. Go live
- In the Partner Portal, choose Request production access and work through the go-live checklist.
- Once Rehabify approves production access, create a
sk_live_key in Developers → API keys. - Set your live webhook endpoint in Developers → Webhooks and store the live signing secret. It's different from your sandbox secret.
- Swap
REHABIFY_API_KEYandREHABIFY_WEBHOOK_SECRETfor their live values. The base URL stays the same.