Search Documentation

Find sections across all documentation pages

Zenoti API: A Developer's Field Guide

A practical guide to the Zenoti REST API: auth, key booking endpoints, real-world rate limits, and what a production integration actually requires.

Zenoti's API is solid REST. If you've worked with any modern JSON API, the shape will feel familiar: predictable URL patterns, standard HTTP verbs, and JSON request/response bodies. This guide covers authentication, the endpoints you actually need for an online booking integration, the rate-limit behavior you'll hit in production, and the gap between "I can call an endpoint" and "this is reliable enough to take real bookings."

Disclosure:We're BookerKit. We sell a wrapper for the Zenoti booking funnel. This guide is honest about what's easy, what's hard, and where our product fits. If you want to build your own integration, everything here will help.

Zenoti API at a Glance

  • Base URL: https://api.zenoti.com/v1/
  • Protocol: REST over HTTPS, JSON request/response bodies
  • Authentication: API key per center, passed as a header
  • Official SDK: None at the time of writing
  • Documentation: Zenoti developer portal (requires login)

BookerKit touches a focused slice of the API surface: services, therapists, availability slots, guests, bookings, and payments/invoices. Everything else (inventory, payroll, marketing campaigns, etc.) is outside our scope but follows the same conventions.

Authentication

Every request carries an API key in the Authorization header:

Authorization: apikey YOUR_API_KEY

Keys are issued per center. If you manage multiple locations, each one has its own key. Keep keys server-side — never expose them in client-side JavaScript or embed code.

If you're using BookerKit, you add the key once in the dashboard and we handle it from there. See the Getting Started guide for setup.

Endpoints for Online Booking

These are the endpoints that matter for a booking integration, roughly in the order you'll call them.

1. Services

GET /v1/Centers/{center_id}/services

Returns the service catalog for a center. The response includes service names, descriptions, durations, prices, categories, and whether online booking is enabled. Filter the response to only show services where online booking is turned on.

2. Therapists

GET /v1/centers/{center_id}/services/{service_id}/therapists

Returns the therapists (providers) who can perform a given service. Use this to let guests choose a specific provider, or skip it entirely and let Zenoti assign one automatically during slot selection.

3. Availability / Slots

Slot retrieval is a multi-step process, not a single call:

  1. Create a draft booking POST /v1/bookings with the guest, service, and optionally a therapist. This returns a booking_id but doesn't confirm anything.
  2. Fetch available slots GET /v1/bookings/{booking_id}/slots returns time slots for the draft. The slots respect therapist schedules, service duration, and center hours.
  3. Reserve a slot PUT /v1/bookings/{booking_id}/slots/{slot_id} locks the chosen time temporarily.

This three-step dance is the most complex part of a booking integration. Slots expire, draft bookings can become stale, and you need to handle the case where a slot is taken between fetch and reserve.

4. Guests

Before booking, you need a guest record. Search for an existing guest by email, or create a new one:

POST /v1/guests

{
  "center_id": "your-center-id",
  "personal_info": {
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane@example.com",
    "mobile_phone": {
      "country_code": 225,
      "number": "5551234567"
    }
  }
}

Always search before creating to avoid duplicate guest records. Zenoti's guest search endpoint accepts email as a query parameter.

5. Bookings

Creating and confirming a booking are separate calls:

  1. CreatePOST /v1/bookings with guest, service, therapist (optional), and center. Returns a draft booking.
  2. Confirm PATCH /v1/bookings/{booking_id}/confirm finalizes the booking. Until you confirm, the booking is a draft and won't appear on the center's schedule.

The two-step model lets you collect payment or run validation between creation and confirmation.

6. Payments & Invoices

Payment handling depends on the center's configuration:

  • Saved payment methods — Guests can have cards on file. Retrieve them and charge against an existing method.
  • Hosted payment URI — For new cards, Zenoti returns a hosted_payment_uri that handles PCI compliance. You redirect the guest to this URI, they enter card details, and Zenoti redirects back.
  • Invoice— After slot reservation, retrieve the invoice to show the guest what they'll be charged.
  • Collect payment — Submit the payment against the invoice using the saved or newly added payment method.

The Referral Source Gotcha

Some Zenoti organizations require a referral.referral_sourcefield in the create-guest payload. If it's missing, the call fails silently or returns a vague error. This isn't always documented clearly — check with your Zenoti admin whether referral source is a required field for guest creation in your org. If it is, you'll need to include it in every guest creation request.

Rate Limits

Zenoti returns rate-limit information in response headers:

  • RateLimit-Remaining — requests left in the current window
  • RateLimit-Reset — seconds until the window resets
  • RateLimit-Limit — total requests allowed per window

The numeric ceiling can vary by org configuration and endpoint. When you hit a 429 Too Many Requests, back off using exponential backoff with jitter. A simple strategy:

// Exponential backoff with jitter
const backoff = (attempt: number): number => {
  const base = Math.min(1000 * Math.pow(2, attempt), 30000)
  const jitter = Math.random() * base * 0.1
  return base + jitter
}

For write operations (creating guests, confirming bookings), use idempotency keys so retries don't create duplicates. And always set request timeouts — a hung connection against a rate-limited API will cascade failures through your stack.

3D Secure and Payment Redirects

When Zenoti returns a hosted_payment_uriin the payment response, the guest needs to complete card entry (and potentially 3D Secure verification) on Zenoti's hosted page. This means:

  • You redirect the guest away from your site to the hosted URI
  • After completion, Zenoti redirects back to your callback URL
  • You need to handle the redirect back — check payment status, confirm the booking, and show the confirmation screen

The redirect flow adds UX friction. The guest leaves your page, enters a card on a Zenoti-branded page, and comes back. If your booking widget runs in an iframe, the redirect gets more complex because you need to break out of the iframe context for the payment page.

Webhooks

Zenoti supports webhooks for booking lifecycle events — creation, confirmation, cancellation, rescheduling, and no-shows. You register a callback URL in the Zenoti admin, and Zenoti sends a POST request when events fire.

If you're using BookerKit, you can also subscribe to booking_started and booking_completed events from BookerKit's signed webhook system. These include UTM parameters and click-ID attribution data that Zenoti's native webhooks don't carry.

Regardless of which webhook system you use, validate signatures on incoming payloads and protect your endpoint against SSRF. Never trust the source IP alone — always verify the cryptographic signature.

What a Production Wrapper Handles

The gap between "I can hit the endpoint" and "this handles real bookings reliably" is where most of the work lives:

  • Transport & reliability — Retry logic, circuit breakers, timeout handling, rate-limit backoff, connection pooling
  • Security — Key rotation, key-per-center management, server-side-only API calls, PCI scope management for payments
  • Booking correctness — Slot expiration handling, draft cleanup, duplicate guest prevention, referral source injection, payment state machines
  • Observability — Request logging, error tracking, latency monitoring, alert on failed bookings, audit trail for every booking attempt

BookerKit handles all of this out of the box. If you're building your own, budget 3-4x the time you think the "happy path" will take.

When to Build vs. Use a Wrapper

Build your own when you need API surface beyond booking — inventory management, payroll, marketing campaigns, custom reporting, or workflows that span multiple Zenoti endpoints in ways a booking widget never would.

Use BookerKit when the booking funnel is your scope and you want attribution data (UTMs, click IDs) flowing through the entire flow. BookerKit gives you a white-label widget, conversion tracking, webhook events with attribution, and the production reliability layer described above — without building or maintaining any of it.

Frequently Asked Questions

Does Zenoti publish an official SDK?

No official SDK at the time of writing. Integrations call the REST API directly in whatever language and HTTP client they prefer.

How do I get a Zenoti API key?

Through your Zenoti account — typically via the admin settings or API access section of your Zenoti dashboard. Each center may issue its own key.

What is the Zenoti API rate limit?

Zenoti returns the current window's limit in the RateLimit-Limit response header. The numeric ceiling can vary by org configuration and endpoint.

Can I use BookerKit for booking and still call the Zenoti API directly for other things?

Yes. BookerKit and a direct Zenoti integration are not mutually exclusive. BookerKit uses your API key for booking operations only. Any other Zenoti endpoint you call directly is unaffected.

Can I get real-time booking events from Zenoti?

Zenoti has webhooks for booking lifecycle events. If you're using BookerKit, you can also subscribe to booking_started and booking_completed events from BookerKit's signed webhook system, which include UTM and click-ID attribution data.

Is BookerKit an official Zenoti partner?

BookerKit is an independent integration, not an official Zenoti partner.

What languages can I use to call the Zenoti API?

Any language with an HTTP client. There's no language-specific SDK — you send HTTP requests with a JSON body and an Authorization header.

Ready to skip the build?

BookerKit gives you a production-ready Zenoti booking widget with full attribution tracking, conversion analytics, and webhook events — no API integration work required.