Skip to main content

Outbound Webhooks

Outbound Webhooks

Last updated: 2026-02-16

This document covers enterprise event delivery from Bounded Health to external systems.

1. What this provides

  • Employer-scoped webhook subscriptions
  • Signed payloads (HMAC-SHA256)
  • Queue-backed delivery retries
  • Delivery history and failure tracking
  • Test-delivery endpoint for go-live validation

2. Subscription management endpoints

All webhook management endpoints require:

  • Clerk session auth, or
  • API key auth with webhook:manage

Endpoints:

  • GET /api/employer/webhooks
  • POST /api/employer/webhooks
  • GET /api/employer/webhooks/{webhookId}
  • PATCH /api/employer/webhooks/{webhookId}
  • DELETE /api/employer/webhooks/{webhookId}
  • POST /api/employer/webhooks/{webhookId}/test

3. Event catalog

Current event types:

  • test.ping
  • tapering.proposed
  • tapering.approved
  • tapering.declined
  • simulation.completed
  • cohort.uploaded
  • authorization.reviewed
  • export.ready
  • invoice.paid

4. Payload contract

All webhook payloads follow this shape:

json
{
  "id": "uuid",
  "event": "cohort.uploaded",
  "timestamp": "2026-02-16T13:45:10.000Z",
  "apiVersion": "2026-02-01",
  "employerId": "emp_...",
  "data": {}
}

5. Signature verification

Headers sent by Bounded Health:

  • x-webhook-signature: sha256=<hex-hmac>
  • x-webhook-id
  • x-webhook-event
  • x-webhook-version
  • x-webhook-timestamp

Signing algorithm:

  • HMAC SHA-256
  • Input: exact raw request body bytes
  • Secret: subscription signing secret returned at creation time

Node.js verification example:

ts
import crypto from "crypto";

export function verifySignature(rawBody: string, signatureHeader: string, secret: string): boolean {
  const expected = `sha256=${crypto.createHmac("sha256", secret).update(rawBody).digest("hex")}`;
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));
}

6. Delivery and retry behavior

  • Webhook jobs are delivered asynchronously from queue workers
  • Per-attempt timeout: 10 seconds
  • Retry attempts: up to 5 with exponential backoff
  • Response body captured (truncated) for diagnostics
  • Delivery log written for every attempt
  • On repeated final-attempt failures, failureCount increments
  • Subscription auto-disables when failureCount >= maxFailures

7. Test endpoint behavior

POST /api/employer/webhooks/{webhookId}/test sends a direct test event:

  • Returns success/failure status and latency
  • Writes a delivery log entry
  • Returns:
    • 404 if subscription not found
    • 400 if subscription is inactive
    • 200 with success: false if endpoint responds non-2xx

8. Production recommendations

  • Use dedicated endpoint URLs per environment
  • Enforce idempotency by de-duplicating on webhook id
  • Return fast 2xx and process asynchronously in your backend
  • Monitor:
    • delivery success rate
    • median and p95 latency
    • disablement events due to repeated failure
  • Rotate signing secrets on a schedule

9. Go-live validation checklist

  • Create subscription with HTTPS URL only
  • Store signing secret in your secret manager
  • Run /test endpoint and validate signature verification
  • Trigger one real domain event and verify payload mapping
  • Confirm your retry and dead-letter handling is active