Empezar
Tres cosas y ya estás dentro: una clave, la URL de tu región y el permiso adecuado en esa clave.
- Crea una clave en Ajustes → Integraciones y automatización → API, marcando solo los permisos que vayas a usar. Se enseña una vez: guárdala donde guardes el resto de secretos.
- Elige tu región arriba. Tu organización vive en una sola y sus datos no salen de ella.
-
Comprueba que funciona con
GET /v1/me, que te dice qué organización eres y qué permisos tiene la clave.
/v1/calls cada pocos segundos llega tarde y gasta de más.
Autenticación
Cada petición lleva la clave en una cabecera:
Authorization: Bearer ak_9f3c1d2b…
Una clave pertenece a la organización, no a una persona: tu
automatización no se cae el día que se va de la empresa quien la configuró. En
el registro de auditoría las acciones salen como
api-key:ak_9f3c1d2b, junto a la persona que responde de esa clave.
Cuando quien creó una clave sale del equipo —expulsión, suspensión o baja—, la clave sigue funcionando y pasa a responder de ella quien lo expulsa o suspende (en una baja voluntaria, el propietario más antiguo). Los administradores reciben un correo con la lista: si esa persona pudo copiar alguna, rótala (crea otra con los mismos permisos, cámbiala en tu integración y revoca la vieja).
Permisos
Sin el permiso, la respuesta es 403 y dice cuál
falta. Da a cada integración lo justo: es lo que hace que revocar una
clave sea una molestia y no una urgencia.
| Permiso | Da acceso a |
|---|---|
calendar:read | Calendars, availability and appointments. |
calendar:write | Create and cancel appointments. |
calls:read | Call list and detail. Metadata only, never the content. |
recordings:read | The recording audio and the transcript too. |
callbacks:read | The callback queue. |
campaigns:read | Campaigns and their counters. |
reports:read | Aggregated reports. |
extensions:read | The list of extensions. |
extensions:write | Add and remove extensions. |
groups:read | Hunt groups and queues, with their members. |
groups:write | Change who belongs to a group or a queue. |
dnc:write | Add numbers to the Do-Not-Call list. Cannot read it or remove anyone. |
assist:read | The operator assistants' profile: name and configuration. |
assist:write | Each query spends your organization's AI credits. |
chatbots:read | The chatbots and ALL their conversations, including what visitors wrote. |
chatbots:write | Open, write to and close conversations, request a person, rate and confirm bot actions. Each message spends AI credits. |
recordings:read cubre el audio y la transcripción:
con calls:read a secas se leen los metadatos de la llamada, nunca su
contenido.
Región
Si llamas a la región equivocada no recibes una lista vacía
—que parecería un dato y no lo es— sino un 421 con la URL buena:
{
"error": {
"type": "wrong_region",
"message": "esta organización se sirve desde otra región; repite la petición contra base_url",
"base_url": "https://eu.alpire.ai"
}
}
La misma clave vale en las dos: lo que cambia es dónde están los datos.
Formato común
Listados y paginación
{ "data": [ … ], "next_cursor": "01JZK8…" }
Paginación por cursor, no por número de página. Pasa el
next_cursor que te llega y para cuando venga null.
limit por defecto 50, máximo 200.
No hay offset a propósito: la tabla de llamadas crece sin tope y un
OFFSET 50000 lo acabarían pagando también las llamadas en curso, que
comparten esa base de datos. Los ids son ULID, así que ordenar por id
es ordenar por fecha.
Fechas
RFC 3339 y en UTC a la salida (2026-08-08T09:30:00Z).
A la entrada se acepta cualquier desplazamiento explícito
(…+02:00). Lo que no se acepta es una fecha sin zona: en un producto
que corre en dos continentes, eso es una cita a la hora equivocada esperando a
ocurrir.
Errores
Mira siempre type, nunca message: el texto puede cambiar,
el tipo no. Todo error de /v1 sale con este formato,
también una ruta mal escrita, un verbo equivocado, un JSON mal cerrado o un
timeout. request_id es el mismo valor que la cabecera
X-Request-Id: guárdalo con el error.
{
"error": {
"type": "bad_request",
"message": "'status' no admite «canceled»; valores válidos: booked, cancelled",
"request_id": "01K5…"
}
}
| HTTP | type | Qué hacer |
|---|---|---|
| 400 | bad_request | Arreglar la petición (cuerpo, Content-Type, parámetros, un filtro con un valor que no existe). No reintentar igual. |
| 401 | unauthorized | Clave ausente, inválida, caducada o revocada. |
| 403 | forbidden | Falta un permiso (el mensaje dice cuál), organización suspendida, plan sin API, plan no operativo (solo escrituras) o, en asistencia, sin créditos de IA. |
| 404 | not_found | No existe — o no es tuyo: misma respuesta a propósito —, o la ruta no es de la API. |
| 405 | method_not_allowed | La ruta existe con otro método. |
| 409 | conflict | Petición válida, pero el mundo dice que no. |
| 409 | idempotency_in_progress | Otra petición con la misma Idempotency-Key sigue en curso: esperar Retry-After. |
| 413 | payload_too_large | El cuerpo pasa del límite. |
| 421 | wrong_region | Repetir contra el base_url del error. |
| 422 | idempotency_mismatch | Esa Idempotency-Key ya se usó con otra petición. Una clave por operación. |
| 429 | rate_limited | Esperar lo que diga Retry-After. |
| 500 | internal | Fallo nuestro. Reintentar con espera creciente. |
| 502 | provider_error | Falló un tercero: el calendario (Cal.com, Google) o el proveedor del modelo de IA. Reintentar con espera creciente. |
| 503 | unavailable | El servicio de chat de la organización no respondió. Reintentar en unos segundos (con el mismo client_id no se duplica). |
| 504 | timeout | Pasó el tiempo máximo. Una escritura puede haberse completado: repítela con la misma Idempotency-Key. |
Límites
- 600 lecturas (
GET) por minuto y clave. - 60 escrituras (
POST,PUT,DELETE) por minuto y clave. - 60 descargas de grabación cada 5 minutos y clave.
- 1200 peticiones por minuto y dirección IP.
- 10 claves activas por organización. Revocar una libera hueco; las revocadas y las caducadas no cuentan.
Los tres primeros son por clave, no por cuenta: dos
integraciones tuyas no se quitan cupo entre ellas. Cada petición paga
un solo cubo, y GET /v1/me no gasta ninguno.
No hace falta que lo lleves a ciegas: toda respuesta que paga cupo trae
X-RateLimit-Limit, X-RateLimit-Remaining y
X-RateLimit-Reset (epoch en segundos) del cubo que gobierna esa
petición concreta — el de lectura, el de escritura o el de grabación, que
tienen ventanas distintas. Si te acercas a cero, frena antes de chocar.
Al pasarte recibes 429 con error.type: "rate_limited" y
la cabecera Retry-After con los segundos que faltan de verdad
para que se reponga ese cubo (también en error.retry_after). Respétala
y reintenta: no hace falta backoff propio.
Idempotencia y tiempos
Toda escritura acepta la cabecera Idempotency-Key: un valor único por
operación (una UUID vale). La primera petición con esa clave se ejecuta y su
respuesta se guarda 24 horas; repetirla con la misma clave y la
misma petición devuelve la misma respuesta, con
Idempotent-Replayed: true, sin volver a ejecutar nada. La misma clave
con otra petición es un 422. Un 400, 401,
403, 429 o 5xx no la consume: corrige o
reintenta con la misma.
Idempotency-Key: 5f0c2a8e-4b7d-4c1e-9f3a-2d6b8e1c7a90
Una petición tiene 30 s, salvo POST /v1/appointments
y POST /v1/assist/ask, que tienen 90 s. Pasado el
tiempo recibes un 504, pero la escritura no se
cancela: termina igual. Ante un 504 o un corte de red,
repite con la misma Idempotency-Key y recibirás el resultado real; sin
ella, repetir puede duplicar.
fetch() desde el
JavaScript de tu web no va a funcionar — y es a propósito: una clave en el
frontend es una clave pública, y la tuya puede leer llamadas y crear citas. El
patrón correcto es tu backend hablando con nosotros y tu frontend hablando con tu
backend.
Endpoints
Cada uno con sus parámetros, la petición ya montada en el lenguaje que elijas arriba y un ejemplo de respuesta real.
Ningún endpoint coincide. Prueba con el nombre de un parámetro
(cursor, party_size) o con un permiso
(calls:read).
Qué deja rastro
Una acción hecha por la API produce lo mismo que si la hubiera hecho una persona en el panel: mismo webhook, mismo registro de auditoría.
| Acción | Webhook | Auditoría |
|---|---|---|
POST /v1/appointments | appointment.booked | appointment.create |
POST /v1/appointments/{id}/cancel | appointment.cancelled | appointment.cancel |
POST /v1/extensions | — | extension.create |
DELETE /v1/extensions/{id} | — | extension.delete |
PUT /v1/groups/{id}/members | — | dial_group.update |
PUT /v1/queues/{id}/members | — | queue.update |
POST /v1/dnc | — | dnc.add |
POST /v1/assist/ask | — | assist.ask |
Las citas creadas por API emiten el mismo evento que las del
mostrador o las que reserva el agente por teléfono, pero el data no es
idéntico según el origen (la API y el panel mandan source,
end y resource_id; la voz, call_id): la tabla
campo a campo está en la referencia de
webhooks. Extensiones, grupos y colas no tienen webhook: tampoco lo emite el
panel.