API-Referenz
Alle Endpunkte der API v1. Beispiele in curl, JavaScript (fetch) und Python (requests); die gewählte Sprache gilt für die ganze Seite.
| Basis-URL | https://hubvocal.com/v1 |
|---|---|
| Auth | Authorization: Bearer vt_live_… |
| Rate-Limit | 120 Anfragen/Min. pro Schlüssel · 429 + Retry-After |
| Scopes | read · write · callsJeder Schlüssel hat read, write und/oder calls. read deckt GET-Anfragen ab, write Änderungen, calls ausgehende Anrufe. Eine Anfrage außerhalb der Scopes des Schlüssels erhält 403. |
| Fehler-Body | {"detail": "..."} |
| OpenAPI | GET /v1/openapi.json |
Zu Authentifizierung, Einbettung und Fehlercodes siehe Entwickler.
Agenten
Die Gespräche laufen im Textmodus. Getestet werden also Anweisungen und Tool-Nutzung, nicht Stimme oder Latenz. Simulationen starten Sie im Dashboard; einen /v1-Endpunkt dafür gibt es noch nicht.
Agenten auflisten
GET /v1/agents
Gibt alle Agenten Ihrer Organisation in der Reihenfolge des Dashboards zurück.
Anfrage
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()) Antwort · 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
}
] Agenten erstellen
POST /v1/agents
Erstellt einen Agenten. Jedes Feld wird auf dem Server validiert; Werte über den Limits werden gekürzt. Übergeben Sie eine bereits vorhandene id, schlägt die Anfrage mit 409 fehl. Zum Aktualisieren verwenden Sie PUT.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
id | body | string | Optional. Kleinbuchstaben, Ziffern und Bindestriche, bis zu 48 Zeichen. Wird ohne Angabe aus dem Namen erzeugt. |
namePflicht | body | string | Anzeigename, bis zu 60 Zeichen. |
langPflicht | body | string | Code der Gesprächssprache, z. B. tr, en, de. Die vollständige Liste liefert GET /v1/voices. |
enginePflicht | body | "live" | "gemini" | "realtime" | "classic" | live: GPT-Live 1, gemini: Gemini Live, realtime: OpenAI Realtime, classic: STT → LLM → TTS. |
instructions | body | string | Systemanweisungen, bis zu 8000 Zeichen. |
greeting | body | string | Was der Agent zuerst sagt, bis zu 400 Zeichen. |
voice | body | object | voice (ein Name aus /v1/voices), speed (nur classic und realtime; bei live und gemini fest auf 1.0), tone (Sprechstil als Freitext, bis zu 600 Zeichen). |
tools | body | object | builtins (end_call, take_message) und webhooks (Tool-ids); insgesamt bis zu 16 Tools. Leer oder nicht angegeben heißt: keine Tools. mcp: [{server_id, tools}] fügt Tools aus Ihren MCP-Servern hinzu; sie zählen zum selben Limit. |
turn | body | object | Einstellungen zum Sprecherwechsel: sensitivity (low, balanced, high; nur classic-Engine), first_speaker (agent oder caller), silence_wait_s (1–5), idle_check_s (0 oder 10–120), agent_timeout_s (0 oder 10–60). Leere Felder verwenden die Standardwerte. |
recording | body | object | recording: {enabled (Standard false), announce (Standard true), announce_text?}. Ist sie eingeschaltet, kündigt der Agent die Aufzeichnung vor der Begrüßung an. |
post_call | body | object | post_call.summary: beim Anrufende eine Zusammenfassung schreiben (Standard true). post_call.fields: bis zu 24 Felder, die nach dem Anruf ausgefüllt werden ({name, type, description, enum?, required?}). |
knowledge | body | object | doc_ids (bis zu 50 Dokumente aus der Wissensdatenbank) und mode ("tool"). Der Agent erhält dann das Tool search_knowledge. |
calendar | body | object | integration_id und calendar_id aus dem Dashboard, duration_min (15–240), buffer_min (0–60), timezone, working_hours, min_notice_h (0–72), max_days_ahead (1–90). Aktiviert die Termin-Tools. |
Anfrage
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()) Antwort · 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
} Fehler
409Ein Agent mit dieser id existiert bereits.
Agenten abrufen
GET /v1/agents/{id}
Gibt einen einzelnen Agenten zurück. published_version ist die Nummer der Live-Version (null, wenn er noch nie veröffentlicht wurde), und draft_changed zeigt, ob der Entwurf davon abweicht.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | Agent-id, z. B. front-desk. |
Anfrage
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()) Antwort · 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
} Fehler
404Kein Agent mit dieser id.
Agenten aktualisieren
PUT /v1/agents/{id}
Ersetzt den Agenten durch das gesendete Objekt. Senden Sie das vollständige Objekt: Ausgelassene Felder erhalten ihre Standardwerte. Die id im Pfad hat Vorrang vor einer id im Body.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | Agent-id, z. B. front-desk. |
namePflicht | body | string | Anzeigename, bis zu 60 Zeichen. |
langPflicht | body | string | Code der Gesprächssprache, z. B. tr, en, de. Die vollständige Liste liefert GET /v1/voices. |
enginePflicht | body | "live" | "gemini" | "realtime" | "classic" | live: GPT-Live 1, gemini: Gemini Live, realtime: OpenAI Realtime, classic: STT → LLM → TTS. |
instructions | body | string | Systemanweisungen, bis zu 8000 Zeichen. |
greeting | body | string | Was der Agent zuerst sagt, bis zu 400 Zeichen. |
voice | body | object | voice (ein Name aus /v1/voices), speed (nur classic und realtime; bei live und gemini fest auf 1.0), tone (Sprechstil als Freitext, bis zu 600 Zeichen). |
tools | body | object | builtins (end_call, take_message) und webhooks (Tool-ids); insgesamt bis zu 16 Tools. Leer oder nicht angegeben heißt: keine Tools. mcp: [{server_id, tools}] fügt Tools aus Ihren MCP-Servern hinzu; sie zählen zum selben Limit. |
turn | body | object | Einstellungen zum Sprecherwechsel: sensitivity (low, balanced, high; nur classic-Engine), first_speaker (agent oder caller), silence_wait_s (1–5), idle_check_s (0 oder 10–120), agent_timeout_s (0 oder 10–60). Leere Felder verwenden die Standardwerte. |
recording | body | object | recording: {enabled (Standard false), announce (Standard true), announce_text?}. Ist sie eingeschaltet, kündigt der Agent die Aufzeichnung vor der Begrüßung an. |
post_call | body | object | post_call.summary: beim Anrufende eine Zusammenfassung schreiben (Standard true). post_call.fields: bis zu 24 Felder, die nach dem Anruf ausgefüllt werden ({name, type, description, enum?, required?}). |
knowledge | body | object | doc_ids (bis zu 50 Dokumente aus der Wissensdatenbank) und mode ("tool"). Der Agent erhält dann das Tool search_knowledge. |
calendar | body | object | integration_id und calendar_id aus dem Dashboard, duration_min (15–240), buffer_min (0–60), timezone, working_hours, min_notice_h (0–72), max_days_ahead (1–90). Aktiviert die Termin-Tools. |
Anfrage
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()) Antwort · 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
} Fehler
404Kein Agent mit dieser id. Erstellen Sie ihn zuerst mit POST.
Agenten löschen
DELETE /v1/agents/{id}
Löscht den Agenten. Eine Organisation behält immer mindestens einen Agenten, daher schlägt das Löschen des letzten fehl.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | Agent-id, z. B. front-desk. |
Anfrage
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()) Antwort · 200
{
"deleted": "front-desk"
} Fehler
400Dies ist der letzte Agent der Organisation.404Kein Agent mit dieser id.
Versionen auflisten
GET /v1/agents/{id}/versions
Die veröffentlichten Versionen des Agenten, neueste zuerst. is_live markiert die Version, die Anrufe derzeit verwenden.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | Agent-id, z. B. front-desk. |
Anfrage
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()) Antwort · 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
}
] Fehler
404Kein Agent mit dieser id.
Version abrufen
GET /v1/agents/{id}/versions/{n}
Der vollständige Agent im Stand dieser Version.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | Agent-id, z. B. front-desk. |
nPflicht | path | integer | Versionsnummer. |
Anfrage
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()) Antwort · 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
} Fehler
404Agent oder Version existiert nicht.
Entwurf veröffentlichen
POST /v1/agents/{id}/publish
Speichert den aktuellen Entwurf als neue Version und schaltet sie live. Bereits laufende Anrufe behalten ihre Version.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | Agent-id, z. B. front-desk. |
note | body | string | Eine kurze Notiz, was sich geändert hat. |
Anfrage
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()) Antwort · 200
{
"version": 4
} Fehler
404Kein Agent mit dieser id.
Zurücksetzen
POST /v1/agents/{id}/rollback
Kopiert eine frühere Version in den Entwurf und veröffentlicht sie als neue Version.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | Agent-id, z. B. front-desk. |
versionPflicht | body | integer | Die Version, die wiederhergestellt werden soll. |
Anfrage
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()) Antwort · 200
{
"version": 5
} Fehler
404Agent oder Version existiert nicht.
Wissensdatenbank
Dokumente auflisten
GET /v1/knowledge
Die Dokumente Ihrer Organisation mit Verarbeitungsstatus, Größe, Anzahl der Abschnitte und den Agenten, die sie verwenden.
Anfrage
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()) Antwort · 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"
]
}
] Dokument hinzufügen
POST /v1/knowledge
Laden Sie eine Datei als multipart/form-data hoch (Feld file), senden Sie Text als JSON {title, text} oder eine einzelne Webseite als JSON {url}. Gibt 201 mit Status processing zurück; fragen Sie das Dokument ab, bis es ready oder failed ist.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
file | body | file | multipart/form-data: pdf, docx, txt, md oder html, bis zu 10 MB. |
title | body | string | Titel, zusammen mit text. |
text | body | string | Inhalt als reiner Text. |
url | body | string | Alternativ eine einzelne https-Seite, die abgerufen wird. |
Anfrage
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()) Antwort · 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": []
} Fehler
400Nicht unterstützter Dateityp, Datei größer als 10 MB, URL ohne https oder mit privater Adresse, oder das Limit der Organisation (200 Dokumente, 200 MB) ist erreicht.
Dokument abrufen
GET /v1/knowledge/{id}
Das Dokument mit einer Vorschau seiner ersten 2000 Zeichen.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | Dokument-id. |
Anfrage
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()) Antwort · 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…"
} Fehler
404Kein Dokument mit dieser id.
Dokument löschen
DELETE /v1/knowledge/{id}
Löscht das Dokument und entfernt es aus allen Agenten, die es verwendet haben.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | Dokument-id. |
Anfrage
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()) Antwort · 200
{
"deleted": "kb_7d21e4"
} Fehler
404Kein Dokument mit dieser id.
Suchen
POST /v1/knowledge/search
Führt dieselbe Suche aus wie der Agent und gibt die am besten passenden Abschnitte mit einem Score zurück. Hilfreich, um Ihre Dokumente vor dem Livegang zu prüfen.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
queryPflicht | body | string | Wonach gesucht werden soll. |
doc_ids | body | string[] | Die Suche auf diese Dokumente beschränken. |
Anfrage
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()) Antwort · 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
}
] Tools
Integrierte Tools
Diese Tools brauchen keine Definition. Schalten Sie sie in tools.builtins des Agenten ein. search_knowledge kommt hinzu, sobald knowledge gesetzt ist. Die Termin-Tools kommen hinzu, sobald calendar gesetzt ist.
| Name | Funktion |
|---|---|
end_call | Der Agent verabschiedet sich und legt 0,75 s nach dem Ende seiner Äußerung auf. Spricht der Anrufer in der Zwischenzeit, wird das Auflegen abgebrochen. Im Anrufprotokoll steht ended_by: "agent". |
take_message | Nimmt name, phone, message und callback_requested auf und schreibt sie in outcomes[] des Anrufprotokolls. Eine Bestätigung wird nicht abgefragt. |
search_knowledge | Wird automatisch hinzugefügt, wenn dem Agenten in knowledge Dokumente zugeordnet sind. Durchsucht sie und hält die Quellen im Anrufprotokoll fest. |
check_availability | Lesend. Liefert freie Zeitfenster zwischen date_from und date_to innerhalb der Arbeitszeiten des Agenten. Wird hinzugefügt, wenn calendar gesetzt ist. |
book_appointment | Schreibend, mit Bestätigungsschritt. Bucht start für name und phone, optional mit einer note. Die Buchung landet in outcomes[]. |
cancel_appointment | Schreibend, mit Bestätigungsschritt. Storniert eine Buchung anhand ihrer booking_ref. |
Kalender-Integrationen (Google Calendar) werden ausschließlich im Dashboard verbunden; dafür gibt es keinen /v1-Endpunkt. Tragen Sie die Integrations- und Kalender-ids aus dem Dashboard im Feld calendar des Agenten ein.
Tools auflisten
GET /v1/tools
Die Webhook-Tools Ihrer Organisation. Das Secret wird nie zurückgegeben; has_secret zeigt, ob eines gesetzt ist.
Anfrage
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()) Antwort · 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
}
] Tool abrufen
GET /v1/tools/{id}
Ein einzelnes Webhook-Tool. Das Secret wird nie zurückgegeben.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | Tool-id. |
Anfrage
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()) Antwort · 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
} Fehler
404Kein Tool mit dieser id.
Tool erstellen
POST /v1/tools
Legt ein Webhook-Tool für die Organisation an. Um es zu nutzen, tragen Sie seine id in tools.webhooks eines Agenten ein. Die URL muss https verwenden und darf nicht auf eine lokale oder private Adresse zeigen.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
namePflicht | body | string | Kleinbuchstaben, Ziffern und Unterstriche, beginnend mit einem Buchstaben (3–41 Zeichen). Eindeutig in der Organisation; end_call und take_message sind reserviert. |
descriptionPflicht | body | string | Sagt dem Modell, wann und wie es das Tool verwenden soll. |
kindPflicht | body | "read" | "write" | read läuft sofort; write lässt den Anrufer zuerst bestätigen. |
urlPflicht | body | string | Ihr Endpunkt. Nur https; lokale und private Adressen werden abgelehnt. |
parameters | body | object | JSON-Schema-Objekt: bis zu 20 Felder vom Typ string, number, integer oder boolean (enum erlaubt). |
timeout_s | body | integer | 2 bis 15 Sekunden. |
enabled | body | boolean | Deaktivierte Tools werden dem Modell nicht angeboten. |
secret | body | string | Dient zum Signieren der Anfragen. Nur schreibbar; beim Aktualisieren behält ein leerer Wert das aktuelle Secret, "-" entfernt es. |
Anfrage
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()) Antwort · 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
} Fehler
400Ein Feld verstößt gegen die Regeln: Namensformat, https-URL, private Adresse, mehr als 20 Parameter oder Timeout außerhalb des zulässigen Bereichs.409Ein Tool mit diesem Namen existiert bereits, oder der Name ist reserviert.
Tool aktualisieren
PUT /v1/tools/{id}
Ersetzt das Tool durch das gesendete Objekt. Lassen Sie secret leer oder weg, um das aktuelle zu behalten; senden Sie "-", um es zu entfernen.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | Tool-id. |
namePflicht | body | string | Kleinbuchstaben, Ziffern und Unterstriche, beginnend mit einem Buchstaben (3–41 Zeichen). Eindeutig in der Organisation; end_call und take_message sind reserviert. |
descriptionPflicht | body | string | Sagt dem Modell, wann und wie es das Tool verwenden soll. |
kindPflicht | body | "read" | "write" | read läuft sofort; write lässt den Anrufer zuerst bestätigen. |
urlPflicht | body | string | Ihr Endpunkt. Nur https; lokale und private Adressen werden abgelehnt. |
parameters | body | object | JSON-Schema-Objekt: bis zu 20 Felder vom Typ string, number, integer oder boolean (enum erlaubt). |
timeout_s | body | integer | 2 bis 15 Sekunden. |
enabled | body | boolean | Deaktivierte Tools werden dem Modell nicht angeboten. |
secret | body | string | Dient zum Signieren der Anfragen. Nur schreibbar; beim Aktualisieren behält ein leerer Wert das aktuelle Secret, "-" entfernt es. |
Anfrage
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()) Antwort · 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
} Fehler
400Ein Feld verstößt gegen die Regeln: Namensformat, https-URL, private Adresse, mehr als 20 Parameter oder Timeout außerhalb des zulässigen Bereichs.404Kein Tool mit dieser id.
Tool löschen
DELETE /v1/tools/{id}
Löscht das Tool und entfernt es aus allen Agenten, die es verwendet haben.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | Tool-id. |
Anfrage
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()) Antwort · 200
{
"deleted": "tl_8f3a2c"
} Fehler
404Kein Tool mit dieser id.
Tool testen
POST /v1/tools/{id}/test
Sendet eine echte Anfrage mit den angegebenen Argumenten an die URL des Tools und gibt Status, benötigte Zeit und bis zu 4 KB des Antwort-Bodys zurück.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | Tool-id. |
arguments | body | object | Beispielargumente passend zu parameters. |
Anfrage
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()) Antwort · 200
{
"ok": true,
"status": 200,
"ms": 412,
"body": "{\"order_id\":\"A-10234\",\"status\":\"shipped\",\"eta\":\"2026-11-07\"}"
} Fehler
404Kein Tool mit dieser id.
MCP-Server
MCP-Server auflisten
GET /v1/mcp-servers
Die MCP-Server Ihrer Organisation mit Status und synchronisierten Tools. auth_set zeigt, ob ein Secret gespeichert ist; das Secret selbst wird nie zurückgegeben.
Anfrage
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()) Antwort · 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"
}
]
}
] MCP-Server hinzufügen
POST /v1/mcp-servers
Speichert den Server und ruft seine Tool-Liste ab. Neue Tools sind ausgeschaltet. Bis zu 10 Server pro Organisation.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
namePflicht | body | string | Kurzname; wird als Präfix in Tool-Namen verwendet (<name>.<tool>). |
urlPflicht | body | string | Der MCP-Endpunkt des Servers. Nur https; lokale und private Adressen werden abgelehnt. |
transport | body | string | streamable_http (Standard) oder sse. |
auth | body | string | none, bearer oder header. |
secret | body | string | Token oder Header-Wert. Wird verschlüsselt gespeichert und nie zurückgegeben; "-" entfernt ihn. |
header_name | body | string | Name des Headers, wenn auth header ist. |
Anfrage
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()) Antwort · 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"
}
]
} Fehler
400Die URL ist nicht https oder zeigt auf eine private Adresse, oder die Tool-Liste des Servers konnte nicht abgerufen werden.409Die Organisation hat bereits 10 MCP-Server.
MCP-Server aktualisieren
PUT /v1/mcp-servers/{id}
Ändert Name, URL, Authentifizierung oder Aktivierung sowie, welche Tools eingeschaltet sind und ob sie read oder write sind. Senden Sie secret: "-", um das Secret zu entfernen.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | id des MCP-Servers. |
name | body | string | Kurzname; wird als Präfix in Tool-Namen verwendet (<name>.<tool>). |
url | body | string | Der MCP-Endpunkt des Servers. Nur https; lokale und private Adressen werden abgelehnt. |
enabled | body | boolean | Ein deaktivierter Server wird Agenten nicht angeboten. |
auth | body | string | none, bearer oder header. |
secret | body | string | Token oder Header-Wert. Wird verschlüsselt gespeichert und nie zurückgegeben; "-" entfernt ihn. |
tools | body | object[] | [{name, enabled, access}] pro Tool; access ist read oder write. |
Anfrage
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()) Antwort · 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"
}
]
} Fehler
400Die URL ist nicht https oder zeigt auf eine private Adresse, oder die Tool-Liste des Servers konnte nicht abgerufen werden.404Kein MCP-Server mit dieser id.
MCP-Server löschen
DELETE /v1/mcp-servers/{id}
Löscht den Server und entfernt seine Tools aus allen Agenten.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | id des MCP-Servers. |
Anfrage
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()) Antwort · 200
{
"deleted": "mcp_2b8e41"
} Fehler
404Kein MCP-Server mit dieser id.
Tools synchronisieren
POST /v1/mcp-servers/{id}/sync
Ruft die Tool-Liste erneut ab. Neu hinzugekommene Tools sind ausgeschaltet, access steht auf write.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | id des MCP-Servers. |
Anfrage
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()) Antwort · 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"
}
]
} Fehler
400Die URL ist nicht https oder zeigt auf eine private Adresse, oder die Tool-Liste des Servers konnte nicht abgerufen werden.404Kein MCP-Server mit dieser id.
Tool testen
POST /v1/mcp-servers/{id}/test
Ruft ein Tool des Servers mit den angegebenen Argumenten auf und gibt die Dauer, bis zu 4 KB Inhalt und die Angabe zurück, ob das Tool einen Fehler gemeldet hat.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | id des MCP-Servers. |
toolPflicht | body | string | Name des aufzurufenden Tools. |
arguments | body | object | Argumente für das Tool, passend zu seinem input_schema. |
Anfrage
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()) Antwort · 200
{
"ok": true,
"ms": 236,
"content": "{\"customer_id\":\"C-1042\",\"name\":\"Ayşe Yılmaz\",\"open_tickets\":0}",
"is_error": false
} Fehler
404Kein MCP-Server mit dieser id.
Webhooks
Webhooks auflisten
GET /v1/webhooks
Die Webhooks Ihrer Organisation mit dem Ergebnis ihrer letzten Zustellung. Das Secret wird hier nie zurückgegeben; secret_set zeigt, ob eines gesetzt ist.
Anfrage
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()) Antwort · 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
}
}
] Webhook erstellen
POST /v1/webhooks
Abonniert Ereignisse für eine URL. Derzeit gibt es nur das Ereignis call.ended. Bis zu 5 Webhooks pro Organisation. Lassen Sie secret weg, wird eines erzeugt und nur in dieser Antwort zurückgegeben.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
urlPflicht | body | string | Ihr Endpunkt. Nur https; lokale und private Adressen werden abgelehnt. |
eventsPflicht | body | string[] | Zu empfangende Ereignisse. Derzeit ["call.ended"]. |
enabled | body | boolean | Deaktivierte Webhooks erhalten nichts. |
secret | body | string | Secret zum Signieren. Wird ohne Angabe erzeugt und nur einmal zurückgegeben. |
Anfrage
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()) Antwort · 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…"
} Fehler
400Die URL verwendet kein https, zeigt auf eine private Adresse, oder ein Ereignis ist unbekannt.409Die Organisation hat bereits 5 Webhooks.
Webhook aktualisieren
PUT /v1/webhooks/{id}
Ändert URL, Ereignisse oder Aktivierungsstatus. Senden Sie secret: "-", um ein neues Secret zu erzeugen; es wird in der Antwort zurückgegeben.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | Webhook-id. |
url | body | string | Ihr Endpunkt. Nur https; lokale und private Adressen werden abgelehnt. |
events | body | string[] | Zu empfangende Ereignisse. Derzeit ["call.ended"]. |
enabled | body | boolean | Deaktivierte Webhooks erhalten nichts. |
secret | body | string | "-" erzeugt ein neues Secret. Weglassen, um das aktuelle zu behalten. |
Anfrage
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()) Antwort · 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
}
} Fehler
400Die URL verwendet kein https, zeigt auf eine private Adresse, oder ein Ereignis ist unbekannt.404Kein Webhook mit dieser id.
Webhook löschen
DELETE /v1/webhooks/{id}
Löscht den Webhook. Ausstehende Zustellungen werden verworfen.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | Webhook-id. |
Anfrage
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()) Antwort · 200
{
"deleted": "wh_3c9d1e"
} Fehler
404Kein Webhook mit dieser id.
Webhook testen
POST /v1/webhooks/{id}/test
Sendet ein Beispielereignis call.ended an die URL und gibt Status, benötigte Zeit und Antwort-Body zurück.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | Webhook-id. |
Anfrage
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()) Antwort · 200
{
"ok": true,
"http_status": 200,
"ms": 184,
"body": "{\"received\":true}"
} Fehler
404Kein Webhook mit dieser id.
Zustellungen auflisten
GET /v1/webhooks/{id}/deliveries
Die letzten 50 Zustellungen eines Webhooks, neueste zuerst, mit Status (pending, ok, failed), Anzahl der Versuche, HTTP-Status, Fehler und Zeitpunkt des nächsten Versuchs.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | Webhook-id. |
Anfrage
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()) Antwort · 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
}
] Fehler
404Kein Webhook mit dieser id.
Zustellung wiederholen
POST /v1/webhooks/deliveries/{id}/retry
Stellt eine fehlgeschlagene Zustellung erneut in die Warteschlange.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
idPflicht | path | string | id der Zustellung aus der Zustellungsliste. |
Anfrage
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()) Antwort · 200
{
"ok": true
} Fehler
404Keine Zustellung mit dieser id, oder sie ist nicht fehlgeschlagen.
Stimmen und Engines
Engines, Stimmen und Limits auflisten
GET /v1/voices
Alles für ein Agentenformular: Engine-Namen, gültige Stimmen pro Engine, Geschwindigkeitsbereiche, Sprachen, Tonfall-Vorlagen, das Modell hinter jeder Engine und Feldlängen-Limits. Das Beispiel unten ist gekürzt.
Anfrage
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()) Antwort · 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
}
} Anrufe
Die Aufbewahrung wird pro Organisation im Dashboard festgelegt: recordings_days (Standard 30), transcripts_days (Standard 365) und pii_redaction. Ein täglicher Job löscht abgelaufene Aufzeichnungen und Transkripte; die Anrufzeile und ihre Kosten bleiben erhalten.
Web-Widget: Öffentliche Schlüssel (pk_…) werden pro Agent im Dashboard angelegt. Das Widget-Script ruft POST /api/public/web-calls auf; siehe die Anleitung auf der Entwicklerseite.
Web-Anruf starten
POST /v1/web-calls
Öffnet einen Raum für einen Browser-Anruf und gibt ein kurzlebiges Zugriffstoken zurück. Übergeben Sie server_url und token im Browser an livekit-client; der Agent tritt dem Raum selbstständig bei. Rufen Sie diesen Endpunkt von Ihrem Server auf, damit der API-Schlüssel nie den Browser erreicht. Statt agent_id können Sie ein vollständiges agent-Objekt senden, um einen Entwurf ohne Speichern zu testen.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
agent_id | body | string | id eines gespeicherten Agenten. Pflicht, sofern Sie nicht agent senden. |
agent | body | object | Ein vollständiges agent-Objekt (dieselben Felder wie bei POST /v1/agents), nur für diesen Anruf. |
version | body | string | integer | Welche Version verwendet wird: "live" (Standard), "draft" oder eine Versionsnummer. |
Anfrage
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()) Antwort · 200
{
"server_url": "wss://hubvocal.com",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…",
"room": "demo-3f9a1c2e",
"max_seconds": 300
} Fehler
400Weder agent_id noch agent wurde gesendet.402Die Organisation hat ihr monatliches Ausgabenlimit erreicht.403Die Organisation ist gesperrt.404Kein Agent mit dieser id.
Ausgehenden Anruf starten
POST /v1/calls
Wählt eine Telefonnummer von einer Rufnummer Ihrer Organisation und verbindet den Agenten, sobald abgenommen wird. Der Anbieter (DIDWW, Twilio oder Ihr eigener SIP-Trunk) ergibt sich aus der from-Nummer.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
fromPflicht | body | string | Eine Rufnummer Ihrer Organisation im E.164-Format (+90…). |
toPflicht | body | string | Die anzurufende Nummer im E.164-Format. |
agent_id | body | string | Zu verwendender Agent. Ohne Angabe nimmt der Agent der from-Nummer ab. |
version | body | string | integer | Welche Version verwendet wird: "live" (Standard), "draft" oder eine Versionsnummer. |
Anfrage
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()) Antwort · 200
{
"room": "tel-out-9c41e0b7",
"provider": "didww",
"sip_call_id": "SCL_7mVt2kQb9xRa"
} Fehler
400Eine Nummer ist nicht im internationalen Format, oder from gehört nicht zu Ihren Rufnummern.402Die Organisation hat ihr monatliches Ausgabenlimit erreicht.502Der Telefonieanbieter hat den Anruf abgelehnt.
Anrufe auflisten
GET /v1/calls
Anrufe von neu nach alt, Web und Telefon. quality ist die Kurzauswertung der Messung im Browser und bei Telefonanrufen null. ended_by ist caller, agent, max_call_seconds, caller_unresponsive, agent_unresponsive, no_answer oder unknown. channel ist web, phone_in oder phone_out. Jede Zeile enthält außerdem outcome und summary_text (die ersten 160 Zeichen der Zusammenfassung). ended_by ist supervisor, wenn ein Supervisor den Anruf über die Live-Konsole beendet hat.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
limit | query | integer | Standard 50, maximal 500. |
agent | query | string | Nur Anrufe dieses Agenten (Agentenname). |
outcome | query | string | Nur Anrufe mit diesem Ergebnis. |
Anfrage
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()) Antwort · 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
}
}
] Anruf abrufen
GET /v1/calls/{room}
Das vollständige Anrufprotokoll: Kostenaufschlüsselung nach Anbieter, Transkript, Zeit bis zur Begrüßung, Quelllücken auf Agentenseite und die Qualitäts-Zeitachse des Browsers in 2-Sekunden-Fenstern. Die Kostenfelder hängen von der Engine ab (live, llm, stt, tts, realtime…); jedes enthält usd. source_gaps.events listet Paare aus [second, ms]. summary enthält text, outcome (resolved, message_taken, transferred, unresolved, no_conversation, spam), sentiment, topics und action_items. supervisors zeigt, wer sich über die Live-Konsole in den Anruf eingeschaltet hat und in welchen Modi. recording enthält available, duration_s und size_bytes. extracted enthält die Werte der post_call.fields des Agenten (null, wenn nicht erwähnt).
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
roomPflicht | path | string | Raumname aus der Anrufliste oder aus POST /v1/web-calls. |
Anfrage
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()) Antwort · 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"
}
]
}
} Fehler
404Anruf nicht gefunden.
Aufzeichnung herunterladen
GET /v1/calls/{room}/recording
Die Audiodaten des Anrufs als OGG/Opus. Unterstützt Range-Anfragen. 404, wenn der Anruf nicht aufgezeichnet wurde oder die Aufzeichnung abgelaufen ist.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
roomPflicht | path | string | Raumname aus der Anrufliste oder aus POST /v1/web-calls. |
Anfrage
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) Antwort · 200
HTTP/1.1 200 OK
Content-Type: audio/ogg
Accept-Ranges: bytes
Content-Length: 201344
<OGG/Opus> Fehler
404Der Anruf wurde nicht aufgezeichnet, oder die Aufzeichnung ist abgelaufen.
Anruf löschen
DELETE /v1/calls/{room}
Löscht Aufzeichnung, Transkript und Zusammenfassung des Anrufs sofort, etwa bei einem Antrag nach KVKK oder GDPR. Die Anrufzeile und ihre Kosten bleiben erhalten.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
roomPflicht | path | string | Raumname aus der Anrufliste oder aus POST /v1/web-calls. |
Anfrage
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()) Antwort · 200
{
"deleted": "demo-8585522d"
} Fehler
404Anruf nicht gefunden.
Anrufe exportieren (CSV)
GET /v1/calls/export
Anrufe eines Zeitraums als CSV, eine Zeile pro Anruf, mit den strukturierten Feldern als zusätzliche Spalten.
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
formatPflicht | query | string | Nur csv. |
from | query | date | YYYY-MM-DD, einschließlich. |
to | query | date | YYYY-MM-DD, einschließlich. |
agent_id | query | string | Nur Anrufe dieses Agenten. |
Anfrage
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) Antwort · 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 Rufnummern
Rufnummern auflisten
GET /v1/numbers
Die Rufnummern Ihrer Organisation und der Agent, der jeweils abnimmt (profile_id), dazu die Bereitschaft jedes Telefonieanbieters. Rufnummern fügt das Plattform-Team hinzu; Sie weisen die Agenten zu.
Anfrage
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()) Antwort · 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
}
}
}
} Agenten einer Rufnummer zuweisen
PUT /v1/numbers/{number}
Legt fest, welcher Agent eingehende Anrufe auf dieser Rufnummer annimmt. Mit agent_id: null heben Sie die Zuweisung auf. Kodieren Sie das + der Nummer im Pfad als %2B (das Präfix 00 funktioniert ebenfalls).
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
numberPflicht | path | string | Die Nummer in E.164, URL-kodiert. |
agent_id | body | string | null | Agent, der eingehende Anrufe annimmt. |
label | body | string | Eine Notiz für Ihr Team, bis zu 60 Zeichen. |
Anfrage
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()) Antwort · 200
{
"ok": true
} Fehler
400Der Agent existiert nicht.404Die Rufnummer gehört nicht zu Ihrer Organisation.
Nutzung
Nutzung
GET /v1/usage
Anrufe, Minuten und Anbieterkosten Ihrer Organisation, nach Anbieter und Tag (Europe/Istanbul). Standard ist der laufende Monat. kind ist llm, tts, stt oder voice (Speech-to-Speech-Engines).
| Name | Ort | Typ | Beschreibung |
|---|---|---|---|
from | query | date | YYYY-MM-DD, einschließlich. |
to | query | date | YYYY-MM-DD, einschließlich. |
Anfrage
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()) Antwort · 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
}
]
} Spezifikation
OpenAPI-Spezifikation
GET /v1/openapi.json Kein Schlüssel nötig
Das OpenAPI-3.1-Dokument dieser API. Kein Schlüssel nötig. Importieren Sie es in Postman, Insomnia oder einen Client-Generator.
Anfrage
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()) Antwort · 200
{
"openapi": "3.1.0",
"info": {
"title": "HubVocal API",
"version": "1.0.0"
},
"servers": [
{
"url": "https://hubvocal.com/v1"
}
],
"paths": {}
}