Apointa LogoAPI de Partners v1

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/v1

Todas 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.

Ir al panel de partners

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ódigoEstadoCuándo
UNAUTHORIZED401Falta la clave API o no es válida
FORBIDDEN403El restaurante no está habilitado para esta clave
VALIDATION_ERROR400El cuerpo de la petición no supera la validación
NOT_FOUND404Restaurante o reserva no encontrados
PROVIDER_ERROR502Error en el sistema de reservas del restaurante
RATE_LIMITED429Límite de peticiones superado

Endpoints

GET/health

Comprobación de estado. No requiere autenticación.

Respuesta

{ "status": "ok", "version": "1.0.0" }
POST/check-availability

Comprueba si una franja concreta está disponible. No crea ninguna reserva.

Campos de la petición

CampoTipoObligatorioDescripción
restaurantIdstringID del restaurante — lo encuentras en tu panel
datestringAAAA-MM-DD (fecha local)
timestringHH:MM en formato 24 horas (hora local)
partySizeintegerNú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"]
  }
}
POST/book

Crea 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

CampoTipoObligatorioDescripción
restaurantIdstringID del restaurante — lo encuentras en tu panel
datestringAAAA-MM-DD
timestringHH:MM
partySizeintegerNúmero de comensales (1-50)
customer.firstNamestringNombre del cliente
customer.lastNamestringnoApellido del cliente. Si falta, reservamos con un marcador neutro.
customer.phonestring[]Teléfonos del cliente como array, el principal primero
customer.emailstringnoCorreo del cliente
specialRequestsstringnoNota 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
  }
}
POST/cancel

Cancela 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

CampoTipoObligatorioDescripción
restaurantIdstringID del restaurante — lo encuentras en tu panel
reservationIdstringnoId de la respuesta de /book. Como alternativa, busca por teléfono.
phonestring[]noTeléfonos del cliente como array — también buscamos en los números alternativos.
datestringnoFecha de la reserva, acota cuando hay varias coincidencias
timestringnoHora 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"
  }
}
POST/update

Solicita un cambio. La reserva se localiza igual que en /cancel. Avisamos al restaurante, que aplica el cambio.

Campos de la petición

CampoTipoObligatorioDescripción
restaurantIdstringID del restaurante — lo encuentras en tu panel
reservationIdstringnoId de la respuesta de /book. Como alternativa, busca por teléfono.
phonestring[]noTeléfonos del cliente como array — también buscamos en los números alternativos.
datestringnoFecha de la reserva, acota cuando hay varias coincidencias
timestringnoHora de la reserva, acota cuando hay varias coincidencias
updates.datestringnoNueva fecha AAAA-MM-DD
updates.timestringnoNueva hora HH:MM
updates.partySizeintegernoNuevo número de comensales
updates.specialRequestsstringnoNueva 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."
  }
}
POST/reservations

Busca reservas existentes por número de teléfono. Solo devuelve reservas futuras.

Campos de la petición

CampoTipoObligatorioDescripción
restaurantIdstringID del restaurante — lo encuentras en tu panel
phonestring[]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.

hey@apointa.org

Documentación de la API de Partners - Apointa