Quickstart
A key, a first call, and a real booking. Five commands.
1. Get a key#
In the app: Settings → API keys and webhooks → 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&timezone=Asia/Tokyo" \
-H "Authorization: Bearer $BB_KEY"
{
"slots": ["2026-11-27T00:00:00.000Z", "2026-11-27T00:30:00.000Z", "…"],
"calendarBlocked": false
}
calendarBlocked is the one to watch: true means we could not reach the connected calendar, so the list may be optimistic. Treat it as "do not book unattended" rather than as a free hand.
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. 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 also skips the calendar write and the booking.created webhook, which makes it the right setting while you are testing.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.