Overview
| Base URL | https://api.fusehealth.com/api/v1 |
| Auth | An API key in the x-api-key header |
| Format | JSON in, JSON out |
| Reference | api.fusehealth.com/api/v1/docs |
| OpenAPI spec | openapi.json |
Every key belongs to one brand, and every request is scoped to that brand: a key can never read or change another brand's data.
API keys
Create keys in the brand portal: Settings → Organization → API Keys. You need to be signed in as the brand's admin. A key is shown once, when you create it, so copy it straight away. FUSE keeps only a hash.
| Type | Looks like | Use it for |
|---|---|---|
| Secret | fuse_live_… | Your server. Full access to the API, limited by the key's permissions. |
| Publishable | fuse_pk_… | Your website's browser code. It can only create checkout sessions; every other call is refused. |
| AI connector | fuse_ai_… | Running the FUSE AI connector yourself. Limited to what the connector allows. |
- Permissions. A key has
read, orreadandwrite. New keys are read-only unless you tick write. - Expiry. Keys last one year by default.
- Limit. Up to 5 active keys per brand. Revoke one in the portal to make room.
- Sandbox keys start with
fuse_test_(see Sandbox).
Your first request
GET /me tells you which brand and key you're using. It's the quickest way to check your setup.
curl https://api.fusehealth.com/api/v1/me \
-H "x-api-key: $FUSE_API_KEY"{
"success": true,
"message": "Authenticated",
"data": {
"clinicId": "…",
"keyName": "My server",
"keyPrefix": "fuse_live_…",
"scopes": ["read", "write"],
"kind": "secret",
"isSandbox": false,
"rateLimitPerMin": null
}
}From there, the reference lists every endpoint with its parameters and responses: programs and their products and pricing, the product catalog, coupons, checkout sessions, your storefront pages and navigation, webhooks, analytics and more.
Responses and errors
Every response uses the same envelope.
{ "success": true, "message": "…", "data": { … } }{ "success": false, "message": "Human-readable reason", "code": "MACHINE_CODE" }Lists are paged with ?page= (from 1) and ?limit= (default 25, maximum 200). Paged responses include pagination with page, limit, total and totalPages.
| Status | Code | Meaning |
|---|---|---|
| 401 | — | Missing, invalid or expired API key |
| 403 | INSUFFICIENT_SCOPE | The key needs the write permission for this call |
| 403 | KEY_KIND_NOT_ALLOWED | A publishable key called something other than checkout |
| 403 | SANDBOX_NOT_ALLOWED | Not available to sandbox keys |
| 409 | ACTIVE_KEY_LIMIT_REACHED | Your brand already has 5 active keys |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests. Wait, then retry |
Rate limits
Limits are per key. By default a key can make 300 requests a minute, with short bursts capped at 100 requests in 10 seconds. Sandbox keys get 60 a minute.
Responses carry RateLimit-* headers showing where you stand. A blocked request gets 429, a Retry-After header in seconds, and retryAfterSeconds in the body. Wait that long before retrying.
Webhooks
Webhooks tell your server when something happens, such as an order being placed, approved by a doctor, sent to the pharmacy or shipped. Register an https endpoint and the events you want with POST /webhooks. The response includes a signing secret (whsec_…), shown once.
Webhook payloads carry IDs, not patient details. Fetch what you need from the API with a secret key.
Verify the signature
Each delivery has an X-Fuse-Signature header of the form t=<unix seconds>,v1=<hex>. The v1 value is an HMAC-SHA256 of <t>.<raw request body>, keyed with your endpoint's signing secret. Compute it over the raw body, before any JSON parsing.
import crypto from "node:crypto";
export function verifyFuseWebhook(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(
signatureHeader.split(",").map((p) => p.split("=", 2)),
);
const expected = crypto
.createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(parts.v1 ?? "");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}Deliveries also carry:
X-Fuse-Event: the event name;X-Fuse-Delivery-Id: unique to each attempt;X-Fuse-Idempotency-Key: the same across retries and replays. Use it to ignore duplicates.
Reply with a 2xx within 5 seconds. Failed deliveries are retried. An endpoint that fails 10 times in a row is turned off. You can list recent deliveries and replay one from the API.
Events
| Area | Events |
|---|---|
| Checkout and payment | session.completed · payment.succeeded · payment.failed |
| Orders | order.created · order.placed · order.cancelled · order.doctor_approved · order.doctor_denied · order.sent_to_pharmacy · order.shipped · order.delivered · shipment.updated |
| Intake | intake.started · intake.abandoned |
| Appointments | appointment.scheduled · appointment.cancelled |
| Contacts | contact.unsubscribed |
Sandbox
Want to try the API before you have a brand? Create a free developer account on the Sign in with FUSE page used by the AI connector (choose Create a free developer account). You confirm your email with a 6-digit code and get:
- a sandbox brand with a demo program to work on;
- a
fuse_test_API key (read and write) and an AI connector key; - 30 days to explore, at 60 requests a minute.
The sandbox uses the same base URL as the live API. It has no real patients, so routes for orders, customers, payouts and refunds aren't available. They return SANDBOX_NOT_ALLOWED.
Going live from the sandbox
When you're ready to sell, your sandbox becomes your real brand. Nothing is copied or rebuilt: the programs, pages and settings you made stay exactly where they are.
- Name the person who can sign for your company. Often that isn't the developer, so you tell us who it is:
curl -X POST https://api.fusehealth.com/api/v1/sandbox/go-live \
-H "x-api-key: $FUSE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"signerFirstName": "Jordan",
"signerLastName": "Lee",
"signerEmail": "jordan@yourcompany.com"
}'{
"success": true,
"data": { "status": "requested", "signerEmail": "jordan@yourcompany.com" },
"message": "Thanks. We have contacted your signer. Your sandbox keeps working until they finish onboarding, and everything you built carries over."
}- Your signer gets an email invitation to join your account as its admin.
- They complete FUSE brand onboarding: verify their identity, sign the agreement, and choose and pay for a plan.
- Your account switches to live automatically the moment onboarding is complete. The sandbox limits and the 30-day window no longer apply.
- Create live API keys in the brand portal, and finish setting up payouts. Our team reviews the brand before your first program can go on sale.
Full reference
The complete, always-current reference is generated from the API itself: every endpoint, parameter, request body and response.
- Interactive reference (
api.fusehealth.com/api/v1/docs) - OpenAPI spec (
openapi.json). Import it into Postman or Insomnia, or generate a client in your language.
