Flujo de reserva
Endpoints orientados al consumidor para la reserva de citas. Estos endpoints también los utiliza el widget BookingJS y están optimizados para integraciones de frontend.
No se requiere autenticación
Resumen
El flujo de reserva estándar consta de tres pasos:
- Obtener citas disponibles - lista de todos los slots reservables
- Reservar cita - bloqueo de 3 minutos para el slot seleccionado
- Completar la reserva - finalizar la cita con los datos del consumidor
| Método | Endpoint | Descripción |
|---|---|---|
GET | /resources/:ref/upcoming_bookables | Obtener citas disponibles |
POST | /rest/1/resources/:ref/reserve_appointment | Reservar cita (3 min) |
POST | /resources/:ref/create_appointment_with_consumer | Completar la reserva |
POST | /products/active_products | Obtener productos activos |
POST | /resources/public_data | Datos públicos de recursos |
OPTIONS | /resources/:ref/upcoming_bookables | Preflight CORS |
1. Obtener citas disponibles
Obtiene todos los slots reservables para uno o varios recursos. Los resultados se agrupan según un formato de fecha configurable, para facilitar una UI en dos niveles (p. ej. vista mensual → lista diaria).
GET /resources/{ref}/upcoming_bookables
Parámetros de ruta
| Parámetro | Tipo | Descripción |
|---|---|---|
ref | string | Referencia de recurso o UUID. Formatos: resourceId@providerUuid@platform - referencia completa; resourceId@platform - forma corta; uuid - UUID directo del recurso |
Parámetros de consulta
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
groupFormat | string | No | Formato Joda-Time para la agrupación. Todos los bookables con el mismo valor terminan en un mismo grupo. Por defecto: yyyy-MM-dd. Ejemplos: MM-yyyy (mensual), MMMM (nombre del mes) |
timeFormat | string | No | Formato para formattedStart/formattedEnd. Por defecto: yyyy-MM-dd HH:mm |
languageTag | string | No | Etiqueta de idioma IETF BCP 47 para la traducción del lado del servidor (p. ej. nombres de meses). Ejemplo: de_DE, fr_FR |
channelKey | string | No | Canal de reserva. Por defecto: RESOURCE_PUBLIC. Consulte Channel Keys |
ref | string | No | Referencias de recurso adicionales. Puede especificarse varias veces para cargar bookables de varios recursos a la vez |
prdRef | string | No | Referencia de producto o UUID. Filtra los bookables que admiten este producto. También tiene en cuenta el leadTime/followUpTime del producto |
Request
curl -X GET "https://www.timum.de/resources/my-resource@myPlatform/upcoming_bookables?groupFormat=MMMM&languageTag=fr_FR"
Response
La respuesta es un objeto con claves dinámicas basadas en groupFormat. Además, incluye un indicador public_visible.
{
"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
}
Tipos de Bookable
| kind | Significado | Particularidad |
|---|---|---|
models.Bookable | Slot de una disponibilidad (timeslot) | Se convierte en un LotAppointment con la primera reserva. Use timeslot_uuid para reserve/create |
models.LotAppointment | Cita de grupo existente con capacidad restante | Cita ya creada. Use appointment_uuid para reserve/create |
Bookable vs LotAppointment
models.Bookable es un slot potencial de una disponibilidad. En cuanto el primer consumidor reserva, se convierte en un models.LotAppointment con un nuevo appointment_uuid. Para reservas posteriores del mismo slot, debe usar este nuevo UUID.Campos de la respuesta
| Campo | Tipo | Descripción |
|---|---|---|
start / end | string | Marca de tiempo ISO 8601 con zona horaria |
formattedStart / formattedEnd | string | Hora formateada según timeFormat |
timeslot_uuid | string | UUID de la disponibilidad subyacente |
appointment_uuid | string? | UUID de la cita (solo para LotAppointment) |
product_uuid | string? | UUID del producto, o null |
resource_uuid | string | UUID del recurso |
capacity | number | Capacidad total del slot |
capacity_left | number | Plazas restantes |
contact_channel | object? | Canal de contacto con type y value |
products | array | Lista de productos disponibles para este slot |
kind | string | models.Bookable o models.LotAppointment |
Códigos de estado
| Código | Significado |
|---|---|
200 | Correcto, bookables devueltos |
204 | No hay bookables disponibles (respuesta vacía) |
2. Reservar cita
Reserva temporalmente una cita durante 3 minutos. Durante ese tiempo, el slot solo puede reservarlo el cliente que hizo la reserva. Esto evita reservas dobles mientras se rellena el formulario.
POST /rest/1/resources/{ref}/reserve_appointment
Llame siempre antes de reservar
create_appointment_with_consumer. Incluso después de expirar los 3 minutos, puede completar la reserva - pero si otro cliente fue más rápido, fallará.Parámetros de consulta
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
ref | string | No | Referencia de recurso o de canal |
channelKey | string | No | Canal de reserva. Por defecto: RESOURCE_PUBLIC |
Request Body
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
timeslot_uuid | string | Condicional* | UUID del timeslot (disponibilidad). Usar para models.Bookable. Aplica la configuración por defecto de la disponibilidad a la nueva cita |
appointment_uuid | string | Condicional* | UUID de la cita existente. Obligatorio para models.LotAppointment |
product_uuid | string | Sí | UUID del producto a reservar |
from | string | Sí | Hora de inicio del bookable (ISO 8601, UTC) |
to | string | Sí | Hora de fin del bookable (ISO 8601, UTC) |
* Para models.Bookable, envíe timeslot_uuid. Para models.LotAppointment, appointment_uuid es obligatorio.
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"
}
]
}
]
}
Guardar customer_uuid
participation.customer_uuid de la respuesta! Necesitará este valor como placeholder_id para la llamada a create_appointment_with_consumer.Comportamiento de la reserva
- La reserva es válida durante 3 minutos
- El
stateesRESERVED - Al expirar, la reserva se elimina automáticamente
capacity_leftse reduce mientras dura la reserva- Puede reservar incluso después del tiempo de espera - pero sin protección frente a reservas dobles
3. Completar la reserva
Completa la reserva con los datos del consumidor. Si ya existe un usuario con el email o número de teléfono indicado, la cita se asigna a esa cuenta. En caso contrario, se crea una nueva cuenta.
POST /resources/{ref}/create_appointment_with_consumer
Parámetros de consulta
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
timeFormat | string | No | Formato para los valores de tiempo en la respuesta |
Request Body
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
start | string | Sí | Hora de inicio (ISO 8601) |
end | string | Sí | Hora de fin (ISO 8601) |
timeslot_uuid | string | Sí | UUID del timeslot o de la cita |
product_uuid | string | No | UUID del producto |
placeholder_id | string | No* | participation.customer_uuid de la respuesta de reserva. Identifica la reserva |
email | string | Sí | Email del consumidor |
firstname | string | Sí | Nombre del consumidor |
lastname | string | Sí | Apellido del consumidor |
mobile | string | No | Número de móvil del consumidor |
message | string | No | Mensaje opcional (máx. 1024 caracteres) |
locale | string | No | Código de idioma (p. ej. de, en). Determina el idioma de los emails transaccionales |
channelKey | string | No | Canal de reserva. Por defecto: RESOURCE_PUBLIC |
* placeholder_id es técnicamente opcional, pero debería enviarlo siempre para garantizar que se use la reserva de su usuario.
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 (Éxito)
{
"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. Este enlace firmado permite al consumidor cancelar su cita de forma autónoma. Puede usar este enlace en su email de confirmación.Response (Error)
{
"api-info": { "version": "1" },
"errors": [
{
"errorCode": "201",
"message": "Das überlappt mit einem anderen Termin."
}
]
}
Detalles del algoritmo
- Si ya existe un usuario con el email o el número de móvil, la cita se asigna a esa cuenta
- Los atributos que falten (firstname, lastname, mobile) se completan en el usuario existente, pero nunca se sobrescriben
- El idioma del nuevo usuario se toma del actor del CRM (o mediante el parámetro
locale) - En citas de grupo: la primera reserva crea la cita, las siguientes aumentan el número de participantes
Códigos de estado
| Código | Significado |
|---|---|
201 | Cita creada correctamente |
400 | Falta un campo obligatorio o no es válido |
412 | Slot ya reservado (errorCode 201) |
Solución de problemas
| Problema | Causa | Solución |
|---|---|---|
| "Das überlappt mit einem anderen Termin" (errorCode 201) | Slot de una disponibilidad ya reservado. La primera reserva genera una nueva cita con un nuevo UUID | Use el nuevo timeslot_uuid de la primera respuesta de reserva para reservas posteriores |
| "Falta el email" aunque está en el body | Problema de redirección por falta de www. | Asegúrese de usar https://www.timum.de (con www.) |
| Redirección 301 sin respuesta | Falta www. en la URL | Use siempre https://www.timum.de |
| AppointmentAlreadyBookedException | El consumidor ya participa en esta cita | Un usuario no puede participar dos veces en la misma cita. Compruebe si hay duplicados |
4. Obtener productos activos
Obtiene todos los productos activos/habilitados de un recurso. La lista de resultados puede estar filtrada según la configuración de canal.
POST /products/active_products
Parámetros de consulta
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
ref | string | Condicional* | Referencia de recurso o de canal. Puede especificarse varias veces |
tslRefs | string | Condicional* | Referencia de cita o de disponibilidad. Puede especificarse varias veces |
channelKey | string | No | Canal de reserva. Por defecto: RESOURCE_PUBLIC |
* Debe especificarse al menos ref o tslRefs.
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
}
]
}
Campos de la respuesta
| Campo | Tipo | Descripción |
|---|---|---|
uuid | string | ID único del producto |
name | string | Nombre para mostrar del producto |
description | string | Descripción del producto (para notas de cliente) |
minDuration | number? | Duración mínima en minutos |
maxDuration | number? | Duración máxima en minutos |
leadTimeMinutes | number? | Tiempo de antelación (desplazamiento/preparación) en minutos |
followUpTimeMinutes | number? | Tiempo posterior (regreso/cierre) en minutos |
exclusive | boolean | Indica si el producto es exclusivo (solo para determinados canales) |
5. Obtener datos públicos
Obtiene información pública sobre el provider, el recurso, la configuración de canal y la persona de contacto. Útil para mostrar los widgets de reserva.
POST /resources/public_data
Parámetros de consulta
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
ref | string | Condicional* | Referencia de recurso o de canal. Puede especificarse varias veces |
tslRefs | string | Condicional* | Referencia de cita o de disponibilidad. Puede especificarse varias veces |
channelKey | string | No | Canal de reserva. Por defecto: RESOURCE_PUBLIC |
* Debe especificarse al menos ref o tslRefs.
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"
}
}
Estructura de la respuesta
contact
| Campo | Descripción |
|---|---|
name | Nombre de la persona de contacto (del contact profile) |
email | Dirección de email pública |
mobile | Número de móvil |
phone | Número de teléfono fijo |
resource
| Campo | Descripción |
|---|---|
uuid | ID único del recurso |
name | Nombre público del recurso |
description | Descripción del recurso |
url | URL externa (p. ej. enlace al anuncio inmobiliario) |
imgUrl | URL de la imagen del recurso |
provider
| Campo | Descripción |
|---|---|
name | Nombre del calendario/empresa |
description | Descripción del provider |
isThemingAllowed | Indica si se permite theming personalizado |
isLocalisationAllowed | Indica si se permite localización personalizada |
areCustomFieldsAllowed | Indica si se permiten campos personalizados |
channel
| Campo | Descripción |
|---|---|
bookingProcess | IMMEDIATE = reserva directa, REQUESTED = solicitud de cita |
6. Preflight CORS
Los navegadores envían automáticamente solicitudes OPTIONS antes de las solicitudes cross-origin. timum las responde automáticamente para todos los endpoints de reserva para consumidores.
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
Soporte CORS automático
Channel Keys
timum admite 4 canales de reserva diferentes. Cada canal tiene su propia configuración de visibilidad, proceso de reserva y filtrado de productos.
| channelKey | Nombre (UI) | Uso |
|---|---|---|
RESOURCE_PUBLIC | Enlace de reserva público | Canal por defecto. Puede publicarse públicamente |
RESOURCE_EXCLUSIVE | Acceso de reserva exclusivo | Para clientes aceptados (libreta de direcciones) |
RESOURCE_REFERENCE | Calendario de reserva incrustado | Para embeds generados automáticamente (p. ej. portales inmobiliarios) |
CALENDAR_PUBLIC | Plugin de sitio web y calendario completo | Para plugins de sitio web con todos los recursos |
La configuración de canal puede ajustarse en el frontend de timum en Recurso → Habilitar reserva de citas.
Procesos de reserva
| Proceso | Descripción |
|---|---|
IMMEDIATE | Reserva directa. La cita se confirma inmediatamente. El consumidor recibe una confirmación, el provider recibe una notificación |
REQUESTED | Solicitud de cita. La cita debe ser confirmada por el provider. El consumidor recibe "Solicitud recibida", el provider recibe la solicitud para confirmarla |
Flujo de reserva completo
Este es el flujo completo para reservar una cita:
Paso 1: Cargar bookables
curl "https://www.timum.de/resources/immobilie-123@is24/upcoming_bookables?groupFormat=yyyy-MM-dd&prdRef=besichtigung-30min@is24"
Paso 2: Reservar el 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"
}'
# La respuesta contiene participation.customer_uuid -> ¡recuérdelo!
Paso 3: Completar la reserva
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"
}'
Temas relacionados
- Resumen de la API - Autenticación, URL base, formato de error
- Configure Offerings - Crear recursos y productos
- Scheduling - Gestionar disponibilidades y citas
- Integración del widget - Integrar el widget BookingJS
