Skip to content

Quickstart

Four calls take a guest from “when is the room free?” to a confirmed booking. Everything below runs against pre-production, where verification codes come back in the response so you can script the whole flow.

  1. Read the site and pick a room

    Terminal window
    curl -s https://api-pre.mrcharles.app/api/v1/public/sites/studio-uno-madrid
    {
    "success": true,
    "data": {
    "studio": { "name": "Studio Uno", "slug": "studio-uno-madrid", "timezone": "Europe/Madrid", "city": "Madrid" },
    "rooms": [
    {
    "id": "d9a36c09-d151-5d4a-a077-296293b07be4",
    "name": "Rehearsal B",
    "hourlyCents": 2500, "currency": "EUR",
    "bookable": true,
    "minMinutes": 60, "maxMinutes": 240, "stepMinutes": 60,
    "paymentModes": ["on_site"], "depositPercent": 100, "cancelNoticeHours": 24
    }
    ],
    "bookingOnline": true,
    "cardPayments": true
    }
    }

    Only rooms with "bookable": true accept online bookings. Full shape: Site payload.

  2. Ask when it is free

    Terminal window
    curl -s "https://api-pre.mrcharles.app/api/v1/public/sites/studio-uno-madrid/rooms/d9a36c09-d151-5d4a-a077-296293b07be4/availability?from=2026-10-08&to=2026-10-10"
    {
    "success": true,
    "data": {
    "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": "23:00" }] }
    ]
    }
    }

    free holds continuous open spans in the studio’s time zone, already excluding other bookings, live holds, buffers, the minimum notice and the horizon. Slice them into start times with stepMinutes. See Availability.

  3. Hold the slot and send the code

    Terminal window
    curl -s -X POST https://api-pre.mrcharles.app/api/v1/public/sites/studio-uno-madrid/holds \
    -H 'Content-Type: application/json' \
    -d '{
    "roomId": "d9a36c09-d151-5d4a-a077-296293b07be4",
    "date": "2026-10-08",
    "startTime": "17:00",
    "durationMinutes": 120,
    "guestName": "Marta Ruiz",
    "channel": "email",
    "contact": "marta@example.com",
    "locale": "es",
    "idempotencyKey": "6f1c0e3a-6b0e-4f0c-9f1a-2a8f1b7c4d55"
    }'
    {
    "success": true,
    "data": {
    "holdId": "b59b9109-1beb-44fa-a679-c2d612844c62",
    "status": "pending",
    "roomName": "Rehearsal B",
    "date": "2026-10-08", "startTime": "17:00", "endTime": "19:00",
    "timezone": "Europe/Madrid",
    "price": { "priceCents": 5000, "depositCents": 5000, "currency": "EUR", "minutes": 120 },
    "channel": "email",
    "contactMasked": "m••••@example.com",
    "expiresAt": "2026-10-06T12:26:23+02:00",
    "resendsLeft": 2,
    "paymentModes": ["on_site"],
    "devCode": "181989"
    }
    }

    The slot is now yours for 12 minutes. The price came back from the server — never compute it yourself. devCode appears only in pre-production without a delivery provider; in production the code is sent by SMS or email.

  4. Verify the code, then confirm

    Terminal window
    curl -s -X POST https://api-pre.mrcharles.app/api/v1/public/holds/b59b9109-.../verify \
    -H 'Content-Type: application/json' -d '{"code":"181989"}'
    curl -s -X POST https://api-pre.mrcharles.app/api/v1/public/holds/b59b9109-.../confirm \
    -H 'Content-Type: application/json' \
    -d '{"paymentMode":"on_site","notes":"Trio, we bring our own cymbals.","email":"marta@example.com"}'
    {
    "success": true,
    "data": {
    "booking": {
    "id": "96f52eb7-3360-486a-8d04-47b1663c049d",
    "reference": "MC-TGRMY5",
    "status": "confirmed",
    "paymentMode": "on_site", "paymentStatus": "unpaid",
    "studioName": "Studio Uno", "roomName": "Rehearsal B",
    "date": "2026-10-08", "startTime": "17:00", "endTime": "19:00",
    "startsAt": "2026-10-08T17:00:00+02:00",
    "priceCents": 5000, "currency": "EUR",
    "cancelNoticeHours": 24, "mayCancel": true
    },
    "manageToken": "1000bb60…47da3a0",
    "manageUrl": "https://studio-uno-madrid.sites.mrcharles.app/r/1000bb60…"
    }
    }

    Done. Show the reference, and give the guest the manageUrl — it is the only way back to the booking.

const API = 'https://api-pre.mrcharles.app/api/v1';
const SLUG = 'studio-uno-madrid';
const json = async (res) => {
const body = await res.json();
if (!res.ok) throw Object.assign(new Error(body.error?.message ?? res.statusText), { code: body.error?.code });
return body.data;
};
// 1 · availability for the next two weeks
const from = new Date().toISOString().slice(0, 10);
const to = new Date(Date.now() + 13 * 864e5).toISOString().slice(0, 10);
const avail = await fetch(`${API}/public/sites/${SLUG}/rooms/${roomId}/availability?from=${from}&to=${to}`).then(json);
// 2 · hold (one idempotency key per attempt — reuse it when retrying)
const idempotencyKey = crypto.randomUUID();
const hold = await fetch(`${API}/public/sites/${SLUG}/holds`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
roomId, date: '2026-10-08', startTime: '17:00', durationMinutes: 120,
guestName, channel: 'phone', contact: '+34600111222', locale: 'es',
idempotencyKey, captchaToken,
}),
}).then(json);
// 3 · verify the 6-digit code the guest received
await fetch(`${API}/public/holds/${hold.holdId}/verify`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code }),
}).then(json);
// 4 · confirm
const { booking, manageUrl } = await fetch(`${API}/public/holds/${hold.holdId}/confirm`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ paymentMode: 'on_site', notes, email }),
}).then(json);