KI-Anruf per REST starten — ein POST, ein Webhook, ein Transkript.
Ihr CRM, Ticketing-Tool, Ihre Marketing-Automation oder Ihr Backend-Job sendet einen einzigen HTTP-POST. Die CodeB-Plattform setzt die Zielnummer auf die Whitelist, wählt sie über den konfigurierten SIP-Trunk an und verbindet eine Live-Voice-AI-Session, die Ihrem System-Prompt folgt. Wenn der Anruf endet, erhält Ihr Webhook-Empfänger einen signierten POST mit dem Ergebnis und einem Transkript-Dateinamen, den Sie bei Bedarf abrufen können. Ende zu Ende — in jeder Sprache unter zwanzig Codezeilen.
Was Sie bauen
Eine Vier-Schritt-Integration: Token holen, Anruf posten, Webhook empfangen, Transkript abrufen. Das Diagramm zeigt, wo jede Komponente lebt. Ihr Code spricht immer nur mit einem Host (Ihrem CodeB-Tenant) über reines HTTPS — kein SIP, kein WebRTC, keine Medien-Plumbing auf Ihrer Seite.
Voraussetzungen
Ein CodeB-Tenant
Beliebige von Ihnen kontrollierte Domain: https://phone.ihrtenant.de. Selbst-gehostet oder auf einem CodeB-betriebenen Tenant. Mandantenisolation ist pro Domain.
Ein Bearer-Token
Entweder ein OIDC-Access-Token (von /oidc.ashx/token) oder ein ak_-API-Schlüssel aus dem Tenant-Admin. Beide funktionieren auf jedem Endpunkt dieser Seite.
Ein konfigurierter SIP-Trunk
Bereits vorhanden, wenn Sie normale Anrufe tätigen / empfangen können. Die Plattform wählt den Trunk über Ihre Outbound-Routing-Regeln — keine Trunk-Wahl pro Anruf nötig.
Ein Sprach-Engine-API-Key
Bringen Sie Ihren eigenen Voice-AI-Engine-Key mit. Sie bringen Ihren eigenen Key mit; die Plattform speichert ihn nicht über den Anruf hinaus.
Einwilligung des Empfängers
Ausgehende KI-Anrufe sind reguliert. Bestätigen Sie das Opt-in (TCPA / DSGVO / ePrivacy / lokale Vorschriften). Die Plattform ist Transport — die Einwilligung liegt bei Ihnen.
Ein Webhook-Empfänger (optional)
Ein öffentlicher HTTPS-Endpoint, der POST annimmt. Ohne ihn findet der Anruf trotzdem statt — Sie pollen dann GET /v1/calls/{id}.
Schritt 1 — Bearer-Token holen
Überspringen Sie diesen Schritt, wenn Sie bereits einen ak_-API-Schlüssel aus
dem Tenant-Admin haben — verwenden Sie ihn direkt als
Authorization: Bearer ak_ihrkeyhier. Für interaktive Flows tauschen Sie
Benutzername/Passwort gegen ein kurzlebiges OIDC-Access-Token:
# Benutzername + Passwort gegen kurzlebiges OIDC-Access-Token tauschen. TOKEN=$(curl -s -X POST "https://phone.ihrtenant.de/oidc.ashx/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=password&username=alice&password=IHR_PASSWORT&scope=openid" \ | python3 -c "import sys,json;print(json.load(sys.stdin)['access_token'])") echo "$TOKEN"
import requests r = requests.post( "https://phone.ihrtenant.de/oidc.ashx/token", data={ "grant_type": "password", "username": "alice", "password": "IHR_PASSWORT", "scope": "openid", }, timeout=10, ) r.raise_for_status() token = r.json()["access_token"] print(token)
const body = new URLSearchParams({ grant_type: "password", username: "alice", password: "IHR_PASSWORT", scope: "openid", }); const r = await fetch("https://phone.ihrtenant.de/oidc.ashx/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body, }); if (!r.ok) throw new Error(`token ${r.status}`); const { access_token } = await r.json(); console.log(access_token);
ak_-API-Schlüssel anlegen und
den Token-Dance überspringen. Es ist derselbe Bearer-Header auf jedem Aufruf.
Schritt 2 — Anruf posten
Senden Sie einen einzigen JSON-Body an POST /api.ashx/v1/calls mit Zielnummer,
Anweisungen für die KI und dem E-Mail-Empfänger für das Transkript. Die Antwort kommt
sofort; der eigentliche Wählversuch läuft asynchron.
Request-Felder
| Feld | Typ | Pflicht | Hinweise |
|---|---|---|---|
phone | string | ja | E.164, z. B. +4915157610183. Nummern werden vor dem Whitelist-Eintrag normalisiert. |
displayName | string | nein | Caller-ID-Label / SIP-From-Display. Standard: phone. |
email | string | ja | Empfänger für Transkript + Ergebnis-Mail nach Anrufende. |
systemPrompt | string | ja | Klartext-Anweisungen an die KI, oder ein Slug eines gespeicherten Tenant-Prompts (z. B. reminder). |
apiKey | string | ja | Voice-AI-Engine-Key. Pro Anruf verwendet — nicht gespeichert. |
model | string | nein | Engine-Model-ID. Standard: Tenant-Konfiguration. |
voice | string | nein | z. B. Aoede, Charon. Standard Aoede. |
language | string | nein | BCP-47-Tag, z. B. en-US, de-DE. Standard en-US. |
maxSeconds | int | nein | Harte Obergrenze, 10–3600. Standard 300. |
retries | int | nein | 0–10. Wie viele erneute Versuche bei Nicht-Antwort / besetzt. Standard 0. |
retryDelayMinutes | int | nein | 1–1440. Abstand zwischen Wiederholungen. Standard 5. |
scheduleAtUtc | string | nein | ISO-8601 UTC. Ohne Angabe sofort wählen. Für Kampagnen mit zukünftiger Zeit. |
Beispiel — Terminerinnerung
curl -X POST https://phone.ihrtenant.de/api.ashx/v1/calls \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "phone": "+4915157610183", "displayName": "Erinnerung", "email": "ops@ihrunternehmen.example", "systemPrompt": "Du rufst Alex an, um den Zahnarzttermin morgen um 14:00 zu bestaetigen. Sei kurz und freundlich. Wenn JA, verabschiede dich. Wenn NEIN, biete denselben Termin naechste Woche an.", "apiKey": "IHR_AI_API_KEY", "voice": "Aoede", "language": "de-DE", "maxSeconds": 180, "retries": 2, "retryDelayMinutes": 10 }'
import requests, os TOKEN = os.environ["CODEB_TOKEN"] payload = { "phone": "+4915157610183", "displayName": "Erinnerung", "email": "ops@ihrunternehmen.example", "systemPrompt": ( "Du rufst Alex an, um den Zahnarzttermin morgen um 14:00 zu " "bestaetigen. Sei kurz und freundlich. Wenn JA, verabschiede " "dich. Wenn NEIN, biete denselben Termin naechste Woche an." ), "apiKey": os.environ["AI_API_KEY"], "voice": "Aoede", "language": "de-DE", "maxSeconds": 180, "retries": 2, "retryDelayMinutes": 10, } r = requests.post( "https://phone.ihrtenant.de/api.ashx/v1/calls", headers={"Authorization": f"Bearer {TOKEN}"}, json=payload, timeout=15, ) r.raise_for_status() call_id = r.json()["callId"] print("angelegt", call_id)
const token = process.env.CODEB_TOKEN; const apiKey = process.env.AI_API_KEY; const r = await fetch("https://phone.ihrtenant.de/api.ashx/v1/calls", { method: "POST", headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json", }, body: JSON.stringify({ phone: "+4915157610183", displayName: "Erinnerung", email: "ops@ihrunternehmen.example", systemPrompt: "Du rufst Alex an, um den Zahnarzttermin morgen um 14:00 zu bestaetigen. Sei kurz und freundlich. Wenn JA, verabschiede dich. Wenn NEIN, biete denselben Termin naechste Woche an.", apiKey, voice: "Aoede", language: "de-DE", maxSeconds: 180, retries: 2, retryDelayMinutes: 10, }), }); if (!r.ok) throw new Error(`POST ${r.status}: ${await r.text()}`); const { callId } = await r.json(); console.log("angelegt", callId);
Antwort
{
"ok": true,
"callId": "oac-0123456789ab",
"tenant": "phone.ihrtenant.de",
"whitelistAdded": true,
"whitelistError": null,
"bridgeReply": "{...}"
}
Speichern Sie die callId auf Ihrem Datensatz. Sie erscheint im Webhook, in
GET /v1/calls/{id} und im Transkript-Dateinamen
(outbound-ai-YYYYMMDD-HHMMSS-<callId>.txt) wieder.
Schritt 3 — Webhook empfangen
Sekunden nach Anrufende erhält Ihre abonnierte Webhook-URL zwei HMAC-signierte POSTs:
outbound-ai.finished mit dem Ergebnis und (falls Aufzeichnung oder Transkription
aktiviert war) transcript.saved mit dem Pfad. Prüfen Sie die Signatur immer
zuerst.
X-CodeB-Signature prüfen
import hmac, hashlib, os from flask import Flask, request, abort WEBHOOK_SECRET = os.environ["CODEB_WEBHOOK_SECRET"].encode() app = Flask(__name__) def verify(raw_body: bytes, sig_header: str) -> bool: expected = hmac.new(WEBHOOK_SECRET, raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, (sig_header or "").strip()) @app.post("/codeb/webhook") def hook(): raw = request.get_data() # Rohbytes, NICHT json() sig = request.headers.get("X-CodeB-Signature", "") if not verify(raw, sig): abort(401) evt = request.get_json() # jetzt sicher parsen if evt["type"] == "outbound-ai.finished": update_crm(evt["data"]["callId"], evt["data"]["status"]) return ("", 204)
import express from "express"; import crypto from "node:crypto"; const SECRET = process.env.CODEB_WEBHOOK_SECRET; const app = express(); // Rohbody nötig für HMAC; vor json() parsen abfangen app.use("/codeb/webhook", express.raw({ type: "application/json" })); app.post("/codeb/webhook", (req, res) => { const sig = (req.headers["x-codeb-signature"] || "").toString().trim(); const expected = crypto.createHmac("sha256", SECRET).update(req.body).digest("hex"); if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) { return res.status(401).end(); } const evt = JSON.parse(req.body.toString()); if (evt.type === "outbound-ai.finished") { updateCrm(evt.data.callId, evt.data.status); } res.status(204).end(); });
So sieht der Webhook-Body aus
{
"type": "outbound-ai.finished",
"tenant": "phone.ihrtenant.de",
"timestamp": "2026-06-23T12:02:31.000Z",
"data": {
"callId": "oac-0123456789ab",
"phone": "+4915157610183",
"displayName": "Erinnerung",
"status": "ended-success",
"endedReason": "finished",
"durationSec": 145,
"dispatchedAtUtc": "2026-06-23T12:00:00.000Z",
"answeredAtUtc": "2026-06-23T12:00:05.500Z",
"endedAtUtc": "2026-06-23T12:02:30.000Z",
"transcriptPath": "outbound-ai-20260623-120000-oac-0123456789ab.txt",
"attempt": 1,
"retriesLeft": 1
}
}
GET /api.ashx/v1/calls/{id}, bis status einer von
ended-success, ended-failed-final oder cancelled ist.
Günstig, keine Infrastruktur, kein Signatur-Tanz.
Schritt 4 — Transkript abrufen
Für Analytik, Follow-up-Generierung oder Compliance-Archivierung holen Sie das Transkript
nach Eintreffen des Webhooks. Die Plattform produziert pro Anruf zwei Artefakte: ein
Klartext-Transkript (.txt) und eine strukturierte JSON-Sidecar (.json)
mit Zeitstempeln, Rollen und Tool-Call-Records pro Sprechakt.
curl -H "Authorization: Bearer $TOKEN" \ "https://phone.ihrtenant.de/api.ashx/v1/transcripts/outbound-ai-20260623-120000-oac-0123456789ab.txt"
Beispiel-Transkript (Klartext, eine Zeile pro Sprechakt):
[12:00:06] KI: Hallo, hier ist Ihr Erinnerungsdienst. Spreche ich mit Alex?
[12:00:09] USER: Ja, hier Alex.
[12:00:11] KI: Kurze Erinnerung an Ihren Zahnarzttermin morgen um 14:00 Uhr.
Schaffen Sie den Termin?
[12:00:17] USER: Ja, ich bin da.
[12:00:19] KI: Wunderbar. Schönen Tag noch. Auf Wiederhören.
Wollen Sie auch Aufzeichnungen? Aktivieren Sie pro VNum oder pro Trunk die Audio-Aufzeichnung
im Admin — eine Stereo-WAV landet in
App_Data/<tenant>/recordings/YYYY/MM/DD/ mit einem JSON-Sidecar gleicher
Metadaten. Volle Endpoint-Referenz auf der
REST-API-Seite.
Häufige Muster
Terminerinnerungen
Ihr Buchungstool feuert den POST 24h vor jeder Buchung. KI bestätigt oder verschiebt. Ergebnis landet via Webhook wieder im Buchungsdatensatz.
Lead-Qualifizierung
Marketing-Automation feuert den POST Sekunden nach Formular-Submit. KI begrüsst, qualifiziert nach Skript und leitet (mit Einwilligung) an Vertrieb weiter.
Zahlungserinnerungen / Mahnungen
Buchhaltung bündelt morgens überfällige Forderungen, postet pro Schuldner einen Anruf. Tonlage über System-Prompt steuerbar. Transkript für den Audit-Trail archiviert.
Service-Follow-up-Umfragen
Ticketing-Tool postet eine Stunde nach Ticket-Close. KI stellt drei strukturierte Fragen. JSON-Transkript speist Ihr NPS-Dashboard.
Interne Mitarbeiterbenachrichtigung
On-Call / Outage-Tooling postet an die Nummer des Engineers. KI liest den Alert vor, bestätigt Acknowledgement, beendet. Günstig, sprachneutral, keine App nötig.
Mehrsprachige Kampagnen
Sprache pro Anruf wählen (de-DE, en-US, fr-FR, es-ES, …). Gleicher Endpoint, gleicher Flow, Voice + Prompt wechseln automatisch.
Hotellerie — Sensoren triggern Anrufe
Lärmmesser oder Rauchmelder in der Kurzzeit-Wohnung schlägt aus → Sensor-Plattform POSTet an Ihren Webhook → KI-Voice-Agent ruft den Gast in Sekunden an, nennt die Wohnung, zitiert den Schwellwert, bittet um Handlung. Praxisbeispiel auf hospitality.html.
Abbrechen, auflisten, inspizieren
Drei weitere Endpoints runden den Lebenszyklus ab:
| Verb + Pfad | Was es tut |
|---|---|
GET /api.ashx/v1/calls | Aktive + jüngste Anrufe listen. Filter nach status: scheduled,in-flight,ended-success usw. Paginierung mit limit + offset. |
GET /api.ashx/v1/calls/{id} | Ein Anruf — Status, Ergebnis, Transkript-Pfad, Versuchszähler, Retry-Budget. |
POST /api.ashx/v1/calls/{id}/hangup | Geplanten Anruf abbrechen oder laufenden beenden. Idempotent. Verwenden, wenn der Kontakt sich anderweitig gemeldet hat, bevor die KI angewählt hat. |
Volle Request/Response-Form mit jedem Feld in der REST-API-Referenz.
Sicherheit & Compliance
- Bearer-Auth auf jedem Aufruf. OIDC-Access-Tokens oder
ak_-API-Schlüssel. Keine anonymen Endpoints, kein IP-Allowlisting für die REST-Oberfläche nötig. - HMAC-signierte Webhooks.
X-CodeB-Signatureist HMAC-SHA256 des Rohbodys mit Ihrem Subscription-Secret. Vor dem Parsen prüfen. Secret jederzeit im Admin rotierbar. - Mandantenisolation. Jeder API-Key, jedes Token, jedes Transkript, jede Aufzeichnung und jeder CDR ist auf den ausstellenden Tenant-Host gescoped. Cross-Tenant-Reads werden im Dispatcher geblockt.
- EU-gehostet, selbst-hostbar. Auf eigener Infrastruktur oder auf einem EU-Region-Tenant. Kein US-Transfer für Signalisierung, Medien, Transkripte oder Aufzeichnungen.
- Einwilligung des Empfängers. Ausgehende KI-Anrufe sind in den meisten Jurisdiktionen reguliert (DSGVO / ePrivacy in der EU, TCPA in den USA, lokale Vorschriften). Die Plattform tätigt den Anruf, den Sie verlangen; Einwilligungs-Erhebung liegt bei Ihnen.
- Auditierbar. Jeder Anruf hinterlässt einen CDR (Caller / Callee / Trunk / Ergebnis / Dauer) und ein optionales Transkript + Aufzeichnung für die Compliance-Aufbewahrung.
Bereit, Outbound-KI in einem Nachmittag zu liefern?
Kostenlosen CodeB-Tenant anlegen, Sprach-Engine-API-Key einfügen, den ersten POST feuern. Volle Referenz + jeder Endpoint auf dem API-Hub.