Werkzeugbausatz für eigene Anbindungen
Klient, Signaturprüfung und Stromleser in Node, PHP und Python: die Schnittstelle ansprechen, Webhooks prüfen und Aufrufe von XICBOT an Ihr System beantworten.
In einfacher Sprache
Diese Seite ist für Entwickler. Der Bausatz ist Programmcode für Ihre eigene Anbindung. Es gibt ihn in drei Programmiersprachen. Mit ihm sprechen Sie die Schnittstelle an. Sie können damit eine Antwort lesen, während sie entsteht. Sie können prüfen, ob eine Meldung an Ihr System echt ist. Die Seite erklärt auch den umgekehrten Weg. Dann ruft der Assistent Ihr System auf.
Alle Themen der Dokumentation
Der Werkzeugbausatz ist Quelltext für Ihre eigene Anbindung an XICBOT, in drei Fassungen: Node (js/), PHP (php/) und Python (python/) mit je vier Dateien. Er enthält einen Klienten für die Schnittstelle mit API-Schlüssel, einen Leser für den Ereignisstrom einer Chat-Antwort, die Prüfung der Signatur eines Webhooks und die Listen der Ereignisarten. Er braucht keine weiteren Pakete: Node ruft über fetch, PHP über die curl-Erweiterung, Python über die Standardbibliothek. Der letzte Abschnitt beschreibt die Gegenrichtung: was Ihr System beantworten muss, wenn ein Assistent es als Werkzeug aufruft.
Was im Bausatz liegt
| Aufgabe | Node (js/) | PHP (php/) | Python (python/) |
|---|---|---|---|
| Klient für die Schnittstelle | klient.mjs: Klasse Klient | klient.php: Klasse XicbotKlient | klient.py: Klasse Klient |
| Leser für den Ereignisstrom | strom.mjs: Klasse Strom | strom.php: Klasse XicbotStrom | strom.py: Klasse Strom |
| Prüfung der Webhook-Signatur | signatur.mjs: signaturPruefen() | signatur.php: xicbot_signatur_pruefen() | signatur.py: signatur_pruefen() |
| Name des Signaturkopfs | SIGNATURKOPF | XICBOT_SIGNATURKOPF | SIGNATURKOPF |
| Ereignisarten | ereignisse.mjs: STROM_EREIGNISSE, RUECKRUF_EREIGNISSE | ereignisse.php: XICBOT_STROM_EREIGNISSE, XICBOT_RUECKRUF_EREIGNISSE | ereignisse.py: STROM_EREIGNISSE, RUECKRUF_EREIGNISSE |
| Aufruf über | fetch, Signatur mit node:crypto | curl-Erweiterung | urllib.request, Signatur mit hmac |
Jede Datei nennt im Kopf die Fassung der Schnittstellenbeschreibung, aus der sie erzeugt ist; daran sehen Sie, welchen Stand der Schnittstelle Ihre Kopie abbildet. Der Klient lädt den Stromleser aus demselben Verzeichnis (./strom.mjs, strom.php, from strom import Strom); legen Sie die Dateien einer Sprache darum gemeinsam ab. Die Signaturprüfung und die Ereignislisten stehen für sich und lassen sich einzeln übernehmen, etwa in einen Empfänger, der nur Webhooks annimmt.
Adresse und Schlüssel
Der Klient bekommt zwei Angaben: die Basisadresse https://app.xicbot.de und einen API-Schlüssel. Schrägstriche am Ende der Basisadresse werden entfernt, die Pfade (/api/v1/integration/…) hängt der Klient selbst an. Fehlt die Basisadresse, brechen alle drei Fassungen schon beim Anlegen mit Basisadresse fehlt ab. Den Schlüssel sendet der Klient im Kopf X-Api-Key. Er beginnt mit xbot_; ein Wert ohne diesen Anfang gilt als fehlend.
| Status | Feld error | Bedeutung |
|---|---|---|
| 400 | key_in_url | Schlüssel als Parameter in der Adresse |
| 401 | key_missing | Kopf X-Api-Key fehlt, ist leer oder beginnt nicht mit xbot_ |
| 401 | invalid_key | Schlüssel unbekannt, zurückgezogen oder abgelaufen; die drei Fälle antworten gleich |
| 402 | plan_feature_missing | Das Paket des Kontos enthält die Funktion für Schlüssel und Webhooks nicht, etwa nach einem Tarifwechsel; available_in nennt die Pakete, die sie enthalten |
| 403 | tenant_suspended | Das Konto zum Schlüssel ist nicht aktiv |
| 403 | scope_missing | Dem Schlüssel fehlt der nötige Bereich, etwa leads oder chat; das Feld required nennt ihn |
| 403 | read_only | Dem Schlüssel fehlt der schreibende Bereich (leads.write, knowledge.write); required nennt ihn |
| 429 | too_many_requests | Minuten- oder Tagesdeckel erreicht, siehe Grenzen |
Schlüssel anlegen
-
Schritt 1
Seite öffnen
Schlüssel und Webhooks liegen unter Verwaltung › Schnittstellen (app.xicbot.de/schnittstellen). Der Menüpunkt erscheint für die Rollen Verwaltung und Inhaber; nur diese Rollen legen Schlüssel an und ziehen sie zurück. Die Rollen beschreibt Team, Rollen und Protokoll.
-
Schritt 2
Name und Bereiche wählen
Im Abschnitt API-Schlüssel geben Sie einen Name mit höchstens 80 Zeichen ein und kreuzen unter Bereiche mindestens einen Bereich an; vorher bleibt Neuen Schlüssel anlegen gesperrt. Unter den Bereichen steht: „Schreiben ist ein eigener Bereich. Ohne ihn liest ein Schlüssel nur.“
-
Schritt 3
Grenze und Ablauf setzen
Anfragen pro Minute ist ein Pflichtfeld von 1 bis 6000, vorbelegt mit 120. Läuft ab in Tagen nimmt 1 bis 3650 an; darunter steht „Leer bedeutet ohne Ablauf.“ Das Ablaufdatum wird beim Anlegen aus der Zahl der Tage berechnet.
-
Schritt 4
Schlüssel übernehmen
Nach Neuen Schlüssel anlegen erscheint die Karte Ihr neuer Schlüssel mit dem Wert:
xbot_und 48 Zeichen. Darunter steht „Jetzt kopieren. Nach dem Schließen ist er nicht wieder abrufbar.“ Der Knopf Schlüssel kopieren legt ihn in die Zwischenablage; geht das nicht, erscheint „Die Zwischenablage ist hier nicht verfügbar. Markieren Sie den Wert und kopieren Sie ihn von Hand.“ Vom Schlüssel selbst speichert XICBOT nur einen SHA-256-Abdruck und die Kennung, die ersten 12 Zeichen.
Die Liste der Schlüssel zeigt Name, Kennung, Bereiche, Zuletzt benutzt, Läuft ab (bei Schlüsseln ohne Ablauf ohne Frist), Status mit gültig, abgelaufen oder zurückgezogen und Aktion. Zuletzt benutzt wird höchstens einmal je Minute nachgetragen. Neben dem Knopf zum Anlegen steht der Zähler der gültigen Schlüssel, etwa 3 / 10. Höchstens zehn Schlüssel sind gleichzeitig gültig; der elfte wird mit 422, too_many_keys und der Meldung „Die Höchstzahl gleichzeitiger Schlüssel ist erreicht.“ abgelehnt. Zurückgezogene und abgelaufene Schlüssel zählen nicht mit.
Zurückziehen fragt in der Zeile nach: „Schlüssel zurückziehen? Anfragen mit diesem Schlüssel werden danach abgewiesen.“ Bestätigen Sie mit Zurückziehen oder brechen Sie mit Abbrechen ab. Danach antwortet die Schnittstelle auf diesen Schlüssel mit 401 und invalid_key; die Zeile bleibt mit dem Status zurückgezogen in der Liste. Einen Schlüssel ändern können Sie nicht. Für andere Bereiche oder eine andere Grenze legen Sie einen neuen an, stellen Ihr System um und ziehen den alten zurück. Mehr zur Verwaltung steht unter API-Schlüssel und Webhooks.
Bereiche
| Bereich | In der App | Erlaubt |
|---|---|---|
conversations | Gespräche lesen | Gespräche auflisten und ein Gespräch mit seinen Nachrichten im Klartext lesen |
leads | Anfragen lesen | Anfragen auflisten |
leads.write | Anfragen anlegen | Eine Anfrage anlegen |
appointments | Termine lesen | Termine auflisten |
analytics | Auswertung lesen | Kennzahlen für 7, 30 oder 90 Tage abrufen |
knowledge.write | Wissensquellen anlegen | Eine Wissensquelle aus Text oder Adresse anlegen |
chat | Chat verwenden | Einem Assistenten eine Frage stellen und die Antwort als Strom lesen |
| ohne | – | GET /api/v1/integration/status beantwortet jeder gültige Schlüssel |
Ein schreibender Bereich schließt den lesenden nicht ein: leads.write legt Anfragen an, liest sie aber nicht. GET /api/v1/integration/status liefert alive, die Kennung des Kontos in tenant, die Bereiche des Schlüssels in scopes und seine Minutengrenze in limit_per_min. So prüfen Sie eine Anbindung, ohne Daten zu lesen. Die einzelnen Vorgänge beschreibt Schnittstelle: Integration.
Grenzen
Jeder Schlüssel hat einen eigenen Deckel je Minute, den Wert aus Anfragen pro Minute. Dazu kommt ein Tagesdeckel aus Ihrem Konto. Er wird je Schlüssel und Kalendertag gezählt und verfällt um Mitternacht, nicht 24 Stunden nach der ersten Anfrage; ein Tagesdeckel von 0 bedeutet: kein Tagesdeckel. Beide Deckel werden geprüft, bevor einer von beiden zählt, eine am Deckel abgewiesene Anfrage verbraucht also nichts. Auch GET /api/v1/integration/status zählt mit. Die Höhe des Tagesdeckels nennt eine 429-Antwort mit scope day im Feld limit; GET /api/v1/integration/status meldet nur den Minutendeckel als limit_per_min.
| Feld der Antwort 429 | Inhalt |
|---|---|
error | too_many_requests |
scope | minute oder day: welcher Deckel gegriffen hat |
limit | Wert dieses Deckels |
retry_after | Sekunden bis zum nächsten sinnvollen Versuch; derselbe Wert steht im Kopf Retry-After |
limit_per_min | Minutendeckel des Schlüssels, auch wenn der Tagesdeckel gegriffen hat |
wait | gleicher Wert wie retry_after |
Der Klient wirft bei 429 einen Fehler mit Status und Text der Antwort; ein Wiederholen nach retry_after bauen Sie selbst. Das Kontingent an Antworten ist davon getrennt: ist es für den Monat verbraucht, antwortet der Chat mit 429 und quota_exhausted, siehe Chat und Ereignisstrom. Übersicht aller Grenzen: Schnittstelle: Paketsperren und Drosseln.
Der Klient
Der Klient bildet jeden Vorgang der Schnittstelle als eigene Methode ab, benannt nach Verfahren und Pfad. Sie legen ihn mit Basisadresse, Schlüssel und Frist an; die Frist begrenzt jeden Aufruf, dessen Antwort in einem Stück kommt.
| Angabe | Node | PHP | Python |
|---|---|---|---|
| Anlegen | new Klient(basis, schluessel, frist) | new XicbotKlient($basis, $schluessel, $frist) | Klient(basis, schluessel, frist, stream_timeout) |
| Voreinstellung der Frist | 15000 Millisekunden | 15 Sekunden | 15 Sekunden |
| Lesefrist im Chat | keine | keine | stream_timeout, voreingestellt 120 Sekunden je Lesevorgang |
| Leere Basisadresse | Error mit Basisadresse fehlt | InvalidArgumentException mit Basisadresse fehlt | ValueError mit Basisadresse fehlt |
| Schreibweise der Methoden | getStatus(), postLeads(daten) | getStatus(), postLeads($daten) | get_status(), post_leads(daten) |
| Rückgabe (außer Chat) | Promise mit dem gelesenen JSON | Array | Dictionary |
Für den Chat setzen die Node- und die PHP-Fassung keine Frist: der Strom läuft, solange die Antwort entsteht. Die Python-Fassung liest den Strom Zeile für Zeile und bricht ab, wenn ein einzelner Lesevorgang länger als stream_timeout auf Daten wartet, voreingestellt 120 Sekunden. Jede Anfrage trägt Content-Type: application/json, Accept: application/json und den Schlüssel in X-Api-Key; beim Chat bittet der Klient um text/event-stream.
| Methode (Node, PHP) | Methode (Python) | Vorgang | Bereich | Pflichtfelder im Klienten |
|---|---|---|---|---|
getStatus() | get_status() | GET /api/v1/integration/status | ohne | – |
getConversations(page) | get_conversations(page) | GET /api/v1/integration/conversations | conversations | – |
getConversationsId(id) | get_conversations_id(id) | GET /api/v1/integration/conversations/{id} | conversations | – |
getLeads(page) | get_leads(page) | GET /api/v1/integration/leads | leads | – |
postLeads(daten) | post_leads(daten) | POST /api/v1/integration/leads | leads.write | subject |
getAppointments(page) | get_appointments(page) | GET /api/v1/integration/appointments | appointments | – |
getAnalytics(period, zone) | get_analytics(period, zone) | GET /api/v1/integration/analytics | analytics | – |
postKnowledgeSources(daten) | post_knowledge_sources(daten) | POST /api/v1/integration/knowledge/sources | knowledge.write | title; text oder url |
postChat(daten, leser) | post_chat(daten, leser) | POST /api/v1/integration/chat | chat | assistant_id, text |
Fehlt ein Pflichtfeld, bricht der Klient vor dem Senden ab, mit der Meldung Pflichtfeld fehlt: <Feld> als Error (Node), InvalidArgumentException (PHP) oder ValueError (Python). Beim Anlegen einer Wissensquelle verlangt er außerdem text oder url und meldet sonst Eines der Felder fehlt: text oder url. Alle übrigen Regeln prüft XICBOT und antwortet bei einem Verstoß mit 422. Gespräche und Anfragen kommen neueste zuerst, Termine nach Beginn, der späteste zuerst. Die drei Listen liefern 50 Einträge je Aufruf; page wählt die Seite, gezählt ab 1, ein fehlender oder kleinerer Wert gilt als 1. Lesen Sie weiter, bis eine Seite weniger als 50 Einträge hat. getAnalytics nimmt mit zone eine Zeitzone nach IANA, Vorgabe Europe/Berlin; eine unbekannte Zone beantwortet XICBOT mit 422 und unknown_timezone. Ein einzelnes Gespräch liefert bis zu 200 Nachrichten in zeitlicher Folge mit role, text und time; eine unbekannte Kennung beantwortet XICBOT mit 404 und not_found.
Was XICBOT prüft
| Vorgang | Feld | Regel |
|---|---|---|
| Anfrage anlegen | subject | Pflicht, höchstens 1000 Zeichen |
| Anfrage anlegen | name | höchstens 120 Zeichen |
| Anfrage anlegen | email | gültige E-Mail-Adresse, höchstens 190 Zeichen |
| Anfrage anlegen | phone | höchstens 60 Zeichen |
| Anfrage anlegen | source | höchstens 60 Zeichen; ohne Angabe api |
| Anfrage anlegen | external_id | höchstens 100 Zeichen; verhindert doppelte Einträge |
| Wissensquelle anlegen | title | Pflicht, höchstens 200 Zeichen |
| Wissensquelle anlegen | text | Pflicht, wenn url fehlt; höchstens 400000 Zeichen |
| Wissensquelle anlegen | url | Pflicht, wenn text fehlt; gültige Adresse, höchstens 500 Zeichen |
| Wissensquelle anlegen | draft, published_at | nur zusammen mit text; draft ist wahr oder falsch, published_at ein Datum |
| Auswertung | period | 7d, 30d oder 90d; ohne Angabe 30d |
| Chat | assistant_id | Pflicht, höchstens 64 Zeichen |
| Chat | conversation_id | höchstens 64 Zeichen; setzt ein Gespräch fort |
| Chat | text | Pflicht, 1 bis 4000 Zeichen; das Konto kann eine kürzere Grenze setzen |
Eine neue Anfrage beantwortet XICBOT mit 201, dem Eintrag unter lead und duplicate_prevented: false; zugleich geht der Webhook lead.created hinaus. Kommt dieselbe external_id ein zweites Mal, entsteht kein zweiter Eintrag: die Antwort ist 200 mit dem vorhandenen Eintrag und duplicate_prevented: true. Senden Sie deshalb nach einer Zeitüberschreitung mit derselben external_id erneut. Wie Anfragen in der App weiterlaufen, steht unter Termine und Vorgänge.
Eine Wissensquelle mit text nimmt XICBOT sofort auf und antwortet mit 201; unter source stehen id, type (text), status und chunks; dazu geht knowledge.indexed oder knowledge.failed hinaus. Die 201 kommt auch dann, wenn die Aufnahme gescheitert ist – ob sie gelungen ist, sagt status (ready). Eine Quelle mit url legt XICBOT mit 202 an, unter source mit id, type website und status new, und holt die Seite im Hintergrund ab. Wie beim Anlegen in der App reiht XICBOT für beide Arten zusätzlich einen Hintergrundauftrag ein, der die Abschnitte anreichert und ihre Vektoren bildet; danach findet die Suche die Quelle in der Regel auch nach der Bedeutung und nicht nur über gleiche Wörter. Adressen, die nicht ins offene Netz zeigen, weist XICBOT mit 422 ab und nennt den Grund in error, etwa target_not_allowed. Mehr zu Quellen steht unter Wissen pflegen.
Fehler im Klienten
| Fall | Node | PHP | Python |
|---|---|---|---|
| Status 400 oder höher | Error: Antwort <Status>: <Text> | RuntimeException: Antwort <Status>: <Text> | RuntimeError: Antwort <Status>: <Text> |
| Keine Verbindung oder Frist abgelaufen | Fehler von fetch, bei der Frist ein TimeoutError | RuntimeException: Aufruf fehlgeschlagen: … | RuntimeError: Aufruf fehlgeschlagen: … |
| Antwort ist kein JSON | leeres Objekt {} | leeres Array [] | leeres Dictionary {} |
| Chat mit Status 400 oder höher | Error: Antwort <Status>: <Text> | RuntimeException: Antwort <Status>: <Text> | RuntimeError: Antwort <Status>: <Text> |
| Chat-Übertragung abgebrochen | Fehler des Stroms; ohne Körper Antwort ohne Strom | RuntimeException: Strom abgebrochen: … | RuntimeError: Strom abgebrochen: …, auch wenn die Lesefrist abläuft |
Der Text nach dem Status ist der Körper der Fehlerantwort, bei XICBOT ein JSON mit error. Die Bedeutung der Werte steht in der Tabelle unter Adresse und Schlüssel und beim Chat.
Chat und Ereignisstrom
POST /api/v1/integration/chat
Stellt einem Assistenten eine Frage und liefert die Antwort als Ereignisstrom (text/event-stream). Ohne conversation_id beginnt ein neues Gespräch; mit ihr geht ein Gespräch weiter, das derselbe Assistent über die Schnittstelle begonnen hat.
- Anmeldung
- Kopf
X-Api-Key, Bereichchat - Begrenzung
- Minuten- und Tagesdeckel des Schlüssels, dazu das Monatskontingent des Kontos
Jedes Ereignis besteht aus einer Zeile event: <Art>, einer oder mehreren Zeilen data: <JSON> und einer Leerzeile. Der Stromleser des Bausatzes gibt ein Ereignis erst heraus, wenn die Leerzeile da ist, fügt mehrere data-Zeilen mit Zeilenumbruch zusammen, entfernt ein \r am Zeilenende und reicht Art und Daten an Ihre Funktion weiter. Ist der Inhalt kein gültiges JSON, kommen die Daten als null (Python: None) an.
| Status | Feld error | Bedeutung |
|---|---|---|
| 404 | not_found | Assistent unbekannt, oder conversation_id ist kein Schnittstellengespräch dieses Assistenten |
| 409 | reply_in_progress | Für dieses Gespräch entsteht gerade eine Antwort |
| 413 | input_too_long | Text länger als die Grenze des Kontos; limit und length nennen beide Werte. Ein neues Gespräch entsteht dabei nicht |
| 422 | no_version | Der Assistent hat noch keine veröffentlichte Fassung |
| 429 | quota_exhausted | Monatskontingent verbraucht; scope ist month, retry_after und der Kopf Retry-After nennen die Sekunden bis zum Monatswechsel. Ein Gespräch, das erst dieser Aufruf begonnen hat, entfernt XICBOT in der Regel wieder |
Ein Gespräch aus dem Widget oder aus einem Testgespräch lässt sich über die Schnittstelle nicht fortsetzen, auch nicht mit dem Bereich conversations: XICBOT antwortet dann mit 404. Wie viel das Konto im Monat beantworten darf, steht unter Pakete, Kontingente und Abrechnung.
Ereignisarten
| Art | Felder | Bedeutung |
|---|---|---|
start | conversation_id, question_id | Die Antwort beginnt; conversation_id brauchen Sie zum Fortsetzen |
text | t | Ein Stück Antworttext; die Stücke ergeben aneinandergereiht die Antwort |
redaction | text, reason, count (nur bei claims) | Ersetzt den bisher gesammelten Text durch text; reason ist claims (Berichtigung ungedeckter Aussagen) oder notice. Danach folgende text-Stücke hängen Sie an |
citations | citations | Die in der Antwort genutzten Belege |
verification | state, claims, total | Ergebnis der Prüfung gegen das Wissen |
enforcement | state, topics, handover | Ergebnis der Prüfung gegen die Assistentenregeln |
follow_ups | questions, dropped, checked, reason | Vorgeschlagene Anschlussfragen |
contact_offer | offered | Die Antwort bietet eine Kontaktaufnahme an |
handoff | handoff, reason | Übergabe an einen Menschen; reason ist etwa assistant, conversation_limit oder enforcement |
live | live, since | Ein Mensch hat das Gespräch übernommen; es folgt end mit live |
fallback | reason, transient | Die Antwort ist gescheitert; es folgt die hinterlegte Notfallantwort als text |
revoked | reason | Die Einwilligung wurde widerrufen (consent_revoked); der Text wird verworfen |
error | code, scope, retry_after, limit | Das Gespräch ist gesperrt oder am Deckel; bei conversation_limit folgen ein Hinweistext und handoff |
tool | Angaben zum Werkzeugaufruf | Ein Werkzeugaufruf des Assistenten |
model_switch | reason, from, to | Interner Wechsel während der Antwort, für Ihre Anbindung ohne Bedeutung |
end | state, message_id, duration_ms und weitere | Letztes Ereignis; state ist done, aborted, fallback, revoked, live oder blocked |
Der Hinweistext bei conversation_limit lautet auf Deutsch: „Dieses Gespräch hat die vereinbarte Länge erreicht. Ich übergebe an einen Menschen – bitte hinterlassen Sie Ihre Frage zusammen mit einer Kontaktmöglichkeit, oder beginnen Sie ein neues Gespräch.“ Ist das Gespräch aus einem anderen Grund gesperrt, folgt auf error die Notfallantwort des Assistenten und end mit blocked.
chat.mjs javascript
import { Klient } from './klient.mjs';
const klient = new Klient('https://app.xicbot.de', process.env.XICBOT_KEY);
let antwort = '';
await klient.postChat({ assistant_id: 'ASSISTENT_ID', text: 'Wann haben Sie geöffnet?' }, (art, daten) => {
if (art === 'start') console.log('Gespräch', daten.conversation_id);
else if (art === 'text') antwort += daten.t;
else if (art === 'redaction') antwort = daten.text;
else if (art === 'error') console.error('Gesperrt:', daten.code);
else if (art === 'end') console.log(antwort, daten.state);
// alle anderen Arten: überspringen
});anfrage.py python
import os
from klient import Klient
klient = Klient('https://app.xicbot.de', os.environ['XICBOT_KEY'])
ergebnis = klient.post_leads({
'subject': 'Rückruf zum Angebot',
'email': 'kunde@example.com',
'external_id': 'crm-4711',
})
# 201 beim ersten Mal, 200 mit duplicate_prevented bei Wiederholung
print(ergebnis['lead']['id'], ergebnis['duplicate_prevented'])Den Aufbau aller Vorgänge beschreibt Schnittstelle: Integration; wie Gespräche in der App erscheinen, steht unter Gespräche.
Webhooks prüfen
Ein Webhook schickt Ereignisse aus Ihrem Konto an eine Adresse Ihres Systems. Sie richten ihn auf derselben Seite ein wie die Schlüssel, unter Verwaltung › Schnittstellen im Abschnitt Webhooks: „Jede Sendung trägt eine Unterschrift im Kopf X-XICBOT-Signature. Fehlgeschlagene Sendungen werden wiederholt.“
-
Schritt 1
Adresse eintragen
Unter Zieladresse tragen Sie eine Adresse mit höchstens 2048 Zeichen ein. Sie muss ins offene Netz zeigen; sonst erscheint „Diese Adresse ist als Ziel nicht zugelassen.“
-
Schritt 2
Ereignisse wählen
Unter Ereignisse kreuzen Sie mindestens eines an. Webhook anlegen bleibt gesperrt, bis Adresse und ein Ereignis gesetzt sind.
-
Schritt 3
Geheimnis übernehmen
Nach dem Anlegen zeigt die Seite Ihr Signaturgeheimnis einmalig:
whsec_und 48 Zeichen, darunter „Damit prüfen Sie die Unterschrift der eingehenden Sendung.“ Mit Geheimnis kopieren übernehmen Sie es in Ihr System.
Jede Sendung ist ein POST mit einem JSON-Körper aus vier Feldern: event (die Ereignisart), at (Zeitpunkt des Ereignisses im ISO-8601-Format, in jeder Wiederholung derselbe), tenant (Kennung Ihres Kontos) und data (die Nutzdaten des Ereignisses). Dazu kommen diese Köpfe:
| Kopf | Inhalt |
|---|---|
X-XICBOT-Event | Die Ereignisart, etwa lead.created |
X-XICBOT-Delivery | Eine UUID je Ereignis und Empfänger; jede Wiederholung trägt dieselbe |
X-XICBOT-Attempt | Nummer des Versuchs, beginnend bei 1 |
X-XICBOT-Signature | t=<Unix-Zeit>,v1=<HMAC-SHA256 in Hex> |
User-Agent | XICBOT-Webhook/1 |
Signatur prüfen
-
Schritt 1
Rohkörper lesen
Unterschrieben ist der Körper genau so, wie er ankommt. Lesen Sie ihn vor jeder JSON-Verarbeitung; ein Körper, der erst gelesen und dann neu geschrieben wird, ergibt eine andere Zeichenkette und eine abweichende Signatur.
-
Schritt 2
Kopf zerlegen
Der Kopf
X-XICBOT-Signatureenthält durch Komma getrennttundv1. Fehlt eines davon oder enthälttetwas anderes als Ziffern, ist die Sendung abzulehnen. -
Schritt 3
Alter prüfen
Weicht
tum mehr als 300 Sekunden von Ihrer Uhr ab, lehnt der Bausatz ab. Der Wert ist der Parameterhoechstalter; 0 schaltet die Prüfung ab, womit eine mitgelesene Sendung beliebig oft eingespielt werden kann. -
Schritt 4
Signatur berechnen
HMAC-SHA256 über
t, einen Punkt und den Rohkörper, mit dem Signaturgeheimnis als Schlüssel, als Hex-Zeichenkette. -
Schritt 5
In gleichbleibender Zeit vergleichen
Der Bausatz vergleicht mit
timingSafeEqual(Node),hash_equals(PHP) oderhmac.compare_digest(Python). Ein abbrechender Vergleich verrät über die Laufzeit, wie viele Zeichen schon stimmen.
| Sprache | Aufruf | Ergebnis |
|---|---|---|
| Node | signaturPruefen(koerper, kopf, geheimnis, hoechstalter = 300) | true oder false; koerper als Zeichenkette oder Buffer |
| PHP | xicbot_signatur_pruefen($koerper, $kopf, $geheimnis, $hoechstalter = 300) | true oder false |
| Python | signatur_pruefen(koerper, kopf, geheimnis, hoechstalter=300) | True oder False, auch bei einem Körper, der kein gültiges UTF-8 ist; koerper als bytes oder str |
webhook.php php
<?php
require_once __DIR__.'/signatur.php';
$koerper = file_get_contents('php://input');
$kopf = $_SERVER['HTTP_X_XICBOT_SIGNATURE'] ?? '';
$geheimnis = (string) getenv('XICBOT_WEBHOOK_SECRET');
if (! xicbot_signatur_pruefen($koerper, $kopf, $geheimnis)) {
http_response_code(401);
exit;
}
$sendung = json_decode($koerper, true);
// $sendung['event'] und $sendung['data'] auswerten
http_response_code(204);Zustellung und Wiederholung
- XICBOT wartet bis zu 8 Sekunden auf Ihre Antwort. Jeder Status von 200 bis 299 gilt als zugestellt; Weiterleitungen verfolgt XICBOT nicht.
- Zugestellt wird an jeden Webhook, der eingeschaltet ist und das Ereignis abonniert hat.
- Die Adresse wird beim Anlegen und vor jedem Versand erneut geprüft.
- Scheitert ein Versuch, folgen bis zu drei Versuche insgesamt, in der Regel nach 30 und nach 120 Sekunden. Jede Wiederholung trägt dieselbe
X-XICBOT-Deliveryund dasselbeat, eine höhereX-XICBOT-Attemptund eine neu berechnete Signatur mit aktuellemt. - Fehler in Folge zählt jeden gescheiterten Versuch, auch eine Sendung, die schon die Prüfung der Adresse vor dem Versand aufhält, und fällt nach einer gelungenen Zustellung auf 0. Ein Signaturgeheimnis, das sich nicht lesen ließ (
secret_unreadable), zählt nicht mit. Bei 20 schaltet XICBOT den Webhook ab; die Zeile zeigt dann abgeschaltet. Schalten Sie ihn wieder ein, beginnt die Zählung bei 0. - Die Schaltfläche in der Zeile nennt die Handlung: Abschalten bei einem laufenden Webhook, Einschalten bei einem abgeschalteten.
- Zustellungen zeigt die letzten 50 Versuche mit Zeit, Ereignisse, Versuch, Status und Dauer in Millisekunden. Löschen fragt zuerst nach: „Diesen Webhook löschen? Zustellungen an diese Adresse enden sofort, und das Signaturgeheimnis lässt sich nicht wiederherstellen.“ Darunter steht die Adresse des Webhooks. Gelöscht wird erst mit Löschen im Dialog; Abbrechen lässt ihn bestehen.
Die Spalte Status zeigt den Status Ihrer Antwort oder einen dieser Gründe:
| Grund | Anzeige |
|---|---|
redirect_not_followed | Das Ziel hat umgeleitet. Umleitungen werden nicht verfolgt – bitte die endgültige Adresse eintragen. |
secret_unreadable | Das Signaturgeheimnis ließ sich nicht lesen. |
webhook_inactive | Der Empfänger ist abgeschaltet. |
webhook_deleted | Der Empfänger ist gelöscht. |
url_unreadable | Die Adresse ist nicht lesbar. |
scheme_not_allowed | Diese Adressart wird nicht unterstützt. |
name_unresolvable | Der Name der Adresse lässt sich nicht auflösen. |
target_not_allowed | Dieses Ziel ist nicht erlaubt, etwa eine interne Adresse. |
Ereignisse
| Ereignis | In der App |
|---|---|
conversation.started | Gespräch begonnen |
conversation.ended | Gespräch beendet |
handoff.triggered | Übergabe ausgelöst |
lead.created | Anfrage erstellt |
appointment.requested | Termin angefragt |
appointment.confirmed | Termin bestätigt |
knowledge.indexed | Wissensquelle aufgenommen |
knowledge.failed | Wissensquelle fehlgeschlagen |
gap.detected | Wissenslücke erkannt |
budget.exhausted | Budget erschöpft |
RUECKRUF_EREIGNISSE im Bausatz enthält dieselben zehn Arten. Schlüssel und Webhooks aus Sicht der Verwaltung beschreibt API-Schlüssel und Webhooks.
Wenn XICBOT Ihr System aufruft
Ein Assistent kann Ihr System als Werkzeug aufrufen. Werkzeuge richten Sie je Assistent unter Auslöser › Werkzeuge ein: mit der Bauform Eigene Schnittstelle für einen freien Aufruf, oder mit einer Bauform, die ein festes Format spricht, etwa Freie Zeiten lesen. Alles zu Freigabe, Mindestrolle und Pflichtangaben steht unter Werkzeuge anlegen und freigeben.
| Feld | Regel |
|---|---|
| Schlüssel | Kleinbuchstabe am Anfang, danach Kleinbuchstaben, Ziffern und Unterstrich; 3 bis 64 Zeichen, je Assistent einmalig |
| Bezeichnung | Pflicht, bis 120 Zeichen |
| Wann soll der Assistent es benutzen? | Pflicht, bis 600 Zeichen |
| Adresse der Schnittstelle | http:// oder https://, ins offene Netz; geprüft beim Speichern und vor jedem Aufruf. Eigene Schnittstelle und die Bauformen mit festem Format verlangen sie: ohne Adresse lässt sich das Werkzeug nicht speichern, und am Feld steht „Diese Bauform spricht mit einem fremden System. Ohne Adresse der Schnittstelle kann sie nicht arbeiten.“ Lehnt der Dienst die Adresse ab, nennt der Satz am Feld den Grund, etwa „Der Name dieser Adresse lässt sich nicht auflösen. Bitte prüfen Sie die Schreibweise.“; alle Sätze stehen unter Welche Adressen erlaubt sind. |
| Zugangsmarke | Geht als authorization: Bearer <Marke> mit; ein leeres Feld lässt eine hinterlegte Marke unverändert |
| Felder der Eingabe | Nur bei Eigene Schnittstelle: bis zu 20 Felder; Name mit Kleinbuchstabe am Anfang, danach a–z, 0–9 und _, bis 40 Zeichen |
| Typ | string, integer, number oder boolean; andere Angaben werden zu string |
| Beschreibung eines Felds | Bis 200 Zeichen |
Felder mit den Namen tenant_id, mandant, mandant_id und konto_id nimmt XICBOT nicht an: Ein Werkzeug mit einem solchen Feld lässt sich nicht speichern. Ihr System erhält nur Felder, die im Werkzeug erklärt sind; was der Assistent darüber hinaus mitschickt, entfällt.
Eigene Schnittstelle
Bei der Bauform Eigene Schnittstelle sendet XICBOT einen POST mit content-type: application/json und accept: application/json an die hinterlegte Adresse. Der Körper hat drei Felder: tool (der Schlüssel des Werkzeugs), input (die erklärten Felder) und conversation_id. Zeichenketten in input kürzt XICBOT auf 2000 Zeichen, Listen auf 50 Einträge und die Verschachtelung auf drei Ebenen. Ein Kopf idempotency-key kommt bei diesem Aufruf nicht mit.
anfrage.json json
{
"tool": "lieferzeit",
"input": { "postleitzahl": "10115" },
"conversation_id": "9d1c…"
}Den Status Ihrer Antwort reicht XICBOT als status an den Assistenten weiter. Ist die Antwort ein JSON-Objekt oder eine JSON-Liste, kommt sie als data an; jede andere Antwort geht als text weiter, gekürzt auf 2000 Zeichen. Welche Felder Ihr JSON trägt, bestimmen Sie.
Bauformen mit festem Format
Die übrigen Bauformen, die ein fremdes System ansprechen, senden einen festen Umschlag mit sieben Feldern und erwarten eine feste Antwort. Schreibende Vorgänge tragen zusätzlich den Kopf idempotency-key: 64 Hex-Zeichen, gebildet aus Gespräch, Werkzeug und Eingabe. Ein wiederholter Aufruf mit gleicher Eingabe im selben Gespräch trägt denselben Wert, sodass Ihr System ihn als Wiederholung erkennen kann. Lesende Vorgänge tragen ihn nicht.
| Feld | Inhalt | Beispiel Freie Zeiten lesen |
|---|---|---|
adapter | Kennung der Bauform | calendar_availability |
domain | Bereich | kalender |
operation | Art des Vorgangs: read oder write | read |
action | Vorgang | availability.list |
tool | Schlüssel des Werkzeugs | wie eingetragen |
conversation_id | Kennung des Gesprächs | – |
input | Die Felder der Bauform | from (Pflicht), to, kind, duration_min |
antwort.json json
{
"ok": true,
"data": {
"slots": ["2026-10-06T09:00", "2026-10-06T10:30"],
"timezone": "Europe/Berlin",
"note": "Samstags nur vormittags"
}
}Aus data übernimmt XICBOT nur die Ergebnisfelder der Bauform, bei Freie Zeiten lesen slots, timezone und note. Zeichenketten werden auf 600 Zeichen gekürzt, Listen auf 20 Einträge, die Verschachtelung auf zwei Ebenen. Lehnt Ihr System ab, antwortet es mit "ok": false und einem Grund in error; XICBOT übernimmt davon bis zu 200 Zeichen. Fehlt ok, gilt die Antwort als Verstoß gegen das Format.
Grenzen des Aufrufs
| Grenze | Wert |
|---|---|
| Wartezeit auf die Antwort | in der Regel 8 Sekunden |
| Verbindungsaufbau | höchstens 5 Sekunden |
| Größe der Antwort | höchstens 64 KB, sonst verworfen |
| Weiterleitungen | werden nicht verfolgt |
| Erfolgreiche Antwort | Status 200 bis 299 |
| Werkzeugaufrufe je Antwort | höchstens 5 |
Ein Aufruf, der gelingt, steht im Verlauf als ausgeführt. Läuft die Wartezeit ab, steht er als Zeitgrenze überschritten da, jeder andere Fehler als fehlgeschlagen mit der Meldung, gekürzt auf 500 Zeichen. Zu jedem Aufruf hält XICBOT die Dauer in Millisekunden fest. Auch eine Weiterleitung endet als fehlgeschlagen. Einen gescheiterten Aufruf wiederholt XICBOT nicht von sich aus. Diese Meldungen können erscheinen:
| Meldung | Anlass |
|---|---|
| Für dieses Werkzeug ist keine gültige Adresse hinterlegt. | Adresse fehlt oder beginnt nicht mit http:// oder https:// |
| Die hinterlegte Adresse zeigt nicht ins offene Netz. | Prüfung unmittelbar vor dem Aufruf |
| Das Werkzeug hat innerhalb von 8 Sekunden nicht geantwortet. | Wartezeit abgelaufen |
| Das Werkzeug war nicht erreichbar. | Verbindung gescheitert |
| Das Werkzeug hat mehr als 64 KB geliefert. Die Antwort wurde verworfen. | Antwort zu groß |
| Das Werkzeug hat weitergeleitet (Status 302). Einer Weiterleitung folgt es nicht. | Status 300 bis 399, hier am Beispiel 302 |
| Das Werkzeug hat mit Status 500 geantwortet. | Anderer Status außerhalb von 200 bis 299, hier 500 |
| Das angebundene System hat nicht nach dem Anbindungsvertrag geantwortet. | Bauform mit festem Format: kein JSON oder kein Feld ok |
| Das angebundene System hat den Vorgang abgelehnt: <Grund> | Bauform mit festem Format: ok ist nicht true |
| Dieses Werkzeug gibt es für diesen Assistenten nicht. | Der Assistent ruft einen unbekannten Schlüssel auf |
| Dieses Werkzeug ist für diesen Zugang nicht freigegeben. | Die Rolle im Gespräch liegt unter der Mindestrolle |
| Der Deckel von 5 Werkzeugaufrufen je Antwort ist erreicht. | Sechster Aufruf in einer Antwort |
Ob ein Aufruf sofort läuft oder erst nach Bestätigung durch den Besucher, legt die Freigabe fest: ohne Rückfrage oder mit Bestätigung. Eigene Schnittstelle ist mit mit Bestätigung vorbelegt. Sofort läuft ein lesendes Werkzeug mit ohne Rückfrage, außerdem die Abfrage freier Zeiten bei Termin vereinbaren; ein schreibendes speichert XICBOT mit mit Bestätigung, gleich was gewählt ist. Fehlt ein Pflichtfeld der Eingabe, endet der Aufruf als fehlgeschlagen, bevor Ihr System angesprochen wird. Details stehen unter Werkzeuge anlegen und freigeben.
Häufige Fragen
Warum schlägt die Signaturprüfung fehl, obwohl das Geheimnis stimmt?
Meist wurde der Körper vor der Prüfung als JSON gelesen und neu geschrieben, statt den Rohkörper zu verwenden. Weitere Ursachen: das Geheimnis eines anderen Webhooks, eine Serveruhr, die mehr als 300 Sekunden abweicht, oder ein Proxy, der den Körper verändert.
Wie erkenne ich, dass eine Webhook-Sendung wiederholt wurde?
Am Kopf X-XICBOT-Attempt, der ab dem zweiten Versuch größer als 1 ist. X-XICBOT-Delivery behält in jeder Wiederholung dieselbe UUID; an ihr erkennen Sie eine Sendung, die Sie schon verarbeitet haben. Für Anfragen hilft außerdem der Inhalt von data, etwa die id.
Warum bekomme ich 401 trotz Schlüssel?
key_missing heißt: der Kopf X-Api-Key fehlt oder der Wert beginnt nicht mit xbot_. invalid_key heißt: der Schlüssel ist unbekannt, zurückgezogen oder abgelaufen. Ob ein Schlüssel zurückgezogen oder abgelaufen ist, zeigt die Spalte Status in der Liste unter Schnittstellen.
Kann ich ein Gespräch aus dem Widget über die Schnittstelle fortsetzen?
Nein. Mit conversation_id geht nur ein Gespräch weiter, das derselbe Assistent über die Schnittstelle begonnen hat; alle anderen beantwortet XICBOT mit 404 und not_found.
Mein System braucht länger als 8 Sekunden. Was passiert?
Der Aufruf endet als Zeitgrenze überschritten, und der Assistent erhält kein Ergebnis. Liefern Sie eine schnelle Antwort, etwa eine Vorgangsnummer, und erledigen Sie die lange Arbeit danach.
Selbst fragen, Assistent dieser Website
Automatischer Assistent