Saltar al contenido
Integraciones
API y avisos

API de StarIA

Para que tu CRM, tu TPV o tu programa de clínica hable con StarIA: consultar la agenda, crear y mover reservas, dar de alta clientes y enterarse al momento de lo que pasa.

Si tienes un negocio: qué es esto y a quién se lo pasas

Una API es una puerta para que dos programas se entiendan solos. Si ya usas otro programa (un CRM, un TPV, el programa de tu clínica o tu propia web) y quieres que las reservas que coge StarIA aparezcan allí, o que lo que se apunta allí llegue a StarIA, esta es la forma.

No tienes que entender lo que viene después. Pásale esta página a tu informático o a la empresa que te lleva el programa. Solo necesitan dos cosas que sacas tú del panel:

Una clave de acceso

Como una llave: quien la tenga entra en los datos de tu negocio. Puedes darla «solo para consultar» o «para consultar y hacer cambios», y retirarla cuando quieras.

Los avisos (opcional)

Si tu informático te da una dirección web, StarIA avisará ahí al momento cada vez que entre, se mueva o se anule una reserva, o cuando el agente atienda una llamada.

Dónde se crean: en tu panel de StarIA, entra en Configuración → Integraciones → Conectar otros programas. Pulsa «Crear clave», ponle un nombre (por ejemplo «TPV») y copia la clave que aparece: solo se enseña una vez. Solo el dueño o un administrador del negocio puede crearlas.

¿Usas Zapier o Make? Con ellos conectas StarIA con cientos de programas (Holded, Mailchimp, HubSpot, Google Sheets…) sin programar: solo pegas la clave. Mira Zapier y Make.

¿No tienes informático o tu programa no se puede conectar? Escríbenos a ayuda@staria.es y te decimos cómo lo hacemos.

Empezar

API REST con JSON. Todas las rutas cuelgan de esta URL base y solo se aceptan peticiones por HTTPS:

https://www.staria.es/api/v1

Tu primera petición:

curl https://www.staria.es/api/v1/services \
  -H "Authorization: Bearer sk_live_…"

Los nombres de campos van en inglés y snake_case. Los importes, en euros. Los identificadores son UUID. La descripción completa en OpenAPI 3.1 está en /openapi.json.

Autenticación y permisos

Cada petición lleva la clave en la cabecera Authorization. Cada clave pertenece a un único negocio y solo ve sus datos.

Authorization: Bearer sk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Tipo de clavePara qué
sk_live_…Producción. Lo que se crea es real y el cliente recibe sus confirmaciones.
sk_test_…Pruebas. Trabaja con los datos reales del negocio, pero lo que se crea queda marcado como prueba (test: true) y no se avisa a ningún cliente.
PermisoQué puede hacer
readSolo consultar (peticiones GET). Cualquier cambio devuelve 403 forbidden.
writeConsultar y además crear o cambiar reservas y clientes (POST, PATCH).

La clave se guarda cifrada (solo su huella): si se pierde no se puede recuperar; se retira en el panel y se crea otra. No la pongas nunca en código que se ejecute en el navegador o en una app móvil.

Límites y errores

Límite: 120 peticiones por minuto y clave. Al pasarlo se responde 429 con la cabecera Retry-After (segundos que faltan para el minuto siguiente).

Todos los errores tienen la misma forma:

{
  "error": {
    "code": "slot_unavailable",
    "message": "Ese hueco ya no está libre."
  }
}
HTTPcodeCuándo
400invalid_requestFalta un dato o no tiene el formato correcto. El mensaje dice cuál.
401unauthorizedFalta la clave, no es válida o se ha retirado.
403forbiddenLa clave es de solo lectura y la petición intenta cambiar algo.
404not_foundNo existe (o no es de este negocio).
409slot_unavailableEl hueco ya no está libre. Consulta /availability y ofrece otra hora.
429rate_limitedMás de 120 peticiones por minuto. Espera los segundos de Retry-After.
500internal_errorError nuestro. Reintenta; si se repite, escribe a ayuda@staria.es.
503unavailableServicio no disponible por ahora. Reintenta en unos minutos.

Fechas y paginación

Las fechas con hora van en ISO 8601 con el desfase de Madrid (Europe/Madrid), por ejemplo 2026-10-06T17:30:00+02:00; en invierno el desfase es +01:00. Los días sueltos van como YYYY-MM-DD y se entienden en hora de Madrid.

Las listas se paginan con limit (máximo 100) y un cursor. Cada respuesta trae next_cursor; pásalo como cursor para la página siguiente. Cuando es null, no hay más.

curl "https://www.staria.es/api/v1/customers?limit=100&cursor=eyJzIjoi…" \
  -H "Authorization: Bearer sk_live_…"

Endpoints

MétodoRutaPermisoQué hace
GET/mereadPrueba de conexión (negocio y clave)
GET/servicesreadServicios del negocio
GET/staffreadProfesionales y recursos (mesas, cabinas…)
GET/availabilityreadHuecos libres de un día
GET/bookingsreadLista de reservas
GET/bookings/{id}readUna reserva
POST/bookingswriteCrear una reserva
PATCH/bookings/{id}writeMover, confirmar, anular o cerrar una reserva
GET/customersreadBuscar clientes
GET/customers/{id}readUn cliente
POST/customerswriteCrear o actualizar un cliente
GET/callsreadLlamadas del agente (las últimas)
GET/messagesreadRecados del agente (los últimos)
POST/webhookswriteSuscribirse a avisos por API
GET/webhookswriteSuscripciones creadas por API
DELETE/webhooks/{id}writeBorrar una suscripción
GET/openapi.jsonreadEspecificación OpenAPI 3.1

GET/mepermiso read

Prueba de conexión: con qué negocio habla la clave, si es real o de pruebas y qué permiso tiene. Úsala para comprobar una clave nueva.

Ejemplo
curl https://www.staria.es/api/v1/me \
  -H "Authorization: Bearer sk_live_…"
Respuesta
{
  "data": {
    "business": {
      "id": "3c9d2e1f-0a4b-4c5d-8e6f-7a8b9c0d1e2f",
      "name": "Clínica Dental Sonrisas",
      "slug": "clinica-sonrisas",
      "business_type": "dental",
      "timezone": "Europe/Madrid"
    },
    "key": { "id": "8e7d6c5b-4a39-4281-9f0e-1d2c3b4a5968", "name": "Zapier", "prefix": "sk_live_ab12cd", "mode": "live", "scope": "write" }
  }
}

GET/servicespermiso read

Los servicios del negocio (lo que se puede reservar), con su duración y su precio.

Parámetros de consulta

NombreTipoObligatorioDescripción
include_inactivebooleanNoCon true incluye también los servicios desactivados.
Ejemplo
curl https://www.staria.es/api/v1/services \
  -H "Authorization: Bearer sk_live_…"
Respuesta
{
  "data": [
    {
      "id": "c2a7e1d4-3f5b-4a8c-b9d0-1e2f3a4b5c6d",
      "name": "Limpieza dental",
      "description": "Limpieza completa con ultrasonidos.",
      "duration_min": 45,
      "category": "Higiene",
      "price": 60.0,
      "currency": "EUR",
      "bookable_online": true,
      "active": true
    }
  ]
}

GET/staffpermiso read

Los profesionales del negocio y los recursos reservables (mesas, cabinas, boxes…). En un restaurante, las mesas son recursos con su capacidad y su zona.

Ejemplo
curl https://www.staria.es/api/v1/staff \
  -H "Authorization: Bearer sk_live_…"
Respuesta
{
  "data": {
    "professionals": [
      { "id": "7f1e2d3c-4b5a-4968-8776-655443322110", "name": "Dra. Lucía Martín", "specialty": "Higienista", "active": true }
    ],
    "resources": [
      { "id": "0d9e8f7a-6b5c-4d3e-8f21-0a9b8c7d6e5f", "name": "Gabinete 1", "capacity": 1, "zone": null, "status": "available" }
    ]
  }
}

GET/availabilitypermiso read

Huecos libres de un día. Úsalo antes de crear una reserva para ofrecer solo horas que existen.

Parámetros de consulta

NombreTipoObligatorioDescripción
service_iduuidSí (no en restaurantes)Servicio que se quiere reservar.
dateYYYY-MM-DDSíDía que se consulta (hora de Madrid).
professional_iduuidNoSolo los huecos de ese profesional.
party_sizeenteroSí en restaurantesNúmero de comensales. En restaurantes es obligatorio y service_id es opcional.
Ejemplo
curl "https://www.staria.es/api/v1/availability?service_id=c2a7e1d4-3f5b-4a8c-b9d0-1e2f3a4b5c6d&date=2026-10-06" \
  -H "Authorization: Bearer sk_live_…"
Respuesta
{
  "data": {
    "date": "2026-10-06",
    "service_id": "c2a7e1d4-3f5b-4a8c-b9d0-1e2f3a4b5c6d",
    "slots": [
      { "start": "2026-10-06T10:00:00+02:00", "end": "2026-10-06T10:45:00+02:00", "professional_id": "7f1e2d3c-4b5a-4968-8776-655443322110" },
      { "start": "2026-10-06T17:30:00+02:00", "end": "2026-10-06T18:15:00+02:00", "professional_id": "7f1e2d3c-4b5a-4968-8776-655443322110" }
    ]
  }
}

GET/bookingspermiso read

Lista de reservas en un rango de fechas, de la más próxima a la más lejana.

Parámetros de consulta

NombreTipoObligatorioDescripción
fromYYYY-MM-DDNoDesde este día (incluido). Por defecto, hoy.
toYYYY-MM-DDNoHasta este día (incluido). Por defecto, hoy + 30 días. Rango máximo: 92 días.
statustextoNopending · confirmed · completed · cancelled · no_show
channeltextoNoweb · phone · whatsapp · panel · api
customer_iduuidNoSolo las reservas de ese cliente.
sorttextoNostart (por defecto, por hora de la cita) · -created_at (las últimas que han entrado) · -updated_at (las últimas que han cambiado). Con -created_at o -updated_at y sin from/to no se filtra por fechas.
limitenteroNoResultados por página (máximo 100).
cursortextoNoEl next_cursor de la página anterior.
Ejemplo
curl "https://www.staria.es/api/v1/bookings?from=2026-10-01&to=2026-10-31&status=confirmed&limit=50" \
  -H "Authorization: Bearer sk_live_…"
Respuesta
{
  "data": [
    {
      "id": "5b0c1f7e-8a2d-4c3b-9e61-2f4a7d9c0b13",
      "status": "confirmed",
      "start": "2026-10-06T17:30:00+02:00",
      "end": "2026-10-06T18:15:00+02:00",
      "date": "2026-10-06",
      "duration_min": 45,
      "channel": "phone",
      "service": { "id": "c2a7e1d4-3f5b-4a8c-b9d0-1e2f3a4b5c6d", "name": "Limpieza dental" },
      "professional": { "id": "7f1e2d3c-4b5a-4968-8776-655443322110", "name": "Dra. Lucía Martín" },
      "resource": null,
      "party_size": null,
      "customer": {
        "id": "a1b2c3d4-e5f6-4789-8abc-def012345678",
        "name": "Carmen Ruiz López",
        "phone": "+34612345678",
        "email": "carmen.ruiz@ejemplo.es"
      },
      "notes": "Prefiere por la tarde.",
      "deposit": null,
      "test": false,
      "created_at": "2026-09-29T11:04:12+02:00",
      "updated_at": "2026-09-29T11:04:12+02:00"
    }
  ],
  "next_cursor": "eyJzIjoiMjAyNi0xMC0wNlQxNTozMDowMFoiLCJpIjoiNWIwYyJ9"
}

GET/bookings/{id}permiso read

Una reserva concreta.

Ejemplo
curl https://www.staria.es/api/v1/bookings/5b0c1f7e-8a2d-4c3b-9e61-2f4a7d9c0b13 \
  -H "Authorization: Bearer sk_live_…"
Respuesta
{
  "id": "5b0c1f7e-8a2d-4c3b-9e61-2f4a7d9c0b13",
  "status": "confirmed",
  "start": "2026-10-06T17:30:00+02:00",
  "end": "2026-10-06T18:15:00+02:00",
  "date": "2026-10-06",
  "duration_min": 45,
  "channel": "phone",
  "service": { "id": "c2a7e1d4-3f5b-4a8c-b9d0-1e2f3a4b5c6d", "name": "Limpieza dental" },
  "professional": { "id": "7f1e2d3c-4b5a-4968-8776-655443322110", "name": "Dra. Lucía Martín" },
  "resource": null,
  "party_size": null,
  "customer": {
    "id": "a1b2c3d4-e5f6-4789-8abc-def012345678",
    "name": "Carmen Ruiz López",
    "phone": "+34612345678",
    "email": "carmen.ruiz@ejemplo.es"
  },
  "notes": "Prefiere por la tarde.",
  "deposit": null,
  "test": false,
  "created_at": "2026-09-29T11:04:12+02:00",
  "updated_at": "2026-09-29T11:04:12+02:00"
}

POST/bookingspermiso write

Crea una reserva. La hora se da con start (ISO 8601) o con date + time (hora de Madrid). El cliente, con customer_id o con los datos de customer (se reutiliza la ficha si el teléfono ya existe). Si el hueco ya no está libre responde 409 slot_unavailable. El cliente recibe la confirmación habitual (salvo con claves sk_test_).

Cuerpo (JSON)

NombreTipoObligatorioDescripción
service_iduuidSí (no en restaurantes)Servicio reservado.
startISO 8601Uno de los dosInicio, p. ej. 2026-10-06T17:30:00+02:00.
date + timeYYYY-MM-DD + HH:mmUno de los dosAlternativa a start, en hora de Madrid.
customer_iduuidUno de los dosCliente ya existente.
customerobjetoUno de los dos{ name, phone, email? } para un cliente nuevo o sin id.
professional_iduuidNoProfesional concreto. Si no se indica, se asigna uno libre.
party_sizeenteroSí en restaurantesComensales.
notestextoNoNotas internas de la reserva.
Ejemplo
curl -X POST https://www.staria.es/api/v1/bookings \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "service_id": "c2a7e1d4-3f5b-4a8c-b9d0-1e2f3a4b5c6d",
    "date": "2026-10-06",
    "time": "17:30",
    "customer": { "name": "Carmen Ruiz López", "phone": "+34612345678" },
    "notes": "Prefiere por la tarde."
  }'
Respuesta
// 201 Created
{
  "id": "5b0c1f7e-8a2d-4c3b-9e61-2f4a7d9c0b13",
  "status": "confirmed",
  "start": "2026-10-06T17:30:00+02:00",
  "end": "2026-10-06T18:15:00+02:00",
  "date": "2026-10-06",
  "duration_min": 45,
  "channel": "api",
  "service": { "id": "c2a7e1d4-3f5b-4a8c-b9d0-1e2f3a4b5c6d", "name": "Limpieza dental" },
  "professional": { "id": "7f1e2d3c-4b5a-4968-8776-655443322110", "name": "Dra. Lucía Martín" },
  "resource": null,
  "party_size": null,
  "customer": {
    "id": "a1b2c3d4-e5f6-4789-8abc-def012345678",
    "name": "Carmen Ruiz López",
    "phone": "+34612345678",
    "email": "carmen.ruiz@ejemplo.es"
  },
  "notes": "Prefiere por la tarde.",
  "deposit": null,
  "test": false,
  "created_at": "2026-09-29T11:04:12+02:00",
  "updated_at": "2026-09-29T11:04:12+02:00"
}

PATCH/bookings/{id}permiso write

Cambia una reserva: moverla, confirmarla, anularla, marcarla como no presentada o como realizada.

Cuerpo (JSON)

NombreTipoObligatorioDescripción
actiontextoSíreschedule · confirm · cancel · no_show · complete
startISO 8601Con rescheduleNuevo inicio (o date + time).
reasontextoNoMotivo (se guarda en la reserva; útil al anular).
Ejemplo
curl -X PATCH https://www.staria.es/api/v1/bookings/5b0c1f7e-8a2d-4c3b-9e61-2f4a7d9c0b13 \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "action": "reschedule", "date": "2026-10-07", "time": "10:00" }'
Respuesta
{
  "id": "5b0c1f7e-8a2d-4c3b-9e61-2f4a7d9c0b13",
  "status": "confirmed",
  "start": "2026-10-07T10:00:00+02:00",
  "end": "2026-10-07T10:45:00+02:00",
  "date": "2026-10-07",
  "duration_min": 45,
  "channel": "phone",
  "service": { "id": "c2a7e1d4-3f5b-4a8c-b9d0-1e2f3a4b5c6d", "name": "Limpieza dental" },
  "professional": { "id": "7f1e2d3c-4b5a-4968-8776-655443322110", "name": "Dra. Lucía Martín" },
  "resource": null,
  "party_size": null,
  "customer": {
    "id": "a1b2c3d4-e5f6-4789-8abc-def012345678",
    "name": "Carmen Ruiz López",
    "phone": "+34612345678",
    "email": "carmen.ruiz@ejemplo.es"
  },
  "notes": "Prefiere por la tarde.",
  "deposit": null,
  "test": false,
  "created_at": "2026-09-29T11:04:12+02:00",
  "updated_at": "2026-09-29T11:04:12+02:00"
}

GET/customerspermiso read

Busca clientes. El teléfono se compara normalizado (da igual con o sin +34, espacios o guiones).

Parámetros de consulta

NombreTipoObligatorioDescripción
phonetextoNoTeléfono exacto (normalizado).
emailtextoNoEmail exacto.
qtextoNoBusca en nombre, teléfono y email.
sorttextoNocreated_at (por defecto, del más antiguo al más nuevo) · -created_at (los más nuevos primero).
limitenteroNoResultados por página (máximo 100).
cursortextoNoEl next_cursor de la página anterior.
Ejemplo
curl "https://www.staria.es/api/v1/customers?phone=612345678" \
  -H "Authorization: Bearer sk_live_…"
Respuesta
{
  "data": [
    {
      "id": "a1b2c3d4-e5f6-4789-8abc-def012345678",
      "first_name": "Carmen",
      "last_name": "Ruiz López",
      "name": "Carmen Ruiz López",
      "phone": "+34612345678",
      "email": "carmen.ruiz@ejemplo.es",
      "notes": null,
      "marketing_opt_in": true,
      "do_not_contact": false,
      "total_bookings": 7,
      "created_at": "2025-03-14T10:22:05+01:00",
      "updated_at": "2026-09-29T11:04:12+02:00"
    }
  ],
  "next_cursor": null
}

GET/customers/{id}permiso read

Un cliente concreto.

Ejemplo
curl https://www.staria.es/api/v1/customers/a1b2c3d4-e5f6-4789-8abc-def012345678 \
  -H "Authorization: Bearer sk_live_…"
Respuesta
{
  "id": "a1b2c3d4-e5f6-4789-8abc-def012345678",
  "first_name": "Carmen",
  "last_name": "Ruiz López",
  "name": "Carmen Ruiz López",
  "phone": "+34612345678",
  "email": "carmen.ruiz@ejemplo.es",
  "notes": null,
  "marketing_opt_in": true,
  "do_not_contact": false,
  "total_bookings": 7,
  "created_at": "2025-03-14T10:22:05+01:00",
  "updated_at": "2026-09-29T11:04:12+02:00"
}

POST/customerspermiso write

Crea un cliente o, si ya hay uno con ese teléfono, lo actualiza. Responde 201 si lo ha creado y 200 si lo ha actualizado.

Cuerpo (JSON)

NombreTipoObligatorioDescripción
first_nametextoSíNombre.
last_nametextoNoApellidos.
phonetextoSíTeléfono (si no lleva prefijo, se entiende +34).
emailtextoNoEmail.
notestextoNoNotas internas.
marketing_opt_inbooleanNoSi acepta recibir promociones.
Ejemplo
curl -X POST https://www.staria.es/api/v1/customers \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "first_name": "Carmen", "last_name": "Ruiz López", "phone": "612 34 56 78", "email": "carmen.ruiz@ejemplo.es" }'
Respuesta
// 200 OK (ya existía) o 201 Created (nuevo)
{
  "id": "a1b2c3d4-e5f6-4789-8abc-def012345678",
  "first_name": "Carmen",
  "last_name": "Ruiz López",
  "name": "Carmen Ruiz López",
  "phone": "+34612345678",
  "email": "carmen.ruiz@ejemplo.es",
  "notes": null,
  "marketing_opt_in": true,
  "do_not_contact": false,
  "total_bookings": 7,
  "created_at": "2025-03-14T10:22:05+01:00",
  "updated_at": "2026-09-29T11:04:12+02:00"
}

GET/callspermiso read

Llamadas que ha atendido el agente de voz, de la más reciente a la más antigua (el mismo objeto que llega en el aviso call.completed).

Parámetros de consulta

NombreTipoObligatorioDescripción
limitenteroNoResultados por página (máximo 100).
cursortextoNoEl next_cursor de la página anterior.
Ejemplo
curl "https://www.staria.es/api/v1/calls?limit=10" \
  -H "Authorization: Bearer sk_live_…"
Respuesta
{
  "data": [
    {
      "id": "2b4c6d8e-0f1a-4b3c-9d5e-7f8091a2b3c4",
      "direction": "inbound",
      "caller_phone": "+34612345678",
      "duration_seconds": 94,
      "outcome": "booking_created",
      "summary": "Carmen pide cita para una limpieza el lunes por la tarde. Reservada a las 17:30.",
      "booking_id": "5b0c1f7e-8a2d-4c3b-9e61-2f4a7d9c0b13",
      "customer_id": "a1b2c3d4-e5f6-4789-8abc-def012345678",
      "started_at": "2026-09-29T11:02:38+02:00",
      "ended_at": "2026-09-29T11:04:12+02:00"
    }
  ],
  "next_cursor": null
}

GET/messagespermiso read

Recados que ha tomado el agente, del más reciente al más antiguo (el mismo objeto que llega en el aviso message.taken).

Parámetros de consulta

NombreTipoObligatorioDescripción
limitenteroNoResultados por página (máximo 100).
cursortextoNoEl next_cursor de la página anterior.
Ejemplo
curl "https://www.staria.es/api/v1/messages?limit=10" \
  -H "Authorization: Bearer sk_live_…"
Respuesta
{
  "data": [
    {
      "id": "6f5e4d3c-2b1a-4098-8765-43210fedcba9",
      "customer_name": "Luis Pardo",
      "customer_phone": "+34699111222",
      "message": "Que le llamen para cambiar la cita del jueves.",
      "channel": "voice",
      "created_at": "2026-09-29T12:15:40+02:00",
      "done": false
    }
  ],
  "next_cursor": null
}

POST/webhookspermiso write

Suscribe una dirección a los avisos sin pasar por el panel (lo que hacen Zapier y Make al encender una automatización: «REST hooks»). Los avisos llegan igual que a las direcciones del panel. La respuesta trae el secret para comprobar la firma: solo se enseña esta vez. Con una clave sk_test_ solo llegan avisos de reservas de prueba, y con una sk_live_ nunca llegan. Si se retira la clave, la suscripción deja de recibir avisos. Como mucho 50 por negocio.

Cuerpo (JSON)

NombreTipoObligatorioDescripción
urltextoSíDirección https:// pública que recibirá los avisos.
eventslista de textoNoEventos a recibir (ver la tabla de avisos). Vacía o sin enviar = todos.
sourcetextoNozapier · make · api (por defecto). El negocio lo ve en su panel.
descriptiontextoNoPara reconocerla (máximo 200 letras).
Ejemplo
curl -X POST https://www.staria.es/api/v1/webhooks \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://tu-programa.es/avisos-staria", "events": ["booking.created", "booking.cancelled"] }'
Respuesta
// 201 Created
{
  "data": {
    "id": "0e1d2c3b-4a59-4687-9786-a5b4c3d2e1f0",
    "url": "https://tu-programa.es/avisos-staria",
    "events": ["booking.created", "booking.cancelled"],
    "source": "api",
    "description": null,
    "active": true,
    "disabled_reason": null,
    "test": false,
    "created_at": "2026-09-29T11:04:12+02:00",
    "secret": "whsec_…"
  }
}

GET/webhookspermiso write

Las suscripciones creadas por API con claves del mismo tipo (reales o de pruebas), sin su secreto. Las direcciones que el negocio añadió a mano en el panel no salen aquí.

Ejemplo
curl https://www.staria.es/api/v1/webhooks \
  -H "Authorization: Bearer sk_live_…"
Respuesta
{
  "data": [
    { "id": "0e1d2c3b-4a59-4687-9786-a5b4c3d2e1f0", "url": "https://tu-programa.es/avisos-staria", "events": ["booking.created"], "source": "api", "active": true, "…": "…" }
  ]
}

DELETE/webhooks/{id}permiso write

Borra una suscripción creada por API (lo que hacen Zapier y Make al apagar la automatización). Las direcciones creadas en el panel no se pueden borrar desde aquí.

Ejemplo
curl -X DELETE https://www.staria.es/api/v1/webhooks/0e1d2c3b-4a59-4687-9786-a5b4c3d2e1f0 \
  -H "Authorization: Bearer sk_live_…"
Respuesta
{ "data": { "id": "0e1d2c3b-4a59-4687-9786-a5b4c3d2e1f0", "deleted": true } }

GET/openapi.jsonpermiso read

La descripción completa de la API en formato OpenAPI 3.1, para importarla en Postman, Insomnia o generar un cliente automáticamente.

Ejemplo
curl https://www.staria.es/api/v1/openapi.json
Respuesta
{
  "openapi": "3.1.0",
  "info": { "title": "API de StarIA", "version": "1" },
  "servers": [{ "url": "https://www.staria.es/api/v1" }],
  "paths": { "…": "…" }
}

Objetos

Los mismos objetos salen en las respuestas de la API y en el campo data de los avisos.

Booking (reserva)

CampoTipoDescripción
iduuidIdentificador.
statustextopending · confirmed · completed · cancelled · no_show
start / endISO 8601Inicio y fin, con el desfase de Madrid.
dateYYYY-MM-DDDía de la reserva (hora de Madrid).
duration_minenteroDuración en minutos.
channeltextoweb · phone · whatsapp · panel · api
service{ id, name } | nullServicio reservado (puede faltar en restaurantes).
professional{ id, name } | nullProfesional asignado.
resource{ id, name } | nullRecurso (mesa, cabina…).
party_sizeentero | nullComensales (restaurantes).
customer{ id, name, phone, email }Cliente.
notestexto | nullNotas.
deposit{ status, amount } | nullSeñal: estado (pending, paid, expired…) e importe en euros.
testbooleantrue si se creó con una clave de pruebas.
created_at / updated_atISO 8601Alta y último cambio.

Customer (cliente)

CampoTipoDescripción
iduuidIdentificador.
first_name / last_name / nametextoNombre, apellidos y nombre completo.
phonetextoTeléfono en formato internacional (+34…).
emailtexto | nullEmail.
notestexto | nullNotas internas.
marketing_opt_inbooleanAcepta promociones.
do_not_contactbooleanPidió que no se le contacte: no le mandes nada.
total_bookingsenteroReservas que ha hecho.
created_at / updated_atISO 8601Alta y último cambio.

Call (llamada)

CampoTipoDescripción
iduuidIdentificador.
directiontextoinbound · outbound
caller_phonetexto | nullTeléfono de quien llama.
duration_secondsenteroDuración.
outcometexto | nullResultado (reserva hecha, recado, información…).
summarytexto | nullResumen de la llamada.
booking_id / customer_iduuid | nullReserva y cliente relacionados, si los hay.
started_at / ended_atISO 8601Inicio y fin.

Message (recado)

CampoTipoDescripción
iduuidIdentificador.
customer_name / customer_phonetextoQuién deja el recado y dónde devolverle la llamada.
messagetextoEl recado.
channeltextophone · whatsapp
created_atISO 8601Cuándo se tomó.
donebooleanSi el negocio ya lo ha atendido.

Avisos (webhooks)

StarIA hace un POST con JSON a la dirección que el negocio configure en el panel cada vez que pasa algo, venga de donde venga el cambio (web, teléfono, WhatsApp, panel o la propia API). Se puede elegir a qué eventos suscribirse; sin elegir, llegan todos.

EventoCuándodata
booking.createdReserva nueva. Entra una reserva por cualquier canal.Booking
booking.rescheduledReserva movida. Cambia la fecha o la hora de una reserva.Booking
booking.cancelledReserva anulada. El cliente o el negocio anula una reserva.Booking
booking.confirmedReserva confirmada. Una reserva pendiente pasa a confirmada.Booking
booking.completedCita realizada. La cita se marca como hecha.Booking
booking.no_showNo se presentó. El cliente no vino.Booking
payment.succeededSeñal pagada. El cliente paga la señal de su reserva.Booking
customer.createdCliente nuevo. Se crea un cliente en StarIA.Customer
call.completedLlamada atendida. El agente de voz termina una llamada.Call
message.takenRecado apuntado. El agente toma un recado para el negocio.Message
pingAviso de prueba. Lo manda el botón «Probar» del panel.{ message }

Qué llega

Un sobre común { id, event, created_at, test, data } y tres cabeceras: StarIA-Event (el evento), StarIA-Delivery (id del aviso, el mismo que id) y StarIA-Signature (la firma). test es true en los avisos de prueba y en lo creado con claves sk_test_.

POST /avisos-staria HTTP/1.1
Host: tu-programa.es
Content-Type: application/json
StarIA-Event: booking.created
StarIA-Delivery: 9e3f6a2b-1c4d-4e5f-8a7b-0c1d2e3f4a5b
StarIA-Signature: t=1790672652,v1=5f2b8c…e41a

{
  "id": "9e3f6a2b-1c4d-4e5f-8a7b-0c1d2e3f4a5b",
  "event": "booking.created",
  "created_at": "2026-09-29T11:04:12+02:00",
  "test": false,
  "data": {
    "id": "5b0c1f7e-8a2d-4c3b-9e61-2f4a7d9c0b13",
    "status": "confirmed",
    "start": "2026-10-06T17:30:00+02:00",
    "end": "2026-10-06T18:15:00+02:00",
    "date": "2026-10-06",
    "duration_min": 45,
    "channel": "phone",
    "service": { "id": "c2a7e1d4-3f5b-4a8c-b9d0-1e2f3a4b5c6d", "name": "Limpieza dental" },
    "professional": { "id": "7f1e2d3c-4b5a-4968-8776-655443322110", "name": "Dra. Lucía Martín" },
    "resource": null,
    "party_size": null,
    "customer": {
      "id": "a1b2c3d4-e5f6-4789-8abc-def012345678",
      "name": "Carmen Ruiz López",
      "phone": "+34612345678",
      "email": "carmen.ruiz@ejemplo.es"
    },
    "notes": "Prefiere por la tarde.",
    "deposit": null,
    "test": false,
    "created_at": "2026-09-29T11:04:12+02:00",
    "updated_at": "2026-09-29T11:04:12+02:00"
  }
}

Comprobar la firma

Al crear la dirección, el panel enseña una vez su clave de firma (whsec_…). Cada aviso lleva StarIA-Signature: t=<segundos>,v1=<hex>, donde v1 es el HMAC-SHA256, con esa clave, de "<t>.<cuerpo tal cual>" en hexadecimal. Para darlo por bueno:

  1. Calcula el HMAC sobre el cuerpo crudo, antes de parsear el JSON.
  2. Compáralo con v1 en tiempo constante.
  3. Rechaza el aviso si t se aleja más de 300 segundos de tu hora actual (evita que alguien reenvíe un aviso viejo).
Node.js (Express)
import crypto from 'node:crypto'
import express from 'express'

const app = express()
const SECRETO = process.env.STARIA_WEBHOOK_SECRET // whsec_…

// Ojo: la firma se calcula sobre el cuerpo TAL CUAL llega (sin parsear).
app.post('/avisos-staria', express.raw({ type: 'application/json' }), (req, res) => {
  const cabecera = req.get('StarIA-Signature') ?? ''
  const partes = Object.fromEntries(cabecera.split(',').map((p) => p.trim().split('=')))
  const t = Number(partes.t)
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return res.sendStatus(400)

  const cuerpo = req.body.toString('utf8')
  const esperado = crypto.createHmac('sha256', SECRETO).update(`${t}.${cuerpo}`).digest('hex')
  const a = Buffer.from(esperado)
  const b = Buffer.from(partes.v1 ?? '')
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) return res.sendStatus(400)

  const aviso = JSON.parse(cuerpo)
  // Si ya procesaste aviso.id, no lo repitas: contesta 200 igualmente.
  res.sendStatus(200) // contesta rápido y procesa después (cola, tarea en segundo plano…)
})
PHP
<?php
$secreto  = getenv('STARIA_WEBHOOK_SECRET'); // whsec_…
$cuerpo   = file_get_contents('php://input'); // cuerpo sin tocar
$cabecera = $_SERVER['HTTP_STARIA_SIGNATURE'] ?? '';

$partes = [];
foreach (explode(',', $cabecera) as $p) {
    [$k, $v] = array_pad(explode('=', trim($p), 2), 2, '');
    $partes[$k] = $v;
}
$t = (int) ($partes['t'] ?? 0);
if (!$t || abs(time() - $t) > 300) { http_response_code(400); exit; }

$esperado = hash_hmac('sha256', $t . '.' . $cuerpo, $secreto);
if (!hash_equals($esperado, $partes['v1'] ?? '')) { http_response_code(400); exit; }

$aviso = json_decode($cuerpo, true);
// Si ya procesaste $aviso['id'], no lo repitas: contesta 200 igualmente.
http_response_code(200);

Respuesta, reintentos y orden

  • Contesta con cualquier 2xx en menos de 10 segundos. Si tienes que hacer algo largo, guarda el aviso y procésalo después.
  • Si no contestas o contestas otra cosa, se reintenta tras 1 min, 5 min, 30 min, 2 h, 6 h y 12 h (7 intentos en total). Después se da por fallido.
  • Tras 50 fallos seguidos la dirección se pausa sola y el negocio lo ve en el panel; al reactivarla se pone el contador a cero.
  • Los avisos pueden llegar desordenados o repetidos. Guarda los id ya procesados y descarta los duplicados; para saber el estado actual de una reserva, fíate de su updated_at o pídela con GET /bookings/{id}.
  • Solo se admiten direcciones https:// públicas (no IP privadas, localhost ni redes internas).
  • El panel enseña los últimos 20 avisos de cada dirección con su estado, para depurar.

Suscribirse por API (REST hooks)

Además del panel, un programa puede darse de alta él solo con POST /webhooks y darse de baja con DELETE /webhooks/{id} (ver Endpoints). Es lo que hacen Zapier y Make. El negocio las ve en su panel con el nombre de quien las creó y puede pausarlas o borrarlas.

Zapier y Make

StarIA tiene conector propio para Zapier y para Make. Con ellos, cuando entra una reserva, se anula, el agente atiende una llamada o toma un recado, puedes mandarlo a Holded, Mailchimp, HubSpot, Google Sheets, Slack… y al revés: crear reservas o clientes en StarIA desde otro programa.

Qué incluye
DisparadoresReserva nueva, movida, anulada, confirmada o completada; no se presentó; cliente nuevo; llamada atendida; recado tomado. Llegan al momento (avisos), no cada 15 minutos.
AccionesCrear una reserva; mover, anular, confirmar o cerrar una reserva; crear o actualizar un cliente.
BúsquedasBuscar un cliente (por teléfono, email o nombre) y buscar una reserva.
  1. Crea una clave en el panel (Configuración → Integraciones → Conectar otros programas). Si la automatización va a crear o cambiar cosas, elige «consultar y hacer cambios»; para los disparadores también hace falta, porque la suscripción a los avisos es un cambio.
  2. En Zapier o en Make, añade StarIA, elige «Conectar» y pega la clave.
  3. Para probar sin tocar la agenda real, usa una clave sk_test_: lo que crees será de prueba y solo recibirás avisos de reservas de prueba.

De momento los conectores se comparten por invitación: pídela a ayuda@staria.es. Por dentro usan esta misma API (/me para comprobar la clave, /webhooks para los avisos y los listados con sort=-created_at para enseñar ejemplos).

¿Dudas o echas algo en falta? Escríbenos a ayuda@staria.es.