REST API

FUSE API

A JSON API for your FUSE brand: programs, products, pricing, coupons, checkout, your storefront and webhooks. Everything you can do in the brand portal, from your own code.

Overview

Base URLhttps://api.fusehealth.com/api/v1
AuthAn API key in the x-api-key header
FormatJSON in, JSON out
Referenceapi.fusehealth.com/api/v1/docs
OpenAPI specopenapi.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.

TypeLooks likeUse it for
Secretfuse_live_…Your server. Full access to the API, limited by the key's permissions.
Publishablefuse_pk_…Your website's browser code. It can only create checkout sessions; every other call is refused.
AI connectorfuse_ai_…Running the FUSE AI connector yourself. Limited to what the connector allows.
  • Permissions. A key has read, or read and write. 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).
Keep secret keys on your server. A secret key can read your orders and customers, which include patient information. Never put one in browser code, a mobile app or a code repository. Every request made with a key is logged. If a key leaks, revoke it in the portal.

Your first request

GET /me tells you which brand and key you're using. It's the quickest way to check your setup.

Request
curl https://api.fusehealth.com/api/v1/me \
  -H "x-api-key: $FUSE_API_KEY"
Response
{
  "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
{ "success": true, "message": "…", "data": { … } }
Error
{ "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.

StatusCodeMeaning
401—Missing, invalid or expired API key
403INSUFFICIENT_SCOPEThe key needs the write permission for this call
403KEY_KIND_NOT_ALLOWEDA publishable key called something other than checkout
403SANDBOX_NOT_ALLOWEDNot available to sandbox keys
409ACTIVE_KEY_LIMIT_REACHEDYour brand already has 5 active keys
429RATE_LIMIT_EXCEEDEDToo 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.

Node.js
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

AreaEvents
Checkout and paymentsession.completed · payment.succeeded · payment.failed
Ordersorder.created · order.placed · order.cancelled · order.doctor_approved · order.doctor_denied · order.sent_to_pharmacy · order.shipped · order.delivered · shipment.updated
Intakeintake.started · intake.abandoned
Appointmentsappointment.scheduled · appointment.cancelled
Contactscontact.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.

Already have a FUSE brand? Don't create a developer account with the same email. Create keys in your brand portal instead.

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.

  1. Name the person who can sign for your company. Often that isn't the developer, so you tell us who it is:
Request
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"
  }'
Response · 202
{
  "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."
}
  1. Your signer gets an email invitation to join your account as its admin.
  2. They complete FUSE brand onboarding: verify their identity, sign the agreement, and choose and pay for a plan.
  3. Your account switches to live automatically the moment onboarding is complete. The sandbox limits and the 30-day window no longer apply.
  4. 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.
Request go-live within your 30 days. Sandbox keys stop working when the window ends, so send the request before then. If your company already works with FUSE, our team will contact you to connect the sandbox to your existing brand instead of creating a second one.

Full reference

The complete, always-current reference is generated from the API itself: every endpoint, parameter, request body and response.