Riferimento API
Tutti gli endpoint dell'API v1. Esempi in curl, JavaScript (fetch) e Python (requests); il linguaggio che scegli resta selezionato in tutta la pagina.
| URL di base | https://hubvocal.com/v1 |
|---|---|
| Autenticazione | Authorization: Bearer vt_live_… |
| Limite di richieste | 120 richieste/min per chiave · 429 + Retry-After |
| Scope | read · write · callsOgni chiave ha read, write e/o calls. read copre le richieste GET, write le modifiche, calls le chiamate in uscita. Una richiesta al di fuori degli scope della chiave riceve 403. |
| Corpo dell'errore | {"detail": "..."} |
| OpenAPI | GET /v1/openapi.json |
Per autenticazione, integrazione e codici di errore, consulta Sviluppatori.
Agenti
Le conversazioni si svolgono in modalità testo, quindi verificano le istruzioni e l'uso degli strumenti, non la voce né la latenza. Le simulazioni si avviano dalla dashboard; per ora non esiste un endpoint /v1 dedicato.
Elenca gli agenti
GET /v1/agents
Restituisce tutti gli agenti della tua organizzazione, nell'ordine in cui compaiono nella dashboard.
Richiesta
curl "https://hubvocal.com/v1/agents" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/agents", {
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.get(
"https://hubvocal.com/v1/agents",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
[
{
"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
}
] Crea un agente
POST /v1/agents
Crea un agente. Ogni campo viene validato sul server; i valori oltre i limiti vengono troncati. Se passi un id già esistente, la richiesta fallisce con 409. Per aggiornare usa PUT.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
id | body | string | Facoltativo. Lettere minuscole, cifre e trattini, fino a 48 caratteri. Se omesso, viene generato dal nome. |
nameobbligatorio | body | string | Nome visualizzato, fino a 60 caratteri. |
langobbligatorio | body | string | Codice della lingua di conversazione, ad es. tr, en, de. L'elenco completo è in GET /v1/voices. |
engineobbligatorio | body | "live" | "gemini" | "realtime" | "classic" | live: GPT-Live 1, gemini: Gemini Live, realtime: OpenAI Realtime, classic: STT → LLM → TTS. |
instructions | body | string | Istruzioni di sistema, fino a 8000 caratteri. |
greeting | body | string | La prima cosa che dice l'agente, fino a 400 caratteri. |
voice | body | object | voice (un nome da /v1/voices), speed (solo classic e realtime; fissa a 1.0 per live e gemini), tone (stile di parlato in testo libero, fino a 600 caratteri). |
tools | body | object | builtins (end_call, take_message) e webhooks (id degli strumenti); fino a 16 strumenti in totale. Se vuoto o assente, l'agente non ha strumenti. mcp: [{server_id, tools}] aggiunge strumenti dai tuoi server MCP; contano nello stesso limite. |
turn | body | object | Impostazioni dei turni di parola: sensitivity (low, balanced, high; solo motore 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). I campi vuoti usano i valori predefiniti. |
recording | body | object | recording: {enabled (predefinito false), announce (predefinito true), announce_text?}. Se attiva, l'agente annuncia la registrazione prima del saluto. |
post_call | body | object | post_call.summary: scrive un riepilogo alla fine della chiamata (predefinito true). post_call.fields: fino a 24 campi da compilare dopo la chiamata ({name, type, description, enum?, required?}). |
knowledge | body | object | doc_ids (fino a 50 documenti della knowledge base) e mode ("tool"). L'agente riceve così lo strumento search_knowledge. |
calendar | body | object | integration_id e calendar_id dalla dashboard, duration_min (15–240), buffer_min (0–60), timezone, working_hours, min_notice_h (0–72), max_days_ahead (1–90). Attiva gli strumenti per gli appuntamenti. |
Richiesta
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"
}
}' const res = await fetch("https://hubvocal.com/v1/agents", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"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"
}
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.post(
"https://hubvocal.com/v1/agents",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
json={
"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",
},
},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 201
{
"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
} Errori
409Esiste già un agente con questo id.
Recupera un agente
GET /v1/agents/{id}
Restituisce un singolo agente. published_version è il numero della versione attiva (null se non è mai stato pubblicato) e draft_changed indica se la bozza è diversa da quella versione.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id dell'agente, ad es. front-desk. |
Richiesta
curl "https://hubvocal.com/v1/agents/front-desk" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/agents/front-desk", {
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.get(
"https://hubvocal.com/v1/agents/front-desk",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"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
} Errori
404Nessun agente con questo id.
Aggiorna un agente
PUT /v1/agents/{id}
Sostituisce l'agente con l'oggetto che invii. Invia l'oggetto completo: i campi omessi tornano ai valori predefiniti. L'id nel percorso prevale su qualsiasi id nel corpo.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id dell'agente, ad es. front-desk. |
nameobbligatorio | body | string | Nome visualizzato, fino a 60 caratteri. |
langobbligatorio | body | string | Codice della lingua di conversazione, ad es. tr, en, de. L'elenco completo è in GET /v1/voices. |
engineobbligatorio | body | "live" | "gemini" | "realtime" | "classic" | live: GPT-Live 1, gemini: Gemini Live, realtime: OpenAI Realtime, classic: STT → LLM → TTS. |
instructions | body | string | Istruzioni di sistema, fino a 8000 caratteri. |
greeting | body | string | La prima cosa che dice l'agente, fino a 400 caratteri. |
voice | body | object | voice (un nome da /v1/voices), speed (solo classic e realtime; fissa a 1.0 per live e gemini), tone (stile di parlato in testo libero, fino a 600 caratteri). |
tools | body | object | builtins (end_call, take_message) e webhooks (id degli strumenti); fino a 16 strumenti in totale. Se vuoto o assente, l'agente non ha strumenti. mcp: [{server_id, tools}] aggiunge strumenti dai tuoi server MCP; contano nello stesso limite. |
turn | body | object | Impostazioni dei turni di parola: sensitivity (low, balanced, high; solo motore 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). I campi vuoti usano i valori predefiniti. |
recording | body | object | recording: {enabled (predefinito false), announce (predefinito true), announce_text?}. Se attiva, l'agente annuncia la registrazione prima del saluto. |
post_call | body | object | post_call.summary: scrive un riepilogo alla fine della chiamata (predefinito true). post_call.fields: fino a 24 campi da compilare dopo la chiamata ({name, type, description, enum?, required?}). |
knowledge | body | object | doc_ids (fino a 50 documenti della knowledge base) e mode ("tool"). L'agente riceve così lo strumento search_knowledge. |
calendar | body | object | integration_id e calendar_id dalla dashboard, duration_min (15–240), buffer_min (0–60), timezone, working_hours, min_notice_h (0–72), max_days_ahead (1–90). Attiva gli strumenti per gli appuntamenti. |
Richiesta
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"
}
}' const res = await fetch("https://hubvocal.com/v1/agents/front-desk", {
method: "PUT",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"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"
}
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.put(
"https://hubvocal.com/v1/agents/front-desk",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
json={
"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",
},
},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"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
} Errori
404Nessun agente con questo id. Crealo prima con POST.
Elimina un agente
DELETE /v1/agents/{id}
Elimina l'agente. Un'organizzazione mantiene sempre almeno un agente, quindi non è possibile eliminare l'ultimo.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id dell'agente, ad es. front-desk. |
Richiesta
curl -X DELETE "https://hubvocal.com/v1/agents/front-desk" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/agents/front-desk", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.delete(
"https://hubvocal.com/v1/agents/front-desk",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"deleted": "front-desk"
} Errori
400È l'ultimo agente dell'organizzazione.404Nessun agente con questo id.
Elenca le versioni
GET /v1/agents/{id}/versions
Le versioni pubblicate dell'agente, dalla più recente. is_live indica quella usata ora dalle chiamate.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id dell'agente, ad es. front-desk. |
Richiesta
curl "https://hubvocal.com/v1/agents/front-desk/versions" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/agents/front-desk/versions", {
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.get(
"https://hubvocal.com/v1/agents/front-desk/versions",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
[
{
"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
}
] Errori
404Nessun agente con questo id.
Recupera una versione
GET /v1/agents/{id}/versions/{n}
L'agente completo così com'era in quella versione.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id dell'agente, ad es. front-desk. |
nobbligatorio | path | integer | Numero di versione. |
Richiesta
curl "https://hubvocal.com/v1/agents/front-desk/versions/2" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/agents/front-desk/versions/2", {
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.get(
"https://hubvocal.com/v1/agents/front-desk/versions/2",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"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
} Errori
404L'agente o la versione non esiste.
Pubblica la bozza
POST /v1/agents/{id}/publish
Salva la bozza attuale come nuova versione e la rende attiva. Le chiamate già in corso mantengono la loro versione.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id dell'agente, ad es. front-desk. |
note | body | string | Una breve nota su cosa è cambiato. |
Richiesta
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"
}' const res = await fetch("https://hubvocal.com/v1/agents/front-desk/publish", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"note": "Shorter greeting"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.post(
"https://hubvocal.com/v1/agents/front-desk/publish",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
json={
"note": "Shorter greeting",
},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"version": 4
} Errori
404Nessun agente con questo id.
Ripristina
POST /v1/agents/{id}/rollback
Copia una versione precedente nella bozza e la pubblica come nuova versione.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id dell'agente, ad es. front-desk. |
versionobbligatorio | body | integer | La versione da ripristinare. |
Richiesta
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
}' const res = await fetch("https://hubvocal.com/v1/agents/front-desk/rollback", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"version": 2
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.post(
"https://hubvocal.com/v1/agents/front-desk/rollback",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
json={
"version": 2,
},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"version": 5
} Errori
404L'agente o la versione non esiste.
Knowledge base
Elenca i documenti
GET /v1/knowledge
I documenti della tua organizzazione con stato di elaborazione, dimensione, numero di frammenti e gli agenti che li usano.
Richiesta
curl "https://hubvocal.com/v1/knowledge" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/knowledge", {
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.get(
"https://hubvocal.com/v1/knowledge",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
[
{
"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"
]
}
] Aggiungi un documento
POST /v1/knowledge
Carica un file come multipart/form-data (campo file), invia testo come JSON {title, text} o una singola pagina web come JSON {url}. Restituisce 201 con stato processing; interroga il documento finché non diventa ready o failed.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
file | body | file | multipart/form-data: pdf, docx, txt, md o html, fino a 10 MB. |
title | body | string | Titolo, da inviare insieme a text. |
text | body | string | Contenuto in testo semplice. |
url | body | string | In alternativa, una singola pagina https da scaricare. |
Richiesta
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."
}' const res = await fetch("https://hubvocal.com/v1/knowledge", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"title": "Opening hours",
"text": "We are open 09:00–18:00 on weekdays and 10:00–14:00 on Saturdays. Closed on Sundays."
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.post(
"https://hubvocal.com/v1/knowledge",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
json={
"title": "Opening hours",
"text": "We are open 09:00–18:00 on weekdays and 10:00–14:00 on Saturdays. Closed on Sundays.",
},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 201
{
"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": []
} Errori
400Tipo di file non supportato, file oltre i 10 MB, URL non https o che punta a un indirizzo privato, oppure è stato raggiunto il limite dell'organizzazione (200 documenti, 200 MB).
Recupera un documento
GET /v1/knowledge/{id}
Il documento con un'anteprima dei primi 2000 caratteri.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id del documento. |
Richiesta
curl "https://hubvocal.com/v1/knowledge/kb_7d21e4" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/knowledge/kb_7d21e4", {
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.get(
"https://hubvocal.com/v1/knowledge/kb_7d21e4",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"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…"
} Errori
404Nessun documento con questo id.
Elimina un documento
DELETE /v1/knowledge/{id}
Elimina il documento e lo rimuove da tutti gli agenti che lo usavano.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id del documento. |
Richiesta
curl -X DELETE "https://hubvocal.com/v1/knowledge/kb_7d21e4" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/knowledge/kb_7d21e4", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.delete(
"https://hubvocal.com/v1/knowledge/kb_7d21e4",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"deleted": "kb_7d21e4"
} Errori
404Nessun documento con questo id.
Cerca
POST /v1/knowledge/search
Esegue la stessa ricerca usata dall'agente e restituisce i frammenti più pertinenti con un punteggio. Utile per verificare i documenti prima di andare in produzione.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
queryobbligatorio | body | string | Cosa cercare. |
doc_ids | body | string[] | Limita la ricerca a questi documenti. |
Richiesta
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"
}' const res = await fetch("https://hubvocal.com/v1/knowledge/search", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"query": "how much is a scale and polish"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.post(
"https://hubvocal.com/v1/knowledge/search",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
json={
"query": "how much is a scale and polish",
},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
[
{
"doc_id": "kb_7d21e4",
"title": "Price list 2026",
"chunk_index": 3,
"text": "Scale and polish: 2,000 TRY, about 30 minutes.",
"score": 0.82
}
] Strumenti
Strumenti integrati
Questi strumenti non richiedono una definizione. Attivali in tools.builtins dell'agente. search_knowledge viene aggiunto quando knowledge è impostato. Gli strumenti per gli appuntamenti vengono aggiunti quando calendar è impostato.
| Nome | Cosa fa |
|---|---|
end_call | L'agente saluta e riaggancia 0,75 s dopo aver finito di parlare. Se nel frattempo il chiamante parla, non riaggancia. Il registro della chiamata riporta ended_by: "agent". |
take_message | Raccoglie name, phone, message e callback_requested e li scrive in outcomes[] nel registro della chiamata. Non chiede conferma. |
search_knowledge | Aggiunto automaticamente quando l'agente ha documenti in knowledge. Cerca al loro interno e salva le fonti nel registro della chiamata. |
check_availability | Lettura. Restituisce gli slot liberi tra date_from e date_to, nell'orario di lavoro dell'agente. Aggiunto quando calendar è impostato. |
book_appointment | Scrittura, con passaggio di conferma. Prenota start a nome di name con phone, più una note facoltativa. La prenotazione finisce in outcomes[]. |
cancel_appointment | Scrittura, con passaggio di conferma. Annulla una prenotazione tramite il suo booking_ref. |
Le integrazioni di calendario (Google Calendar) si collegano solo dalla dashboard; non esiste un endpoint /v1 per gestirle. Nel campo calendar dell'agente usa gli id di integrazione e calendario che trovi nella dashboard.
Elenca gli strumenti
GET /v1/tools
Gli strumenti webhook della tua organizzazione. Il segreto non viene mai restituito; has_secret indica se è impostato.
Richiesta
curl "https://hubvocal.com/v1/tools" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/tools", {
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.get(
"https://hubvocal.com/v1/tools",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
[
{
"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
}
] Recupera uno strumento
GET /v1/tools/{id}
Un singolo strumento webhook. Il segreto non viene mai restituito.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id dello strumento. |
Richiesta
curl "https://hubvocal.com/v1/tools/tl_8f3a2c" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/tools/tl_8f3a2c", {
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.get(
"https://hubvocal.com/v1/tools/tl_8f3a2c",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"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
} Errori
404Nessuno strumento con questo id.
Crea uno strumento
POST /v1/tools
Definisce uno strumento webhook per l'organizzazione. Per usarlo, aggiungi il suo id a tools.webhooks di un agente. L'URL deve essere https e non può puntare a un indirizzo locale o privato.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
nameobbligatorio | body | string | Lettere minuscole, cifre e trattini bassi, a partire da una lettera (3–41 caratteri). Unico nell'organizzazione; end_call e take_message sono riservati. |
descriptionobbligatorio | body | string | Dice al modello quando e come usare lo strumento. |
kindobbligatorio | body | "read" | "write" | read viene eseguito subito; write chiede prima conferma al chiamante. |
urlobbligatorio | body | string | Il tuo endpoint. Solo https; gli indirizzi locali e privati vengono rifiutati. |
parameters | body | object | Oggetto JSON Schema: fino a 20 campi di tipo string, number, integer o boolean (enum consentito). |
timeout_s | body | integer | Da 2 a 15 secondi. |
enabled | body | boolean | Gli strumenti disattivati non vengono proposti al modello. |
secret | body | string | Serve a firmare le richieste. Solo scrittura; in aggiornamento, un valore vuoto mantiene il segreto attuale e "-" lo rimuove. |
Richiesta
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…"
}' const res = await fetch("https://hubvocal.com/v1/tools", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"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…"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.post(
"https://hubvocal.com/v1/tools",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
json={
"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…",
},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 201
{
"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
} Errori
400Un campo non rispetta le regole: formato del nome, URL https, indirizzo privato, più di 20 parametri o timeout fuori intervallo.409Esiste già uno strumento con questo nome, oppure il nome è riservato.
Aggiorna uno strumento
PUT /v1/tools/{id}
Sostituisce lo strumento con l'oggetto che invii. Lascia secret vuoto o omettilo per mantenere quello attuale; invia "-" per rimuoverlo.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id dello strumento. |
nameobbligatorio | body | string | Lettere minuscole, cifre e trattini bassi, a partire da una lettera (3–41 caratteri). Unico nell'organizzazione; end_call e take_message sono riservati. |
descriptionobbligatorio | body | string | Dice al modello quando e come usare lo strumento. |
kindobbligatorio | body | "read" | "write" | read viene eseguito subito; write chiede prima conferma al chiamante. |
urlobbligatorio | body | string | Il tuo endpoint. Solo https; gli indirizzi locali e privati vengono rifiutati. |
parameters | body | object | Oggetto JSON Schema: fino a 20 campi di tipo string, number, integer o boolean (enum consentito). |
timeout_s | body | integer | Da 2 a 15 secondi. |
enabled | body | boolean | Gli strumenti disattivati non vengono proposti al modello. |
secret | body | string | Serve a firmare le richieste. Solo scrittura; in aggiornamento, un valore vuoto mantiene il segreto attuale e "-" lo rimuove. |
Richiesta
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": ""
}' const res = await fetch("https://hubvocal.com/v1/tools/tl_8f3a2c", {
method: "PUT",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"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": ""
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.put(
"https://hubvocal.com/v1/tools/tl_8f3a2c",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
json={
"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": "",
},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"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
} Errori
400Un campo non rispetta le regole: formato del nome, URL https, indirizzo privato, più di 20 parametri o timeout fuori intervallo.404Nessuno strumento con questo id.
Elimina uno strumento
DELETE /v1/tools/{id}
Elimina lo strumento e lo rimuove da tutti gli agenti che lo usavano.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id dello strumento. |
Richiesta
curl -X DELETE "https://hubvocal.com/v1/tools/tl_8f3a2c" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/tools/tl_8f3a2c", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.delete(
"https://hubvocal.com/v1/tools/tl_8f3a2c",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"deleted": "tl_8f3a2c"
} Errori
404Nessuno strumento con questo id.
Prova uno strumento
POST /v1/tools/{id}/test
Invia una richiesta reale all'URL dello strumento con gli argomenti che indichi e restituisce lo stato, il tempo impiegato e fino a 4 KB del corpo della risposta.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id dello strumento. |
arguments | body | object | Argomenti di esempio, conformi a parameters. |
Richiesta
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"
}
}' const res = await fetch("https://hubvocal.com/v1/tools/tl_8f3a2c/test", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"arguments": {
"order_id": "A-10234"
}
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.post(
"https://hubvocal.com/v1/tools/tl_8f3a2c/test",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
json={
"arguments": {
"order_id": "A-10234",
},
},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"ok": true,
"status": 200,
"ms": 412,
"body": "{\"order_id\":\"A-10234\",\"status\":\"shipped\",\"eta\":\"2026-11-07\"}"
} Errori
404Nessuno strumento con questo id.
Server MCP
Elenca i server MCP
GET /v1/mcp-servers
I server MCP della tua organizzazione con il loro stato e gli strumenti sincronizzati. auth_set indica se è salvato un segreto, che non viene mai restituito.
Richiesta
curl "https://hubvocal.com/v1/mcp-servers" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/mcp-servers", {
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.get(
"https://hubvocal.com/v1/mcp-servers",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
[
{
"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"
}
]
}
] Aggiungi un server MCP
POST /v1/mcp-servers
Salva il server e ne recupera l'elenco degli strumenti. I nuovi strumenti sono disattivati. Fino a 10 server per organizzazione.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
nameobbligatorio | body | string | Nome breve; usato come prefisso nei nomi degli strumenti (<name>.<tool>). |
urlobbligatorio | body | string | L'endpoint MCP del server. Solo https; gli indirizzi locali e privati vengono rifiutati. |
transport | body | string | streamable_http (predefinito) o sse. |
auth | body | string | none, bearer o header. |
secret | body | string | Token o valore dell'header. Salvato cifrato, mai restituito; "-" lo rimuove. |
header_name | body | string | Nome dell'header quando auth è header. |
Richiesta
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_…"
}' const res = await fetch("https://hubvocal.com/v1/mcp-servers", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"name": "crm",
"url": "https://mcp.example.com/mcp",
"auth": "bearer",
"secret": "sk_live_…"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.post(
"https://hubvocal.com/v1/mcp-servers",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
json={
"name": "crm",
"url": "https://mcp.example.com/mcp",
"auth": "bearer",
"secret": "sk_live_…",
},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 201
{
"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"
}
]
} Errori
400L'URL non è https o punta a un indirizzo privato, oppure non è stato possibile recuperare l'elenco degli strumenti del server.409L'organizzazione ha già 10 server MCP.
Aggiorna un server MCP
PUT /v1/mcp-servers/{id}
Modifica nome, URL, autenticazione o stato di attivazione, oltre a quali strumenti sono attivi e se sono di lettura o di scrittura. Invia secret: "-" per rimuovere il segreto.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id del server MCP. |
name | body | string | Nome breve; usato come prefisso nei nomi degli strumenti (<name>.<tool>). |
url | body | string | L'endpoint MCP del server. Solo https; gli indirizzi locali e privati vengono rifiutati. |
enabled | body | boolean | Un server disattivato non viene offerto agli agenti. |
auth | body | string | none, bearer o header. |
secret | body | string | Token o valore dell'header. Salvato cifrato, mai restituito; "-" lo rimuove. |
tools | body | object[] | [{name, enabled, access}] per strumento; access è read o write. |
Richiesta
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"
}
]
}' const res = await fetch("https://hubvocal.com/v1/mcp-servers/mcp_2b8e41", {
method: "PUT",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"tools": [
{
"name": "find_customer",
"enabled": true,
"access": "read"
},
{
"name": "create_ticket",
"enabled": true,
"access": "write"
}
]
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.put(
"https://hubvocal.com/v1/mcp-servers/mcp_2b8e41",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
json={
"tools": [
{
"name": "find_customer",
"enabled": True,
"access": "read",
},
{
"name": "create_ticket",
"enabled": True,
"access": "write",
},
],
},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"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"
}
]
} Errori
400L'URL non è https o punta a un indirizzo privato, oppure non è stato possibile recuperare l'elenco degli strumenti del server.404Nessun server MCP con questo id.
Elimina un server MCP
DELETE /v1/mcp-servers/{id}
Elimina il server e rimuove i suoi strumenti da tutti gli agenti.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id del server MCP. |
Richiesta
curl -X DELETE "https://hubvocal.com/v1/mcp-servers/mcp_2b8e41" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/mcp-servers/mcp_2b8e41", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.delete(
"https://hubvocal.com/v1/mcp-servers/mcp_2b8e41",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"deleted": "mcp_2b8e41"
} Errori
404Nessun server MCP con questo id.
Sincronizza gli strumenti
POST /v1/mcp-servers/{id}/sync
Recupera di nuovo l'elenco degli strumenti. Quelli nuovi arrivano disattivati, con accesso impostato su write.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id del server MCP. |
Richiesta
curl -X POST "https://hubvocal.com/v1/mcp-servers/mcp_2b8e41/sync" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/mcp-servers/mcp_2b8e41/sync", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.post(
"https://hubvocal.com/v1/mcp-servers/mcp_2b8e41/sync",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"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"
}
]
} Errori
400L'URL non è https o punta a un indirizzo privato, oppure non è stato possibile recuperare l'elenco degli strumenti del server.404Nessun server MCP con questo id.
Testa uno strumento
POST /v1/mcp-servers/{id}/test
Chiama uno strumento del server con gli argomenti indicati e restituisce il tempo impiegato, fino a 4 KB di contenuto e se lo strumento ha segnalato un errore.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id del server MCP. |
toolobbligatorio | body | string | Nome dello strumento da chiamare. |
arguments | body | object | Argomenti per lo strumento, conformi al suo input_schema. |
Richiesta
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"
}
}' const res = await fetch("https://hubvocal.com/v1/mcp-servers/mcp_2b8e41/test", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"tool": "find_customer",
"arguments": {
"phone": "+905321234567"
}
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.post(
"https://hubvocal.com/v1/mcp-servers/mcp_2b8e41/test",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
json={
"tool": "find_customer",
"arguments": {
"phone": "+905321234567",
},
},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"ok": true,
"ms": 236,
"content": "{\"customer_id\":\"C-1042\",\"name\":\"Ayşe Yılmaz\",\"open_tickets\":0}",
"is_error": false
} Errori
404Nessun server MCP con questo id.
Webhook
Elenca i webhook
GET /v1/webhooks
I webhook della tua organizzazione con l'esito della loro ultima consegna. Il segreto non viene mai restituito qui; secret_set indica se è impostato.
Richiesta
curl "https://hubvocal.com/v1/webhooks" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/webhooks", {
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.get(
"https://hubvocal.com/v1/webhooks",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
[
{
"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
}
}
] Crea un webhook
POST /v1/webhooks
Iscrive un URL agli eventi. Al momento l'unico evento è call.ended. Fino a 5 webhook per organizzazione. Se ometti secret, ne viene generato uno, restituito solo in questa risposta.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
urlobbligatorio | body | string | Il tuo endpoint. Solo https; gli indirizzi locali e privati vengono rifiutati. |
eventsobbligatorio | body | string[] | Eventi da ricevere. Al momento ["call.ended"]. |
enabled | body | boolean | I webhook disattivati non ricevono nulla. |
secret | body | string | Segreto di firma. Generato se omesso; restituito una sola volta. |
Richiesta
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"
]
}' const res = await fetch("https://hubvocal.com/v1/webhooks", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"url": "https://crm.example.com/hooks/voice",
"events": [
"call.ended"
]
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.post(
"https://hubvocal.com/v1/webhooks",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
json={
"url": "https://crm.example.com/hooks/voice",
"events": [
"call.ended",
],
},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 201
{
"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…"
} Errori
400L'URL non è https, punta a un indirizzo privato oppure un evento è sconosciuto.409L'organizzazione ha già 5 webhook.
Aggiorna un webhook
PUT /v1/webhooks/{id}
Modifica l'URL, gli eventi o lo stato di attivazione. Invia secret: "-" per generare un nuovo segreto, che viene restituito nella risposta.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id del webhook. |
url | body | string | Il tuo endpoint. Solo https; gli indirizzi locali e privati vengono rifiutati. |
events | body | string[] | Eventi da ricevere. Al momento ["call.ended"]. |
enabled | body | boolean | I webhook disattivati non ricevono nulla. |
secret | body | string | "-" genera un nuovo segreto. Omettilo per mantenere quello attuale. |
Richiesta
curl -X PUT "https://hubvocal.com/v1/webhooks/wh_3c9d1e" \
-H "Authorization: Bearer $VT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"enabled": false
}' const res = await fetch("https://hubvocal.com/v1/webhooks/wh_3c9d1e", {
method: "PUT",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"enabled": false
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.put(
"https://hubvocal.com/v1/webhooks/wh_3c9d1e",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
json={
"enabled": False,
},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"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
}
} Errori
400L'URL non è https, punta a un indirizzo privato oppure un evento è sconosciuto.404Nessun webhook con questo id.
Elimina un webhook
DELETE /v1/webhooks/{id}
Elimina il webhook. Le consegne in attesa vengono scartate.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id del webhook. |
Richiesta
curl -X DELETE "https://hubvocal.com/v1/webhooks/wh_3c9d1e" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/webhooks/wh_3c9d1e", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.delete(
"https://hubvocal.com/v1/webhooks/wh_3c9d1e",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"deleted": "wh_3c9d1e"
} Errori
404Nessun webhook con questo id.
Prova un webhook
POST /v1/webhooks/{id}/test
Invia all'URL un evento call.ended di esempio e restituisce lo stato, il tempo impiegato e il corpo della risposta.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id del webhook. |
Richiesta
curl -X POST "https://hubvocal.com/v1/webhooks/wh_3c9d1e/test" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/webhooks/wh_3c9d1e/test", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.post(
"https://hubvocal.com/v1/webhooks/wh_3c9d1e/test",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"ok": true,
"http_status": 200,
"ms": 184,
"body": "{\"received\":true}"
} Errori
404Nessun webhook con questo id.
Elenca le consegne
GET /v1/webhooks/{id}/deliveries
Le ultime 50 consegne di un webhook, dalla più recente, con stato (pending, ok, failed), numero di tentativi, stato HTTP, errore e orario del prossimo tentativo.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id del webhook. |
Richiesta
curl "https://hubvocal.com/v1/webhooks/wh_3c9d1e/deliveries" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/webhooks/wh_3c9d1e/deliveries", {
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.get(
"https://hubvocal.com/v1/webhooks/wh_3c9d1e/deliveries",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
[
{
"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
}
] Errori
404Nessun webhook con questo id.
Ritenta una consegna
POST /v1/webhooks/deliveries/{id}/retry
Rimette in coda una consegna non riuscita.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
idobbligatorio | path | string | Id della consegna, dall'elenco delle consegne. |
Richiesta
curl -X POST "https://hubvocal.com/v1/webhooks/deliveries/dl_51a3c4/retry" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/webhooks/deliveries/dl_51a3c4/retry", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.post(
"https://hubvocal.com/v1/webhooks/deliveries/dl_51a3c4/retry",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"ok": true
} Errori
404Nessuna consegna con questo id, oppure non è fallita.
Voci e motori
Elenca motori, voci e limiti
GET /v1/voices
Tutto ciò che serve per costruire un modulo per gli agenti: nomi dei motori, voci valide per ogni motore, intervalli di velocità, lingue, toni predefiniti, il modello dietro ogni motore e i limiti di lunghezza dei campi. L'esempio qui sotto è abbreviato.
Richiesta
curl "https://hubvocal.com/v1/voices" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/voices", {
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.get(
"https://hubvocal.com/v1/voices",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"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
}
} Chiamate
La conservazione si imposta per organizzazione nella dashboard: recordings_days (predefinito 30), transcripts_days (predefinito 365) e pii_redaction. Un processo giornaliero elimina registrazioni e trascrizioni scadute; la riga della chiamata e il suo costo restano.
Widget web: le chiavi pubbliche (pk_…) si creano per agente nella dashboard. Lo script del widget chiama POST /api/public/web-calls; consulta la guida nella pagina Sviluppatori.
Avvia una chiamata web
POST /v1/web-calls
Apre una stanza per una chiamata dal browser e restituisce un token di accesso a breve durata. Passa server_url e token a livekit-client nel browser; l'agente entra nella stanza da solo. Chiama questo endpoint dal tuo server, così la chiave API non arriva mai al browser. Al posto di agent_id puoi inviare un oggetto agente completo per provare una bozza senza salvarla.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
agent_id | body | string | Id di un agente salvato. Obbligatorio se non invii agent. |
agent | body | object | Un oggetto agente completo (stessi campi di POST /v1/agents) usato solo per questa chiamata. |
version | body | string | integer | Quale versione usare: "live" (predefinito), "draft" o un numero di versione. |
Richiesta
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"
}' const res = await fetch("https://hubvocal.com/v1/web-calls", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"agent_id": "front-desk"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.post(
"https://hubvocal.com/v1/web-calls",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
json={
"agent_id": "front-desk",
},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"server_url": "wss://hubvocal.com",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…",
"room": "demo-3f9a1c2e",
"max_seconds": 300
} Errori
400Non è stato inviato né agent_id né agent.402L'organizzazione ha raggiunto il limite di spesa mensile.403L'organizzazione è sospesa.404Nessun agente con questo id.
Effettua una chiamata in uscita
POST /v1/calls
Chiama un numero di telefono da uno dei numeri della tua organizzazione e collega l'agente quando la chiamata riceve risposta. Il fornitore (DIDWW, Twilio o il tuo SIP trunk) viene scelto in base al numero from.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
fromobbligatorio | body | string | Uno dei numeri della tua organizzazione, in formato E.164 (+90…). |
toobbligatorio | body | string | Il numero da chiamare, in formato E.164. |
agent_id | body | string | Agente da usare. Se omesso, risponde l'agente assegnato al numero from. |
version | body | string | integer | Quale versione usare: "live" (predefinito), "draft" o un numero di versione. |
Richiesta
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"
}' const res = await fetch("https://hubvocal.com/v1/calls", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"from": "+908501234567",
"to": "+905321234567",
"agent_id": "front-desk"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.post(
"https://hubvocal.com/v1/calls",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
json={
"from": "+908501234567",
"to": "+905321234567",
"agent_id": "front-desk",
},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"room": "tel-out-9c41e0b7",
"provider": "didww",
"sip_call_id": "SCL_7mVt2kQb9xRa"
} Errori
400Un numero non è in formato internazionale, oppure from non è uno dei tuoi numeri.402L'organizzazione ha raggiunto il limite di spesa mensile.502Il fornitore di telefonia ha rifiutato la chiamata.
Elenca le chiamate
GET /v1/calls
Chiamate dalla più recente alla meno recente, web e telefoniche. quality è il riepilogo delle misurazioni lato browser ed è null per le chiamate telefoniche. ended_by è caller, agent, max_call_seconds, caller_unresponsive, agent_unresponsive, no_answer o unknown. channel è web, phone_in o phone_out. Ogni riga contiene anche outcome e summary_text (i primi 160 caratteri del riepilogo). ended_by è supervisor quando un supervisore ha chiuso la chiamata dalla console live.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
limit | query | integer | Predefinito 50, massimo 500. |
agent | query | string | Solo le chiamate di questo agente (nome dell'agente). |
outcome | query | string | Solo le chiamate con questo esito. |
Richiesta
curl "https://hubvocal.com/v1/calls?limit=20" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/calls?limit=20", {
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.get(
"https://hubvocal.com/v1/calls",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
params={
"limit": 20,
},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
[
{
"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
}
}
] Recupera una chiamata
GET /v1/calls/{room}
Il registro completo della chiamata: dettaglio dei costi per fornitore, trascrizione, tempi del saluto, interruzioni alla sorgente lato agente e la timeline di qualità del browser in finestre di 2 secondi. I campi di costo dipendono dal motore (live, llm, stt, tts, realtime…); ognuno contiene usd. source_gaps.events elenca coppie [secondo, ms]. summary contiene text, outcome (resolved, message_taken, transferred, unresolved, no_conversation, spam), sentiment, topics e action_items. supervisors indica chi si è unito alla chiamata dalla console live e in quali modalità. recording contiene available, duration_s e size_bytes. extracted contiene i valori dei post_call.fields dell'agente (null se non menzionati).
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
roomobbligatorio | path | string | Nome della stanza, dall'elenco delle chiamate o da POST /v1/web-calls. |
Richiesta
curl "https://hubvocal.com/v1/calls/demo-8585522d" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/calls/demo-8585522d", {
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.get(
"https://hubvocal.com/v1/calls/demo-8585522d",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"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"
}
]
}
} Errori
404Chiamata non trovata.
Scarica la registrazione
GET /v1/calls/{room}/recording
L'audio della chiamata in OGG/Opus. Supporta le richieste Range. 404 se la chiamata non è stata registrata o la registrazione è scaduta.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
roomobbligatorio | path | string | Nome della stanza, dall'elenco delle chiamate o da POST /v1/web-calls. |
Richiesta
curl "https://hubvocal.com/v1/calls/demo-8585522d/recording" \
-H "Authorization: Bearer $VT_API_KEY" \
-o call.ogg import fs from "node:fs";
const res = await fetch("https://hubvocal.com/v1/calls/demo-8585522d/recording", {
headers: { Authorization: `Bearer ${process.env.VT_API_KEY}` },
});
if (!res.ok) throw new Error(`${res.status}: ${(await res.json()).detail}`);
fs.writeFileSync("call.ogg", Buffer.from(await res.arrayBuffer())); import os
import requests
resp = requests.get(
"https://hubvocal.com/v1/calls/demo-8585522d/recording",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=60,
)
resp.raise_for_status()
with open("call.ogg", "wb") as f:
f.write(resp.content) Risposta · 200
HTTP/1.1 200 OK
Content-Type: audio/ogg
Accept-Ranges: bytes
Content-Length: 201344
<OGG/Opus> Errori
404La chiamata non è stata registrata o la registrazione è scaduta.
Elimina una chiamata
DELETE /v1/calls/{room}
Elimina subito la registrazione, la trascrizione e il riepilogo della chiamata, ad esempio per una richiesta ai sensi della KVKK o del GDPR. La riga della chiamata e il suo costo restano.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
roomobbligatorio | path | string | Nome della stanza, dall'elenco delle chiamate o da POST /v1/web-calls. |
Richiesta
curl -X DELETE "https://hubvocal.com/v1/calls/demo-8585522d" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/calls/demo-8585522d", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.delete(
"https://hubvocal.com/v1/calls/demo-8585522d",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"deleted": "demo-8585522d"
} Errori
404Chiamata non trovata.
Esporta le chiamate (CSV)
GET /v1/calls/export
Le chiamate di un intervallo di date in CSV, una riga per chiamata, con i campi strutturati come colonne aggiuntive.
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
formatobbligatorio | query | string | Solo csv. |
from | query | date | YYYY-MM-DD, incluso. |
to | query | date | YYYY-MM-DD, incluso. |
agent_id | query | string | Solo le chiamate di questo agente. |
Richiesta
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 import fs from "node:fs";
const res = await fetch("https://hubvocal.com/v1/calls/export?format=csv&from=2026-11-01&to=2026-11-05", {
headers: { Authorization: `Bearer ${process.env.VT_API_KEY}` },
});
if (!res.ok) throw new Error(`${res.status}: ${(await res.json()).detail}`);
fs.writeFileSync("calls.csv", Buffer.from(await res.arrayBuffer())); import os
import requests
resp = requests.get(
"https://hubvocal.com/v1/calls/export",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
params={
"format": "csv",
"from": "2026-11-01",
"to": "2026-11-05",
},
timeout=60,
)
resp.raise_for_status()
with open("calls.csv", "wb") as f:
f.write(resp.content) Risposta · 200
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 Numeri di telefono
Elenca i numeri di telefono
GET /v1/numbers
I numeri di telefono della tua organizzazione e l'agente che risponde su ciascuno (profile_id), oltre allo stato di ogni fornitore di telefonia. I numeri vengono aggiunti dal team della piattaforma; tu assegni gli agenti.
Richiesta
curl "https://hubvocal.com/v1/numbers" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/numbers", {
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.get(
"https://hubvocal.com/v1/numbers",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"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
}
}
}
} Assegna un agente a un numero
PUT /v1/numbers/{number}
Sceglie l'agente che risponde alle chiamate in entrata su questo numero. Invia agent_id: null per rimuovere l'assegnazione. Codifica il + del numero come %2B nel percorso (funziona anche il prefisso 00).
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
numberobbligatorio | path | string | Il numero in E.164, codificato per URL. |
agent_id | body | string | null | Agente che risponde alle chiamate in entrata. |
label | body | string | Una nota per il tuo team, fino a 60 caratteri. |
Richiesta
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"
}' const res = await fetch("https://hubvocal.com/v1/numbers/%2B908501234567", {
method: "PUT",
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"agent_id": "front-desk",
"label": "Main line"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.put(
"https://hubvocal.com/v1/numbers/%2B908501234567",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
json={
"agent_id": "front-desk",
"label": "Main line",
},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"ok": true
} Errori
400L'agente non esiste.404Il numero non appartiene alla tua organizzazione.
Utilizzo
Utilizzo
GET /v1/usage
Chiamate, minuti e costi dei fornitori della tua organizzazione, per fornitore e per giorno (Europe/Istanbul). Per impostazione predefinita, il mese corrente. kind è llm, tts, stt o voice (motori speech-to-speech).
| Nome | In | Tipo | Descrizione |
|---|---|---|---|
from | query | date | YYYY-MM-DD, incluso. |
to | query | date | YYYY-MM-DD, incluso. |
Richiesta
curl "https://hubvocal.com/v1/usage?from=2026-11-01&to=2026-11-05" \
-H "Authorization: Bearer $VT_API_KEY" const res = await fetch("https://hubvocal.com/v1/usage?from=2026-11-01&to=2026-11-05", {
headers: {
Authorization: `Bearer ${process.env.VT_API_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import os
import requests
resp = requests.get(
"https://hubvocal.com/v1/usage",
headers={"Authorization": f"Bearer {os.environ['VT_API_KEY']}"},
params={
"from": "2026-11-01",
"to": "2026-11-05",
},
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"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
}
]
} Specifica
Specifica OpenAPI
GET /v1/openapi.json Nessuna chiave richiesta
Il documento OpenAPI 3.1 di questa API. Nessuna chiave richiesta. Importalo in Postman, Insomnia o in un generatore di client.
Richiesta
curl "https://hubvocal.com/v1/openapi.json" const res = await fetch("https://hubvocal.com/v1/openapi.json");
const data = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${data.detail}`);
console.log(data); import requests
resp = requests.get(
"https://hubvocal.com/v1/openapi.json",
timeout=30,
)
if not resp.ok:
raise RuntimeError(f"{resp.status_code}: {resp.json()['detail']}")
print(resp.json()) Risposta · 200
{
"openapi": "3.1.0",
"info": {
"title": "HubVocal API",
"version": "1.0.0"
},
"servers": [
{
"url": "https://hubvocal.com/v1"
}
],
"paths": {}
}