Postcall Webhook

Empfangen Sie Transkripte und Analysedaten nach jedem Anruf

Postcall Webhook – Dokumentation

Nach jedem Anruf sendet VOISA automatisch eine HTTP-POST-Anfrage mit dem Transkript und den Analysedaten an Ihre konfigurierte Webhook-URL.

Einrichtung

  1. Öffnen Sie die Agent-Einstellungen unter Erweitert > Postcall Webhook
  2. Geben Sie Ihre HTTPS-URL ein (z.B. https://ihre-domain.de/api/voisa-webhook)
  3. Speichern Sie den Agenten
  4. Kopieren Sie das angezeigte Webhook Secret – Sie benötigen es zur Signaturprüfung

Anforderungen an die URL

  • Muss HTTPS verwenden
  • Darf nicht localhost oder eine interne IP-Adresse sein
  • Darf nicht auf *.voisa.ai zeigen

Payload

Jeder Webhook-Aufruf ist eine POST-Anfrage mit Content-Type: application/json.

{
  "type": "post_call_transcription",
  "event_timestamp": 1739537297,
  "data": {
    "agent_id": "agent_abc123",
    "conversation_id": "conv_xyz789",
    "transcript": [
      {
        "role": "agent",
        "message": "Hallo, vielen Dank für Ihren Anruf. Wie kann ich Ihnen helfen?",
        "time_in_call_secs": 0
      },
      {
        "role": "user",
        "message": "Ich möchte einen Termin vereinbaren.",
        "time_in_call_secs": 3
      },
      {
        "role": "agent",
        "message": "Gerne! Für wann hätten Sie es denn gerne?",
        "time_in_call_secs": 5
      }
    ],
    "metadata": {
      "start_time_unix_secs": 1739537200,
      "call_duration_secs": 45
    },
    "analysis": {
      "call_successful": "true",
      "transcript_summary": "Der Anrufer hat einen Termin für Freitag um 14 Uhr vereinbart.",
      "evaluation_criteria_results": {},
      "data_collection_results": {}
    },
    "conversation_initiation_client_data": {
      "dynamic_variables": {
        "caller_name": "Max Mustermann",
        "caller_number": "+4917612345678"
      }
    }
  },
  "voisa": {
    "forwarded_at": "2026-03-04T15:30:00.000Z",
    "agent_name": "Praxis Dr. Müller"
  }
}

Felder

FeldTypBeschreibung
typestringImmer "post_call_transcription"
event_timestampnumberUnix-Timestamp des Events
data.agent_idstringID des VOISA-Agenten
data.conversation_idstringEindeutige ID des Gesprächs
data.transcriptarrayListe der Gesprächsnachrichten
data.transcript[].rolestring"agent" oder "user"
data.transcript[].messagestringGesprochener Text
data.transcript[].time_in_call_secsnumberSekunden seit Gesprächsbeginn
data.metadata.start_time_unix_secsnumberUnix-Timestamp des Gesprächsstarts
data.metadata.call_duration_secsnumberGesamtdauer in Sekunden
data.analysis.call_successfulstring"true", "false" oder "unknown"
data.analysis.transcript_summarystringKI-generierte Zusammenfassung
data.analysis.evaluation_criteria_resultsobjectErgebnisse der Bewertungskriterien (falls konfiguriert)
data.analysis.data_collection_resultsobjectErfasste Daten (falls konfiguriert)
data.conversation_initiation_client_data.dynamic_variablesobjectDynamische Variablen, die beim Anrufstart übergeben wurden
voisa.forwarded_atstringISO-8601 Zeitstempel der Weiterleitung
voisa.agent_namestringName des Agenten

Hinweis: Audio-URLs, Kosten und interne Metadaten werden aus Datenschutzgründen nicht weitergeleitet.


Signaturprüfung

Jede Anfrage enthält einen X-Voisa-Signature Header zur Verifizierung:

X-Voisa-Signature: t=1709537300,v0=5a2f...c3e1
  • t – Unix-Timestamp der Signaturerstellung
  • v0 – HMAC-SHA256 Signatur

Signatur berechnen

Die Signatur wird über den String <timestamp>.<body> mit Ihrem Webhook Secret berechnet:

HMAC-SHA256(secret, "<timestamp>.<json-body>")

Beispiel (Node.js)

const crypto = require('crypto');

function verifyWebhook(req, secret) {
  const signature = req.headers['x-voisa-signature'];
  if (!signature) return false;

  // Header parsen
  const parts = {};
  signature.split(',').forEach(part => {
    const [key, value] = part.split('=');
    parts[key] = value;
  });

  const timestamp = parts['t'];
  const receivedHmac = parts['v0'];

  // Eigene Signatur berechnen
  const body = JSON.stringify(req.body); // oder raw body als String
  const hmac = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${body}`)
    .digest('hex');

  // Vergleichen (timing-safe)
  return crypto.timingSafeEqual(
    Buffer.from(hmac, 'hex'),
    Buffer.from(receivedHmac, 'hex')
  );
}

Beispiel (Python)

import hmac
import hashlib

def verify_webhook(headers, body, secret):
    signature = headers.get('X-Voisa-Signature', '')
    if not signature:
        return False

    parts = dict(p.split('=', 1) for p in signature.split(','))
    timestamp = parts.get('t', '')
    received_hmac = parts.get('v0', '')

    expected = hmac.new(
        secret.encode(),
        f"{timestamp}.{body}".encode(),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(expected, received_hmac)

Antwort

Ihr Endpoint sollte mit einem HTTP 200 Status antworten. Der Response-Body wird ignoriert.

Bei fehlgeschlagenen Anfragen (Timeout, 5xx) wird kein automatischer Retry durchgeführt.


FAQ

Kann ich den Webhook für mehrere Agenten nutzen? Ja, jeder Agent kann seine eigene URL haben, oder Sie verwenden dieselbe URL und unterscheiden anhand der data.agent_id.

Welche Daten werden nicht weitergeleitet? Audio-Aufnahmen, Audio-URLs, Kostenaufstellungen und interne Metadaten werden aus Datenschutz- und Sicherheitsgründen entfernt.

Was passiert, wenn mein Endpoint nicht erreichbar ist? Der Webhook-Aufruf wird einmalig versucht. Es gibt keinen automatischen Retry. Wir empfehlen, eingehende Webhooks in eine Queue zu schreiben und asynchron zu verarbeiten.

Kann ich die URL nachträglich ändern? Ja, ändern Sie einfach die URL in den Agent-Einstellungen. Das Secret bleibt gleich.

Was passiert, wenn ich die URL entferne? Der Webhook wird vollständig deaktiviert und die zugehörige Konfiguration wird automatisch bereinigt.