API quickstart
Call Cadence from your own backend with an organization key, or from the browser with the keyless public surface. Times are UTC ISO-8601 instants — render them in the customer's timezone.
Base URL & auth
Base URL
https://cadence.abhix-ai.com/api/v1Auth header
Authorization: Bearer ck_…Get a key in the dashboard under Settings → API keys (sign up first if needed). Cadence stores only a hash, so the key is shown once at creation. Keep it server-side — never embed it in a browser or the booking widget.
Keep your key on the server
ck_… key can read and write everything in your org. Put it in a backend environment variable and proxy requests through your own route handlers, as in the example below. For browser apps, use the public surface instead.List services
Every booking starts with a service id. List the active services for your org:
Request
curl https://cadence.abhix-ai.com/api/v1/services \
-H "Authorization: Bearer ck_live_…"Then fetch open slots for one (practitioner_id is optional — omit it for "any available"):
Availability
curl "https://cadence.abhix-ai.com/api/v1/availability?service_id=3f9a1c20-8b6e-4f2a-9d1c-2e7b6a5c4d10" \
-H "Authorization: Bearer ck_live_…"All ids (service, practitioner, booking, customer) are UUIDs returned by the list endpoints — the 3f9a1c20-8b6e-4f2a-9d1c-2e7b6a5c4d10 above is just an example. Copy the real ones from GET /services, /practitioners, etc.
Create a booking
Post a booking against an open slot. Omit practitioner_id and Cadence picks a free, least-loaded practitioner. Send an Idempotency-Key header so retries are safe — a replay returns the existing booking with 200.
Use a fresh key per distinct booking
Idempotency-Key for every distinct booking (a UUID per checkout), and reuse the same one only when retrying that exact request.Request
curl -X POST https://cadence.abhix-ai.com/api/v1/bookings \
-H "Authorization: Bearer ck_live_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: visit-42" \
-d '{
"service_id": "3f9a1c20-8b6e-4f2a-9d1c-2e7b6a5c4d10",
"start": "2026-06-01T13:00:00Z",
"customer": { "name": "Pat Patient", "email": "pat@example.com" }
}'Responses: 201 created · 200 idempotent replay · 404 unknown service/practitioner · 422 not an open slot · 409 the slot was taken by a concurrent booking. The no-double-booking guarantee is enforced in Postgres.
To manage a booking later: GET /bookings/{id}, POST /bookings/{id}/reschedule (body { "start": "…" }), and POST /bookings/{id}/cancel.
Keep the key server-side
The pattern: your frontend calls your own route handler, which adds the key and forwards to Cadence. The secret never reaches the browser.
Next.js route handler
// app/api/bookings/route.ts — the key never reaches the browser.
export async function POST(req: Request) {
const body = await req.json();
const res = await fetch("https://cadence.abhix-ai.com/api/v1/bookings", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CADENCE_ORG_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": body.idem,
},
body: JSON.stringify(body),
});
return Response.json(await res.json(), { status: res.status });
}Public surface (browser-safe, no key)
For client-side widgets and pages, the publishable surface resolves a tenant by slug and needs no API key. It exposes only the booking surface; abuse is bounded by the platform rate limiter.
GET /api/v1/public/orgs/{slug}→ org id, name, timezone, brandingGET /api/v1/public/orgs/{slug}/services·/practitionersGET /api/v1/public/orgs/{slug}/availability?service_id=&from=&to=POST /api/v1/public/orgs/{slug}/bookings— same semantics asPOST /bookings
No-key example
# No key — resolve a tenant by slug. Safe to call from the browser.
curl https://cadence.abhix-ai.com/api/v1/public/orgs/movela/services
curl -X POST https://cadence.abhix-ai.com/api/v1/public/orgs/movela/bookings \
-H "Content-Type: application/json" \
-d '{
"service_id": "3f9a1c20-8b6e-4f2a-9d1c-2e7b6a5c4d10",
"start": "2026-06-01T13:00:00Z",
"customer": { "name": "Pat", "phone": "+1…" }
}'Webhooks
Register an endpoint to receive a signed POST when bookings change, so your systems stay in sync without polling. The signing secret is returned once at creation (add an endpoint in Settings).
Subscribe to all events, or a subset. The full set:
booking.created— a new booking was madebooking.cancelled— a booking was cancelledbooking.rescheduled— a booking moved to a new timebooking.completed— a booking was marked completedbooking.no_show— a booking was marked a no-showvideo.started— the provider started the video call (video-enabled orgs only)video.ended— the video call ended (video-enabled orgs only)
Delivery headers
X-Cadence-Event: booking.created
X-Cadence-Signature: sha256=<hex hmac of the raw body>Every delivery shares one envelope: an event id/type/created_at plus a data.booking object. Note the booking carries a customer_id (not the customer's name/email) — resolve PII with a keyed read if you need it.
Payload
{
"id": "9b1d4e7a-2c3f-4a8b-9e1d-6f0a2b3c4d5e",
"type": "booking.created",
"created_at": "2026-06-01T12:00:00+00:00",
"data": {
"booking": {
"id": "7c2b1c20-8b6e-4f2a-9d1c-2e7b6a5c4d10",
"status": "confirmed",
"practitioner_id": "…",
"service_id": "3f9a1c20-8b6e-4f2a-9d1c-2e7b6a5c4d10",
"customer_id": "…",
"start": "2026-06-01T13:00:00+00:00",
"end": "2026-06-01T13:30:00+00:00",
"location_type": "virtual"
}
}
}Verify every delivery. The signature is an HMAC-SHA256 of the raw request body, keyed by your endpoint's signing secret. Recompute it and compare in constant time before trusting the payload:
Verify (Python)
from cadence_client import verify_signature
if not verify_signature(secret, raw_request_body, request.headers["X-Cadence-Signature"]):
raise HTTPException(401)SDKs
Prefer a typed client? Both wrap the same API and throw a typed error on non-2xx (409 taken · 422 not open).
@cadence/client (TS) and cadence-client (Python) packages are in private beta and not yet published, so npm install @cadence/client / pip install cadence-client won't resolve just yet. Use the raw REST calls above today — the SDKs are the ergonomics you'll reach for once they ship. Want early access?TypeScript / JavaScript
npm install @cadence/clientimport { CadenceClient } from "@cadence/client";
// Server-side, full API — the ck_… key is a server secret.
const cadence = new CadenceClient({ apiKey: process.env.CADENCE_ORG_API_KEY! });
const services = await cadence.listServices();
const avail = await cadence.getAvailability({ serviceId: services[0].id });
const booking = await cadence.createBooking({
serviceId: services[0].id,
start: avail.slots[0].start, // omit practitionerId → any available
customer: { name: "Pat", email: "pat@example.com" },
idempotencyKey: "visit-42", // safe retries
});Python
pip install cadence-clientfrom cadence_client import CadenceClient
async with CadenceClient("https://cadence.abhix-ai.com", api_key="ck_live_…") as cad:
services = await cad.list_services()
avail = await cad.get_availability(service_id=services[0].id)
booking = await cad.create_booking(
service_id=services[0].id,
start=avail.slots[0].start, # any available practitioner
customer={"name": "Pat", "email": "pat@example.com"},
idempotency_key="visit-42",
)There's also a browser-safe CadencePublicClient (TS) for the keyless public surface — see the prebuilt UI guide for the React components built on it.