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.
Häufige Fragen
Muss ich SIP, WebRTC oder Medienverarbeitung selbst einrichten?
Nein. Ihr Code spricht immer nur HTTPS mit einem Host (Ihrem CodeB-Tenant). Die Plattform tätigt den SIP-Anruf, hängt die KI-Session an, transcodiert Audio, kümmert sich um ICE und räumt beim Hangup auf. Sie senden einen JSON-POST und (optional) erhalten einen signierten Webhook.
Kann ich Anrufe in die Zukunft planen oder nur sofort wählen?
Beides. Ohne scheduleAtUtc wählt die Plattform sofort. Mit ISO-8601-UTC-Zeitstempel reiht sie den Anruf ein und feuert ihn zu diesem Zeitpunkt. In beiden Fällen genau ein POST aus Ihrem Code.
Wie authentifiziere ich mich an der API?
Zwei Möglichkeiten, beide Bearer-Tokens. Entweder ein kurzlebiges OIDC-Access-Token von /oidc.ashx/token oder ein langlebiger ak_-API-Schlüssel, einmal im Tenant-Admin angelegt. Derselbe Authorization-Header auf jedem Endpoint.
Wie verifiziere ich, dass ein Webhook wirklich von CodeB stammt?
Der X-CodeB-Signature-Header ist HMAC-SHA256 des Roh-Request-Bodys mit dem Shared-Secret Ihrer Subscription. Berechnen Sie auf Ihrer Seite denselben HMAC und vergleichen Sie zeitkonstant, bevor Sie den Body parsen.
Was passiert mit meinem Sprach-Engine-API-Key?
Er wird für die Dauer des einen Anrufs verwendet, mit dem Sie ihn übergeben haben, und dann vergessen. Die Plattform speichert ihn nicht über den Anruf hinaus und schreibt ihn in keinen persistenten Log.
Wie steht es mit TCPA / DSGVO / ePrivacy-Compliance?
Die Plattform ist Transport: sie tätigt den Anruf, den Sie verlangen, und liefert einen auditierbaren Datensatz (CDR + optional Transkript + optional signierte Aufzeichnung). Einwilligung erfassen und Opt-outs respektieren liegt beim Anrufer.