Tool kit for custom connections
Client, signature check and stream reader in Node, PHP and Python: call the API, verify webhooks and answer calls from XICBOT to your system.
In plain language
This page is for developers. The kit is program code for your own connection. It comes in three programming languages. With it you talk to the interface. You can read an answer while it is being written. You can check that a message to your system is genuine. The page also explains the other direction. Then the assistant calls your system.
All documentation topics
The tool kit is source code for your own connection to XICBOT, in three editions: Node (js/), PHP (php/) and Python (python/), each with four files. It contains a client for the API-key interface, a reader for the event stream of a chat reply, the signature check for webhooks and the lists of event types. It needs no further packages: Node calls via fetch, PHP via the curl extension, Python via the standard library. The last section covers the opposite direction: what your system has to answer when an assistant calls it as a tool.
What the kit contains
| Task | Node (js/) | PHP (php/) | Python (python/) |
|---|---|---|---|
| Client for the interface | klient.mjs: class Klient | klient.php: class XicbotKlient | klient.py: class Klient |
| Event stream reader | strom.mjs: class Strom | strom.php: class XicbotStrom | strom.py: class Strom |
| Webhook signature check | signatur.mjs: signaturPruefen() | signatur.php: xicbot_signatur_pruefen() | signatur.py: signatur_pruefen() |
| Name of the signature header | SIGNATURKOPF | XICBOT_SIGNATURKOPF | SIGNATURKOPF |
| Event types | ereignisse.mjs: STROM_EREIGNISSE, RUECKRUF_EREIGNISSE | ereignisse.php: XICBOT_STROM_EREIGNISSE, XICBOT_RUECKRUF_EREIGNISSE | ereignisse.py: STROM_EREIGNISSE, RUECKRUF_EREIGNISSE |
| Calls via | fetch, signature with node:crypto | curl extension | urllib.request, signature with hmac |
Each file names in its header the version of the interface description it was generated from, so you can tell which state of the API your copy reflects. The client loads the stream reader from the same directory (./strom.mjs, strom.php, from strom import Strom), so keep the files of one language together. The signature check and the event lists stand on their own and can be taken over individually, for example into a receiver that only accepts webhooks.
Address and key
The client takes two values: the base address https://app.xicbot.de and an API key. Trailing slashes on the base address are removed; the client appends the paths (/api/v1/integration/…) itself. If the base address is missing, all three editions stop with Basisadresse fehlt as soon as the client is created. The client sends the key in the X-Api-Key header. It starts with xbot_; a value without that prefix counts as missing.
| Status | Field error | Meaning |
|---|---|---|
| 400 | key_in_url | Key sent as a parameter in the address |
| 401 | key_missing | Header X-Api-Key missing, empty or not starting with xbot_ |
| 401 | invalid_key | Key unknown, revoked or expired; all three cases answer the same way |
| 402 | plan_feature_missing | The account's plan does not include the feature for keys and webhooks, for example after a plan change; available_in lists the plans that include it |
| 403 | tenant_suspended | The account behind the key is not active |
| 403 | scope_missing | The key lacks the scope the call needs, for example leads or chat; the required field names it |
| 403 | read_only | The key lacks the writing scope (leads.write, knowledge.write); required names it |
| 429 | too_many_requests | Per-minute or daily cap reached, see Limits |
Creating a key
-
Step 1
Open the page
Keys and webhooks are under Administration › Interfaces (app.xicbot.de/schnittstellen). The menu item appears for the Administration and Owner roles; only these roles create and revoke keys. The roles are described in Team, roles and audit log.
-
Step 2
Choose name and scopes
In the API keys section enter a Name of at most 80 characters and tick at least one scope under Scopes; until then Create a new key stays disabled. Below the scopes it says: “Writing is a separate scope. Without it a key only reads.”
-
Step 3
Set limit and expiry
Requests per minute is a required field from 1 to 6000, preset to 120. Expires in days accepts 1 to 3650; below it the page says “Empty means no expiry.” The expiry date is calculated from the number of days when the key is created.
-
Step 4
Copy the key
After Create a new key the card Your new key shows the value:
xbot_followed by 48 characters. Below it: “Copy it now. After closing it cannot be retrieved again.” The Copy key button puts it on the clipboard; if that fails, the page says “The clipboard is not available here. Select the value and copy it manually.” Of the key itself, XICBOT stores only a SHA-256 fingerprint and the Identifier, the first 12 characters.
The key list shows Name, Identifier, Scopes, Last used, Expires (no limit for keys without expiry), Status with valid, expired or revoked, and Action. Last used is updated at most once a minute. Next to the create button a counter shows the valid keys, for example 3 / 10. At most ten keys can be valid at the same time; the eleventh is refused with 422, too_many_keys and the message “The maximum number of simultaneous keys has been reached.” Revoked and expired keys do not count.
Revoke asks for confirmation in the row: “Revoke this key? Requests using it will be rejected from then on.” Confirm with Revoke or back out with Cancel. From then on the API answers this key with 401 and invalid_key; the row stays in the list with the status revoked. A key cannot be edited. For other scopes or another limit, create a new key, switch your system over and revoke the old one. More on managing keys is in API keys and webhooks.
Scopes
| Scope | In the app | Allows |
|---|---|---|
conversations | Read conversations | List conversations and read one conversation with its messages in plain text |
leads | Read enquiries | List enquiries |
leads.write | Create enquiries | Create an enquiry |
appointments | Read appointments | List appointments |
analytics | Read analytics | Retrieve metrics for 7, 30 or 90 days |
knowledge.write | Create knowledge sources | Create a knowledge source from text or an address |
chat | Use chat | Ask an assistant a question and read the reply as a stream |
| none | – | Any valid key gets an answer from GET /api/v1/integration/status |
A writing scope does not include the reading one: leads.write creates enquiries but does not read them. GET /api/v1/integration/status returns alive, the account identifier in tenant, the key's scopes in scopes and its per-minute limit in limit_per_min. This lets you test a connection without reading any data. The individual requests are described in Integration endpoints.
Limits
Each key has its own per-minute cap, the value from Requests per minute. On top of that there is a daily cap from your account. It is counted per key and calendar day and expires at midnight, not 24 hours after the first request; a daily cap of 0 means no daily cap. Both caps are checked before either is counted, so a request rejected at a cap consumes nothing. GET /api/v1/integration/status counts as well. The level of the daily cap is given in the limit field of a 429 reply with scope day; GET /api/v1/integration/status only reports the per-minute cap as limit_per_min.
| Field of the 429 response | Content |
|---|---|
error | too_many_requests |
scope | minute or day: which cap applied |
limit | Value of that cap |
retry_after | Seconds until a retry makes sense; the same value is in the Retry-After header |
limit_per_min | The key's per-minute cap, even when the daily cap applied |
wait | Same value as retry_after |
On 429 the client throws an error with the status and body of the response; you build retrying after retry_after yourself. The reply quota is separate: once it is used up for the month, the chat answers with 429 and quota_exhausted, see Chat and event stream. Overview of all limits: API limits.
The client
The client maps each operation of the API to a method of its own, named after the HTTP method and the path. You create it with the base address, the key and a timeout; the timeout limits every call whose reply arrives in one piece.
| Item | Node | PHP | Python |
|---|---|---|---|
| Creating it | new Klient(basis, schluessel, frist) | new XicbotKlient($basis, $schluessel, $frist) | Klient(basis, schluessel, frist, stream_timeout) |
| Default timeout | 15000 milliseconds | 15 seconds | 15 seconds |
| Read timeout for chat | none | none | stream_timeout, 120 seconds per read by default |
| Empty base address | Error with Basisadresse fehlt | InvalidArgumentException with Basisadresse fehlt | ValueError with Basisadresse fehlt |
| Method naming | getStatus(), postLeads(daten) | getStatus(), postLeads($daten) | get_status(), post_leads(daten) |
| Return value (except chat) | Promise with the parsed JSON | array | dictionary |
The Node and PHP editions set no timeout for chat, because the stream runs for as long as the reply is being produced. The Python edition reads the stream line by line and stops when a single read waits longer than stream_timeout for data, 120 seconds by default. Every request carries Content-Type: application/json, Accept: application/json and the key in X-Api-Key; for chat the client asks for text/event-stream instead.
| Method (Node, PHP) | Method (Python) | Operation | Scope | Required by the client |
|---|---|---|---|---|
getStatus() | get_status() | GET /api/v1/integration/status | none | – |
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 or url |
postChat(daten, leser) | post_chat(daten, leser) | POST /api/v1/integration/chat | chat | assistant_id, text |
If a required field is missing, the client stops before sending and raises Pflichtfeld fehlt: <field> as Error (Node), InvalidArgumentException (PHP) or ValueError (Python). When creating a knowledge source it also requires text or url and otherwise raises Eines der Felder fehlt: text oder url. XICBOT checks every other rule itself and answers a violation with 422. Conversations and enquiries are returned newest first, appointments by start time with the latest first. The three lists return 50 entries per call; page picks the page, counting from 1, and a missing or smaller value counts as 1. Keep reading until a page holds fewer than 50 entries. getAnalytics takes an IANA time zone in zone, default Europe/Berlin; XICBOT answers an unknown zone with 422 and unknown_timezone. A single conversation returns up to 200 messages in chronological order, each with role, text and time; an unknown ID is answered with 404 and not_found.
What XICBOT validates
| Operation | Field | Rule |
|---|---|---|
| Create enquiry | subject | required, at most 1000 characters |
| Create enquiry | name | at most 120 characters |
| Create enquiry | email | valid email address, at most 190 characters |
| Create enquiry | phone | at most 60 characters |
| Create enquiry | source | at most 60 characters; api if omitted |
| Create enquiry | external_id | at most 100 characters; prevents duplicate entries |
| Create knowledge source | title | required, at most 200 characters |
| Create knowledge source | text | required if url is missing; at most 400000 characters |
| Create knowledge source | url | required if text is missing; valid address, at most 500 characters |
| Create knowledge source | draft, published_at | only together with text; draft is true or false, published_at a date |
| Analytics | period | 7d, 30d or 90d; 30d if omitted |
| Chat | assistant_id | required, at most 64 characters |
| Chat | conversation_id | at most 64 characters; continues a conversation |
| Chat | text | required, 1 to 4000 characters; the account can set a lower limit |
XICBOT answers a new enquiry with 201, the entry under lead and duplicate_prevented: false, and sends the lead.created webhook at the same time. If the same external_id arrives a second time, no second entry is created: the reply is 200 with the existing entry and duplicate_prevented: true. After a timeout, resend with the same external_id. How enquiries are handled in the app is described in Appointments and cases.
A knowledge source with text is processed straight away; the reply is 201 with id, type (text), status and chunks under source, and knowledge.indexed or knowledge.failed is sent. The 201 also comes back when processing failed, so check status (ready means success). A source with url is created with 202 and, under source, id, type website and status new, and the page is fetched in the background. As when a source is created in the app, XICBOT also queues a background job for both kinds that enriches the sections and builds their vectors; after that, search usually finds the source by meaning as well, not only by matching words. Addresses that do not point to the public internet are rejected with 422 and the reason in error, for example target_not_allowed. More about sources is in Maintain knowledge.
Errors in the client
| Case | Node | PHP | Python |
|---|---|---|---|
| Status 400 or higher | Error: Antwort <status>: <text> | RuntimeException: Antwort <status>: <text> | RuntimeError: Antwort <status>: <text> |
| No connection or timeout expired | error from fetch, a TimeoutError for the timeout | RuntimeException: Aufruf fehlgeschlagen: … | RuntimeError: Aufruf fehlgeschlagen: … |
| Reply is not JSON | empty object {} | empty array [] | empty dictionary {} |
| Chat with status 400 or higher | Error: Antwort <status>: <text> | RuntimeException: Antwort <status>: <text> | RuntimeError: Antwort <status>: <text> |
| Chat transfer interrupted | stream error; without a body Antwort ohne Strom | RuntimeException: Strom abgebrochen: … | RuntimeError: Strom abgebrochen: …, also when the read timeout expires |
The text after the status is the body of the error reply, which XICBOT sends as JSON with an error field. The values are explained in the table under Address and key and in the chat section.
Chat and event stream
POST /api/v1/integration/chat
Asks an assistant a question and returns the answer as an event stream (text/event-stream). Without conversation_id a new conversation starts; with it, a conversation continues that the same assistant began through the API.
- Authentication
X-Api-Keyheader, scopechat- Rate limit
- The key's per-minute and daily caps, plus the account's monthly quota
Each event consists of an event: <type> line, one or more data: <JSON> lines and a blank line. The kit's stream reader hands an event over only once the blank line has arrived, joins several data lines with a line break, strips a trailing \r and passes type and data to your function. If the content is not valid JSON, the data arrives as null (Python: None).
| Status | Field error | Meaning |
|---|---|---|
| 404 | not_found | Unknown assistant, or conversation_id is not an API conversation of this assistant |
| 409 | reply_in_progress | A reply is currently being produced for this conversation |
| 413 | input_too_long | Text longer than the account limit; limit and length give both values. No new conversation is created |
| 422 | no_version | The assistant has no published version yet |
| 429 | quota_exhausted | Monthly quota used up; scope is month, retry_after and the Retry-After header give the seconds until the month changes. XICBOT usually removes a conversation that only this call started |
A conversation that started in the widget or in a test chat cannot be continued through the API, not even with the conversations scope; XICBOT answers with 404. How much the account may answer per month is described in Plans, quotas and billing.
Event types
| Type | Fields | Meaning |
|---|---|---|
start | conversation_id, question_id | The reply begins; you need conversation_id to continue |
text | t | A piece of reply text; concatenated, the pieces form the reply |
redaction | text, reason, count (only with claims) | Replaces the text collected so far with text; reason is claims (correction of unsupported statements) or notice. Append any text pieces that follow |
citations | citations | The sources used in the reply |
verification | state, claims, total | Result of the check against the knowledge |
enforcement | state, topics, handover | Result of the check against the assistant rules |
follow_ups | questions, dropped, checked, reason | Suggested follow-up questions |
contact_offer | offered | The reply offers to get in touch |
handoff | handoff, reason | Handover to a person; reason is for example assistant, conversation_limit or enforcement |
live | live, since | A person has taken over the conversation; end with live follows |
fallback | reason, transient | The reply failed; the stored fallback reply follows as text |
revoked | reason | Consent was withdrawn (consent_revoked); the text is discarded |
error | code, scope, retry_after, limit | The conversation is blocked or at its cap; with conversation_limit a notice text and handoff follow |
tool | details of the tool call | A tool call by the assistant |
model_switch | reason, from, to | Internal switch during the reply, of no relevance to your integration |
end | state, message_id, duration_ms and more | Last event; state is done, aborted, fallback, revoked, live or blocked |
With conversation_limit the notice text in English reads: “This conversation has reached its agreed length. I am handing it over to a person – please leave your question together with a way to reach you, or start a new conversation.” If the conversation is blocked for another reason, error is followed by the assistant's fallback reply and end with 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: 'ASSISTANT_ID', text: 'When are you open?' }, (art, daten) => {
if (art === 'start') console.log('Conversation', daten.conversation_id);
else if (art === 'text') antwort += daten.t;
else if (art === 'redaction') antwort = daten.text;
else if (art === 'error') console.error('Blocked:', daten.code);
else if (art === 'end') console.log(antwort, daten.state);
// any other type: skip
});anfrage.py python
import os
from klient import Klient
klient = Klient('https://app.xicbot.de', os.environ['XICBOT_KEY'])
ergebnis = klient.post_leads({
'subject': 'Call back about the quote',
'email': 'customer@example.com',
'external_id': 'crm-4711',
})
# 201 the first time, 200 with duplicate_prevented on a repeat
print(ergebnis['lead']['id'], ergebnis['duplicate_prevented'])The structure of every operation is described in Integration endpoints; how conversations appear in the app is covered in Conversations.
Verifying webhooks
A webhook sends events from your account to an address on your system. You set it up on the same page as the keys, under Administration › Interfaces in the Webhooks section: “Every delivery carries a signature in the X-XICBOT-Signature header. Failed deliveries are retried.”
-
Step 1
Enter the address
Under Target address enter an address of at most 2048 characters. It has to point to the public internet; otherwise the page shows “This address is not permitted as a target.”
-
Step 2
Choose events
Under Events tick at least one. Create webhook stays disabled until the address and one event are set.
-
Step 3
Copy the secret
After creation the page shows Your signing secret once:
whsec_followed by 48 characters, with the note “Use it to verify the signature of an incoming delivery.” Use Copy secret to take it over into your system.
Each delivery is a POST with a JSON body of four fields: event (the event type), at (time of the event in ISO 8601 format, the same in every retry), tenant (your account ID) and data (the event payload). These headers come with it:
| Header | Content |
|---|---|
X-XICBOT-Event | The event type, for example lead.created |
X-XICBOT-Delivery | A UUID per event and receiver; every retry carries the same one |
X-XICBOT-Attempt | Number of the attempt, starting at 1 |
X-XICBOT-Signature | t=<Unix time>,v1=<HMAC-SHA256 in hex> |
User-Agent | XICBOT-Webhook/1 |
Checking the signature
-
Step 1
Read the raw body
The body is signed exactly as it arrives. Read it before any JSON processing; a body that is parsed and serialised again becomes a different string with a different signature.
-
Step 2
Split the header
The
X-XICBOT-Signatureheader holdstandv1, separated by a comma. If either is missing ortcontains anything other than digits, reject the delivery. -
Step 3
Check the age
If
tdiffers from your clock by more than 300 seconds, the kit rejects the delivery. The value is thehoechstalterparameter; 0 turns the check off, which lets a captured delivery be replayed any number of times. -
Step 4
Compute the signature
HMAC-SHA256 over
t, a dot and the raw body, with the signing secret as key, as a hex string. -
Step 5
Compare in constant time
The kit compares with
timingSafeEqual(Node),hash_equals(PHP) orhmac.compare_digest(Python). A comparison that stops early reveals through its running time how many characters already match.
| Language | Call | Result |
|---|---|---|
| Node | signaturPruefen(koerper, kopf, geheimnis, hoechstalter = 300) | true or false; koerper as string or Buffer |
| PHP | xicbot_signatur_pruefen($koerper, $kopf, $geheimnis, $hoechstalter = 300) | true or false |
| Python | signatur_pruefen(koerper, kopf, geheimnis, hoechstalter=300) | True or False, also for a body that is not valid UTF-8; koerper as bytes or 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);
// evaluate $sendung['event'] and $sendung['data']
http_response_code(204);Delivery and retries
- XICBOT waits up to 8 seconds for your reply. Any status from 200 to 299 counts as delivered; XICBOT does not follow redirects.
- A delivery goes to every webhook that is switched on and subscribed to the event.
- The address is checked when the webhook is created and again before each delivery.
- If an attempt fails, there are up to three attempts in total, usually after 30 and after 120 seconds. Each retry carries the same
X-XICBOT-Deliveryand the sameat, a higherX-XICBOT-Attemptand a newly computed signature with a currentt. - Consecutive failures counts every failed attempt, including a delivery that the address check before sending already stops, and drops to 0 after a successful delivery. A signing secret that could not be read (
secret_unreadable) does not count. At 20, XICBOT switches the webhook off and the row shows disabled. When you switch it back on, the count starts again at 0. - The button in the row names the action: Turn off for a running webhook, Turn on for one that is switched off.
- Deliveries shows the last 50 attempts with Time, Events, Attempt, Status and Duration in milliseconds. Delete asks first: “Delete this webhook? Deliveries to this address stop immediately, and the signing secret cannot be restored.” The webhook address is shown below. The webhook is only deleted with Delete in the dialog; Cancel keeps it.
The Status column shows the status of your reply or one of these reasons:
| Reason | Shown as |
|---|---|
redirect_not_followed | The target redirected. Redirects are not followed – please enter the final address. |
secret_unreadable | The signing secret could not be read. |
webhook_inactive | The receiver is switched off. |
webhook_deleted | The receiver has been deleted. |
url_unreadable | The address cannot be read. |
scheme_not_allowed | This address type is not supported. |
name_unresolvable | The host name of the address cannot be resolved. |
target_not_allowed | This target is not allowed, for example an internal address. |
Events
| Event | In the app |
|---|---|
conversation.started | Conversation started |
conversation.ended | Conversation ended |
handoff.triggered | Handover triggered |
lead.created | Enquiry created |
appointment.requested | Appointment requested |
appointment.confirmed | Appointment confirmed |
knowledge.indexed | Knowledge source indexed |
knowledge.failed | Knowledge source failed |
gap.detected | Knowledge gap detected |
budget.exhausted | Budget exhausted |
RUECKRUF_EREIGNISSE in the kit holds the same ten types. Keys and webhooks from the administration side are covered in API keys and webhooks.
When XICBOT calls your system
An assistant can call your system as a tool. Tools are set up per assistant under Triggers › Tools: with the Type Own interface for a free-form call, or with a type that speaks a fixed format, for example Read free slots. Approval, minimum role and required details are covered in Create and approve tools.
| Field | Rule |
|---|---|
| Key | lower-case letter first, then lower-case letters, digits and underscore; 3 to 64 characters, unique per assistant |
| Label | required, up to 120 characters |
| When should the assistant use it? | required, up to 600 characters |
| Interface address | http:// or https://, on the public internet; checked on saving and before each call. Own interface and the types with a fixed format require it: without an address the tool cannot be saved, and the field shows “This type talks to an external system. Without an interface address it cannot work.” If the service rejects the address, the sentence at the field gives the reason, for example “The host name of this address cannot be resolved. Please check the spelling.”; all sentences are listed under Which addresses are allowed. |
| Access token | Sent as authorization: Bearer <token>; an empty field leaves a stored token unchanged |
| Input fields | Only with Own interface: up to 20 fields; name starts with a lower-case letter, then a–z, 0–9 and _, up to 40 characters |
| Type | string, integer, number or boolean; anything else becomes string |
| Description of a field | up to 200 characters |
XICBOT does not accept fields named tenant_id, mandant, mandant_id or konto_id: a tool with such a field cannot be saved. Your system receives only the fields declared in the tool; anything else the assistant sends is dropped.
Own interface
With the Own interface type, XICBOT sends a POST with content-type: application/json and accept: application/json to the stored address. The body has three fields: tool (the tool's Key), input (the declared fields) and conversation_id. Strings in input are cut to 2000 characters, lists to 50 entries and nesting to three levels. No idempotency-key header is sent with this call.
anfrage.json json
{
"tool": "delivery_time",
"input": { "postcode": "10115" },
"conversation_id": "9d1c…"
}XICBOT passes the status of your reply to the assistant as status. If the reply is a JSON object or a JSON array, it arrives as data; any other reply is passed on as text, cut to 2000 characters. You decide which fields your JSON carries.
Types with a fixed format
The other types that talk to an external system send a fixed envelope with seven fields and expect a fixed reply. Writing operations also carry the idempotency-key header: 64 hex characters derived from conversation, tool and input. A repeated call with the same input in the same conversation carries the same value, so your system can recognise it as a repeat. Reading operations do not carry it.
| Field | Content | Example Read free slots |
|---|---|---|
adapter | ID of the type | calendar_availability |
domain | Domain | kalender |
operation | Kind of operation: read or write | read |
action | Operation | availability.list |
tool | the tool's Key | as entered |
conversation_id | ID of the conversation | – |
input | The fields of the type | from (required), to, kind, duration_min |
antwort.json json
{
"ok": true,
"data": {
"slots": ["2026-10-06T09:00", "2026-10-06T10:30"],
"timezone": "Europe/Berlin",
"note": "Saturdays mornings only"
}
}From data XICBOT takes only the result fields of the type, for Read free slots that is slots, timezone and note. Strings are cut to 600 characters, lists to 20 entries and nesting to two levels. To decline, your system replies with "ok": false and a reason in error; XICBOT keeps up to 200 characters of it. If ok is missing, the reply counts as a breach of the format.
Call limits
| Limit | Value |
|---|---|
| Waiting time for the reply | 8 seconds by default |
| Connection setup | at most 5 seconds |
| Size of the reply | at most 64 KB, otherwise discarded |
| Redirects | not followed |
| Successful reply | status 200 to 299 |
| Tool calls per reply | at most 5 |
A call that succeeds is recorded in the history as executed. If the waiting time runs out it shows as timed out; any other error shows as failed with the message, cut to 500 characters. XICBOT records the duration of each call in milliseconds. A redirect also ends as failed. XICBOT does not retry a failed call on its own. These messages can appear:
| Message (German original) | Cause |
|---|---|
| Für dieses Werkzeug ist keine gültige Adresse hinterlegt. | address missing or not starting with http:// or https:// |
| Die hinterlegte Adresse zeigt nicht ins offene Netz. | check right before the call |
| Das Werkzeug hat innerhalb von 8 Sekunden nicht geantwortet. | waiting time expired |
| Das Werkzeug war nicht erreichbar. | connection failed |
| Das Werkzeug hat mehr als 64 KB geliefert. Die Antwort wurde verworfen. | reply too large |
| Das Werkzeug hat weitergeleitet (Status 302). Einer Weiterleitung folgt es nicht. | status 300 to 399, here with 302 as example |
| Das Werkzeug hat mit Status 500 geantwortet. | other status outside 200 to 299, here 500 |
| Das angebundene System hat nicht nach dem Anbindungsvertrag geantwortet. | fixed-format type: no JSON or no ok field |
| Das angebundene System hat den Vorgang abgelehnt: <Grund> | fixed-format type: ok is not true |
| Dieses Werkzeug gibt es für diesen Assistenten nicht. | the assistant calls an unknown Key |
| Dieses Werkzeug ist für diesen Zugang nicht freigegeben. | the role in the conversation is below the minimum role |
| Der Deckel von 5 Werkzeugaufrufen je Antwort ist erreicht. | sixth call within one reply |
Whether a call runs straight away or only after the visitor confirms it is set by Approval: without asking or with confirmation. Own interface is preset to with confirmation. A reading tool set to without asking runs straight away, as does the query for free slots with Book appointment; XICBOT stores a writing tool with with confirmation whatever was chosen. If a required input field is missing, the call ends as failed before your system is contacted. Details are in Create and approve tools.
Frequent questions
Why does the signature check fail although the secret is right?
Usually the body was parsed as JSON and serialised again before the check instead of using the raw body. Other causes are the secret of a different webhook, a server clock that is more than 300 seconds off, or a proxy that alters the body.
How do I recognise that a webhook delivery was retried?
By the X-XICBOT-Attempt header, which is greater than 1 from the second attempt on. X-XICBOT-Delivery keeps the same UUID in every retry, so you can recognise a delivery you have already processed. For enquiries the content of data, such as the id, also helps.
Why do I get 401 even though I send a key?
key_missing means the X-Api-Key header is absent or its value does not start with xbot_. invalid_key means the key is unknown, revoked or expired. Whether a key has been revoked or has expired is shown in the Status column of the list under Interfaces.
Can I continue a widget conversation through the API?
No. conversation_id only continues a conversation that the same assistant started through the API; XICBOT answers any other with 404 and not_found.
My system needs more than 8 seconds. What happens?
The call ends as timed out and the assistant receives no result. Reply quickly, for example with a reference number, and do the long work afterwards.
Ask it yourself, Assistant of this website
Automated assistant