Availability
GET /public/sites/{slug}/rooms/{roomId}/availability
Section titled “GET /public/sites/{slug}/rooms/{roomId}/availability”curl -s "https://api.mrcharles.app/api/v1/public/sites/studio-uno-madrid/rooms/d9a36c09-…/availability?from=2026-10-08&to=2026-10-10"| Auth | none |
| Cache | no-store — always fetch fresh |
| Rate limit | 120 req/min per IP |
| Parameter | Required | Default | Notes |
|---|---|---|---|
from | no | today | YYYY-MM-DD in the studio’s time zone |
to | no | from | Inclusive. A window longer than 31 days is clipped |
Response
Section titled “Response”{ "roomId": "d9a36c09-d151-5d4a-a077-296293b07be4", "timezone": "Europe/Madrid", "hourlyCents": 2500, "currency": "EUR", "minMinutes": 60, "maxMinutes": 240, "stepMinutes": 60, "minNoticeMin": 60, "maxAdvanceDays": 90, "days": [ { "date": "2026-10-08", "free": [{ "start": "09:00", "end": "22:00" }] }, { "date": "2026-10-09", "free": [{ "start": "09:00", "end": "13:00" }, { "start": "15:30", "end": "23:00" }] }, { "date": "2026-10-10", "free": [] } ]}| Field | Meaning |
|---|---|
free[] | Continuous open spans, not slots. end is exclusive |
minNoticeMin | A booking must start at least this many minutes from now |
maxAdvanceDays | Nothing can be booked beyond this horizon |
stepMinutes | The grid start times sit on, and the increment between durations |
Spans already exclude: confirmed bookings, live holds, the studio’s own calendar, buffers between bookings, the minimum notice and the horizon. A day the room is closed comes back with "free": [].
Turning spans into start times
Section titled “Turning spans into start times”const toMin = (t) => +t.slice(0, 2) * 60 + +t.slice(3, 5);const toHHMM = (m) => `${String(Math.floor(m / 60)).padStart(2, '0')}:${String(m % 60).padStart(2, '0')}`;
function startTimes(free, duration, step) { const out = []; for (const { start, end } of free) { const s = toMin(start), e = toMin(end); for (let t = Math.ceil(s / step) * step; t + duration <= e; t += step) out.push(toHHMM(t)); } return out;}
startTimes([{ start: '09:00', end: '13:00' }], 120, 60);// ['09:00', '10:00', '11:00']And the durations that still fit from a chosen start:
function durations(free, start, { minMinutes, maxMinutes, stepMinutes }) { const s = toMin(start); const span = free.find((f) => toMin(f.start) <= s && s < toMin(f.end)); if (!span) return []; const room = toMin(span.end) - s; const out = []; for (let d = minMinutes; d <= maxMinutes && d <= room; d += stepMinutes) out.push(d); return out;}Errors
Section titled “Errors”| Status | Code | Cause |
|---|---|---|
| 404 | RESOURCE_NOT_FOUND | Unknown slug or room |
| 404 | SITE_NOT_LIVE | The studio’s site is not published |
| 409 | ROOM_NOT_BOOKABLE | The room does not take online bookings |