Preskočiť na hlavný obsah
← Página principal

API pública

API REST (versión v1) para la web propia del negocio, una recepcionista con IA, Zapier, Make y otras integraciones. Las respuestas son JSON con el campo ok; en caso de error, también error. La descripción completa de todas las rutas y campos está en OpenAPI 3.1.

📄 OpenAPI 3.1 (JSON)🔔 Webhooks🔑 Crear clave de API

1. Autenticación con clave

Cada solicitud debe incluir la cabecera Authorization: Bearer rk_live_…. La clave se crea en el panel → Claves de API. La clave completa se muestra solo una vez — guárdela en variables de entorno o en un gestor de contraseñas; nosotros solo almacenamos su huella. Cada clave pertenece a un negocio, por eso el negocio no se indica en las solicitudes.

curl "https://app.terminuj.com/api/v1/services" \
  -H "Authorization: Bearer rk_live_XXXXXXXXXXXXXXXXXXXXXXXX"

2. Permisos de la clave

Al crear la clave elige sus permisos — concédale solo los que la integración realmente necesita.

PermisoRutas (/api/v1/…)
booking:readGET /bookings, GET /bookings/{id}, GET /time-off
booking:writePOST /bookings, PATCH /bookings/{id} (cancelar, cambiar de hora, confirmar), POST /time-off, DELETE /time-off/{id}
customer:readGET /customers
service:readGET /services, GET /availability
webhook:manageGET/POST /webhooks, GET/DELETE /webhooks/{id}, GET /webhooks/{id}/deliveries

3. Usos más habituales

Horarios libres

Para cada día devuelve si está abierto y las horas libres tras descontar reservas, días libres y pausas del servicio; como pronto, dentro de 2 h. from = día con el formato AAAA-MM-DD (por defecto, hoy), days 1–62 (por defecto, 28), opcionalmente staff = un profesional concreto. La zona horaria es Europe/Bratislava.

curl "https://app.terminuj.com/api/v1/availability?service=<id-sluzby>&from=2026-06-15&days=7" \
  -H "Authorization: Bearer rk_live_XXXXXXXXXXXXXXXXXXXXXXXX"

Crear una reserva

startAt en UTC con Z al final (12:00Z = 14:00 en verano en Bratislava). El correo del cliente es obligatorio; sin él, solo con "channel": "telefon" (cita tomada por teléfono — basta con el nombre y el teléfono). Las citas del formulario público de su web envíelas con "channel": "web"; si el negocio exige el consentimiento para empezar el servicio antes de que termine el plazo de desistimiento, añada "serviceStartConsent": true según la casilla que haya marcado el cliente (si no, 422 consent_required). Opcionalmente preferredStaffId, customer.notes y locale (sk | en | cs | hu | de | es — idioma de los mensajes de la API y de los demás mensajes al cliente sobre esta reserva; por defecto, el idioma del negocio). Si se vuelve a enviar el mismo servicio y la misma hora con el mismo correo en menos de 10 minutos, se devuelve la misma reserva (doble clic, corte de red).

En las reservas hechas por API, Terminuj no envía el correo de confirmación al cliente — la confirmación la envía su integración. El propietario recibe un aviso según los ajustes del negocio. La reserva cuenta para el total mensual del plan.

curl -X POST "https://app.terminuj.com/api/v1/bookings" \
  -H "Authorization: Bearer rk_live_XXXXXXXXXXXXXXXXXXXXXXXX" \
  -H "Content-Type: application/json" \
  -d '{
    "serviceId": "<id-sluzby>",
    "startAt": "2026-06-15T12:00:00Z",
    "customer": {
      "fullName": "Jana Nováková",
      "email": "jana@example.com",
      "phone": "+421900123456"
    }
  }'

Cancelar, cambiar de hora o confirmar una reserva

Una sola ruta, según el campo action: {"action": "cancel", "reason": "…"}, {"action": "reschedule", "startAt": "…Z"} o {"action": "confirm"} (solo una cita pendiente de señal). Al cancelar, el cliente recibe un correo; al cambiar de hora y al confirmar, si el negocio tiene activados los correos de confirmación.

curl -X PATCH "https://app.terminuj.com/api/v1/bookings/<id-rezervacie>" \
  -H "Authorization: Bearer rk_live_XXXXXXXXXXXXXXXXXXXXXXXX" \
  -H "Content-Type: application/json" \
  -d '{ "action": "cancel", "reason": "Zákazník neprišiel" }'

Buscar clientes

search busca en el nombre, el correo y el teléfono.

curl "https://app.terminuj.com/api/v1/customers?search=jana&limit=10" \
  -H "Authorization: Bearer rk_live_XXXXXXXXXXXXXXXXXXXXXXXX"

Las demás rutas (lista de reservas con el filtro from/to/status/createdFrom, servicios, bloqueo de tiempo con /time-off, webhooks) se describen en OpenAPI.

4. Paginación

Las listas admiten los parámetros ?limit=25&cursor=<id>. Por defecto 25, como máximo 100 por página (reservas, clientes, entregas de webhooks); servicios por defecto 50, como máximo 200. La respuesta contiene { pagination: { nextCursor, hasMore, limit } }.

# Primera página
curl "https://app.terminuj.com/api/v1/bookings?limit=25" -H "Authorization: Bearer rk_live_…"

# Página siguiente (nextCursor de la respuesta anterior)
curl "https://app.terminuj.com/api/v1/bookings?limit=25&cursor=<nextCursor>" -H "Authorization: Bearer rk_live_…"

5. Límite de solicitudes

El límite se cuenta por clave en una ventana deslizante de 1 minuto:

  • Lectura (booking:read, customer:read, service:read): 600 solicitudes por minuto
  • Escritura (booking:write, webhook:manage): 60 solicitudes por minuto

Al superarlo, se recibe 429 rate_limit_exceeded con la cabecera Retry-After (dentro de cuántos segundos volver a intentarlo) y el campo retryAfterSeconds. Las cabeceras X-RateLimit-Limit, X-RateLimit-Remaining y X-RateLimit-Reset llegan con el rechazo.

6. Códigos de error

HTTPerrorSignificado
401missing_or_invalid_authorizationFalta la cabecera Authorization o la clave no tiene el formato rk_live_ + 24 caracteres
401invalid_api_keyNo existe esa clave
401api_key_revokedLa clave fue revocada en el panel
401api_key_expiredLa clave ha caducado (expiresAt)
402trial_endedEl periodo de prueba del negocio ha terminado — 14 días desde el registro (resetAt = fin de la prueba); nuevas reservas solo tras elegir un plan
402monthly_limit_exceededEl negocio ha agotado el número mensual de reservas de su plan de socio (used, limit, resetAt)
403insufficient_scopeLa clave no tiene el permiso necesario (required, have)
403manual_requires_app«manual: true» solo lo puede enviar una persona con sesión iniciada en la app Terminuj, no una clave de API
404not_foundLa reserva, el día libre o el webhook no existe en este negocio (también service_not_found)
409slot_conflictLa hora se ha ocupado mientras tanto; al crear la reserva llegan también suggestedAlternatives
409already_cancelledEl estado de la reserva no permite el cambio (también already_completed, cannot_cancel, not_reschedulable, already_confirmed, cannot_confirm)
422invalid_bodyEl cuerpo de la solicitud no coincide con el esquema — detalles en detail
422consent_requiredEl negocio exige el consentimiento para empezar el servicio antes de que termine el plazo de desistimiento y falta serviceStartConsent: true
422start_in_pastEl inicio está en el pasado (también outside_working_hours = fuera del horario de apertura)
429rate_limit_exceededSe ha superado el límite de solicitudes de la clave — véase la sección 5
429daily_cap_reachedEl negocio ha alcanzado el tope diario de reservas públicas (últimas 24 h) — escribir con una clave cuenta igual que el formulario

7. Webhooks

Si desea que Terminuj le avise por sí mismo de una reserva nueva, cancelada o cambiada de hora, consulte /docs/webhooks — firma HMAC SHA-256, reintentos tras 1 min, 5 min, 30 min y 2 h, ejemplos de código.

Los webhooks también se pueden gestionar por API: GET/POST /api/v1/webhooks, GET/DELETE /api/v1/webhooks/{id} y GET /api/v1/webhooks/{id}/deliveries (registro de entregas).

8. Soporte

Error o solicitud de una nueva función: podpora@terminuj.com.