Skip to content
Features AI front desk Switch Pricing FAQ
FR Log in Start free
Start free Features AI front desk Switch Pricing FAQ Try the live demo Log in Français

The Rndez API

Read a Rndez shop from your own code: the shop, its services and team, free times, bookings and clients. Free on every plan, Free included.

Version 1 · read only for now

  • Start in 5 minutes
  • Keys and permissions
  • Test mode
  • Limits
  • Errors
  • Paging and sync
  • Every route

Start in 5 minutes

  1. Log in to Rndez. Open Setup → Connections.
  2. Tap Make a key. Name it, leave “See the shop” and “See bookings” ticked. Turn on Test key to try it on a made-up shop.
  3. Copy the key. You see it once.
  4. Call the API:
export RNDEZ_KEY="rndez_test_…"
curl "https://rndez.com/api/v1/open/shop" -H "Authorization: Bearer $RNDEZ_KEY"

The answer:

{ "data": { "id": "test-biz_…", "name": "Demo Studio (test)", "timeZone": "America/Toronto", "test": true, … } }

Every route is under https://rndez.com/api/v1/open. Answers are JSON. Money is in cents. Dates are YYYY-MM-DD; times are minutes after midnight in the shop’s time zone, and also ISO times in UTC.

The machine-readable description (OpenAPI 3.1): /api/v1/open/openapi.json

Keys and permissions

Send the key in every call: Authorization: Bearer rndez_live_…. Keep it on your server, never in a web page or an app people download: anyone with the key reads what it may read.

  • shop:read: See the shop (details, services, team, hours, days closed, free times)
  • bookings:read: See bookings (with the client’s name only)
  • clients:read: See clients (emails, phones, notes, visits; also the client’s contact on bookings)
  • money:read: See money (prices, deposits, paid, tips)

A staff member can make a key for themselves only. It sees the shop and their own bookings, never the client list or money.

The owner sees when each key was last used and every call in the last 30 days, and can turn a key off in one tap. A key that is turned off stops at once.

Test mode

A key that starts with rndez_test_ reaches a private copy of the demo shop, made for your shop: made-up team, services, bookings and clients. It never reaches real data and never sends an email or a text. Switch to a rndez_live_ key when your code works.

Limits

  • Free: 60 calls a minute per key.
  • Solo: 300 a minute.
  • Shop: 1,000 a minute, per location.

Every answer says where you are: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset. Over the limit you get 429 and a Retry-After header with the seconds to wait. The shop keeps working.

Errors

Every refusal has the same shape, in English and French, with the parameter that is wrong:

{ "error": { "code": "invalid", "message": "limit must be a number from 1 to 500.", "messageFr": "Demande invalide.", "field": "limit" } }
  • 400 invalid: a bad parameter.
  • 401 unauthorized: no key, a wrong key, or a key that is turned off.
  • 403 scope: the key does not have that permission.
  • 404 not_found: nothing with that id in this shop.
  • 429 slow_down: over the limit. Wait Retry-After seconds.

Paging and sync

Lists answer { "data": [...], "next": "…" }. Pass next as ?cursor= to get the next page; it is null on the last one. 100 a page; ?limit= up to 500.

To keep a copy in sync, save the time of your last call and ask only for what changed: ?updatedSince=2026-10-13T09:00:00Z. Cancelled bookings stay in the list with status: "cancelled".

let cursor = null;
const all = [];
do {
  const url = new URL("https://rndez.com/api/v1/open/bookings");
  url.searchParams.set("updatedSince", lastSync);
  if (cursor) url.searchParams.set("cursor", cursor);
  const answer = await fetch(url, { headers: { Authorization: `Bearer ${process.env.RNDEZ_KEY}` } });
  if (answer.status === 429) { await new Promise((r) => setTimeout(r, Number(answer.headers.get("retry-after")) * 1000)); continue; }
  const page = await answer.json();
  all.push(...page.data);
  cursor = page.next;
} while (cursor);

Every route

All of them are GET for now. Making and changing bookings and clients, and webhooks, come next.

GET /shop

The shop

Permission: shop:read (See the shop)

curl "https://rndez.com/api/v1/open/shop" \
  -H "Authorization: Bearer $RNDEZ_KEY"
const answer = await fetch("https://rndez.com/api/v1/open/shop", {
  headers: { Authorization: `Bearer ${process.env.RNDEZ_KEY}` },
});
const { data } = await answer.json();

GET /services

Services and add-ons

Permission: shop:read (See the shop)

curl "https://rndez.com/api/v1/open/services" \
  -H "Authorization: Bearer $RNDEZ_KEY"
const answer = await fetch("https://rndez.com/api/v1/open/services", {
  headers: { Authorization: `Bearer ${process.env.RNDEZ_KEY}` },
});
const { data } = await answer.json();

GET /staff

The team

Permission: shop:read (See the shop)

curl "https://rndez.com/api/v1/open/staff" \
  -H "Authorization: Bearer $RNDEZ_KEY"
const answer = await fetch("https://rndez.com/api/v1/open/staff", {
  headers: { Authorization: `Bearer ${process.env.RNDEZ_KEY}` },
});
const { data } = await answer.json();

GET /hours

Opening hours

Permission: shop:read (See the shop)

curl "https://rndez.com/api/v1/open/hours" \
  -H "Authorization: Bearer $RNDEZ_KEY"
const answer = await fetch("https://rndez.com/api/v1/open/hours", {
  headers: { Authorization: `Bearer ${process.env.RNDEZ_KEY}` },
});
const { data } = await answer.json();

GET /closures

Days closed and time off

Permission: shop:read (See the shop)

curl "https://rndez.com/api/v1/open/closures" \
  -H "Authorization: Bearer $RNDEZ_KEY"
const answer = await fetch("https://rndez.com/api/v1/open/closures", {
  headers: { Authorization: `Bearer ${process.env.RNDEZ_KEY}` },
});
const { data } = await answer.json();

GET /availability

Free times. The same free times the booking page shows (same rules: hours, notice, booking window, no double booking).

Permission: shop:read (See the shop)

  • service (required): A service id from /services.
  • staff: A staff id. Leave out for anyone.
  • from: First date, YYYY-MM-DD (default: today in the shop's time zone).
  • to: Last date (default: from). At most 31 days.
curl "https://rndez.com/api/v1/open/availability?service=cut&from=2026-10-20&to=2026-10-21" \
  -H "Authorization: Bearer $RNDEZ_KEY"
const answer = await fetch("https://rndez.com/api/v1/open/availability?service=cut&from=2026-10-20&to=2026-10-21", {
  headers: { Authorization: `Bearer ${process.env.RNDEZ_KEY}` },
});
const { data } = await answer.json();

GET /bookings

Bookings. Sorted by date and time. Cancelled bookings are included (status cancelled). A staff key sees only that person's bookings.

Permission: bookings:read (See bookings)

  • from: First date, YYYY-MM-DD.
  • to: Last date. At most 366 days after from.
  • staff: Only this staff member.
  • status: Only this status.
  • limit: How many per page: 1 to 500 (default 100). Above 500 counts as 500.
  • cursor: The `next` value from the page before.
  • updatedSince: Only what changed at or after this moment (ISO 8601, e.g. 2026-10-13T09:00:00Z). Records saved before updatedAt existed use createdAt.
curl "https://rndez.com/api/v1/open/bookings?from=2026-10-20&to=2026-10-21" \
  -H "Authorization: Bearer $RNDEZ_KEY"
const answer = await fetch("https://rndez.com/api/v1/open/bookings?from=2026-10-20&to=2026-10-21", {
  headers: { Authorization: `Bearer ${process.env.RNDEZ_KEY}` },
});
const { data, next } = await answer.json();

GET /bookings/{id}

One booking

Permission: bookings:read (See bookings)

  • id (required): The booking id.
curl "https://rndez.com/api/v1/open/bookings/b_4Tq9…" \
  -H "Authorization: Bearer $RNDEZ_KEY"
const answer = await fetch("https://rndez.com/api/v1/open/bookings/b_4Tq9…", {
  headers: { Authorization: `Bearer ${process.env.RNDEZ_KEY}` },
});
const { data } = await answer.json();

GET /clients

Clients. Sorted by name. A shop in a group that shares clients sees the group's list.

Permission: clients:read (See clients)

  • q: Search by name, email or phone.
  • limit: How many per page: 1 to 500 (default 100). Above 500 counts as 500.
  • cursor: The `next` value from the page before.
  • updatedSince: Only what changed at or after this moment (ISO 8601, e.g. 2026-10-13T09:00:00Z). Records saved before updatedAt existed use createdAt.
curl "https://rndez.com/api/v1/open/clients" \
  -H "Authorization: Bearer $RNDEZ_KEY"
const answer = await fetch("https://rndez.com/api/v1/open/clients", {
  headers: { Authorization: `Bearer ${process.env.RNDEZ_KEY}` },
});
const { data, next } = await answer.json();

GET /clients/{id}

One client

Permission: clients:read (See clients)

  • id (required): The client id (cl_…).
curl "https://rndez.com/api/v1/open/clients/cl_8fJ2kQ…" \
  -H "Authorization: Bearer $RNDEZ_KEY"
const answer = await fetch("https://rndez.com/api/v1/open/clients/cl_8fJ2kQ…", {
  headers: { Authorization: `Bearer ${process.env.RNDEZ_KEY}` },
});
const { data } = await answer.json();

Our promise

/v1 is never broken. New things are added; anything removed gets 12 months of notice by email. Questions? Write to us.

Product

FeaturesAI front deskPricingTry the live demoFAQNo-show policy generator

Made for

BarbersHair salonsNail salonsTattoo studiosEstheticians and waxingMassage therapistsMontréal and Québec

Switch from

How switching worksFreshaBooksySquareVagaroResurvaBooksy vs Fresha

Rndez

Contact usCreators: get paid for videosPrivacyTermsCookie choicesFrançaisMade in Montréal

© 2026 Rndez

We use cookies to see how visitors use this site. Nothing turns on unless you agree. Our privacy policy