Integra CallHalla
en tus sistemas
Una API REST sobre la misma plataforma que usa la aplicación: agentes, campañas, llamadas, contactos, números de teléfono y webhooks. Esta página documenta lo que existe hoy.
Autenticación
Todas las peticiones llevan la cabecera Authorization. Hay dos mecanismos:
JWT de sesión
El que usa la propia aplicación web. El token de acceso se obtiene al iniciar sesión y se renueva automáticamente vía POST /api/auth/refresh con la cookie segura de refresco.
API keys (agl_…)
Claves de integración para tráfico servidor a servidor. Se crean en Ajustes → API Keys y empiezan por el prefijo agl_. La clave completa solo se muestra una vez: el sistema guarda un hash y conserva los primeros 8 caracteres para que puedas identificarla.
Authorization: Bearer <token>curl "https://callhalla.io/api/agents" \
-H "Authorization: Bearer agl_live_..."Convenciones
URL base
Todas las rutas viven bajo el prefijo /api, con recursos en kebab-case y verbos REST semánticos (GET para leer, POST para crear o accionar, PATCH para editar, DELETE para borrar).
https://callhalla.io/apiRespuestas exitosas
La API usa dos estructuras: la entidad directa (o un array plano) para recursos individuales y listados simples, y un envoltorio de paginación para colecciones voluminosas.
{
"id": "agent-uuid-123",
"name": "Asistente Comercial",
"type": "natural",
"isActive": true
}{
"data": [
{
"id": "contact-uuid-999",
"firstName": "Alan",
"phone": "+34600000000",
"status": "pending"
}
],
"pagination": {
"page": 1,
"pageSize": 25,
"totalItems": 142,
"totalPages": 6
}
}Parámetros de paginación: page (base 1, por defecto 1) y pageSize o limit (por defecto 25).
Errores
Todos los errores devuelven JSON con la misma estructura — nunca HTML ni texto plano. El campo correlationId permite a soporte localizar tu petición en los logs.
{
"success": false,
"error": "Agent limit reached. Maximum allowed: 3",
"code": "PLAN_LIMIT_EXCEEDED",
"statusCode": 403,
"timestamp": "2026-05-26T09:20:00.000Z",
"correlationId": "8f2d9c1a-7b3c-4d5e-8f9a-0b1c2d3e4f5a"
}| Código | HTTP | Causa común |
|---|---|---|
VALIDATION_ERROR | 400 | Parámetros mal formados o tipos incorrectos |
AUTHENTICATION_ERROR | 401 | Token JWT ausente o expirado, o API key inválida |
AUTHORIZATION_ERROR | 403 | Rol de usuario insuficiente |
NOT_FOUND | 404 | El recurso no existe |
CONFLICT_ERROR | 409 | Operación denegada por el estado actual (p. ej. pausar una campaña ya pausada) |
INSUFFICIENT_CREDITS | 402 | Saldo de créditos de llamada agotado |
PLAN_LIMIT_EXCEEDED | 403 | Cuota máxima del plan alcanzada (agentes, números…) |
RATE_LIMIT_ERROR | 429 | Límite de peticiones por ventana rebasado |
PAYMENT_ERROR | 402 | Error al procesar el cobro |
EXTERNAL_SERVICE_ERROR | 502 | Fallo en un proveedor externo (telefonía, voz…) |
DATABASE_ERROR | 500 | Error interno de base de datos |
CONFIGURATION_ERROR | 500 | Fallo de configuración del servidor |
WEBHOOK_VALIDATION | 401 | Firma del carrier telefónico incorrecta |
Recursos principales
Los grupos de endpoints disponibles, con los representativos de cada recurso. No es el catálogo completo: preferimos documentar poco y que sea exacto.
Agentes
Crea y gestiona tus agentes de voz.
- GET
/api/agents— Listar agentes - POST
/api/agents— Crear un agente
Campañas
Crea campañas de llamadas y contrólalas (ejecutar, pausar, reanudar, cancelar).
- GET
/api/campaigns— Listar campañas - POST
/api/campaigns— Crear una campaña - POST
/api/campaigns/:id/execute— Ejecutar una campaña
Llamadas
Historial y detalle. Las llamadas se generan al ejecutar campañas o al recibir llamadas entrantes; no se crean directamente por API.
- GET
/api/calls— Listar llamadas (paginado) - GET
/api/calls/:id— Detalle de una llamada
Contactos
Consulta los contactos de tu cuenta. Se cargan por CSV dentro de una campaña.
- GET
/api/contacts— Listar contactos - POST
/api/campaigns/:campaignId/contacts/upload— Subir contactos (CSV) a una campaña
Números de teléfono
Busca números disponibles y gestiona los de tu cuenta.
- GET
/api/phone-numbers— Listar tus números - POST
/api/phone-numbers/buy— Comprar un número
Webhooks
Suscripciones a eventos con entrega firmada y reintentos.
- GET
/api/webhooks— Listar webhooks - POST
/api/webhooks— Crear un webhook
Ejemplos
curl "https://callhalla.io/api/calls?page=1&pageSize=25" \
-H "Authorization: Bearer agl_live_..."curl -X POST "https://callhalla.io/api/webhooks" \
-H "Authorization: Bearer agl_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Mi integración",
"url": "https://example.com/webhooks/callhalla",
"events": ["call.completed", "campaign.completed"]
}'Webhooks
Recibe un POST con JSON en tu URL cada vez que ocurre un evento al que estés suscrito. El payload siempre tiene la misma forma:
{
"event": "call.completed",
"timestamp": "2026-06-12T10:30:00.000Z",
"data": { ... }
}Eventos disponibles
Estos son los eventos que acepta la API al crear un webhook:
Llamadas salientes
call.startedcall.ringingcall.answeredcall.completedcall.failedcall.transferredcall.no_answercall.busycall.voicemail
Llamadas entrantes
inbound_call.receivedinbound_call.answeredinbound_call.completedinbound_call.missed
Campañas
campaign.startedcampaign.pausedcampaign.resumedcampaign.completedcampaign.failedcampaign.cancelled
Flujos
flow.startedflow.completedflow.failed
Citas
appointment.bookedappointment.confirmedappointment.cancelledappointment.rescheduledappointment.completedappointment.no_showappointment.reminder_sent
Formularios
form.submittedform.lead_created
Firma HMAC
Cada entrega incluye la cabecera X-Webhook-Signature: un HMAC-SHA256 en hexadecimal del cuerpo, calculado con el secreto del webhook (generado al crearlo). También se envían X-Webhook-Event (evento) y X-Webhook-Delivery (id único de entrega).
Reintentos
Si tu endpoint no responde 2xx, la entrega se reintenta hasta 5 veces en total, esperando 1, 5, 15, 30 y 60 minutos entre intentos. Cada petición tiene un timeout de 30 segundos. Puedes consultar las entregas en GET /api/webhooks/:id/logs y probar tu configuración con POST /api/webhooks/:id/test.
const crypto = require("crypto");
function verifyWebhook(rawBody, signatureHeader, secret) {
// signatureHeader = "sha256=<hex>"
const expected =
"sha256=" +
crypto.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
return (
signatureHeader.length === expected.length &&
crypto.timingSafeEqual(
Buffer.from(signatureHeader),
Buffer.from(expected)
)
);
}
app.post("/webhooks/callhalla", (req, res) => {
const ok = verifyWebhook(
req.rawBody,
req.headers["x-webhook-signature"],
process.env.CALLHALLA_WEBHOOK_SECRET
);
if (!ok) return res.status(401).end();
res.status(200).end();
});