InvuBook API
A REST API to read and manage your workspace's customers, services, providers and appointments. JSON over HTTPS, authenticated with an API key.
Introduction
Every request is made to your workspace's base URL:
https://<your-workspace>.invubook.com/api/v1
<your-workspace> with your subdomain (the one you sign in on). The exact base URL for your workspace is shown in Settings → Developers → API keys. During the current rollout, *.invubook.com hosts also work.The API key alone determines which workspace you're acting on, so you never pass a workspace id in requests - a key for one workspace can only ever see that workspace's data.
Authentication
Create keys in Settings → Developers → API keys. The full key (ivb_live_…) is shown once at creation - store it somewhere safe; it can't be retrieved again. Send it on every request as a bearer token:
curl https://acme.invubook.com/api/v1/customers \
-H "Authorization: Bearer ivb_live_xxxxxxxxxxxxxxxxxxxx"
The X-API-Key header is also accepted:
curl https://acme.invubook.com/api/v1/customers \
-H "X-API-Key: ivb_live_xxxxxxxxxxxxxxxxxxxx"
Scopes
| Scope | Allows |
|---|---|
| read | GET requests only. Any write returns 403. |
| read_write | Full read and write (GET, POST, PATCH). |
Revoke a key any time from the same screen; revoked keys immediately return 401.
Rate limits
Each key is limited to 600 requests per minute. Exceeding it returns 429 Too Many Requests; back off and retry.
Errors
Errors use standard HTTP status codes and a JSON body: {"detail": "..."}.
| Code | Meaning |
|---|---|
200 / 201 | Success. |
400 | Invalid request (e.g. unknown customer_id). |
401 | Missing, invalid or revoked API key. |
403 | Read-only key attempted a write, or workspace inactive. |
404 | Resource not found. |
422 | Validation error (missing/invalid field). |
429 | Rate limit exceeded. |
Pagination & responses
List endpoints return an object with a data array and accept limit and offset query params. Single-object endpoints return the object directly.
GET /v1/customers?limit=50&offset=100
{
"data": [ { "id": "…", "first_name": "Ada", … }, … ]
}
Customers
People in your workspace. Fields: id, first_name, last_name, email, phone, dob, notes, custom (object), flag_status, flag_reason, no_show_count, created_at.
List / search customers. Query: q (search name, email, phone), limit (1-200, default 50), offset.
curl "https://acme.invubook.com/api/v1/customers?q=ada&limit=20" \
-H "Authorization: Bearer ivb_live_…"
Retrieve a single customer.
{
"id": "12295a07-1f71-4942-a9a1-70b92f91a751",
"first_name": "Nadine",
"last_name": "Sandhu",
"email": "nadine@example.com",
"phone": "6478501212",
"dob": null,
"notes": null,
"custom": {},
"flag_status": "none",
"no_show_count": 0,
"created_at": "2026-08-02T02:29:52Z"
}
Create a customer. first_name is required; everything else is optional.
curl -X POST https://acme.invubook.com/api/v1/customers \
-H "Authorization: Bearer ivb_live_…" \
-H "Content-Type: application/json" \
-d '{"first_name":"Ada","last_name":"Lovelace","email":"ada@example.com","phone":"4165551234"}'
Update any subset of fields. Only the fields you send are changed.
curl -X PATCH https://acme.invubook.com/api/v1/customers/12295a07-… \
-H "Authorization: Bearer ivb_live_…" \
-H "Content-Type: application/json" \
-d '{"phone":"6470000000","notes":"Prefers mornings"}'
Services
Bookable services. Read-only. Fields: id, name, short_name, duration_min, price, color, capacity, is_active, sort_order.
List services. Query: active (true/false) to filter.
Retrieve one service.
Providers
Staff who deliver services. Read-only. Fields: id, name, title, email, phone, color, slug, is_active, is_bookable, sort_order.
List providers. Query: active to filter.
Retrieve one provider.
Appointments
Bookings. Fields: id, provider_id, service_id, customer_id, location_id, starts_at, ends_at, status, source, notes, price, title, custom, created_at.
List appointments. Query: from, to (ISO-8601 window on starts_at), status, customer_id, provider_id, limit (1-500, default 100), offset.
curl "https://acme.invubook.com/api/v1/appointments?from=2026-09-01T00:00:00Z&to=2026-09-08T00:00:00Z" \
-H "Authorization: Bearer ivb_live_…"
Retrieve one appointment.
Create an appointment. Required: provider_id, service_id, customer_id, starts_at (ISO-8601). The end time and price are derived from the service, so they always stay in sync.
curl -X POST https://acme.invubook.com/api/v1/appointments \
-H "Authorization: Bearer ivb_live_…" \
-H "Content-Type: application/json" \
-d '{
"provider_id": "…",
"service_id": "…",
"customer_id": "…",
"starts_at": "2026-09-01T14:30:00Z",
"status": "confirmed"
}'
GET /v1/appointments first if that matters to your integration.Update an appointment. Send any subset of starts_at (reschedule - ends_at is recomputed from the service), status, provider_id, notes.
curl -X PATCH https://acme.invubook.com/api/v1/appointments/… \
-H "Authorization: Bearer ivb_live_…" \
-H "Content-Type: application/json" \
-d '{"status":"cancelled"}'
Common status values: confirmed, arrived, completed, cancelled, no_show.
InvuBook API v1 · Need help? Contact support from your dashboard.