Skip to content

Webhooks

Subscribe to versioned event types and receive them at your callback URL. Every delivery is signed with an HMAC over the raw body, carried in the X-MedFlo-Signature header, so you can verify authenticity and integrity.

Webhook management requires the webhooks.manage scope, and subscriptions are org-scoped under /api/v1/ofctx/{org_id}/webhooks.

Manage subscriptions in the portal

You can register, list, test-fire, and revoke subscriptions interactively in the webhook manager — no cURL required.

Event types

Event typeFires when
resident.admittedA resident is admitted to a facility.
resident.dischargedA resident is discharged.
resident.transferredA resident is transferred between facilities.
coverage.updatedA resident's payer coverage changes.
clinical.condition.createdA new diagnosis is recorded.
clinical.order.createdA new order (e.g. medication) is created.

Discover the current list at any time: GET /api/v1/ofctx/{org_id}/webhooks/event-types.

Endpoints

MethodPathDescription
POST/…/webhooksRegister a subscription. The signing secret is returned once.
GET/…/webhooksList your org's subscriptions (secret never re-shown).
DELETE/…/webhooks/{id}Revoke a subscription.
POST/…/webhooks/{id}/testEmit a signed test event and get back the exact bytes + signature to validate your verifier.

Subscribe

Register a callback_url and one or more event_types. The response includes a signing_secret — this is the only time it's returned, so store it immediately.

bash
# Register a signed webhook subscription. The signing_secret is returned ONCE.
TOKEN="eyJ..."   # a token issued with the webhooks.manage scope

curl -s -X POST \
  'https://medflo-pcc-vendor-api-eez5kqwsxa-uw.a.run.app/api/v1/ofctx/sandbox-org-0001/webhooks' \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "callback_url": "https://example.com/hooks/medflo",
    "event_types": ["resident.admitted", "coverage.updated"],
    "description": "prod ingest"
  }'

# → { "data": { "id": "...", "signing_secret": "whsec_...",  <-- store this now
#       "signature_header": "X-MedFlo-Signature", "status": "active", ... } }

Verify the signature

On each delivery, compute an HMAC-SHA256 of the exact raw request body using your subscription's signing_secret, then compare it (constant-time) against the X-MedFlo-Signature header. Reject the delivery if they don't match.

Use the raw bytes

Verify against the raw body you received — do not parse and re-serialize the JSON first, or key ordering / whitespace will change the bytes and the signature won't match.
// Verify an inbound webhook (Node).
import crypto from "node:crypto";

function verifyMedFloSignature(rawBody, signatureHeader, signingSecret) {
  // rawBody must be the EXACT bytes MedFlo POSTed (do not re-serialize).
  const expected = crypto
    .createHmac("sha256", signingSecret)
    .update(rawBody)
    .digest("hex");

  // Constant-time compare to avoid timing leaks.
  const a = Buffer.from(expected);
  const b = Buffer.from(signatureHeader || "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

// In your handler, read the RAW body (not the parsed JSON):
//   const ok = verifyMedFloSignature(rawBody, req.headers["x-medflo-signature"], SIGNING_SECRET);
//   if (!ok) return res.status(401).end();

Test your verifier

Call the /test endpoint to have MedFlo build a signed delivery and return the exact body, the signature, and the signature_header name. Run your verifier against those bytes to confirm it accepts a valid signature and rejects a tampered one — before wiring up production traffic.

Payload shape

Deliveries carry a versioned envelope with api_version, the event_type, and a minimal data body. Payloads carry the minimum necessary and never include unnecessary PHI. See Versioning for how event payloads evolve.