Booking Flow
Consumer-facing endpoints for appointment booking. These endpoints are also used by the BookingJS widget and are optimized for frontend integrations.
No authentication required
Overview
The standard booking flow consists of three steps:
- Retrieve available appointments - list of all bookable slots
- Reserve appointment - 3-minute hold on the selected slot
- Complete booking - finalize the appointment with consumer data
| Method | Endpoint | Description |
|---|---|---|
GET | /resources/:ref/upcoming_bookables | Retrieve available appointments |
POST | /rest/1/resources/:ref/reserve_appointment | Reserve appointment (3 min) |
POST | /resources/:ref/create_appointment_with_consumer | Finalize booking |
POST | /products/active_products | Retrieve active products |
POST | /resources/public_data | Public resource data |
OPTIONS | /resources/:ref/upcoming_bookables | CORS preflight |
1. Retrieve Available Appointments
Retrieves all bookable time slots for one or more resources. The results are grouped by a configurable date format to make a two-level UI (e.g. month view → day list) easy to implement.
GET /resources/{ref}/upcoming_bookables
Path Parameters
| Parameter | Type | Description |
|---|---|---|
ref | string | Resource reference or UUID. Formats: resourceId@providerUuid@platform - full reference; resourceId@platform - short form; uuid - direct UUID of the resource |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
groupFormat | string | No | Joda-Time format for grouping. All bookables with the same value end up in one group. Default: yyyy-MM-dd. Examples: MM-yyyy (monthly), MMMM (month name) |
timeFormat | string | No | Format for formattedStart/formattedEnd. Default: yyyy-MM-dd HH:mm |
languageTag | string | No | IETF BCP 47 language tag for server-side translation (e.g. month names). Example: de_DE, fr_FR |
channelKey | string | No | Booking channel. Default: RESOURCE_PUBLIC. See Channel Keys |
ref | string | No | Additional resource references. Can be specified multiple times to load bookables for several resources at once |
prdRef | string | No | Product reference or UUID. Filters to bookables that support this product. Also takes the product's leadTime/followUpTime into account |
Request
curl -X GET "https://www.timum.de/resources/my-resource@myPlatform/upcoming_bookables?groupFormat=MMMM&languageTag=fr_FR"
Response
The response is an object with dynamic keys based on the groupFormat. It also includes a public_visible flag.
{
"avril 18": [
{
"formattedStart": "2018-04-30 14:00",
"formattedEnd": "2018-04-30 14:30",
"start": "2018-04-30T14:00:00+02:00",
"end": "2018-04-30T14:30:00+02:00",
"timeslot_uuid": "6cb6df60-48d8-11e8-a5e5-263fa1a58213",
"product_uuid": "7fc85970-a2d0-11e2-9cd0-1231430706c1",
"resource_uuid": "636627f0-006c-11ec-a5c8-02e4d9518b64",
"product_name": "Gesellschaftsspiele spielen",
"resource_name": "2nd Level Support",
"contact_channel": null,
"capacity": 1,
"capacity_left": 1,
"products": [],
"kind": "models.Bookable"
}
],
"mai 18": [
{
"appointment_uuid": "864b80c0-483f-11f0-b6e3-72fe2304273f",
"formattedStart": "2018-05-02 14:00",
"formattedEnd": "2018-05-02 14:30",
"start": "2018-05-02T14:00:00+02:00",
"end": "2018-05-02T14:30:00+02:00",
"timeslot_uuid": "267b2d70-48d9-11e8-a5e5-263fa1a58213",
"product_uuid": "0bd09a60-1d8f-11e9-bf75-06ecf2a1ba22",
"resource_uuid": "636627f0-006c-11ec-a5c8-02e4d9518b64",
"product_name": "Video Call 30",
"resource_name": "2nd Level Support",
"contact_channel": {
"type": "location",
"value": "Telefon und Bildschirmfreigabe (wir rufen Sie an)"
},
"capacity": 5,
"capacity_left": 3,
"products": [
{ "uuid": "0bd09a60-1d8f-11e9-bf75-06ecf2a1ba22", "name": "Video Call 30" }
],
"kind": "models.LotAppointment"
}
],
"public_visible": true
}
Bookable Types
| kind | Meaning | Notes |
|---|---|---|
models.Bookable | Slot from an availability (timeslot) | Becomes a LotAppointment on the first booking. Use timeslot_uuid for reserve/create |
models.LotAppointment | Existing group appointment with remaining capacity | Appointment that has already been created. Use appointment_uuid for reserve/create |
Bookable vs LotAppointment
models.Bookable is a potential slot from an availability. As soon as the first consumer books it, it becomes a models.LotAppointment with a new appointment_uuid. For further bookings of the same slot, you must use this new UUID!Response Fields
| Field | Type | Description |
|---|---|---|
start / end | string | ISO 8601 timestamp with time zone |
formattedStart / formattedEnd | string | Time formatted according to timeFormat |
timeslot_uuid | string | UUID of the underlying availability |
appointment_uuid | string? | UUID of the appointment (only for LotAppointment) |
product_uuid | string? | UUID of the product, or null |
resource_uuid | string | UUID of the resource |
capacity | number | Total capacity of the slot |
capacity_left | number | Remaining free spots |
contact_channel | object? | Contact channel with type and value |
products | array | List of available products for this slot |
kind | string | models.Bookable or models.LotAppointment |
Status Codes
| Code | Meaning |
|---|---|
200 | Success, bookables returned |
204 | No bookables available (empty response) |
2. Reserve Appointment
Temporarily reserves an appointment for 3 minutes. During this time, the slot can only be booked by the customer who made the reservation. This prevents double bookings while the form is being filled out.
POST /rest/1/resources/{ref}/reserve_appointment
Always call before booking
create_appointment_with_consumer! Even after the 3 minutes have expired, you can still complete the booking - but if another customer was faster, it will fail.Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
ref | string | No | Resource or channel reference |
channelKey | string | No | Booking channel. Default: RESOURCE_PUBLIC |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
timeslot_uuid | string | Conditional* | UUID of the timeslot (availability). Use for models.Bookable. Applies the availability's default settings to the new appointment |
appointment_uuid | string | Conditional* | UUID of the existing appointment. Required for models.LotAppointment |
product_uuid | string | Yes | UUID of the product to be booked |
from | string | Yes | Start time of the bookable (ISO 8601, UTC) |
to | string | Yes | End time of the bookable (ISO 8601, UTC) |
* For models.Bookable, send timeslot_uuid. For models.LotAppointment, appointment_uuid is required.
Request
curl -X POST "https://www.timum.de/rest/1/resources/my-resource@myPlatform/reserve_appointment" \
-H "Content-Type: application/json" \
-d '{
"timeslot_uuid": "6cb6df60-48d8-11e8-a5e5-263fa1a58213",
"product_uuid": "7fc85970-a2d0-11e2-9cd0-1231430706c1",
"from": "2025-01-15T13:00:00Z",
"to": "2025-01-15T13:30:00Z"
}'
Response
{
"api-info": { "version": "1" },
"participation": {
"uuid": "a48dcf00-483c-11f0-b6e3-72fe2304273f",
"appointment_uuid": "a48d0bb0-483c-11f0-b6e3-72fe2304273f",
"timeslot_uuid": "a48e6b40-483c-11f0-b6e3-72fe2304273f",
"resource_uuid": "636627f0-006c-11ec-a5c8-02e4d9518b64",
"product_uuid": "0bd09a60-1d8f-11e9-bf75-06ecf2a1ba22",
"from": "2025-06-16T12:25:00Z",
"to": "2025-06-16T12:55:00Z",
"appointment_capacity": 1,
"appointment_capacity_left": 0,
"customer_uuid": "a48df610-483c-11f0-b6e3-72fe2304273f",
"customer_mobile": null,
"customer_fullName": null,
"customer_email": null,
"customer_note": null,
"state": "RESERVED",
"formatedAddress": "Telefon und Bildschirmfreigabe (wir rufen Sie an)",
"messages": []
},
"appointments": [
{
"uuid": "a48d0bb0-483c-11f0-b6e3-72fe2304273f",
"kind": "models.LotAppointment",
"product_id": "0bd09a60-1d8f-11e9-bf75-06ecf2a1ba22",
"product_name": "Video Call 30",
"resource_id": "636627f0-006c-11ec-a5c8-02e4d9518b64",
"from": "2025-06-16T12:25:00Z",
"to": "2025-06-16T12:55:00Z",
"state": "ACTIVE",
"capacity": 1,
"capacity_left": 0,
"customers": [
{
"customer_placeholder_id": "a48df610-483c-11f0-b6e3-72fe2304273f",
"customer_uuid": "a48df610-483c-11f0-b6e3-72fe2304273f",
"participationState": "RESERVED",
"participation_id": "a48dcf00-483c-11f0-b6e3-72fe2304273f"
}
]
}
]
}
Save customer_uuid
participation.customer_uuid from the response! You will need this value as placeholder_id for the create_appointment_with_consumer call.Reservation Behavior
- The reservation is valid for 3 minutes
- The
stateisRESERVED - After it expires, the reservation is automatically deleted
capacity_leftis reduced while the reservation is active- You can still book even after the timeout - but without protection against double booking
3. Finalize Booking
Completes the booking with the consumer's data. If a user with the given email or phone number already exists, the appointment is assigned to that account. Otherwise, a new account is created.
POST /resources/{ref}/create_appointment_with_consumer
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
timeFormat | string | No | Format for time values in the response |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
start | string | Yes | Start time (ISO 8601) |
end | string | Yes | End time (ISO 8601) |
timeslot_uuid | string | Yes | UUID of the timeslot or appointment |
product_uuid | string | No | UUID of the product |
placeholder_id | string | No* | participation.customer_uuid from the reserve response. Identifies the reservation |
email | string | Yes | Consumer's email |
firstname | string | Yes | Consumer's first name |
lastname | string | Yes | Consumer's last name |
mobile | string | No | Consumer's mobile number |
message | string | No | Optional message (max. 1024 characters) |
locale | string | No | Language code (e.g. de, en). Determines the language of transactional emails |
channelKey | string | No | Booking channel. Default: RESOURCE_PUBLIC |
* placeholder_id is technically optional, but you should always send it to make sure your user's reservation is used.
Request
curl -X POST "https://www.timum.de/resources/my-resource@myPlatform/create_appointment_with_consumer" \
-H "Content-Type: application/json" \
-d '{
"start": "2025-01-15T13:00:00Z",
"end": "2025-01-15T13:30:00Z",
"timeslot_uuid": "6cb6df60-48d8-11e8-a5e5-263fa1a58213",
"product_uuid": "7fc85970-a2d0-11e2-9cd0-1231430706c1",
"placeholder_id": "a48df610-483c-11f0-b6e3-72fe2304273f",
"channelKey": "RESOURCE_PUBLIC",
"email": "max@example.com",
"firstname": "Max",
"lastname": "Mustermann",
"mobile": "0173 1234567",
"locale": "de",
"message": "Ich freue mich auf den Termin."
}'
Response (Success)
{
"api-info": { "version": "1" },
"createdAppointment": {
"appointment_uuid": "864b80c0-483f-11f0-b6e3-72fe2304273f",
"start": "2025-06-17T11:05:00+02:00",
"end": "2025-06-17T12:05:00+02:00",
"timeslot_uuid": "864c4410-483f-11f0-b6e3-72fe2304273f",
"product_uuid": "0bd09a60-1d8f-11e9-bf75-06ecf2a1ba22",
"resource_uuid": "636627f0-006c-11ec-a5c8-02e4d9518b64",
"contact_channel": {
"type": "location",
"value": "Telefon und Bildschirmfreigabe (wir rufen Sie an)"
},
"product_name": "Video Call 30",
"resource_name": "2nd Level Support",
"capacity": 1,
"capacity_left": 0,
"products": [
{ "uuid": "0bd09a60-1d8f-11e9-bf75-06ecf2a1ba22", "name": "Video Call 30" }
],
"kind": "models.LotAppointment",
"cancelLink": "https://www.timum.de/rebook/6366c430-006c-11ec-a5c8-02e4d9518b64?..."
}
}
cancelLink
cancelLink. This signed link allows the consumer to cancel their appointment independently. You can use this link in your confirmation email.Response (Error)
{
"api-info": { "version": "1" },
"errors": [
{
"errorCode": "201",
"message": "Das überlappt mit einem anderen Termin."
}
]
}
Algorithm Details
- If a user with the email or mobile number already exists, the appointment is assigned to that account
- Missing attributes (firstname, lastname, mobile) are added to the existing user, but not overwritten
- The new user's language is taken from the CRM actor (or via the
localeparameter) - For group appointments: the first booking creates the appointment, further bookings increase the number of participants
Status Codes
| Code | Meaning |
|---|---|
201 | Appointment created successfully |
400 | Required field missing or invalid |
412 | Slot already booked (errorCode 201) |
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
| "Das überlappt mit einem anderen Termin" (errorCode 201) | Slot on an availability already booked. First booking creates a new appointment with a new UUID | Use the new timeslot_uuid from the first booking response for further bookings |
| "Email missing" even though it's in the body | Redirect issue due to missing www. | Make sure you use https://www.timum.de (with www.) |
| 301 redirect with no response | Missing www. in the URL | Always use https://www.timum.de |
| AppointmentAlreadyBookedException | The consumer is already participating in this appointment | A user cannot participate in the same appointment twice. Check for duplicates |
4. Retrieve Active Products
Retrieves all active/enabled products for a resource. The result list may be filtered depending on the channel settings.
POST /products/active_products
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
ref | string | Conditional* | Resource or channel reference. Can be specified multiple times |
tslRefs | string | Conditional* | Appointment or availability reference. Can be specified multiple times |
channelKey | string | No | Booking channel. Default: RESOURCE_PUBLIC |
* At least ref or tslRefs must be specified.
Request
curl -X POST "https://www.timum.de/products/active_products?ref=my-resource@myPlatform&channelKey=RESOURCE_PUBLIC"
Response
{
"products": [
{
"uuid": "92867f70-4836-11e5-bc04-021a52c25043",
"name": "Besichtigung",
"description": "",
"minDuration": 30,
"maxDuration": 45,
"leadTimeMinutes": 0,
"followUpTimeMinutes": 20,
"exclusive": false
},
{
"uuid": "0bb978c0-5740-11eb-8b95-024759471364",
"name": "Beratungsgespräch",
"description": "Ausführliches Beratungsgespräch",
"minDuration": 60,
"maxDuration": 90,
"leadTimeMinutes": null,
"followUpTimeMinutes": null,
"exclusive": true
}
]
}
Response Fields
| Field | Type | Description |
|---|---|---|
uuid | string | Unique product ID |
name | string | Display name of the product |
description | string | Product description (for customer notes) |
minDuration | number? | Minimum duration in minutes |
maxDuration | number? | Maximum duration in minutes |
leadTimeMinutes | number? | Lead time (travel/preparation) in minutes |
followUpTimeMinutes | number? | Follow-up time (return travel/wrap-up) in minutes |
exclusive | boolean | Whether the product is exclusive (only for certain channels) |
5. Retrieve Public Data
Retrieves public information about the provider, resource, channel settings, and contact person. Useful for rendering booking widgets.
POST /resources/public_data
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
ref | string | Conditional* | Resource or channel reference. Can be specified multiple times |
tslRefs | string | Conditional* | Appointment or availability reference. Can be specified multiple times |
channelKey | string | No | Booking channel. Default: RESOURCE_PUBLIC |
* At least ref or tslRefs must be specified.
Request
curl -X POST "https://www.timum.de/resources/public_data?ref=my-resource@myPlatform&channelKey=RESOURCE_PUBLIC"
Response
{
"contact": {
"name": "Max Makler",
"email": "kontakt@example.de",
"mobile": "0173 1234567",
"phone": "030 12345678"
},
"resource": {
"uuid": "636627f0-006c-11ec-a5c8-02e4d9518b64",
"name": "Musterstraße 1",
"description": "Schöne 3-Zimmer-Wohnung mit Balkon",
"contactChannelType": "",
"msgHelpText": "",
"url": "https://example.com/expose/123",
"imgUrl": "https://cdn.example.com/images/123.jpg"
},
"provider": {
"name": "Mustermakler GmbH",
"description": "Ihr Partner für Immobilien in Berlin",
"isThemingAllowed": true,
"isLocalisationAllowed": true,
"areCustomFieldsAllowed": true
},
"channel": {
"bookingProcess": "IMMEDIATE"
}
}
Response Structure
contact
| Field | Description |
|---|---|
name | Name of the contact person (from contact profile) |
email | Public email address |
mobile | Mobile number |
phone | Landline number |
resource
| Field | Description |
|---|---|
uuid | Unique resource ID |
name | Public name of the resource |
description | Description of the resource |
url | External URL (e.g. link to the property listing) |
imgUrl | Image URL of the resource |
provider
| Field | Description |
|---|---|
name | Calendar/company name |
description | Description of the provider |
isThemingAllowed | Whether custom theming is allowed |
isLocalisationAllowed | Whether custom localization is allowed |
areCustomFieldsAllowed | Whether custom fields are allowed |
channel
| Field | Description |
|---|---|
bookingProcess | IMMEDIATE = direct booking, REQUESTED = appointment request |
6. CORS Preflight
Browsers automatically send OPTIONS requests before cross-origin requests. timum answers these automatically for all consumer booking endpoints.
OPTIONS /resources/{ref}/upcoming_bookables
Response Headers
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization, X-Auth-Token
Access-Control-Max-Age: 36
Automatic CORS support
Channel Keys
timum supports 4 different booking channels. Each channel has its own settings for visibility, booking process, and product filtering.
| channelKey | Name (UI) | Usage |
|---|---|---|
RESOURCE_PUBLIC | Public booking link | Default channel. Can be published publicly |
RESOURCE_EXCLUSIVE | Exclusive booking access | For accepted customers (address book) |
RESOURCE_REFERENCE | Embedded booking calendar | For automatically generated embeds (e.g. real estate portals) |
CALENDAR_PUBLIC | Website plugin & overall calendar | For website plugins with all resources |
The channel settings can be configured in the timum frontend under Resource → Enable appointment booking.
Booking Processes
| Process | Description |
|---|---|
IMMEDIATE | Direct booking. The appointment is confirmed immediately. The consumer receives a confirmation, the provider receives a notification |
REQUESTED | Appointment request. The appointment must be confirmed by the provider. The consumer receives "Request received", the provider receives the request for confirmation |
Complete Booking Flow
Here is the complete flow for booking an appointment:
Step 1: Load Bookables
curl "https://www.timum.de/resources/immobilie-123@is24/upcoming_bookables?groupFormat=yyyy-MM-dd&prdRef=besichtigung-30min@is24"
Step 2: Reserve Slot
curl -X POST "https://www.timum.de/rest/1/resources/immobilie-123@is24/reserve_appointment" \
-H "Content-Type: application/json" \
-d '{
"timeslot_uuid": "6cb6df60-48d8-11e8-a5e5-263fa1a58213",
"product_uuid": "7fc85970-a2d0-11e2-9cd0-1231430706c1",
"from": "2025-01-15T14:00:00Z",
"to": "2025-01-15T14:30:00Z"
}'
# Response contains participation.customer_uuid -> remember it!
Step 3: Finalize Booking
curl -X POST "https://www.timum.de/resources/immobilie-123@is24/create_appointment_with_consumer" \
-H "Content-Type: application/json" \
-d '{
"start": "2025-01-15T14:00:00Z",
"end": "2025-01-15T14:30:00Z",
"timeslot_uuid": "6cb6df60-48d8-11e8-a5e5-263fa1a58213",
"product_uuid": "7fc85970-a2d0-11e2-9cd0-1231430706c1",
"placeholder_id": "a48df610-483c-11f0-b6e3-72fe2304273f",
"email": "interessent@example.com",
"firstname": "Max",
"lastname": "Interessent",
"mobile": "0173 9876543",
"locale": "de"
}'
Related Topics
- API Overview - Authentication, base URL, error format
- Configure Offerings - Create resources and products
- Scheduling - Manage availabilities and appointments
- Widget Integration - Integrate the BookingJS widget
