---
title: "Register an outbound webhook endpoint"
method: POST
path: "/api/v1/webhooks"
tags: ["Webhooks"]
---

# Register an outbound webhook endpoint

`POST /api/v1/webhooks`

## Request

- Base URL: `https://api-dev.fusehealth.com`
- URL: `https://api-dev.fusehealth.com/api/v1/webhooks`
- Auth: API key in header `x-api-key`

Register a publicly-reachable HTTPS endpoint to receive signed, opaque-id-only checkout lifecycle events (`session.completed`, `order.created`, `payment.succeeded`), intake conversion events (`intake.started`, `intake.abandoned`), order/fulfillment events (`order.placed`, `order.cancelled`, `order.doctor_approved`, `order.doctor_denied`, `order.sent_to_pharmacy`, `order.shipped`, `order.delivered`, `payment.failed`, `shipment.updated`), telehealth events (`appointment.scheduled`, `appointment.cancelled`) and suppression sync (`contact.unsubscribed`) instead of polling GET /checkout/sessions/{id}. The full accepted event set is the 17-value enum below — registration rejects anything else with 400. Each delivery body is `{ id, event, createdAt, data:{ …opaque ids + event-specific fields } }`; the exact per-event payload key sets are documented in docs/api/external-brand-api/OUTBOUND-WEBHOOKS.md (source of truth: BRAND_WEBHOOK_PAYLOAD_KEYS in patient-api/src/services/brand-webhook-dispatch.service.ts). Note `order.doctor_denied` is accepted at registration but currently has no firing source in the API — it is declared for forward compatibility and will not be delivered until the denial chokepoint emits it. Verify `X-Fuse-Signature: t=<unixSeconds>,v1=<hex>` (HMAC-SHA256 over `<t>.<rawBody>` keyed by the endpoint secret) and dedupe on `X-Fuse-Idempotency-Key` (stable across retries and replays; `X-Fuse-Delivery-Id` is a per-attempt id, not a dedupe key). URLs that resolve to private/link-local addresses are refused and 3xx redirects are not followed (SSRF protection). The secret is returned in THIS response ONLY and is never retrievable again. Requires write scope; secret keys only.

## Request body

- object
  - `url` string, uri, required: Absolute https:// URL.
  - `events` string[], required

## Response `201`

Created — secret returned once

- object
  - `success` boolean
  - `data` object
    - `id` string, uuid
    - `url` string
    - `events` string[]
    - `isActive` boolean
    - `secret` string: Signing secret (whsec_…) — shown once.

## Other responses

- `400`: Bad request / business-rule failure (may include a `code`)
- `401`: Missing, invalid, inactive, or expired API key (or JWT)
- `403`: Insufficient scope, or a publishable key used on a route it may not call

---

[API](https://skmtc.dev/fusehealth/apis/fuse-brand-api-external.md) · [All operations](https://skmtc.dev/fusehealth/apis/fuse-brand-api-external/llms.txt) · [OpenAPI document](https://skmtc.dev/fusehealth/apis/fuse-brand-api-external/revisions/50821832728c?raw)
