Docs/Webhooks

Retry policy

When a webhook delivery counts as failed, the exact retry schedule, at-least-once delivery, ordering with data.sequence, and resending from the portal.

What counts as success

A delivery succeeds when your endpoint returns any 2xx status within 10 seconds.

Anything else is a failure and is retried:

  • a non-2xx status, including 4xx and 5xx
  • a redirect (3xx). Redirects are never followed.
  • no response within 10 seconds
  • a connection or TLS error

Retry schedule

After the first attempt fails, Rehabify makes 8 retries. Each delay is measured from the previous attempt, so the whole schedule spans about 44.6 hours. The attempt number is sent in the X-Rehabify-Delivery-Attempt header.

Attempt (X-Rehabify-Delivery-Attempt)Delay after the previous attemptTime since the first attempt
1 (first attempt)Immediate0
2+15 s15 s
3+1 min1 min 15 s
4+5 min6 min 15 s
5+30 min36 min 15 s
6+2 h2 h 36 min 15 s
7+6 h8 h 36 min 15 s
8+12 h20 h 36 min 15 s
9 (last retry)+24 h44 h 36 min 15 s (≈44.6 h)

If attempt 9 also fails, the delivery is marked failed. Rehabify doesn't retry it again automatically.

Resending failed deliveries

You can resend any delivery from the Partner Portal under Developers → Webhooks. A resent event keeps the same event id, so your deduplication logic treats it correctly. After an outage, fix your endpoint first, then resend the failed deliveries.

To catch up on state you may have missed, you can also list recent referrals with GET /partners/referrals, using created_after and status as filters.

At-least-once delivery

Delivery is at-least-once: the same event can arrive more than once, for example when your endpoint processed it but the response was lost. Deduplicate on the event id. It's also sent as X-Rehabify-Event-Id, and it's stable across retries and resends.

Out-of-order events

Retries and network delays mean events can arrive out of order. For example, referral.in_treatment could arrive before a retried referral.booked. Use data.sequence, which increases by 1 for every event of the same referral, or data.referral.updated_at, to ignore stale events.

// Pseudo-storage: replace `db` with your database. Run inside a transaction.
export async function handleRehabifyEvent(event, db) {
  if (await db.processedEvents.has(event.id)) return; // duplicate delivery
  await db.processedEvents.add(event.id);

  if (event.type === "ping" || event.data.test) return; // test events don't change state

  const { referral, sequence } = event.data;
  const current = await db.referrals.get(referral.id);
  if (current && current.lastSequence >= sequence) return; // stale or out-of-order event

  await db.referrals.upsert(referral.id, {
    status: referral.status,
    provider: referral.provider,
    sessions: referral.sessions,
    lastSequence: sequence,
  });
}

Keep your endpoint fast

  • Verify the signature, store the event, and return 200 straight away. Do slow work, such as calling other APIs or sending emails, in a background job.
  • Return 2xx for event types you don't handle. Otherwise they'll be retried for nearly two days.
  • Return 4xx only when signature verification fails. Every non-2xx response is retried.
Retry policy | Rehabify Docs | Physio Around Me by Rehabify Health