com.jamesrosingmd/zenoti
MCP server for Zenoti spa/wellness/medspa — guests, appointments, bookings, invoices
Open source Open in the app JSON README (API)
About
MCP server for Zenoti spa/wellness/medspa — guests, appointments, bookings, invoices
Details
- Kind
- MCP servers
- Topic
- Finance & crypto
- Publisher
- com.jamesrosingmd
- Origin
- official
- Category
- ferramentas
- Transport
- local
- Version
- 1.1.0
- Last push
- 2026-08-07T18:38:16Z
- Repository state
- ativo
- Language
- TypeScript
- License
- MIT
- Added
- 2026-08-29 03:01:11
- Updated
- 2026-08-29 03:01:11
- Origin id
com.jamesrosingmd/zenoti
README
# Zenoti MCP Server
A Model Context Protocol (MCP) server for the [Zenoti](https://www.zenoti.com/) spa/wellness/medspa management platform. Exposes appointments, guests, service booking, invoices, catalog, and forms as tools an AI agent can call.
### Hosted version available
Do not want to manage credentials, hosting, and updates yourself? [DOCK](https://dockhq.vercel.app) is the managed version of this server: encrypted per-practice auth, audit logs, draft-first write actions, and a workflow library. Founding practices lock lifetime pricing: Front Desk $49/mo (Zenoti), Billing Desk $99/mo (Tebra, BAA included), Full Practice $129/mo (both). https://dockhq.vercel.app
## Installation
```bash
npm install -g zenoti-mcp-server
```
Or run from source:
```bash
git clone https://github.com/tacit-code/zenoti-mcp-server.git
cd zenoti-mcp-server
npm install
npm run build
```
## Configuration
| Variable | Required | Description |
|---|---|---|
| `ZENOTI_API_KEY` | yes | API key from Zenoti Admin → Settings → Apps (create a backend app) |
| `ZENOTI_API_URL` | no | Base URL, default `https://api.zenoti.com` (with or without `/v1`) |
| `ZENOTI_CENTER_ID` | recommended | Default center GUID; tools accept `center_id` to override per call |
Claude Desktop / Claude Code config:
```json
{
"mcpServers": {
"zenoti": {
"command": "npx",
"args": ["-y", "zenoti-mcp-server"],
"env": {
"ZENOTI_API_KEY": "your_api_key",
"ZENOTI_CENTER_ID": "your_center_guid"
}
}
}
}
```
The server starts even without credentials and returns a readable configuration error on each tool call, so a misconfigured client shows the problem instead of crash-looping.
## Tools
### Catalog
| Tool | Purpose |
|---|---|
| `zenoti-centers-list` | List organization centers (find center GUIDs) |
| `zenoti-services-list` | Services of a center (compact by default, `verbose` for raw) |
| `zenoti-services-get` | Full service details |
| `zenoti-therapists-list` | Therapists of a center, optionally filtered to a service |
| `zenoti-employee-schedules` | Who is on shift for a date range (date-scoped staffing) |
### Guests
| Tool | Purpose |
|---|---|
| `zenoti-guests-search` | Search by name/email/phone/code/tags (≥1 criterion required) |
| `zenoti-guests-create` | Create a guest profile |
| `zenoti-guests-get` | Guest details by GUID |
| `zenoti-guests-update` | Update fields — does a safe read-merge-write (Zenoti replaces the whole object on update) |
| `zenoti-guests-appointments` | Appointment history for a guest |
| `zenoti-guests-notes-list` / `zenoti-guests-notes-add` | Guest notes and profile alerts |
| `zenoti-guests-memberships` | Guest's memberships (status, credits, dues) |
| `zenoti-guests-packages` | Guest's packages/series with remaining redemptions |
### Appointments
| Tool | Purpose |
|---|---|
| `zenoti-appointments-list` | Center appointments for a date range (compact by default) |
| `zenoti-appointments-get` | Appointment details (`appointment_id`) |
| `zenoti-appointments-checkin` / `zenoti-appointments-undo-checkin` | Check-in state (`appointment_group_id`) |
| `zenoti-appointments-noshow` | Mark no-show (`appointment_group_id`) |
| `zenoti-appointments-progress` | Start/open/complete service (`appointment_id`) |
| `zenoti-appointments-cancel` | Cancel a booking (by `invoice_id`) |
| `zenoti-appointments-reschedule` | Start a reschedule (creates a booking draft tied to the invoice) |
### Booking flow
1. `zenoti-availability-slots` — creates a booking draft for guest+service+date and returns `booking_id` plus open slots
2. `zenoti-booking-reserve` — hold a chosen slot (holds are short-lived)
3. `zenoti-booking-confirm` — finalize; the response includes the invoice
### Invoices
| Tool | Purpose |
|---|---|
| `zenoti-invoices-get` | Invoice with line items, payments, and optional dues/fees |
| `zenoti-invoices-pay-custom` | Record a cash/custom payment (financial action) |
| `zenoti-invoices-pay-card` | Charge the guest's card on file (financial action) |
| `zenoti-invoices-close` | Close a fully-paid invoice |
| `zenoti-invoices-email` | Email the receipt/invoice to the guest |
| `zenoti-invoices-confirm-visit` | Confirm (or undo-confirm) the visit — a scheduling status, despite living on the invoice |
### Reports
| Tool | Purpose |
|---|---|
| `zenoti-reports-sales` | What was sold over a date range |
| `zenoti-reports-collections` | Money received over a date range |
### Forms & feedback
| Tool | Purpose |
|---|---|
| `zenoti-forms-list` / `zenoti-forms-get` | Forms and submitted form data of an appointment |
| `zenoti-guest-forms-list` | Forms on a guest profile |
| `zenoti-feedback-submit` | Insert externally collected guest feedback |
## Zenoti API gotchas this server handles
- **Three identifiers in one workflow.** Check-in/no-show/feedback take `appointment_group_id`; details/progress/forms take `appointment_id`; cancel and payments take `invoice_id`. Tool schemas name the exact one required.
- **Guest updates replace the whole object.** The update tool fetches the current profile and merges your changes; a naive partial `PUT` would erase fields.
- **Rate limit is 60 calls/minute** (org-wide). The HTTP layer honors `Retry-After` on 429. Reads also retry on 5xx/network failures/timeouts; writes retry **only** on 429 (a rate-limit rejection was never processed, so retrying can't duplicate a booking — any other write failure surfaces immediately).
- **Pagination caps at `size=100`**; larger values are rejected by Zenoti with a 422.
- **Webhooks have no subscription API** — they are configured in the Zenoti web UI (Admin → Webhooks) and require the Zenoti API package. This server intentionally has no webhook tool.
- **Undocumented numeric enums.** Gender and appointment-progress codes aren't in Zenoti's public docs; the mappings used here are documented in the tool descriptions and can be overridden (`progress_code`).
## Development
```bash
npm run dev # run from source (tsx)
npm test # unit + end-to-end tests (mock Zenoti API; no real credentials needed)
npm run typecheck # tsc --noEmit
npm run build # emit dist/
```
The e2e suite spawns the real server over stdio via the MCP SDK client and asserts every tool's method, path, query, and body against Zenoti's documented endpoints.
## License
MIT