Bookings
Show your booking types and open times on your own website, and book a time from your own form.
Use these endpoints to build your own booking form, for example a "Reserve studio time" page that matches the rest of your site. You set up booking types, hours and questions in the dashboard. Your site reads the open times and books one, and the person gets the same confirmation email and calendar entry as when they book on your Lahuta booking page.
| You want to | Use |
|---|---|
| List what people can book | GET /v1/bookings/types |
| Show the open times for a range of days | GET /v1/bookings/types/{slug}/slots?from=...&to=... |
| Book a time | POST /v1/bookings |
If you don't need a custom form, link to your hosted booking page instead: lahuta.org/<your-org>/book/<type slug>. See Booking pages.
List booking types
GET /v1/bookings/types returns your active booking types as a plain array, with the questions each one asks.
curl https://api.lahuta.org/v1/bookings/types \
-H "X-Api-Key: $LAHUTA_API_KEY"[
{
"id": "0197a7e3-5c02-7b19-8f4a-2d6e1c9b3a58",
"slug": "open-studio",
"title": "Open studio time",
"description": "Two hours at a wheel, with clay and tools.",
"durationMinutes": 120,
"timezone": "America/Los_Angeles",
"instructions": "Wear clothes you don't mind getting muddy.",
"emailField": "required",
"phoneField": "optional",
"questions": [
{
"id": "0197a7e3-5c10-7e62-9b05-4a8f3d1c7e29",
"question": "Have you thrown on a wheel before?",
"type": "radio",
"options": ["Never", "A few times", "Regularly"],
"required": true
}
],
"organizer": { "name": "Northside Pottery", "slug": "northside-pottery" }
}
]emailField and phoneField tell you which contact details your form must ask for. Build your form's extra fields from questions.
Show open times
Ask for the slots of one type between two dates. Days are counted in the booking type's time zone, and both dates are included.
curl "https://api.lahuta.org/v1/bookings/types/open-studio/slots?from=2026-10-05&to=2026-10-11" \
-H "X-Api-Key: $LAHUTA_API_KEY"[
{
"startsAt": "2026-10-05T17:00:00.000Z",
"endsAt": "2026-10-05T19:00:00.000Z"
},
{
"startsAt": "2026-10-05T19:00:00.000Z",
"endsAt": "2026-10-05T21:00:00.000Z"
},
{
"startsAt": "2026-10-06T17:00:00.000Z",
"endsAt": "2026-10-06T19:00:00.000Z"
}
]- Only times that are still open and still ahead are returned. Past times and taken times are left out.
fromandtoare both required, asYYYY-MM-DD. A range longer than 62 days is cut to its first 62 days.- Times are UTC. Show them in the booking type's
timezone, or the visitor's, and say which.
Book a time
Send the time the person picked, who they are, and their answers.
curl -X POST https://api.lahuta.org/v1/bookings \
-H "X-Api-Key: $LAHUTA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"key": "studio-form:3307",
"type": "open-studio",
"startsAt": "2026-10-05T17:00:00.000Z",
"contact": {
"kind": "person",
"firstName": "Alex",
"lastName": "Moreno",
"emails": [{ "label": null, "value": "alex.moreno@example.com" }],
"phones": [{ "label": null, "value": "+15035550192" }]
},
"answers": {
"0197a7e3-5c10-7e62-9b05-4a8f3d1c7e29": "A few times"
},
"source": "website"
}'{
"bookingId": "0197b355-0d61-7a2f-8c94-1e5b7d3f2a06",
"contactId": "0197b355-0d4e-7f10-9a38-6c2e4b8d1f53",
"startsAt": "2026-10-05T17:00:00.000Z",
"endsAt": "2026-10-05T19:00:00.000Z",
"manageUrl": "https://lahuta.org/bookings/q2Vt8LmR4xPz",
"opened": true
}| Field | Required | What it does |
|---|---|---|
key | Yes | Your name for this booking. Sending it again returns the same booking with "opened": false, and nobody is booked or emailed twice |
type | Yes | The booking type's slug |
startsAt | Yes | One of the startsAt values from the slots call |
contact | Yes | Who's booking. Saved the same way as PUT /v1/contacts. The confirmation goes to their first email address and first phone number |
answers | No | Answers to the type's questions, keyed by question id |
source | No | Where the booking came from, up to 100 characters. Saved on the booking, and on the contact if it has no source yet |
manageUrl is the person's own page for moving or cancelling the booking. Their confirmation email links to it with a Manage your booking button, so you only need it if you want to show it on your thank-you page.
Answer formats
Question type | Answer |
|---|---|
text | A string |
number | A number |
date | "YYYY-MM-DD" |
radio | One of the question's options, as a string |
checkbox | true or false. A required checkbox must be true |
multiSelect | An array of the question's options |
address | An object with street, city, state and zip |
What happens next
- The person gets a confirmation email with a calendar entry, the same one your booking page sends.
- The booking shows up under Bookings in the dashboard, and the person is saved to your CRM.
- Your team gets an email, for everyone who has New bookings turned on under Settings → Notifications.
Errors
| Error | When | What to do |
|---|---|---|
404 NotFound | No active booking type has that slug | Refresh your list of types |
409 SlotUnavailable | The time was taken or isn't open anymore | Fetch the slots again and let the person pick another |
409 BookingClosed | The booking type stopped taking bookings | Stop offering it |
422 AnswersInvalid | Required questions are unanswered, or answers have the wrong shape. questionIds lists them | Mark those questions on your form |
422 ContactInfoRequired | A required email or phone is missing, or neither was given. fields lists what's needed | Ask for those fields |
429 TooManyRequests | More than 300 bookings this hour | Wait retryAfterSeconds. See Rate limits |
Two people can pick the same time at once. The first booking wins and the second gets SlotUnavailable, so always handle it.
See Bookings in the API reference for every field.