API de Partners
API de reservas para integraciones de terceros — consultar disponibilidad, reservar, cancelar, modificar y consultar reservas, sea cual sea el sistema de reservas del restaurante.
URL base
https://apointa.org/api/partner/v1Todas las peticiones deben usar HTTPS.
Autenticación
Todos los endpoints (excepto /health) requieren un token bearer:
Authorization: Bearer apt_live_...Recibes exactamente una clave API por cuenta de partner. Cubre todos los restaurantes que hayas añadido en tu panel. Acceder a un restaurante no habilitado devuelve 403 FORBIDDEN.
Límites de peticiones
60 peticiones por minuto por clave API en todos los endpoints. Al superarlo se devuelve 429 Too Many Requests.
Consejo: en voice AI, cachea las comprobaciones de disponibilidad para la misma tupla (restaurantId, date, time, partySize).
Gestión de errores
Todos los errores se devuelven en JSON:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Human-readable description"
}
}| Código | Estado | Cuándo |
|---|---|---|
| UNAUTHORIZED | 401 | Falta la clave API o no es válida |
| FORBIDDEN | 403 | El restaurante no está habilitado para esta clave |
| VALIDATION_ERROR | 400 | El cuerpo de la petición no supera la validación |
| NOT_FOUND | 404 | Restaurante o reserva no encontrados |
| PROVIDER_ERROR | 502 | Error en el sistema de reservas del restaurante |
| RATE_LIMITED | 429 | Límite de peticiones superado |
Endpoints
/healthComprobación de estado. No requiere autenticación.
Respuesta
{ "status": "ok", "version": "1.0.0" }/check-availabilityComprueba si una franja concreta está disponible. No crea ninguna reserva.
Campos de la petición
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| restaurantId | string | sí | ID del restaurante — lo encuentras en tu panel |
| date | string | sí | AAAA-MM-DD (fecha local) |
| time | string | sí | HH:MM en formato 24 horas (hora local) |
| partySize | integer | sí | Número de comensales (1-50) |
Petición
{
"restaurantId": "35YDWSFbm7I",
"date": "2026-08-15",
"time": "19:30",
"partySize": 4
}Respuesta
{
"success": true,
"data": {
"available": true,
"closed": false,
"alternatives": ["19:00", "19:45", "20:00"]
}
}/bookCrea una reserva real. Cada llamada correcta genera una reserva vinculante — no lo uses para pruebas. Solo el nombre y el teléfono son obligatorios.
Campos de la petición
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| restaurantId | string | sí | ID del restaurante — lo encuentras en tu panel |
| date | string | sí | AAAA-MM-DD |
| time | string | sí | HH:MM |
| partySize | integer | sí | Número de comensales (1-50) |
| customer.firstName | string | sí | Nombre del cliente |
| customer.lastName | string | no | Apellido del cliente. Si falta, reservamos con un marcador neutro. |
| customer.phone | string[] | sí | Teléfonos del cliente como array, el principal primero |
| customer.email | string | no | Correo del cliente |
| specialRequests | string | no | Nota de texto libre |
Petición
{
"restaurantId": "35YDWSFbm7I",
"date": "2026-08-15",
"time": "19:30",
"partySize": 4,
"customer": {
"firstName": "Max",
"lastName": "Mustermann",
"phone": ["+491701234567", "+4989123456"],
"email": "max@example.com"
},
"specialRequests": "Table by the window"
}Respuesta
{
"success": true,
"data": {
"reservationId": "gn-a1b2c3d4",
"status": "confirmed",
"date": "2026-08-15",
"time": "19:30",
"partySize": 4
}
}/cancelCancela una reserva. Por reservationId o — si tu sistema no la conoce — por teléfono, opcionalmente acotando con fecha y hora.
Campos de la petición
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| restaurantId | string | sí | ID del restaurante — lo encuentras en tu panel |
| reservationId | string | no | Id de la respuesta de /book. Como alternativa, busca por teléfono. |
| phone | string[] | no | Teléfonos del cliente como array — también buscamos en los números alternativos. |
| date | string | no | Fecha de la reserva, acota cuando hay varias coincidencias |
| time | string | no | Hora de la reserva, acota cuando hay varias coincidencias |
Petición
{
"restaurantId": "35YDWSFbm7I",
"reservationId": "gn-a1b2c3d4"
}Respuesta
{
"success": true,
"data": {
"reservationId": "gn-a1b2c3d4",
"status": "cancelled",
"date": "2026-08-15",
"time": "19:30"
}
}/updateSolicita un cambio. La reserva se localiza igual que en /cancel. Avisamos al restaurante, que aplica el cambio.
Campos de la petición
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| restaurantId | string | sí | ID del restaurante — lo encuentras en tu panel |
| reservationId | string | no | Id de la respuesta de /book. Como alternativa, busca por teléfono. |
| phone | string[] | no | Teléfonos del cliente como array — también buscamos en los números alternativos. |
| date | string | no | Fecha de la reserva, acota cuando hay varias coincidencias |
| time | string | no | Hora de la reserva, acota cuando hay varias coincidencias |
| updates.date | string | no | Nueva fecha AAAA-MM-DD |
| updates.time | string | no | Nueva hora HH:MM |
| updates.partySize | integer | no | Nuevo número de comensales |
| updates.specialRequests | string | no | Nueva nota |
Petición
{
"restaurantId": "35YDWSFbm7I",
"phone": ["+491701234567"],
"date": "2026-08-15",
"time": "19:30",
"updates": {
"partySize": 6,
"specialRequests": "Guest with allergies"
}
}Respuesta
{
"success": true,
"data": {
"reservationId": "gn-a1b2c3d4",
"status": "pending",
"message": "Update request forwarded to restaurant."
}
}/reservationsBusca reservas existentes por número de teléfono. Solo devuelve reservas futuras.
Campos de la petición
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| restaurantId | string | sí | ID del restaurante — lo encuentras en tu panel |
| phone | string[] | sí | Teléfonos del cliente como array — también buscamos en los números alternativos. |
Petición
{
"restaurantId": "35YDWSFbm7I",
"phone": ["+491701234567", "+4989123456"]
}Respuesta
{
"success": true,
"data": {
"reservations": [
{
"reservationId": "gn-a1b2c3d4",
"date": "2026-08-15",
"time": "19:30",
"partySize": 4,
"specialRequests": "Table by the window"
}
]
}
}Importante: solo reservas de este sistema
La API solo ve las reservas creadas a través de este sistema. Si el cliente reservó por teléfono, en la web del restaurante o por otro canal, es invisible para /reservations, /update y /cancel.
En ese caso /cancel y /update responden con 404 NOT_FOUND. Al mismo tiempo avisamos automáticamente al restaurante por correo con todos los datos que nos enviaste.
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "No reservation found that was created through this API. The restaurant staff has been notified and will handle the cancellation."
}
}Contempla este caso en tu sistema e informa a quien llama de que se ha avisado a un empleado y se encargará — es exactamente lo que ocurre en segundo plano.
Formato del número de teléfono
Envía siempre el prefijo del país en formato E.164:
+491701234567(Alemania)+436641234567(Austria)+41791234567(Suiza)
Los teléfonos se envían siempre como array. Si tu sistema conoce un segundo número del cliente, inclúyelo — los buscamos todos.
Soporte
Contacta con el equipo de Apointa indicando: marca de tiempo de la petición (UTC), restaurantId, cuerpo completo de la petición (censura los datos personales), estado HTTP y cuerpo de la respuesta.
