Referencia API

Todos los endpoints de la API v1. Ejemplos en curl, JavaScript (fetch) y Python (requests); el lenguaje que elijas se mantiene en toda la página.

URL basehttps://hubvocal.com/v1
AutenticaciónAuthorization: Bearer vt_live_…
Límite de solicitudes120 solicitudes/min por clave · 429 + Retry-After
Ámbitosread · write · calls
Cada clave tiene read, write y/o calls. read cubre las solicitudes GET, write los cambios y calls las llamadas salientes. Una solicitud fuera de los ámbitos de la clave recibe 403.
Cuerpo del error{"detail": "..."}
OpenAPIGET /v1/openapi.json

Para autenticación, integración y códigos de error, consulta Desarrolladores.

Agentes

Las conversaciones se ejecutan en modo texto, así que prueban las instrucciones y el uso de herramientas, no la voz ni la latencia. Las simulaciones se ejecutan desde el panel; todavía no hay un endpoint /v1 para ellas.

Listar agentes

GET /v1/agents

Devuelve todos los agentes de tu organización, en el orden en que aparecen en el panel.

Solicitud

curl
curl "https://hubvocal.com/v1/agents" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
[
  {
    "id": "front-desk",
    "name": "Front desk",
    "lang": "en",
    "engine": "live",
    "instructions": "You are the front desk assistant for a dental clinic. Help callers book appointments and answer general questions briefly. Never make things up; offer a callback from the team instead.",
    "greeting": "Hello, thanks for calling the clinic. How can I help you today?",
    "voice": {
      "voice": "marin",
      "speed": 1
    },
    "tools": {
      "builtins": [
        "end_call",
        "take_message"
      ],
      "webhooks": [
        "tl_8f3a2c"
      ]
    },
    "turn": {
      "first_speaker": "agent",
      "idle_check_s": 30
    },
    "recording": {
      "enabled": false,
      "announce": true
    },
    "post_call": {
      "summary": true,
      "fields": [
        {
          "name": "appointment_date",
          "type": "date",
          "description": "Requested appointment date, if any"
        },
        {
          "name": "callback",
          "type": "boolean",
          "description": "Does the caller want a callback?"
        }
      ]
    },
    "knowledge": {
      "doc_ids": [
        "kb_7d21e4"
      ],
      "mode": "tool"
    },
    "published_version": 3,
    "draft_changed": false
  }
]

Crear un agente

POST /v1/agents

Crea un agente. Cada campo se valida en el servidor; los valores que superan los límites se recortan. Si pasas un id que ya existe, la solicitud falla con 409. Para actualizar, usa PUT.

NombreEnTipoDescripción
id body string Opcional. Letras minúsculas, dígitos y guiones, hasta 48 caracteres. Si se omite, se genera a partir del nombre.
name
obligatorio
body string Nombre visible, hasta 60 caracteres.
lang
obligatorio
body string Código del idioma de conversación, p. ej. tr, en, de. La lista completa está en GET /v1/voices.
engine
obligatorio
body "live" | "gemini" | "realtime" | "classic" live: GPT-Live 1, gemini: Gemini Live, realtime: OpenAI Realtime, classic: STT → LLM → TTS.
instructions body string Instrucciones del sistema, hasta 8000 caracteres.
greeting body string Lo primero que dice el agente, hasta 400 caracteres.
voice body object voice (un nombre de /v1/voices), speed (solo classic y realtime; fija en 1.0 para live y gemini), tone (estilo de habla en texto libre, hasta 600 caracteres).
tools body object builtins (end_call, take_message) y webhooks (ids de herramientas); hasta 16 herramientas en total. Si está vacío o se omite, el agente no tiene herramientas. mcp: [{server_id, tools}] añade herramientas de tus servidores MCP; cuentan para el mismo límite.
turn body object Ajustes de turnos de palabra: sensitivity (low, balanced, high; solo en el motor classic), first_speaker (agent o caller), silence_wait_s (1–5), idle_check_s (0 o 10–120), agent_timeout_s (0 o 10–60). Los campos vacíos usan los valores predeterminados.
recording body object recording: {enabled (false por defecto), announce (true por defecto), announce_text?}. Si está activada, el agente anuncia la grabación antes del saludo.
post_call body object post_call.summary: redacta un resumen cuando termina la llamada (true por defecto). post_call.fields: hasta 24 campos que se rellenan tras la llamada ({name, type, description, enum?, required?}).
knowledge body object doc_ids (hasta 50 documentos de la base de conocimiento) y mode ("tool"). El agente recibe entonces la herramienta search_knowledge.
calendar body object integration_id y calendar_id del panel, duration_min (15–240), buffer_min (0–60), timezone, working_hours, min_notice_h (0–72), max_days_ahead (1–90). Activa las herramientas de citas.

Solicitud

curl
curl -X POST "https://hubvocal.com/v1/agents" \
  -H "Authorization: Bearer $VT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Front desk",
  "lang": "en",
  "engine": "live",
  "instructions": "You are the front desk assistant for a dental clinic. Help callers book appointments and answer general questions briefly. Never make things up; offer a callback from the team instead.",
  "greeting": "Hello, thanks for calling the clinic. How can I help you today?",
  "voice": {
    "voice": "marin",
    "speed": 1
  },
  "tools": {
    "builtins": [
      "end_call",
      "take_message"
    ],
    "webhooks": [
      "tl_8f3a2c"
    ]
  },
  "turn": {
    "first_speaker": "agent",
    "idle_check_s": 30
  },
  "recording": {
    "enabled": false,
    "announce": true
  },
  "post_call": {
    "summary": true,
    "fields": [
      {
        "name": "appointment_date",
        "type": "date",
        "description": "Requested appointment date, if any"
      },
      {
        "name": "callback",
        "type": "boolean",
        "description": "Does the caller want a callback?"
      }
    ]
  },
  "knowledge": {
    "doc_ids": [
      "kb_7d21e4"
    ],
    "mode": "tool"
  }
}'

Respuesta · 201

JSON
{
  "id": "front-desk",
  "name": "Front desk",
  "lang": "en",
  "engine": "live",
  "instructions": "You are the front desk assistant for a dental clinic. Help callers book appointments and answer general questions briefly. Never make things up; offer a callback from the team instead.",
  "greeting": "Hello, thanks for calling the clinic. How can I help you today?",
  "voice": {
    "voice": "marin",
    "speed": 1
  },
  "tools": {
    "builtins": [
      "end_call",
      "take_message"
    ],
    "webhooks": [
      "tl_8f3a2c"
    ]
  },
  "turn": {
    "first_speaker": "agent",
    "idle_check_s": 30
  },
  "recording": {
    "enabled": false,
    "announce": true
  },
  "post_call": {
    "summary": true,
    "fields": [
      {
        "name": "appointment_date",
        "type": "date",
        "description": "Requested appointment date, if any"
      },
      {
        "name": "callback",
        "type": "boolean",
        "description": "Does the caller want a callback?"
      }
    ]
  },
  "knowledge": {
    "doc_ids": [
      "kb_7d21e4"
    ],
    "mode": "tool"
  },
  "published_version": null,
  "draft_changed": true
}

Errores

  • 409 Ya existe un agente con este id.

Obtener un agente

GET /v1/agents/{id}

Devuelve un solo agente. published_version es el número de la versión activa (null si nunca se ha publicado) y draft_changed indica si el borrador difiere de ella.

NombreEnTipoDescripción
id
obligatorio
path string Id del agente, p. ej. front-desk.

Solicitud

curl
curl "https://hubvocal.com/v1/agents/front-desk" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
{
  "id": "front-desk",
  "name": "Front desk",
  "lang": "en",
  "engine": "live",
  "instructions": "You are the front desk assistant for a dental clinic. Help callers book appointments and answer general questions briefly. Never make things up; offer a callback from the team instead.",
  "greeting": "Hello, thanks for calling the clinic. How can I help you today?",
  "voice": {
    "voice": "marin",
    "speed": 1
  },
  "tools": {
    "builtins": [
      "end_call",
      "take_message"
    ],
    "webhooks": [
      "tl_8f3a2c"
    ]
  },
  "turn": {
    "first_speaker": "agent",
    "idle_check_s": 30
  },
  "recording": {
    "enabled": false,
    "announce": true
  },
  "post_call": {
    "summary": true,
    "fields": [
      {
        "name": "appointment_date",
        "type": "date",
        "description": "Requested appointment date, if any"
      },
      {
        "name": "callback",
        "type": "boolean",
        "description": "Does the caller want a callback?"
      }
    ]
  },
  "knowledge": {
    "doc_ids": [
      "kb_7d21e4"
    ],
    "mode": "tool"
  },
  "published_version": 3,
  "draft_changed": false
}

Errores

  • 404 No hay ningún agente con este id.

Actualizar un agente

PUT /v1/agents/{id}

Reemplaza el agente por el objeto que envías. Envía el objeto completo: los campos que omitas vuelven a sus valores predeterminados. El id de la ruta prevalece sobre cualquier id del cuerpo.

NombreEnTipoDescripción
id
obligatorio
path string Id del agente, p. ej. front-desk.
name
obligatorio
body string Nombre visible, hasta 60 caracteres.
lang
obligatorio
body string Código del idioma de conversación, p. ej. tr, en, de. La lista completa está en GET /v1/voices.
engine
obligatorio
body "live" | "gemini" | "realtime" | "classic" live: GPT-Live 1, gemini: Gemini Live, realtime: OpenAI Realtime, classic: STT → LLM → TTS.
instructions body string Instrucciones del sistema, hasta 8000 caracteres.
greeting body string Lo primero que dice el agente, hasta 400 caracteres.
voice body object voice (un nombre de /v1/voices), speed (solo classic y realtime; fija en 1.0 para live y gemini), tone (estilo de habla en texto libre, hasta 600 caracteres).
tools body object builtins (end_call, take_message) y webhooks (ids de herramientas); hasta 16 herramientas en total. Si está vacío o se omite, el agente no tiene herramientas. mcp: [{server_id, tools}] añade herramientas de tus servidores MCP; cuentan para el mismo límite.
turn body object Ajustes de turnos de palabra: sensitivity (low, balanced, high; solo en el motor classic), first_speaker (agent o caller), silence_wait_s (1–5), idle_check_s (0 o 10–120), agent_timeout_s (0 o 10–60). Los campos vacíos usan los valores predeterminados.
recording body object recording: {enabled (false por defecto), announce (true por defecto), announce_text?}. Si está activada, el agente anuncia la grabación antes del saludo.
post_call body object post_call.summary: redacta un resumen cuando termina la llamada (true por defecto). post_call.fields: hasta 24 campos que se rellenan tras la llamada ({name, type, description, enum?, required?}).
knowledge body object doc_ids (hasta 50 documentos de la base de conocimiento) y mode ("tool"). El agente recibe entonces la herramienta search_knowledge.
calendar body object integration_id y calendar_id del panel, duration_min (15–240), buffer_min (0–60), timezone, working_hours, min_notice_h (0–72), max_days_ahead (1–90). Activa las herramientas de citas.

Solicitud

curl
curl -X PUT "https://hubvocal.com/v1/agents/front-desk" \
  -H "Authorization: Bearer $VT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Front desk",
  "lang": "en",
  "engine": "live",
  "instructions": "You are the front desk assistant for a dental clinic. Help callers book appointments and answer general questions briefly. Never make things up; offer a callback from the team instead.",
  "greeting": "Hello, thanks for calling the clinic. How can I help you today?",
  "voice": {
    "voice": "cedar",
    "speed": 1
  },
  "tools": {
    "builtins": [
      "end_call",
      "take_message"
    ],
    "webhooks": [
      "tl_8f3a2c"
    ]
  },
  "turn": {
    "first_speaker": "agent",
    "idle_check_s": 30
  },
  "recording": {
    "enabled": false,
    "announce": true
  },
  "post_call": {
    "summary": true,
    "fields": [
      {
        "name": "appointment_date",
        "type": "date",
        "description": "Requested appointment date, if any"
      },
      {
        "name": "callback",
        "type": "boolean",
        "description": "Does the caller want a callback?"
      }
    ]
  },
  "knowledge": {
    "doc_ids": [
      "kb_7d21e4"
    ],
    "mode": "tool"
  }
}'

Respuesta · 200

JSON
{
  "id": "front-desk",
  "name": "Front desk",
  "lang": "en",
  "engine": "live",
  "instructions": "You are the front desk assistant for a dental clinic. Help callers book appointments and answer general questions briefly. Never make things up; offer a callback from the team instead.",
  "greeting": "Hello, thanks for calling the clinic. How can I help you today?",
  "voice": {
    "voice": "cedar",
    "speed": 1
  },
  "tools": {
    "builtins": [
      "end_call",
      "take_message"
    ],
    "webhooks": [
      "tl_8f3a2c"
    ]
  },
  "turn": {
    "first_speaker": "agent",
    "idle_check_s": 30
  },
  "recording": {
    "enabled": false,
    "announce": true
  },
  "post_call": {
    "summary": true,
    "fields": [
      {
        "name": "appointment_date",
        "type": "date",
        "description": "Requested appointment date, if any"
      },
      {
        "name": "callback",
        "type": "boolean",
        "description": "Does the caller want a callback?"
      }
    ]
  },
  "knowledge": {
    "doc_ids": [
      "kb_7d21e4"
    ],
    "mode": "tool"
  },
  "published_version": 3,
  "draft_changed": true
}

Errores

  • 404 No hay ningún agente con este id. Créalo primero con POST.

Eliminar un agente

DELETE /v1/agents/{id}

Elimina el agente. Una organización siempre conserva al menos un agente, así que no se puede eliminar el último.

NombreEnTipoDescripción
id
obligatorio
path string Id del agente, p. ej. front-desk.

Solicitud

curl
curl -X DELETE "https://hubvocal.com/v1/agents/front-desk" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
{
  "deleted": "front-desk"
}

Errores

  • 400 Es el último agente de la organización.
  • 404 No hay ningún agente con este id.

Listar versiones

GET /v1/agents/{id}/versions

Las versiones publicadas del agente, de la más reciente a la más antigua. is_live marca la que usan ahora las llamadas.

NombreEnTipoDescripción
id
obligatorio
path string Id del agente, p. ej. front-desk.

Solicitud

curl
curl "https://hubvocal.com/v1/agents/front-desk/versions" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
[
  {
    "version": 3,
    "note": "Shorter greeting",
    "published_at": 1793829000,
    "published_by": "ayse@example.com",
    "is_live": true
  },
  {
    "version": 2,
    "note": "Added price list",
    "published_at": 1793740000,
    "published_by": "ayse@example.com",
    "is_live": false
  }
]

Errores

  • 404 No hay ningún agente con este id.

Obtener una versión

GET /v1/agents/{id}/versions/{n}

El agente completo tal como estaba en esa versión.

NombreEnTipoDescripción
id
obligatorio
path string Id del agente, p. ej. front-desk.
n
obligatorio
path integer Número de versión.

Solicitud

curl
curl "https://hubvocal.com/v1/agents/front-desk/versions/2" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
{
  "id": "front-desk",
  "name": "Front desk",
  "lang": "en",
  "engine": "live",
  "instructions": "You are the front desk assistant for a dental clinic. Help callers book appointments and answer general questions briefly. Never make things up; offer a callback from the team instead.",
  "greeting": "Hello, thanks for calling the clinic. How can I help you today?",
  "voice": {
    "voice": "marin",
    "speed": 1
  },
  "tools": {
    "builtins": [
      "end_call",
      "take_message"
    ],
    "webhooks": [
      "tl_8f3a2c"
    ]
  },
  "turn": {
    "first_speaker": "agent",
    "idle_check_s": 30
  },
  "recording": {
    "enabled": false,
    "announce": true
  },
  "post_call": {
    "summary": true,
    "fields": [
      {
        "name": "appointment_date",
        "type": "date",
        "description": "Requested appointment date, if any"
      },
      {
        "name": "callback",
        "type": "boolean",
        "description": "Does the caller want a callback?"
      }
    ]
  },
  "knowledge": {
    "doc_ids": [
      "kb_7d21e4"
    ],
    "mode": "tool"
  },
  "version": 2
}

Errores

  • 404 No existe ese agente o esa versión.

Publicar el borrador

POST /v1/agents/{id}/publish

Guarda el borrador actual como una nueva versión y la activa. Las llamadas que ya están en curso conservan su versión.

NombreEnTipoDescripción
id
obligatorio
path string Id del agente, p. ej. front-desk.
note body string Una nota breve sobre lo que ha cambiado.

Solicitud

curl
curl -X POST "https://hubvocal.com/v1/agents/front-desk/publish" \
  -H "Authorization: Bearer $VT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "note": "Shorter greeting"
}'

Respuesta · 200

JSON
{
  "version": 4
}

Errores

  • 404 No hay ningún agente con este id.

Revertir

POST /v1/agents/{id}/rollback

Copia una versión anterior en el borrador y la publica como una versión nueva.

NombreEnTipoDescripción
id
obligatorio
path string Id del agente, p. ej. front-desk.
version
obligatorio
body integer La versión que quieres recuperar.

Solicitud

curl
curl -X POST "https://hubvocal.com/v1/agents/front-desk/rollback" \
  -H "Authorization: Bearer $VT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "version": 2
}'

Respuesta · 200

JSON
{
  "version": 5
}

Errores

  • 404 No existe ese agente o esa versión.

Base de conocimiento

Listar documentos

GET /v1/knowledge

Los documentos de tu organización, con su estado de procesamiento, tamaño, número de fragmentos y los agentes que los usan.

Solicitud

curl
curl "https://hubvocal.com/v1/knowledge" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
[
  {
    "id": "kb_7d21e4",
    "title": "Price list 2026",
    "kind": "file",
    "filename": "price-list-2026.pdf",
    "url": null,
    "size_bytes": 248113,
    "chunks": 42,
    "status": "ready",
    "error": null,
    "created_at": 1793740000,
    "used_by": [
      "front-desk"
    ]
  }
]

Añadir un documento

POST /v1/knowledge

Sube un archivo como multipart/form-data (campo file), envía texto como JSON {title, text} o una sola página web como JSON {url}. Devuelve 201 con el estado processing; consulta el documento hasta que pase a ready o failed.

NombreEnTipoDescripción
file body file multipart/form-data: pdf, docx, txt, md o html, de hasta 10 MB.
title body string Título; se envía junto con text.
text body string Contenido en texto plano.
url body string Alternativa: una sola página https que se descarga.

Solicitud

curl
curl -X POST "https://hubvocal.com/v1/knowledge" \
  -H "Authorization: Bearer $VT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "title": "Opening hours",
  "text": "We are open 09:00–18:00 on weekdays and 10:00–14:00 on Saturdays. Closed on Sundays."
}'

Respuesta · 201

JSON
{
  "id": "kb_9f03b1",
  "title": "Opening hours",
  "kind": "text",
  "filename": null,
  "url": null,
  "size_bytes": 96,
  "chunks": 0,
  "status": "processing",
  "error": null,
  "created_at": 1793740000,
  "used_by": []
}

Errores

  • 400 Tipo de archivo no admitido, archivo de más de 10 MB, URL que no es https o que apunta a una dirección privada, o se ha alcanzado el límite de la organización (200 documentos, 200 MB).

Obtener un documento

GET /v1/knowledge/{id}

El documento, con una vista previa de sus primeros 2000 caracteres.

NombreEnTipoDescripción
id
obligatorio
path string Id del documento.

Solicitud

curl
curl "https://hubvocal.com/v1/knowledge/kb_7d21e4" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
{
  "id": "kb_7d21e4",
  "title": "Price list 2026",
  "kind": "file",
  "filename": "price-list-2026.pdf",
  "url": null,
  "size_bytes": 248113,
  "chunks": 42,
  "status": "ready",
  "error": null,
  "created_at": 1793740000,
  "used_by": [
    "front-desk"
  ],
  "preview": "Price list 2026. Check-up 1,500 TRY, scale and polish 2,000 TRY…"
}

Errores

  • 404 No hay ningún documento con este id.

Eliminar un documento

DELETE /v1/knowledge/{id}

Elimina el documento y lo quita de todos los agentes que lo usaban.

NombreEnTipoDescripción
id
obligatorio
path string Id del documento.

Solicitud

curl
curl -X DELETE "https://hubvocal.com/v1/knowledge/kb_7d21e4" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
{
  "deleted": "kb_7d21e4"
}

Errores

  • 404 No hay ningún documento con este id.

Buscar

POST /v1/knowledge/search

Ejecuta la misma búsqueda que usa el agente y devuelve los fragmentos que mejor coinciden, con una puntuación. Útil para revisar tus documentos antes de pasar a producción.

NombreEnTipoDescripción
query
obligatorio
body string Qué buscar.
doc_ids body string[] Limita la búsqueda a estos documentos.

Solicitud

curl
curl -X POST "https://hubvocal.com/v1/knowledge/search" \
  -H "Authorization: Bearer $VT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "query": "how much is a scale and polish"
}'

Respuesta · 200

JSON
[
  {
    "doc_id": "kb_7d21e4",
    "title": "Price list 2026",
    "chunk_index": 3,
    "text": "Scale and polish: 2,000 TRY, about 30 minutes.",
    "score": 0.82
  }
]

Herramientas

Herramientas integradas

Estas herramientas no necesitan definición. Actívalas en tools.builtins del agente. search_knowledge se añade cuando knowledge está configurado. Las herramientas de citas se añaden cuando calendar está configurado.

NombreQué hace
end_callEl agente se despide y cuelga 0,75 s después de terminar de hablar. Si el llamante habla mientras tanto, no cuelga. El registro de llamada muestra ended_by: "agent".
take_messageRecoge name, phone, message y callback_requested y los guarda en outcomes[] del registro de llamada. No pide confirmación.
search_knowledgeSe añade automáticamente cuando el agente tiene documentos en knowledge. Busca en ellos y anota las fuentes en el registro de llamada.
check_availabilityLectura. Devuelve los huecos libres entre date_from y date_to, dentro del horario laboral del agente. Se añade cuando calendar está configurado.
book_appointmentEscritura, con paso de confirmación. Reserva start a nombre de name con phone, y una note opcional. La reserva se guarda en outcomes[].
cancel_appointmentEscritura, con paso de confirmación. Cancela una reserva por su booking_ref.

Las integraciones de calendario (Google Calendar) solo se conectan desde el panel; no tienen endpoint /v1. Usa los ids de integración y de calendario que muestra el panel en el campo calendar del agente.

Listar herramientas

GET /v1/tools

Las herramientas webhook de tu organización. El secreto nunca se devuelve; has_secret indica si hay uno configurado.

Solicitud

curl
curl "https://hubvocal.com/v1/tools" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
[
  {
    "id": "tl_8f3a2c",
    "name": "check_order",
    "description": "Looks up the shipping status of an order by its number.",
    "kind": "read",
    "url": "https://crm.example.com/hooks/order",
    "parameters": {
      "type": "object",
      "properties": {
        "order_id": {
          "type": "string",
          "description": "Order number"
        }
      },
      "required": [
        "order_id"
      ]
    },
    "timeout_s": 8,
    "enabled": true,
    "has_secret": true,
    "created_at": 1793830000,
    "updated_at": 1793830000
  }
]

Obtener una herramienta

GET /v1/tools/{id}

Una sola herramienta webhook. El secreto nunca se devuelve.

NombreEnTipoDescripción
id
obligatorio
path string Id de la herramienta.

Solicitud

curl
curl "https://hubvocal.com/v1/tools/tl_8f3a2c" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
{
  "id": "tl_8f3a2c",
  "name": "check_order",
  "description": "Looks up the shipping status of an order by its number.",
  "kind": "read",
  "url": "https://crm.example.com/hooks/order",
  "parameters": {
    "type": "object",
    "properties": {
      "order_id": {
        "type": "string",
        "description": "Order number"
      }
    },
    "required": [
      "order_id"
    ]
  },
  "timeout_s": 8,
  "enabled": true,
  "has_secret": true,
  "created_at": 1793830000,
  "updated_at": 1793830000
}

Errores

  • 404 No hay ninguna herramienta con este id.

Crear una herramienta

POST /v1/tools

Define una herramienta webhook para la organización. Para usarla, añade su id a tools.webhooks de un agente. La URL debe ser https y no puede apuntar a una dirección local o privada.

NombreEnTipoDescripción
name
obligatorio
body string Letras minúsculas, dígitos y guiones bajos, empezando por una letra (3–41 caracteres). Único en la organización; end_call y take_message están reservados.
description
obligatorio
body string Le indica al modelo cuándo y cómo usar la herramienta.
kind
obligatorio
body "read" | "write" read se ejecuta de inmediato; write pide primero confirmación al llamante.
url
obligatorio
body string Tu endpoint. Solo https; se rechazan las direcciones locales y privadas.
parameters body object Objeto JSON Schema: hasta 20 campos de tipo string, number, integer o boolean (se admite enum).
timeout_s body integer De 2 a 15 segundos.
enabled body boolean Las herramientas desactivadas no se ofrecen al modelo.
secret body string Se usa para firmar las solicitudes. Solo escritura; al actualizar, vacío conserva el secreto actual y "-" lo elimina.

Solicitud

curl
curl -X POST "https://hubvocal.com/v1/tools" \
  -H "Authorization: Bearer $VT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "check_order",
  "description": "Looks up the shipping status of an order by its number.",
  "kind": "read",
  "url": "https://crm.example.com/hooks/order",
  "parameters": {
    "type": "object",
    "properties": {
      "order_id": {
        "type": "string",
        "description": "Order number"
      }
    },
    "required": [
      "order_id"
    ]
  },
  "timeout_s": 8,
  "enabled": true,
  "secret": "whsec_2b7f…"
}'

Respuesta · 201

JSON
{
  "id": "tl_8f3a2c",
  "name": "check_order",
  "description": "Looks up the shipping status of an order by its number.",
  "kind": "read",
  "url": "https://crm.example.com/hooks/order",
  "parameters": {
    "type": "object",
    "properties": {
      "order_id": {
        "type": "string",
        "description": "Order number"
      }
    },
    "required": [
      "order_id"
    ]
  },
  "timeout_s": 8,
  "enabled": true,
  "has_secret": true,
  "created_at": 1793830000,
  "updated_at": 1793830000
}

Errores

  • 400 Un campo no cumple las reglas: formato del nombre, URL https, dirección privada, más de 20 parámetros o tiempo de espera fuera de rango.
  • 409 Ya existe una herramienta con este nombre, o el nombre está reservado.

Actualizar una herramienta

PUT /v1/tools/{id}

Reemplaza la herramienta por el objeto que envías. Deja secret vacío u omítelo para conservar el actual; envía "-" para eliminarlo.

NombreEnTipoDescripción
id
obligatorio
path string Id de la herramienta.
name
obligatorio
body string Letras minúsculas, dígitos y guiones bajos, empezando por una letra (3–41 caracteres). Único en la organización; end_call y take_message están reservados.
description
obligatorio
body string Le indica al modelo cuándo y cómo usar la herramienta.
kind
obligatorio
body "read" | "write" read se ejecuta de inmediato; write pide primero confirmación al llamante.
url
obligatorio
body string Tu endpoint. Solo https; se rechazan las direcciones locales y privadas.
parameters body object Objeto JSON Schema: hasta 20 campos de tipo string, number, integer o boolean (se admite enum).
timeout_s body integer De 2 a 15 segundos.
enabled body boolean Las herramientas desactivadas no se ofrecen al modelo.
secret body string Se usa para firmar las solicitudes. Solo escritura; al actualizar, vacío conserva el secreto actual y "-" lo elimina.

Solicitud

curl
curl -X PUT "https://hubvocal.com/v1/tools/tl_8f3a2c" \
  -H "Authorization: Bearer $VT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "check_order",
  "description": "Looks up the shipping status of an order by its number.",
  "kind": "read",
  "url": "https://crm.example.com/hooks/order",
  "parameters": {
    "type": "object",
    "properties": {
      "order_id": {
        "type": "string",
        "description": "Order number"
      }
    },
    "required": [
      "order_id"
    ]
  },
  "timeout_s": 10,
  "enabled": true,
  "secret": ""
}'

Respuesta · 200

JSON
{
  "id": "tl_8f3a2c",
  "name": "check_order",
  "description": "Looks up the shipping status of an order by its number.",
  "kind": "read",
  "url": "https://crm.example.com/hooks/order",
  "parameters": {
    "type": "object",
    "properties": {
      "order_id": {
        "type": "string",
        "description": "Order number"
      }
    },
    "required": [
      "order_id"
    ]
  },
  "timeout_s": 10,
  "enabled": true,
  "has_secret": true,
  "created_at": 1793830000,
  "updated_at": 1793833600
}

Errores

  • 400 Un campo no cumple las reglas: formato del nombre, URL https, dirección privada, más de 20 parámetros o tiempo de espera fuera de rango.
  • 404 No hay ninguna herramienta con este id.

Eliminar una herramienta

DELETE /v1/tools/{id}

Elimina la herramienta y la quita de todos los agentes que la usaban.

NombreEnTipoDescripción
id
obligatorio
path string Id de la herramienta.

Solicitud

curl
curl -X DELETE "https://hubvocal.com/v1/tools/tl_8f3a2c" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
{
  "deleted": "tl_8f3a2c"
}

Errores

  • 404 No hay ninguna herramienta con este id.

Probar una herramienta

POST /v1/tools/{id}/test

Envía una solicitud real a la URL de la herramienta con los argumentos que indiques y devuelve el estado, el tiempo que tardó y hasta 4 KB del cuerpo de la respuesta.

NombreEnTipoDescripción
id
obligatorio
path string Id de la herramienta.
arguments body object Argumentos de ejemplo, conformes a parameters.

Solicitud

curl
curl -X POST "https://hubvocal.com/v1/tools/tl_8f3a2c/test" \
  -H "Authorization: Bearer $VT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "arguments": {
    "order_id": "A-10234"
  }
}'

Respuesta · 200

JSON
{
  "ok": true,
  "status": 200,
  "ms": 412,
  "body": "{\"order_id\":\"A-10234\",\"status\":\"shipped\",\"eta\":\"2026-11-07\"}"
}

Errores

  • 404 No hay ninguna herramienta con este id.

Servidores MCP

Listar servidores MCP

GET /v1/mcp-servers

Los servidores MCP de tu organización con su estado y sus herramientas sincronizadas. auth_set indica si hay un secreto guardado; el secreto nunca se devuelve.

Solicitud

curl
curl "https://hubvocal.com/v1/mcp-servers" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
[
  {
    "id": "mcp_2b8e41",
    "name": "crm",
    "url": "https://mcp.example.com/mcp",
    "transport": "streamable_http",
    "auth": "bearer",
    "auth_set": true,
    "enabled": true,
    "status": "ok",
    "error": null,
    "tools_synced_at": 1793831000,
    "tools": [
      {
        "name": "find_customer",
        "description": "Find a customer by phone number.",
        "input_schema": {
          "type": "object",
          "properties": {
            "phone": {
              "type": "string"
            }
          },
          "required": [
            "phone"
          ]
        },
        "enabled": true,
        "access": "read"
      },
      {
        "name": "create_ticket",
        "description": "Open a support ticket.",
        "input_schema": {
          "type": "object",
          "properties": {
            "subject": {
              "type": "string"
            },
            "body": {
              "type": "string"
            }
          },
          "required": [
            "subject"
          ]
        },
        "enabled": false,
        "access": "write"
      }
    ]
  }
]

Añadir un servidor MCP

POST /v1/mcp-servers

Guarda el servidor y obtiene su lista de herramientas. Las herramientas nuevas quedan desactivadas. Hasta 10 servidores por organización.

NombreEnTipoDescripción
name
obligatorio
body string Nombre corto; se usa como prefijo en los nombres de las herramientas (<name>.<tool>).
url
obligatorio
body string El endpoint MCP del servidor. Solo https; se rechazan las direcciones locales y privadas.
transport body string streamable_http (por defecto) o sse.
auth body string none, bearer o header.
secret body string Token o valor del encabezado. Se guarda cifrado y nunca se devuelve; "-" lo elimina.
header_name body string Nombre del encabezado cuando auth es header.

Solicitud

curl
curl -X POST "https://hubvocal.com/v1/mcp-servers" \
  -H "Authorization: Bearer $VT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "crm",
  "url": "https://mcp.example.com/mcp",
  "auth": "bearer",
  "secret": "sk_live_…"
}'

Respuesta · 201

JSON
{
  "id": "mcp_2b8e41",
  "name": "crm",
  "url": "https://mcp.example.com/mcp",
  "transport": "streamable_http",
  "auth": "bearer",
  "auth_set": true,
  "enabled": true,
  "status": "ok",
  "error": null,
  "tools_synced_at": 1793831000,
  "tools": [
    {
      "name": "find_customer",
      "description": "Find a customer by phone number.",
      "input_schema": {
        "type": "object",
        "properties": {
          "phone": {
            "type": "string"
          }
        },
        "required": [
          "phone"
        ]
      },
      "enabled": false,
      "access": "write"
    },
    {
      "name": "create_ticket",
      "description": "Open a support ticket.",
      "input_schema": {
        "type": "object",
        "properties": {
          "subject": {
            "type": "string"
          },
          "body": {
            "type": "string"
          }
        },
        "required": [
          "subject"
        ]
      },
      "enabled": false,
      "access": "write"
    }
  ]
}

Errores

  • 400 La URL no es https o apunta a una dirección privada, o no se pudo obtener la lista de herramientas del servidor.
  • 409 La organización ya tiene 10 servidores MCP.

Actualizar un servidor MCP

PUT /v1/mcp-servers/{id}

Cambia el nombre, la URL, la autenticación o si está activado, y también qué herramientas están activas y si son de lectura o de escritura. Envía secret: "-" para eliminar el secreto.

NombreEnTipoDescripción
id
obligatorio
path string Id del servidor MCP.
name body string Nombre corto; se usa como prefijo en los nombres de las herramientas (<name>.<tool>).
url body string El endpoint MCP del servidor. Solo https; se rechazan las direcciones locales y privadas.
enabled body boolean Un servidor desactivado no se ofrece a los agentes.
auth body string none, bearer o header.
secret body string Token o valor del encabezado. Se guarda cifrado y nunca se devuelve; "-" lo elimina.
tools body object[] [{name, enabled, access}] por herramienta; access es read o write.

Solicitud

curl
curl -X PUT "https://hubvocal.com/v1/mcp-servers/mcp_2b8e41" \
  -H "Authorization: Bearer $VT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "tools": [
    {
      "name": "find_customer",
      "enabled": true,
      "access": "read"
    },
    {
      "name": "create_ticket",
      "enabled": true,
      "access": "write"
    }
  ]
}'

Respuesta · 200

JSON
{
  "id": "mcp_2b8e41",
  "name": "crm",
  "url": "https://mcp.example.com/mcp",
  "transport": "streamable_http",
  "auth": "bearer",
  "auth_set": true,
  "enabled": true,
  "status": "ok",
  "error": null,
  "tools_synced_at": 1793831000,
  "tools": [
    {
      "name": "find_customer",
      "description": "Find a customer by phone number.",
      "input_schema": {
        "type": "object",
        "properties": {
          "phone": {
            "type": "string"
          }
        },
        "required": [
          "phone"
        ]
      },
      "enabled": true,
      "access": "read"
    },
    {
      "name": "create_ticket",
      "description": "Open a support ticket.",
      "input_schema": {
        "type": "object",
        "properties": {
          "subject": {
            "type": "string"
          },
          "body": {
            "type": "string"
          }
        },
        "required": [
          "subject"
        ]
      },
      "enabled": true,
      "access": "write"
    }
  ]
}

Errores

  • 400 La URL no es https o apunta a una dirección privada, o no se pudo obtener la lista de herramientas del servidor.
  • 404 No hay ningún servidor MCP con este id.

Eliminar un servidor MCP

DELETE /v1/mcp-servers/{id}

Elimina el servidor y quita sus herramientas de todos los agentes.

NombreEnTipoDescripción
id
obligatorio
path string Id del servidor MCP.

Solicitud

curl
curl -X DELETE "https://hubvocal.com/v1/mcp-servers/mcp_2b8e41" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
{
  "deleted": "mcp_2b8e41"
}

Errores

  • 404 No hay ningún servidor MCP con este id.

Sincronizar herramientas

POST /v1/mcp-servers/{id}/sync

Vuelve a obtener la lista de herramientas. Las herramientas nuevas llegan desactivadas y con acceso write.

NombreEnTipoDescripción
id
obligatorio
path string Id del servidor MCP.

Solicitud

curl
curl -X POST "https://hubvocal.com/v1/mcp-servers/mcp_2b8e41/sync" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
{
  "id": "mcp_2b8e41",
  "name": "crm",
  "url": "https://mcp.example.com/mcp",
  "transport": "streamable_http",
  "auth": "bearer",
  "auth_set": true,
  "enabled": true,
  "status": "ok",
  "error": null,
  "tools_synced_at": 1793831000,
  "tools": [
    {
      "name": "find_customer",
      "description": "Find a customer by phone number.",
      "input_schema": {
        "type": "object",
        "properties": {
          "phone": {
            "type": "string"
          }
        },
        "required": [
          "phone"
        ]
      },
      "enabled": true,
      "access": "read"
    },
    {
      "name": "create_ticket",
      "description": "Open a support ticket.",
      "input_schema": {
        "type": "object",
        "properties": {
          "subject": {
            "type": "string"
          },
          "body": {
            "type": "string"
          }
        },
        "required": [
          "subject"
        ]
      },
      "enabled": false,
      "access": "write"
    }
  ]
}

Errores

  • 400 La URL no es https o apunta a una dirección privada, o no se pudo obtener la lista de herramientas del servidor.
  • 404 No hay ningún servidor MCP con este id.

Probar una herramienta

POST /v1/mcp-servers/{id}/test

Llama a una herramienta del servidor con los argumentos que indiques y devuelve el tiempo que tardó, hasta 4 KB de contenido y si la herramienta informó de un error.

NombreEnTipoDescripción
id
obligatorio
path string Id del servidor MCP.
tool
obligatorio
body string Nombre de la herramienta que se va a llamar.
arguments body object Argumentos para la herramienta, conforme a su input_schema.

Solicitud

curl
curl -X POST "https://hubvocal.com/v1/mcp-servers/mcp_2b8e41/test" \
  -H "Authorization: Bearer $VT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "tool": "find_customer",
  "arguments": {
    "phone": "+905321234567"
  }
}'

Respuesta · 200

JSON
{
  "ok": true,
  "ms": 236,
  "content": "{\"customer_id\":\"C-1042\",\"name\":\"Ayşe Yılmaz\",\"open_tickets\":0}",
  "is_error": false
}

Errores

  • 404 No hay ningún servidor MCP con este id.

Webhooks

Listar webhooks

GET /v1/webhooks

Los webhooks de tu organización con el resultado de su última entrega. El secreto nunca se devuelve aquí; secret_set indica si hay uno configurado.

Solicitud

curl
curl "https://hubvocal.com/v1/webhooks" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
[
  {
    "id": "wh_3c9d1e",
    "url": "https://crm.example.com/hooks/voice",
    "events": [
      "call.ended"
    ],
    "enabled": true,
    "secret_set": true,
    "created_at": 1793830000,
    "last_delivery": {
      "status": "ok",
      "at": 1793832440.2,
      "http_status": 200
    }
  }
]

Crear un webhook

POST /v1/webhooks

Suscribe una URL a eventos. Por ahora el único evento es call.ended. Hasta 5 webhooks por organización. Si omites secret, se genera uno y se devuelve solo en esta respuesta.

NombreEnTipoDescripción
url
obligatorio
body string Tu endpoint. Solo https; se rechazan las direcciones locales y privadas.
events
obligatorio
body string[] Eventos que quieres recibir. Por ahora ["call.ended"].
enabled body boolean Los webhooks desactivados no reciben nada.
secret body string Secreto de firma. Si se omite, se genera uno; se devuelve una sola vez.

Solicitud

curl
curl -X POST "https://hubvocal.com/v1/webhooks" \
  -H "Authorization: Bearer $VT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://crm.example.com/hooks/voice",
  "events": [
    "call.ended"
  ]
}'

Respuesta · 201

JSON
{
  "id": "wh_3c9d1e",
  "url": "https://crm.example.com/hooks/voice",
  "events": [
    "call.ended"
  ],
  "enabled": true,
  "secret_set": true,
  "created_at": 1793830000,
  "last_delivery": null,
  "secret": "whsec_9QmZ…"
}

Errores

  • 400 La URL no es https, apunta a una dirección privada o hay un evento desconocido.
  • 409 La organización ya tiene 5 webhooks.

Actualizar un webhook

PUT /v1/webhooks/{id}

Cambia la URL, los eventos o si está activado. Envía secret: "-" para generar un secreto nuevo; se devuelve en la respuesta.

NombreEnTipoDescripción
id
obligatorio
path string Id del webhook.
url body string Tu endpoint. Solo https; se rechazan las direcciones locales y privadas.
events body string[] Eventos que quieres recibir. Por ahora ["call.ended"].
enabled body boolean Los webhooks desactivados no reciben nada.
secret body string "-" genera un secreto nuevo. Omítelo para conservar el actual.

Solicitud

curl
curl -X PUT "https://hubvocal.com/v1/webhooks/wh_3c9d1e" \
  -H "Authorization: Bearer $VT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "enabled": false
}'

Respuesta · 200

JSON
{
  "id": "wh_3c9d1e",
  "url": "https://crm.example.com/hooks/voice",
  "events": [
    "call.ended"
  ],
  "enabled": false,
  "secret_set": true,
  "created_at": 1793830000,
  "last_delivery": {
    "status": "ok",
    "at": 1793832440.2,
    "http_status": 200
  }
}

Errores

  • 400 La URL no es https, apunta a una dirección privada o hay un evento desconocido.
  • 404 No hay ningún webhook con este id.

Eliminar un webhook

DELETE /v1/webhooks/{id}

Elimina el webhook. Las entregas pendientes se descartan.

NombreEnTipoDescripción
id
obligatorio
path string Id del webhook.

Solicitud

curl
curl -X DELETE "https://hubvocal.com/v1/webhooks/wh_3c9d1e" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
{
  "deleted": "wh_3c9d1e"
}

Errores

  • 404 No hay ningún webhook con este id.

Probar un webhook

POST /v1/webhooks/{id}/test

Envía un evento call.ended de ejemplo a la URL y devuelve el estado, el tiempo que tardó y el cuerpo de la respuesta.

NombreEnTipoDescripción
id
obligatorio
path string Id del webhook.

Solicitud

curl
curl -X POST "https://hubvocal.com/v1/webhooks/wh_3c9d1e/test" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
{
  "ok": true,
  "http_status": 200,
  "ms": 184,
  "body": "{\"received\":true}"
}

Errores

  • 404 No hay ningún webhook con este id.

Listar entregas

GET /v1/webhooks/{id}/deliveries

Las últimas 50 entregas de un webhook, de la más reciente a la más antigua, con su estado (pending, ok, failed), el número de intentos, el estado HTTP, el error y la hora del próximo intento.

NombreEnTipoDescripción
id
obligatorio
path string Id del webhook.

Solicitud

curl
curl "https://hubvocal.com/v1/webhooks/wh_3c9d1e/deliveries" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
[
  {
    "id": "dl_51b7a9",
    "event": "call.ended",
    "call_id": "demo-8585522d",
    "status": "ok",
    "attempts": 1,
    "http_status": 200,
    "error": null,
    "created_at": 1793832440.2,
    "next_attempt_at": null
  },
  {
    "id": "dl_51a3c4",
    "event": "call.ended",
    "call_id": "tel-6b1f04d2",
    "status": "pending",
    "attempts": 2,
    "http_status": 503,
    "error": "HTTP 503",
    "created_at": 1793832101.7,
    "next_attempt_at": 1793833901.7
  }
]

Errores

  • 404 No hay ningún webhook con este id.

Reintentar una entrega

POST /v1/webhooks/deliveries/{id}/retry

Vuelve a poner en cola una entrega fallida.

NombreEnTipoDescripción
id
obligatorio
path string Id de la entrega, de la lista de entregas.

Solicitud

curl
curl -X POST "https://hubvocal.com/v1/webhooks/deliveries/dl_51a3c4/retry" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
{
  "ok": true
}

Errores

  • 404 No hay ninguna entrega con este id, o no ha fallado.

Voces y motores

Listar motores, voces y límites

GET /v1/voices

Todo lo que necesitas para crear un formulario de agente: nombres de motores, voces válidas por motor, rangos de velocidad, idiomas, tonos predefinidos, el modelo detrás de cada motor y límites de longitud de los campos. El ejemplo de abajo está abreviado.

Solicitud

curl
curl "https://hubvocal.com/v1/voices" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
{
  "engines": {
    "live": "GPT-Live 1 (OpenAI full-duplex ses modeli)",
    "gemini": "Gemini Live (Google ses-sese model)",
    "realtime": "Realtime (OpenAI ses-sese model)",
    "classic": "Klasik (Deepgram + OpenAI LLM + OpenAI TTS)"
  },
  "voices": {
    "live": [
      "marin",
      "cedar",
      "gleam",
      "meridian"
    ],
    "gemini": [
      "Kore",
      "Aoede",
      "Charon",
      "Puck"
    ],
    "realtime": [
      "marin",
      "cedar",
      "alloy"
    ],
    "classic": [
      "alloy",
      "coral",
      "sage"
    ]
  },
  "speed": {
    "live": {
      "min": 1,
      "max": 1
    },
    "gemini": {
      "min": 1,
      "max": 1
    },
    "realtime": {
      "min": 0.25,
      "max": 1.5
    },
    "classic": {
      "min": 0.25,
      "max": 4
    }
  },
  "langs": {
    "tr": "Türkçe",
    "en": "English"
  },
  "presets": [
    {
      "id": "calm",
      "label": "Sakin ve güven veren",
      "text": "Sakin, yumuşak ve güven veren bir tonla konuş."
    }
  ],
  "models": {
    "live": {
      "model": "gpt-live-1",
      "backend": "gpt-5.6-luna"
    },
    "gemini": {
      "model": "gemini-3.8-live",
      "ready": true
    },
    "realtime": {
      "model": "gpt-realtime-2.1-mini"
    },
    "classic": {
      "stt": "nova-3",
      "llm": "gpt-4.1-mini",
      "tts": "gpt-4o-mini-tts"
    }
  },
  "limits": {
    "tone": 600,
    "instructions": 8000,
    "greeting": 400
  }
}

Llamadas

La conservación se configura por organización en el panel: recordings_days (30 por defecto), transcripts_days (365 por defecto) y pii_redaction. Una tarea diaria elimina las grabaciones y transcripciones caducadas; la fila de la llamada y su coste se mantienen.

Widget web: las claves públicas (pk_…) se crean por agente en el panel. El script del widget llama a POST /api/public/web-calls; consulta la guía en la página de Desarrolladores.

Iniciar una llamada web

POST /v1/web-calls

Abre una sala para una llamada desde el navegador y devuelve un token de acceso de corta duración. Pasa server_url y token a livekit-client en el navegador; el agente entra en la sala por sí solo. Haz esta llamada desde tu servidor para que la clave de API nunca llegue al navegador. En lugar de agent_id, puedes enviar un objeto de agente completo para probar un borrador sin guardarlo.

NombreEnTipoDescripción
agent_id body string Id de un agente guardado. Obligatorio si no envías agent.
agent body object Un objeto de agente completo (los mismos campos que POST /v1/agents) que se usa solo en esta llamada.
version body string | integer Qué versión usar: "live" (por defecto), "draft" o un número de versión.

Solicitud

curl
curl -X POST "https://hubvocal.com/v1/web-calls" \
  -H "Authorization: Bearer $VT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "agent_id": "front-desk"
}'

Respuesta · 200

JSON
{
  "server_url": "wss://hubvocal.com",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…",
  "room": "demo-3f9a1c2e",
  "max_seconds": 300
}

Errores

  • 400 No se envió agent_id ni agent.
  • 402 La organización alcanzó su límite de gasto mensual.
  • 403 La organización está suspendida.
  • 404 No hay ningún agente con este id.

Hacer una llamada telefónica saliente

POST /v1/calls

Marca un número de teléfono desde uno de los números de tu organización y conecta al agente cuando contestan. El proveedor (DIDWW, Twilio o tu propio SIP trunk) se elige según el número from.

NombreEnTipoDescripción
from
obligatorio
body string Uno de los números de tu organización, en formato E.164 (+90…).
to
obligatorio
body string El número al que llamar, en formato E.164.
agent_id body string Agente que se usará. Si se omite, contesta el agente asignado al número from.
version body string | integer Qué versión usar: "live" (por defecto), "draft" o un número de versión.

Solicitud

curl
curl -X POST "https://hubvocal.com/v1/calls" \
  -H "Authorization: Bearer $VT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "from": "+908501234567",
  "to": "+905321234567",
  "agent_id": "front-desk"
}'

Respuesta · 200

JSON
{
  "room": "tel-out-9c41e0b7",
  "provider": "didww",
  "sip_call_id": "SCL_7mVt2kQb9xRa"
}

Errores

  • 400 Un número no está en formato internacional, o from no es uno de tus números.
  • 402 La organización alcanzó su límite de gasto mensual.
  • 502 El proveedor de telefonía rechazó la llamada.

Listar llamadas

GET /v1/calls

Llamadas de la más reciente a la más antigua, web y telefónicas. quality es el resumen de la medición del navegador y es null en las llamadas telefónicas. ended_by es caller, agent, max_call_seconds, caller_unresponsive, agent_unresponsive, no_answer o unknown. channel es web, phone_in o phone_out. Cada fila incluye además outcome y summary_text (los primeros 160 caracteres del resumen). ended_by es supervisor cuando un supervisor finalizó la llamada desde la consola en vivo.

NombreEnTipoDescripción
limit query integer Por defecto 50, máximo 500.
agent query string Solo las llamadas de este agente (nombre del agente).
outcome query string Solo las llamadas con este resultado.

Solicitud

curl
curl "https://hubvocal.com/v1/calls?limit=20" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
[
  {
    "room": "demo-8585522d",
    "org_id": "6f1c2a40-3b7e-4d0c-9a51-2f8e7c1d9b03",
    "org_name": "Example Clinic",
    "channel": "web",
    "profile": "Front desk",
    "engine": "live",
    "lang": "en",
    "voice": "marin",
    "started_at": 1793832409.1,
    "ended_at": 1793832434.2,
    "duration_s": 25.1,
    "total_usd": 0.021215,
    "ended_by": "caller",
    "outcome": "message_taken",
    "summary_text": "The caller asked for an appointment on Thursday afternoon; a message was taken for a callback.",
    "extracted": {
      "appointment_date": "2026-11-12",
      "callback": true
    },
    "first_audio_s": 2.43,
    "turns": 4,
    "source_gaps": {
      "count": 0,
      "total_ms": 0,
      "max_ms": 0
    },
    "quality": {
      "verdict": "Clean: no loss, no gaps.",
      "protocol": "UDP",
      "loss_pct": 0,
      "conceal_pct": 0,
      "jitter_ms": 4,
      "rtt_ms": 38
    }
  }
]

Obtener una llamada

GET /v1/calls/{room}

El registro completo de la llamada: desglose de costes por proveedor, transcripción, tiempos del saludo, cortes en origen del agente y la línea de tiempo de calidad del navegador en ventanas de 2 segundos. Los campos de coste dependen del motor (live, llm, stt, tts, realtime…); cada uno incluye usd. source_gaps.events enumera pares [segundo, ms]. summary contiene text, outcome (resolved, message_taken, transferred, unresolved, no_conversation, spam), sentiment, topics y action_items. supervisors indica quién se unió a la llamada desde la consola en vivo y en qué modos. recording contiene available, duration_s y size_bytes. extracted contiene los valores de los post_call.fields del agente (null si no se mencionaron).

NombreEnTipoDescripción
room
obligatorio
path string Nombre de la sala, de la lista de llamadas o de POST /v1/web-calls.

Solicitud

curl
curl "https://hubvocal.com/v1/calls/demo-8585522d" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
{
  "call": {
    "room": "demo-8585522d",
    "org_id": "6f1c2a40-3b7e-4d0c-9a51-2f8e7c1d9b03",
    "org_name": "Example Clinic",
    "channel": "web",
    "profile": "Front desk",
    "engine": "live",
    "lang": "en",
    "voice": "marin",
    "started_at": 1793832409.1,
    "ended_at": 1793832434.2,
    "duration_s": 25.1,
    "total_usd": 0.021215,
    "ended_by": "caller",
    "outcome": "message_taken",
    "summary_text": "The caller asked for an appointment on Thursday afternoon; a message was taken for a callback.",
    "extracted": {
      "appointment_date": "2026-11-12",
      "callback": true
    },
    "first_audio_s": 2.43,
    "turns": 4,
    "source_gaps": {
      "count": 2,
      "total_ms": 230,
      "max_ms": 141,
      "agent_audio_s": 31.2,
      "events": [
        [
          16.4,
          141
        ],
        [
          40.1,
          89
        ]
      ]
    },
    "quality": {
      "verdict": "Clean: no loss, no gaps.",
      "protocol": "UDP",
      "loss_pct": 0,
      "conceal_pct": 0,
      "jitter_ms": 4,
      "rtt_ms": 38
    },
    "cost": {
      "live": {
        "billed_s": 25.1,
        "reported": true,
        "voice_usd": 0.020917,
        "backend": {
          "in": 1840,
          "cached": 1024,
          "out": 96
        },
        "backend_usd": 0.000298
      },
      "llm": {
        "input_tokens": 1840,
        "cached_tokens": 1024,
        "output_tokens": 96,
        "usd": 0.000298
      },
      "tts": {
        "input_tokens": 0,
        "audio_tokens": 0,
        "audio_s": 25.1,
        "usd": 0.020917
      },
      "stt": {
        "audio_s": 0,
        "usd": 0
      }
    },
    "tools": [
      {
        "t": 14.2,
        "name": "check_order",
        "kind": "read",
        "ok": true,
        "ms": 412,
        "status": 200
      }
    ],
    "outcomes": [],
    "summary": {
      "text": "The caller asked for an appointment on Thursday afternoon; a message was taken for a callback.",
      "language": "en",
      "outcome": "message_taken",
      "sentiment": "neutral",
      "topics": [
        "appointment"
      ],
      "action_items": [
        "Call the caller back on Thursday morning"
      ]
    },
    "recording": {
      "available": true,
      "duration_s": 25.1,
      "size_bytes": 201344
    },
    "supervisors": [
      {
        "user": "ayse@example.com",
        "joined_at": 1793832420.4,
        "left_at": 1793832431,
        "modes": [
          "listen",
          "whisper"
        ]
      }
    ],
    "greeting": {
      "connect_s": 0.15,
      "session_started_s": 0.05,
      "reply_requested_s": 0.05,
      "first_audio_s": 2.4,
      "reply_done_s": 7.1
    },
    "transcript": [
      {
        "role": "assistant",
        "text": "Hello, thanks for calling the clinic. How can I help you today?",
        "t": 2.4,
        "interrupted": false
      },
      {
        "role": "user",
        "text": "I would like an appointment on Thursday afternoon.",
        "t": 9.8
      }
    ]
  },
  "quality": {
    "summary": {
      "verdict": "Clean: no loss, no gaps.",
      "protocol": "UDP",
      "rtt_ms": 38,
      "protocols_seen": [
        "UDP"
      ],
      "loss_pct": 0,
      "conceal_pct": 0,
      "jitter_ms": 4,
      "buffer_ms": 62
    },
    "timeline": [
      {
        "t": 2,
        "loss": 0,
        "conceal": 0,
        "jitter": 3,
        "buffer": 60,
        "proto": "UDP"
      },
      {
        "t": 4,
        "loss": 0,
        "conceal": 0,
        "jitter": 4,
        "buffer": 62,
        "proto": "UDP"
      }
    ]
  }
}

Errores

  • 404 Llamada no encontrada.

Descargar la grabación

GET /v1/calls/{room}/recording

El audio de la llamada en OGG/Opus. Admite solicitudes Range. 404 si la llamada no se grabó o la grabación ha caducado.

NombreEnTipoDescripción
room
obligatorio
path string Nombre de la sala, de la lista de llamadas o de POST /v1/web-calls.

Solicitud

curl
curl "https://hubvocal.com/v1/calls/demo-8585522d/recording" \
  -H "Authorization: Bearer $VT_API_KEY" \
  -o call.ogg

Respuesta · 200

audio/ogg
HTTP/1.1 200 OK
Content-Type: audio/ogg
Accept-Ranges: bytes
Content-Length: 201344

<OGG/Opus>

Errores

  • 404 La llamada no se grabó o la grabación ha caducado.

Eliminar una llamada

DELETE /v1/calls/{room}

Elimina de inmediato la grabación, la transcripción y el resumen de la llamada, por ejemplo ante una solicitud en virtud de la KVKK o del GDPR. La fila de la llamada y su coste se mantienen.

NombreEnTipoDescripción
room
obligatorio
path string Nombre de la sala, de la lista de llamadas o de POST /v1/web-calls.

Solicitud

curl
curl -X DELETE "https://hubvocal.com/v1/calls/demo-8585522d" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
{
  "deleted": "demo-8585522d"
}

Errores

  • 404 Llamada no encontrada.

Exportar llamadas (CSV)

GET /v1/calls/export

Las llamadas de un intervalo de fechas en CSV, una fila por llamada, con los campos estructurados como columnas adicionales.

NombreEnTipoDescripción
format
obligatorio
query string Solo csv.
from query date YYYY-MM-DD, inclusive.
to query date YYYY-MM-DD, inclusive.
agent_id query string Solo las llamadas de este agente.

Solicitud

curl
curl "https://hubvocal.com/v1/calls/export?format=csv&from=2026-11-01&to=2026-11-05" \
  -H "Authorization: Bearer $VT_API_KEY" \
  -o calls.csv

Respuesta · 200

text/csv
room,started_at,agent,channel,duration_s,total_usd,outcome,appointment_date,callback
demo-8585522d,2026-11-05T09:46:49Z,front-desk,web,25.1,0.021215,message_taken,2026-11-12,true

Números de teléfono

Listar números de teléfono

GET /v1/numbers

Los números de teléfono de tu organización y el agente que atiende cada uno (profile_id), además del estado de cada proveedor de telefonía. El equipo de la plataforma añade los números; tú asignas los agentes.

Solicitud

curl
curl "https://hubvocal.com/v1/numbers" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
{
  "numbers": [
    {
      "number": "+908501234567",
      "provider": "didww",
      "org_id": "6f1c2a40-3b7e-4d0c-9a51-2f8e7c1d9b03",
      "org_name": "Example Clinic",
      "profile_id": "front-desk",
      "label": "Main line"
    }
  ],
  "sip": {
    "enabled": true,
    "room_prefix": "tel-",
    "providers": {
      "didww": {
        "inbound_ips": "46.19.209.14,46.19.210.14",
        "inbound_auth": false,
        "outbound_host": "trunk.example.com",
        "outbound_auth": true,
        "outbound_ready": true,
        "inbound_ready": true
      }
    }
  }
}

Asignar un agente a un número

PUT /v1/numbers/{number}

Elige el agente que atiende las llamadas entrantes en este número. Envía agent_id: null para quitar la asignación. Codifica el + del número como %2B en la ruta (también funciona el prefijo 00).

NombreEnTipoDescripción
number
obligatorio
path string El número en E.164, codificado para URL.
agent_id body string | null Agente que atiende las llamadas entrantes.
label body string Una nota para tu equipo, hasta 60 caracteres.

Solicitud

curl
curl -X PUT "https://hubvocal.com/v1/numbers/%2B908501234567" \
  -H "Authorization: Bearer $VT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "agent_id": "front-desk",
  "label": "Main line"
}'

Respuesta · 200

JSON
{
  "ok": true
}

Errores

  • 400 El agente no existe.
  • 404 El número no pertenece a tu organización.

Uso

Uso

GET /v1/usage

Llamadas, minutos y coste de proveedores de tu organización, por proveedor y por día (Europe/Istanbul). Por defecto, el mes en curso. kind es llm, tts, stt o voice (motores de voz a voz).

NombreEnTipoDescripción
from query date YYYY-MM-DD, inclusive.
to query date YYYY-MM-DD, inclusive.

Solicitud

curl
curl "https://hubvocal.com/v1/usage?from=2026-11-01&to=2026-11-05" \
  -H "Authorization: Bearer $VT_API_KEY"

Respuesta · 200

JSON
{
  "from": "2026-11-01",
  "to": "2026-11-05",
  "totals": {
    "calls": 42,
    "minutes": 81.2,
    "usd": 3.94
  },
  "by_provider": [
    {
      "provider": "openai",
      "model": "gpt-live-1",
      "kind": "voice",
      "calls": 30,
      "input_tokens": 0,
      "cached_tokens": 0,
      "output_tokens": 0,
      "audio_s": 2900.1,
      "usd": 2.41
    }
  ],
  "by_day": [
    {
      "date": "2026-11-05",
      "usd": 1.2,
      "minutes": 20.5,
      "calls": 9
    }
  ],
  "by_org": [
    {
      "org_id": "6f1c2a40-3b7e-4d0c-9a51-2f8e7c1d9b03",
      "org_name": "Example Clinic",
      "usd": 3.94,
      "minutes": 81.2,
      "calls": 42
    }
  ]
}

Especificación

Especificación OpenAPI

GET /v1/openapi.json No requiere clave

El documento OpenAPI 3.1 de esta API. No requiere clave. Impórtalo en Postman, Insomnia o un generador de clientes.

Solicitud

curl
curl "https://hubvocal.com/v1/openapi.json"

Respuesta · 200

JSON
{
  "openapi": "3.1.0",
  "info": {
    "title": "HubVocal API",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://hubvocal.com/v1"
    }
  ],
  "paths": {}
}