Quickstart
A key, a first call, and a real booking. Five commands.
1. Get a key#
In the app: Settings → Integrations → API keys → Create key. Choose Read and write if you want to follow this to the end, and copy the key. Then, so the rest of the page can be pasted as-is:
export BB_KEY=bbk_your_key_here
export BB=https://api.marubox.jp/v1
2. Check it works#
curl -s $BB/me -H "Authorization: Bearer $BB_KEY"
You should get your account back, with the scopes the key carries:
{
"id": "6ab462cc1a2501386f745820",
"name": "Your Name",
"email": "[email protected]",
"handle": "your-handle",
"locale": "en",
"timezone": "Asia/Tokyo",
"bookingPageUrl": "https://app.marubox.jp/your-handle",
"plan": "standard",
"scopes": ["event_types:read", "bookings:read", "bookings:write", "webhooks:manage"]
}
A 401 here means the key is wrong, or revoked, or you sent Bearer without the space.
3. See what can be booked#
curl -s "$BB/event-types" -H "Authorization: Bearer $BB_KEY"
Each item has an id you will need next, a name resolved to a plain string, a nameI18n with the original per-language values, and durationMinutes. Keep one id:
export ET=6ab8a7d51aa02b70af1eba43
4. Find a free time#
Never guess at a time — ask. This is the same availability the public booking page uses, so it already accounts for your working hours, buffers, minimum notice, existing bookings and anything busy on your connected calendar.
curl -s "$BB/event-types/$ET/slots?from=2026-11-27&to=2026-12-04" \
-H "Authorization: Bearer $BB_KEY"
{
"slots": ["2026-11-27T00:00:00.000Z", "2026-11-27T00:30:00.000Z", "…"],
"calendarBlocked": false
}
from and to are UTC dates, not the host's: the window runs from 00:00:00Z on from to 23:59:59Z on to. For a host whose time zone is behind UTC that is not their calendar day — asking for a single date returns the tail of the previous local evening and omits the end of the day you meant, with no error to tell you. If you want one local day, ask for a day either side and filter the returned instants in the zone you care about. The results are always UTC instants, so converting them is yours to do.
calendarBlocked is the one to watch, but not for the reason you might expect: true means we could not reach the connected calendar, and rather than offer times we cannot stand behind, we offer none. slots comes back empty and booking is refused with 409 calendar_unavailable until the host reconnects it. So an empty list with calendarBlocked: true is not "no free times" — it is "ask again later", and it is worth telling your user the difference.
5. Make a booking#
curl -s -X POST $BB/bookings \
-H "Authorization: Bearer $BB_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"eventTypeId": "'$ET'",
"start": "2026-11-27T00:00:00.000Z",
"booker": {
"name": "Aiko Tanaka",
"email": "[email protected]",
"timezone": "Asia/Tokyo"
},
"notes": "Created from the quickstart"
}'
201 and the booking comes back. The customer is emailed a confirmation and the event is written to your calendar, exactly as if you had added it by hand in the app. Pass "notify": false to skip all of that while you are experimenting.
Two headers earn their place here:
Idempotency-Key— a UUID you make up. If the request times out you cannot know whether the booking was made, and a blind retry double-books somebody. Send the same key and you get the same booking back instead of a second one, for 24 hours. One exception, because it is the case that bites: if that booking has since been cancelled, the key is forgotten and your request books afresh — so rebooking the same customer into the same time still works, rather than handing you back the cancellation. Covered on the reference.Content-Type: application/json— without it the body is not parsed and you get a validation error about missing fields.
If the time has gone, you get 409 and no booking: the API never double-books, and there is no way to ask it to.
{"error":{"code":"slot_unavailable","message":"that time is not available"}}
6. Cancel it#
curl -s -X POST $BB/bookings/BOOKING_ID/cancel \
-H "Authorization: Bearer $BB_KEY" \
-H "Content-Type: application/json" \
-d '{"message": "Tidying up after the quickstart"}'
The customer is emailed, and a paid booking is refunded in full. Cancelling twice is safe: the second call returns the booking unchanged and sends nothing.
Next#
- Webhooks so you hear about changes instead of polling for them.
- The reference for the filters on
GET /bookings, which is what a polling trigger needs. - Errors before you write the error branch.
Common questions
Can I create a booking at any time I like, or only a listed slot?
Only a time that is genuinely free. The API checks availability the same way the booking page does and returns 409 slot_unavailable otherwise. There is no override.Does an API booking email the customer?
Yes, unless you send "notify": false. That stops the customer’s email and nothing else — the calendar entry is still written and booking.created still fires, because a subscriber asked to be told when a booking is made, not when a customer is emailed.Why did my POST fail with validation_failed when the JSON looked right?
Usually a missing Content-Type: application/json header, which means the body was never parsed. The details array in the error names each field it could not find.Last reviewed 28 September 2026.