NeuraVoz API
REST + webhooks para construir agentes de voz IA conectados a tu negocio. Eventos reales de tu empresa activan llamadas con variables dinámicas, contexto y conversación natural.
Introducción
Base URL: https://neuravoice.tech/v1
Todas las respuestas son JSON. Todos los timestamps son ISO 8601 UTC. Todos los números E.164 (+12025550123).
El SDK oficial llegará en fase 2. Por ahora cualquier cliente HTTP funciona — los ejemplos usan curl.
¿No sos developer?
Mirá la guía paso a paso para usar todo desde el dashboard sin escribir código.
Quickstart
Tres pasos para tener una llamada IA andando:
- Creá una API key en /dashboard/developers.
- Creá un agente vía dashboard o API (
POST /v1/agents). - Disparale una llamada:
curl -X POST https://neuravoice.tech/v1/calls \
-H "Authorization: Bearer nvk_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"agent_id": "TU_AGENT_ID",
"from": "+18336528039",
"to": "+5491155551234",
"dynamic_variables": { "nombre": "Carlos" }
}'SignalWire llama al to. Cuando contesta, el agente conversa. Vos recibís webhooks con cada cambio de estado.
Autenticación
Las API keys se crean en /dashboard/developers. Cada key empieza con nvk_live_ y se muestra una sola vez al crearse — guárdala segura.
Pásala en cada request como Authorization: Bearer ...:
curl https://neuravoice.tech/v1/agents \ -H "Authorization: Bearer nvk_live_abc123..."
Las keys se hashean (SHA-256) antes de almacenarse. No guardamos plaintext en ninguna parte después del primer reveal.
Agentes
Un agente es una configuración (nombre, prompt, voz, idioma) gestionada por nuestro motor de IA conversacional. Cuando lo conectás a una llamada, el agente conversa en tiempo real con el destinatario.
Listar agentes
GET /v1/agents
Authorization: Bearer nvk_live_...
# Respuesta
{
"object": "list",
"data": [
{
"id": "uuid",
"name": "Recepcionista Clínica",
"prompt": "Eres una recepcionista...",
"first_message": "Hola, soy María de Clínica Vista...",
"voice_id": "EXAVITQu4vr4xnSDxMaL",
"language": "es",
"provider_agent_id": "agent_xxx",
"created_at": "2026-05-20T...",
"updated_at": "2026-05-20T..."
}
]
}Crear agente
POST /v1/agents
Content-Type: application/json
{
"name": "Recordatorios Dental",
"prompt": "Eres una asistente de Clínica Dental. Recuerda al paciente su cita de mañana. Sé breve y amable. Si quiere reprogramar, dile que se contactará un humano.",
"first_message": "Hola {{nombre}}, te llamo de Clínica Dental para confirmar tu cita.",
"voice_id": "EXAVITQu4vr4xnSDxMaL",
"language": "es"
}
# Respuesta 201
{
"id": "uuid",
"name": "Recordatorios Dental",
...
}El campo first_message y prompt pueden usar variables como {{nombre}} — se reemplazan al iniciar cada llamada con los valores que pases en dynamic_variables.
Actualizar / Eliminar
PATCH /v1/agents/{id} # mismo body que POST
DELETE /v1/agents/{id}Llamadas
Lanza una llamada con un agente. Tu número (FROM) llama al destino (TO) y al contestar se conecta el agente.
POST /v1/calls
Authorization: Bearer nvk_live_...
Content-Type: application/json
Idempotency-Key: unique-request-id-123
{
"agent_id": "uuid-del-agente",
"from": "+18336528039",
"to": "+12025551234",
"dynamic_variables": {
"nombre": "Carlos",
"cita_fecha": "mañana a las 3 PM",
"empresa": "Clínica Vista"
},
"external_ref": "appointment_98231"
}
# Respuesta 201
{
"id": "CallSid",
"agent_id": "uuid",
"from": "+18336528039",
"to": "+12025551234",
"status": "queued",
"rate_per_min": 0.18,
"external_ref": "appointment_98231",
"created_at": "..."
}Obtener estado de la llamada
GET /v1/calls/{id}
# Devuelve cost, agent_cost, duration_seconds, status,
# recording_url, transcription, dynamic_variables, external_refListar llamadas
GET /v1/calls?limit=50 GET /v1/calls?external_ref=appointment_98231 # filtrado por tu ID
Llamadas programadas
Pasá scheduled_at (ISO-8601, hasta 30 días en el futuro) a POST /v1/calls y la llamada queda encolada hasta ese momento.
POST /v1/calls
{
"agent_id": "uuid",
"from": "+18336528039",
"to": "+5491155551234",
"scheduled_at": "2026-06-15T14:30:00Z",
"dynamic_variables": { "nombre": "Lucia" }
}
# Respuesta 202
{
"object": "scheduled_call",
"id": "uuid",
"scheduled_at": "2026-06-15T14:30:00Z",
"status": "pending"
}Un cron worker en NeuraVoz dispara la llamada con tolerancia de ±1 minuto. Idempotency-Key también persiste en la cola para evitar duplicados.
Variables dinámicas
Cada llamada puede incluir hasta 50 variables de hasta 500 caracteres cada una. Se inyectan al agente al iniciar la sesión.
En el prompt y first_message del agente referenciás las variables con doble llave:
first_message: "Hola {{nombre}}, te llamo de {{empresa}}. Tu pedido {{orden}} sale {{fecha}}."
# Al hacer POST /v1/calls:
{
"dynamic_variables": {
"nombre": "Carlos",
"empresa": "Vista",
"orden": "ORD-8821",
"fecha": "mañana"
}
}Nombres válidos: [a-zA-Z_][a-zA-Z0-9_]{0,63}. Caracteres de control se eliminan automáticamente.
Idempotency
POST /v1/calls acepta el header opcional Idempotency-Key. Si el cliente reintenta con la misma key dentro de 24h, la API devuelve la respuesta original sin crear una segunda llamada.
Recomendado siempre: en tu sistema usá un UUID por intención de llamada. Si tu cron reintenta, no duplicarás cargos.
Webhooks salientes
Recibí eventos en tu URL cuando ocurren cosas en NeuraVoz. Configurá endpoints en /dashboard/developers o vía API.
Crear endpoint
POST /v1/webhooks
{
"url": "https://api.tuapp.com/webhooks/neuravoz",
"events": ["call.completed", "call.failed"],
"description": "Producción"
}
# Respuesta 201
{
"id": "uuid",
"url": "...",
"events": ["call.completed", "call.failed"],
"secret": "whsec_xxxxx" ← solo se muestra al crear
}Verificar firma
Cada request a tu endpoint incluye un header X-NeuraVoz-Signature: t=<timestamp>,v1=<hmac_hex>. Verificalo así:
# Node.js / TypeScript
import { createHmac } from "node:crypto";
function verify(secret: string, header: string, rawBody: string): boolean {
const [tsPart, sigPart] = header.split(",");
const ts = tsPart.split("=")[1];
const sig = sigPart.split("=")[1];
const mac = createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex");
return mac === sig && Math.abs(Date.now()/1000 - parseInt(ts, 10)) < 300; // 5-min tolerance
}Payload
{
"id": "evt_xxxx", # idempotente — usa para dedup
"type": "call.completed",
"created_at": "2026-05-20T12:34:56Z",
"data": {
"id": "CallSid",
"from": "+18336528039",
"to": "+12025551234",
"duration_seconds": 47,
"cost": 0.15,
"agent_cost": 0.15,
"status": "completed",
"agent_id": "uuid",
"external_ref": "appointment_98231",
"dynamic_variables": { ... }
}
}Tu endpoint debe responder 2xx. Reintentos con backoff: 1m → 5m → 30m → 2h → 12h → dead. También usá el header X-NeuraVoz-Event-Id para idempotencia.
Catálogo de eventos
agent.created— un agente fue creadoagent.updated— actualizadoagent.deleted— eliminadocall.queued— llamada creada, en cola del providercall.ringing— sonandocall.answered— contestadacall.completed— terminada (con duration, cost)call.failed— falló (busy, no-answer, canceled, error)
Errores
Formato uniforme:
{
"error": {
"code": "insufficient_balance",
"message": "Balance $0.20 below minimum $0.50 for agent calls"
}
}Códigos comunes: unauthorized (401), insufficient_scope (403), quota_exceeded (403), not_found (404), insufficient_balance (402), rate_limited (429), provider_error (502).
Rate limits
Por defecto: 60 requests por minuto por API key. Si lo excedés recibís 429 rate_limited. La ventana es de 1 minuto fijo (no sliding).
Para campañas masivas usá llamadas individuales con Idempotency-Key y reintentos exponenciales. SignalWire impone 1 outbound call/sec/número — si necesitás throughput, comprá varios números y rotá el from.
¿Necesitás un límite más alto? Escribí a soporte indicando el caso de uso esperado.