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
Replace <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

ScopeAllows
readGET requests only. Any write returns 403.
read_writeFull 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": "..."}.

CodeMeaning
200 / 201Success.
400Invalid request (e.g. unknown customer_id).
401Missing, invalid or revoked API key.
403Read-only key attempted a write, or workspace inactive.
404Resource not found.
422Validation error (missing/invalid field).
429Rate 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.

GET/v1/customers

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_…"
GET/v1/customers/{id}

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"
}
POST/v1/customers read_write

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"}'
PATCH/v1/customers/{id} read_write

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.

GET/v1/services

List services. Query: active (true/false) to filter.

GET/v1/services/{id}

Retrieve one service.

Providers

Staff who deliver services. Read-only. Fields: id, name, title, email, phone, color, slug, is_active, is_bookable, sort_order.

GET/v1/providers

List providers. Query: active to filter.

GET/v1/providers/{id}

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.

GET/v1/appointments

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_…"
GET/v1/appointments/{id}

Retrieve one appointment.

POST/v1/appointments read_write

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"
  }'
The write endpoint does not run availability/double-booking checks - validate the slot against GET /v1/appointments first if that matters to your integration.
PATCH/v1/appointments/{id} read_write

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.