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=svc_123" \
-H "Authorization: Bearer ck_live_…"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.
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": "svc_123",
"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": "svc_123",
"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. Events: booking.created, booking.cancelled, booking.rescheduled. The signing secret is returned once at creation (add an endpoint in Settings).
Delivery headers
X-Cadence-Event: booking.created
X-Cadence-Signature: sha256=<hex hmac of the raw body>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).
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.