Skip to content
XICBOT

Settings

Deutsch
Documentation · API reference

Conversation and management endpoints

App session endpoints for conversations, visitor blocks, team, routing, catalogue, shop, analytics, cases and audit log: fields, limits, roles and errors.

In plain language

The app uses these endpoints to read and change its data. They cover chats and blocked visitors. The team and the routing rules belong here too. Other endpoints deal with the catalogue, the shop and analytics. Cases and the activity log are also covered. You need the token from the sign-in. Your own systems with a key use other endpoints.

Last updated

All documentation topics

This page covers the endpoints the app itself uses to read and change conversations, visitor blocks, the team, routing rules, catalogue and shop connection, analytics, cases and the audit log. All of them live under https://app.xicbot.de/api/v1/ and expect a session token in the Authorization: Bearer … header, as described in Authentication. Connected systems using an API key have their own endpoints, see Integration.

Access, roles and throttles

Every request passes three stages. First the server checks the session token and whether the account is active. Next, the signed-in person’s email address has to be confirmed; until it is, every endpoint on this page answers 403 with email_unverified, and the app shows “This area stays locked until the email address is confirmed.” Last, each endpoint checks its minimum role, and some also check a plan feature and a named throttle.

Roles

Roles are ranked: a higher role may do whatever a lower one may. How to assign roles is explained in Team and roles.

Role in the APILabel in the appEndpoints on this page
viewerReadreading endpoints except the audit log, own availability, accepting an invitation
editorEditworking on, creating and writing to conversations, routing rules, catalogue, case status
adminAdministrationdeleting conversations, retention periods, visitor blocks, team, shop connection, audit log
ownerOwnerplain text of a conversation, plain-text export, weekly report subscription

Common rejections

Almost any endpoint can return these. The body is JSON with an error field; the general format and 422 validation errors are described in API basics.

StatuserrorMeaning
401–Session token missing or no longer valid, see Authentication.
403no_tenantThe person belongs to no account.
403tenant_<Status>The account is not active. message reads “Das Konto ist derzeit nicht aktiv.” (German)
403email_unverifiedThe email address is not confirmed.
403role_too_lowThe role is not sufficient; required names the role needed.
402plan_feature_missingThe plan lacks the feature; with feature, plan, plan_name and available_in.
402plan_limit_reachedA plan limit has been reached; with limit, allowed, used, plan and plan_name.
503plan_limit_unmeasuredThe counter for a plan limit could not be read; with limit and reason. The endpoint then does not let the request through.
429–A named throttle was exceeded, see Limits.
500not_loggedThe audit log entry could not be written; the change is not saved. The app shows “Not saved: the entry in the audit log could not be written. Please try again.”

Which features and limits your plan includes is listed in Plans and quotas and on the pricing page; how the plan gate answers is covered in Limits.

Throttles

Some endpoints carry a named throttle. It counts per signed-in person, or per request origin if there is none, in a one-minute window.

ThrottleRequests per minuteEndpoints
conversations30CSV export, creating a conversation, rating, plain text, attachments, analytics report
conversation-send20Sending a message into a conversation
catalog20Creating, changing and importing products; saving, testing and syncing the shop connection
invitations5Sending an invitation

Conversations

In the app these endpoints sit behind Conversations and Inbox; what you see there is described in Conversations and Inbox. Any role may read; every action on a conversation needs at least Edit.

List

GET /api/v1/conversations

Returns one page of conversations, masked, filtered and sorted.

Authentication
Session token, any role
Rate limit
no named throttle
ParameterValuesDefaultEffect
page1 to 100001Page
per_page1 to 20050Entries per page
sortcreated_at, message_count, frustration_score, status, escalated_atcreated_atSort column; ties are broken by the ID
dirasc, descdescDirection
assistantID, up to 64 characters–only this assistant’s conversations
channeltest, widget, api–Where the conversation came from
statusopen, closed, escalated–State
languageunknown or a language code such as de or de-at–unknown finds conversations whose language was not detected
mailbox0, 1–1: handed over and not yet resolved, the list in the Inbox
assignednone, me or a person’s ID, up to 64 characters–Assignment; none finds conversations nobody is assigned to
queueName of a queue, up to 80 characters, or none–Queue set by routing; none finds conversations without a queue
unverified0, 1–1: conversations with at least one reply whose claim is marked unverified
qSearch term, up to 120 characters–Search in title and message text
from, toDate or timestamp–Range by creation time; a value without offset applies in zone; a plain date in to includes that whole day
zoneTime zone name, up to 64 charactersEurope/BerlinZone in which from and to are read; day boundaries and the day after a plain date are calculated there. Unknown and with from or to: 422 with unknown_timezone

A value of the wrong type is rejected rather than ignored, with 422. The search matches substrings; % and _ are taken literally. With the Owner role the search runs on plain text, with any other role on the masked version, so you only find what your role can see. If the search on the masked version finds more than 20000 conversations, the endpoint answers 422 with search_too_broad and limit instead of shortening the list; narrow the date range or filters. Wherever the filter is logged, the search appears only as "q": true, not the term itself.

FieldContent
conversationsConversations on this page, fields see Conversation fields
totalNumber of all matches
page, per_pagePage and page size
sort, dirSorting applied
languageLanguage filter applied
languagesUp to 50 detected conversation languages, alphabetical
maskedtrue: the list is masked

Conversation fields

FieldContent
idID
assistant_id, assistantAssistant ID and name
channelwidget, test or api
hostOrigin for the widget; for test and API conversations the host name from the request’s Origin or Referer, null if neither is present
languageDetected language
titleTitle, masked except in plain text
statusopen, closed or escalated
message_countNumber of messages
created, updatedISO 8601 timestamps
frustration_scoreFrustration score of the conversation
escalation_reason, escalated_atHandover reason, masked, and time of handover
assigned_user_id, assigned_to, assigned_atAssigned person; assigned_to is their name, or their email address if there is none
taken_over_atStart of an active takeover, otherwise null
queueQueue set by routing
resolved_atTime it was resolved
flagged_atTime it was flagged, otherwise null
tagsTags, [] if none
internal_noteInternal note
maskedtrue unless the conversation is read in plain text

Single view, plain text and attachments

GET /api/v1/conversations/{id}

Returns one conversation with its history, masked. An unknown ID gives 404 with not_found.

Authentication
Session token, any role
Rate limit
no named throttle
FieldContent
conversationFields as in Conversation fields
messagesHistory made of the latest 500 messages and the latest 500 tool calls, older entries are dropped at the start; texts masked; assistant replies carry rating (1, 0 or null)
maskedtrue in this view
masked_kindsKinds of personal data masked in the history
access_logThe last 20 plain-text accesses to this conversation (conversation.revealed) with id, time, action, who and reason
attachmentsAttachments with id, name, mime, bytes, sha256, lead_id and time

In tool calls in the history the structure fields stay unmasked: id, key, kind, state, duration_ms, approval_path, needs_confirmation, chain_capped and created_at; the other fields go through masking. The values of unverified claims are masked as well. The app states: “Personal data is masked: email, phone, IBAN and postal address.”

POST /api/v1/conversations/{id}/reveal

Returns the same conversation in plain text, with masked: false. Every access is logged as conversation.revealed with its reason and then shows up in access_log.

Authentication
Session token, Owner only
Rate limit
conversations, 30 per minute
FieldRequiredRuleMeaning
reasonyes5 to 200 charactersReason for the access; in the app Reason for this access
StatuserrorMeaning
403role_too_lowRole is not Owner; required is owner.
422–Reason missing or too short; the app says “Please give a reason of at least five characters.”
404not_foundUnknown conversation.
500not_loggedNo audit log entry, so no plain text.

GET /api/v1/attachments/{id}

Downloads an attachment as a file. Every download is logged as attachment.read.

Authentication
Session token, any role
Rate limit
conversations, 30 per minute
Response or headerContent
200File with Content-Disposition: attachment; a type that is not on the allow list is sent as application/octet-stream
Content-Security-Policydefault-src 'none'; sandbox
X-Content-Type-Optionsnosniff
Cache-Controlprivate, no-store, max-age=0
Referrer-Policyno-referrer
404 not_foundUnknown attachment
410 file_missingThe record exists but the file is missing; with measured: false

CSV export

Both exports require the plan feature for conversation export and carry the conversations throttle. If your plan lacks the feature, they answer 402 plan_feature_missing, with feature set to conversation_export. Without reveal=1 the content is masked. With reveal=1 you need the Owner role and a reason. Every export is logged as conversation.exported, with the plain-text flag, reason, row count and filter; if no entry is created, only the plain-text export stops with 500 and not_logged; the masked export carries on without an entry.

GET /api/v1/conversations/export

Exports the filtered conversations as CSV, one row per conversation with the columns id, created_at, assistant, channel, language, status, messages, title and escalation. The list filters apply. In the app this is Filtered conversations as CSV.

Authentication
Session token, any role; plain text Owner only
Rate limit
conversations, 30 per minute
ParameterValuesMeaning
List filtersas abovenarrow the export
reveal0, 11 requests plain text
reasonup to 200 characters, at least 5 with reveal=1Reason for the plain-text export
ResponseContent
content-typetext/csv; charset=utf-8
content-dispositionDownload named conversations.csv
x-rowsNumber of rows
x-masked1 masked, 0 plain text
BodyColumns separated by semicolons, CRLF line endings
422 too_manyMore than 5000 matches; with total and limit. The app says “Too many conversations for one export. Please narrow the date range.”
422 reason_requiredreveal=1 without a reason of at least 5 characters; with min
403 role_too_lowreveal=1 without the Owner role
402 plan_feature_missingYour plan does not include conversation export; with feature, plan, plan_name and available_in

GET /api/v1/conversations/{id}/export

Exports the history of one conversation as CSV, one row per message or tool call, with the columns time, role, text, state, rating, tool_key and tool_state. The file name is gespraech-<id>.csv.

Authentication
Session token, any role; plain text Owner only
Rate limit
conversations, 30 per minute
ResponseContent
x-rowsNumber of rows
x-capped1 if the history was cut off at 500 messages or 500 tool calls, otherwise 0
404 not_foundUnknown conversation
Others402 plan_feature_missing, reveal, reason and their errors as for the list export

Working on conversations

Every endpoint in this section needs at least Edit. An unknown conversation ID gives 404 with not_found. Unless stated otherwise, they answer with the conversation in the conversation fields.

POST /api/v1/conversations/{id}/escalate

Hands a conversation over to the team manually: status becomes escalated, escalated_at is set and an earlier resolution is cleared. The reason is appended to any existing handover reason, 300 characters in total; without a reason the text is “Von Hand übergeben” (German). This handover also runs through the routing rules. If the conversation is already handed over, it stays unchanged, including its reason, and the rules do not run again. Either way the response contains conversation and unchanged (true if nothing was changed).

Authentication
Session token, Edit or higher
Rate limit
no named throttle
FieldRequiredRuleMeaning
reasonnoup to 200 charactersHandover reason

POST /api/v1/conversations/{id}/assign

Sets the assigned person. user_id: null or an empty string removes the assignment. An ID that does not belong to the account gives 422 with unknown_user. In the app the buttons are Take on and Release.

Authentication
Session token, Edit or higher
Rate limit
no named throttle
FieldRequiredRuleMeaning
user_idnoID, up to 64 characters, or nullAssigned person

POST /api/v1/conversations/{id}/takeover

Takes over a widget conversation and makes you the assigned person; an existing assigned_at stays. While the takeover lasts, the assistant does not answer; your messages reach the visitor as team messages. The app calls this Take over conversation and then shows “You are handling this conversation. The assistant does not answer.” Calling it again leaves the timestamps unchanged. 409 only comes while someone else in the account holds the takeover; an assignment without a takeover passes to you. A takeover by a person who no longer belongs to the account counts as free and passes to you with a new time.

Authentication
Session token, Edit or higher
Rate limit
no named throttle
StatuserrorMeaning
422not_liveThe conversation did not come from the widget.
403consent_revokedThe visitor withdrew consent, see Consent and visitor rights.
409taken_over_by_otherSomeone else holds the takeover; assigned_to names them.

POST /api/v1/conversations/{id}/release

Ends the takeover; taken_over_at becomes null and the assignment stays. In the app this is End takeover. Releasing a conversation that is not taken over is not an error. If someone else in the account holds the takeover, the endpoint answers 409 with taken_over_by_other; a takeover by a person who no longer belongs to the account can be ended.

Authentication
Session token, Edit or higher
Rate limit
no named throttle

POST /api/v1/conversations/{id}/resolve

Marks the conversation as resolved: status becomes closed, resolved_at is set, your own takeover ends (taken_over_at becomes null) and the conversation.ended webhook fires. A conversation that is already closed stays unchanged, including its first resolved_at, and fires no second event; the response then carries unchanged: true. If another person in the account is handling the conversation, you get 409 with taken_over_by_other and assigned_to. In the app this is Mark as done.

Authentication
Session token, Edit or higher
Rate limit
no named throttle

POST /api/v1/conversations/{id}/mark

Sets the flag, tags and internal note. Only what is in the body changes. In the app the fields are Flag this conversation, Tags and Internal note.

Authentication
Session token, Edit or higher
Rate limit
no named throttle
FieldRequiredRuleMeaning
flaggednotrue or falsetrue sets flagged_at, an existing flag keeps its time; false clears it
tagsnoList of up to 10 entries, each up to 40 charactersSurrounding spaces and duplicates are removed; an empty list removes all tags
notenoup to 2000 charactersInternal note; empty removes it

POST /api/v1/conversations/{id}/messages/{message}/rating

Rates an assistant reply: 1 Helpful, 0 Not helpful, null removes the rating (Not rated yet). The response contains message with id and rating.

Authentication
Session token, Edit or higher
Rate limit
conversations, 30 per minute
StatuserrorMeaning
422–rating is missing from the body or is not 0, 1 or null.
404not_foundThe message does not belong to this conversation.
422not_rateableThe message was not written by the assistant.

POST /api/v1/conversations/{id}/tool/{call}/confirm

Confirms a proposed tool call that is waiting for approval. The response contains call and duplicate. If the call is no longer in the proposed state, or another click got there first, you get the same state back with duplicate: true instead of an error. An unknown call gives 404 with not_found. Tools are covered in Tools.

Authentication
Session token, Edit or higher
Rate limit
no named throttle

POST /api/v1/conversations/{id}/tool/{call}/reject

Rejects a proposed tool call. Response and behaviour on a second click match the confirm endpoint; an unknown call gives 404 with not_found.

Authentication
Session token, Edit or higher
Rate limit
no named throttle

Create, write and delete

POST /api/v1/conversations

Creates a test conversation with channel test and status open. The 201 response contains conversation and greeting, the greeting of the active version. What test conversations are for is explained in Test and acceptance.

Authentication
Session token, Edit or higher
Rate limit
conversations, 30 per minute
FieldRequiredRuleMeaning
assistant_idyesID, up to 64 charactersAssistant for the conversation
StatuserrorMeaning
404not_foundUnknown assistant.
422no_versionThe assistant has no active version.

POST /api/v1/conversations/{id}/send

Writes a message into a conversation. During a takeover the text is stored as a team message (role operator) and the response is 201 with message (id, role, text, author, state, time). Otherwise the assistant answers, and the reply arrives as an event stream as described in Integration.

Authentication
Session token, Edit or higher
Rate limit
conversation-send, 20 per minute
FieldRequiredRuleMeaning
textyes1 to 4000 charactersMessage
StatuserrorMeaning
409taken_over_by_otherSomeone else holds the takeover; assigned_to names them.
409not_taken_overWidget conversation without a takeover: only whoever took over may write into a live visitor conversation.
422no_versionThe conversation’s assistant or version no longer exists.
409reply_in_progressA reply is already running for this conversation.

DELETE /api/v1/conversations/{id}

Deletes a conversation and logs it as conversation.deleted. The response is {"deleted": true}. Without an audit log entry the conversation stays; you then get 500 with not_logged. Retention and deletion are covered in Retention and deletion.

Authentication
Session token, Administration or higher
Rate limit
no named throttle

Retention periods

GET /api/v1/conversations/retention

Returns the periods in days: retention with general, test and api, plus default_general, the starting value for new accounts (90).

Authentication
Session token, any role
Rate limit
no named throttle

PUT /api/v1/conversations/retention

Sets the three periods and answers in the same shape as the read endpoint. The change is logged as retention.updated. In the app the fields are All conversations, Test conversations and Via the API; it also states “Retention periods can be changed by users with the Administration or Owner role.”

Authentication
Session token, Administration or higher
Rate limit
no named throttle
FieldRequiredRuleMeaning
generalyes, may be null1 to 3650Period for all conversations; null means no period set
testyes, may be null1 to 3650Test conversations; null follows the general period
apiyes, may be null1 to 3650Conversations via the API; null follows the general period

Visitor blocks

A block turns away a visitor’s future widget conversations, either by their address (address) or by their session (session). It starts from a conversation; the block stores a hash of that conversation’s address or session. How to block from within the app is described in Conversations.

GET /api/v1/visitor-blocks

Returns the latest 200 blocks in data, newest first.

Authentication
Session token, any role
Rate limit
no named throttle

POST /api/v1/visitor-blocks

Creates a block and logs it as visitor.blocked. If a block already exists for the same kind and hash, it is updated: reason and duration are overwritten, the hit counter is kept. The response is 201 with the block in data. Without an audit log entry the block is neither created nor changed; you then get 500 with not_logged.

Authentication
Session token, Administration or higher
Rate limit
no named throttle
FieldRequiredRuleMeaning
conversation_idyesUUIDConversation to block from
kindyesaddress, sessionKind of block
reasonyes8 to 200 charactersReason
daysno1 to 3650Duration in days; open-ended if omitted
StatuserrorMeaning
404not_foundUnknown conversation.
422no_addressNo value for the chosen kind is stored for the conversation.
500not_loggedNo audit log entry, no block.

DELETE /api/v1/visitor-blocks/{id}

Lifts a block, logged as visitor.unblocked. The response is {"ok": true}; an unknown block gives 404 with not_found.

Authentication
Session token, Administration or higher
Rate limit
no named throttle
FieldContent
id, kind, reasonID, kind and reason
expires_atEnd of the block or null for open-ended
activeWhether the block currently applies
hits, last_hit_atRejected attempts and the most recent one
created_at, created_byCreation time and name of the person who blocked
conversation_id, hostSource conversation and its origin

Team

In the app these endpoints sit behind Team. Roles and invitations from the interface’s point of view are described in Team and roles.

GET /api/v1/team

Returns members, open invitations and the seat count. At most 200 people are returned, sorted by email address; if the list is cut off, the app shows “The first {n} people are shown.”

Authentication
Session token, any role
Rate limit
no named throttle
FieldContent
membersPeople with id, name, email, role, available and available_at
total, cappedTotal number of people and whether the list is cut off
invitationsOpen invitations with id, email, role, expires_at and created_at
seatsSeats with limit, used, measured, reason and exceeded; if the counter cannot be read, you get measured: false with reason: no_counter

POST /api/v1/availability

Switches your own availability. available_at is set when switching on, if it is still empty, and cleared when switching off. The response contains member. In the app this is Mark me available and Mark me unavailable, with the note “Anyone marked available is shown in the inbox as available for a handover.” Until your email address is confirmed, the app shows “You can mark yourself as available once your email address is confirmed.” instead of the button.

Authentication
Session token, any role
Rate limit
no named throttle
FieldRequiredRuleMeaning
availableyestrue or falseAvailable or not

POST /api/v1/team/invitations

Invites a person. The invitation is valid for 168 hours, that is seven days, and is sent by email with a link to the Accept invitation page, in the inviting person’s language. If sending fails, the invitation is still created. Logged as team.invited; the response is 201 with invitation. In the app: Invite someone, button Send invitation.

Authentication
Session token, Administration or higher
Rate limit
invitations, 5 per minute
FieldRequiredRuleMeaning
emailyesvalid address, up to 190 charactersAddress of the invitee
roleyesviewer, editor, adminRole after accepting; Owner cannot be assigned this way
StatuserrorMeaning
422validationfields.email contains validation.already_member (already in the team) or validation.invitation_open (an invitation is still open).
403role_too_lowYour own role is below the one being assigned.
402plan_limit_reachedAll seats are taken. The app says “All seats are taken. Withdraw an invitation or move to a larger plan.”

DELETE /api/v1/team/invitations/{id}

Withdraws an open invitation; it is marked as withdrawn, not deleted. Logged as team.invitation_revoked, response {"revoked": true}. Unknown or no longer open invitations give 404 with not_found. In the app: Withdraw.

Authentication
Session token, Administration or higher
Rate limit
no named throttle

PATCH /api/v1/team/members/{id}

Changes a person’s role to viewer, editor or admin. Logged as team.role_changed; the response contains member.

Authentication
Session token, Administration or higher
Rate limit
no named throttle
StatuserrorMeaning
404not_foundThe person does not belong to the account.
422not_selfYour own role; the app says “You cannot change your own role here.”
422owner_protectedThe owner; the app says “The owner cannot be changed or removed.”
403role_too_lowYour own role is not sufficient for this change.

DELETE /api/v1/team/members/{id}

Removes a person from the team and logs it as team.member_removed. If they are currently working in this account, their session tokens are deleted; if they then belong to no other account, their login is deleted, otherwise they switch to the oldest remaining account that has not been deleted. If they are currently working in another account, their sessions there continue. Response {"removed": true}; not_self and owner_protected as for role changes. In the app: Remove, with the prompt “Remove this person from the team? Open sessions end immediately.”

Authentication
Session token, Administration or higher
Rate limit
no named throttle

POST /api/v1/team/invitations/accept

Accepts an invitation as the signed-in person. Logged as team.invitation_accepted; the response contains joined: true, tenant with id and name, and role. The app then shows “You are now part of the team.”

Authentication
Session token, any role
Rate limit
no named throttle
FieldRequiredRuleMeaning
tokenyesup to 120 charactersToken from the invitation link
StatuserrorMeaning
422invalid_tokenToken unknown, expired or withdrawn; the app says “This invitation has expired or was withdrawn. Please ask for a new one.”
403wrong_accountThe signed-in email address is not the invited one. Both addresses are compared in their normalised form, for example without a suffix after a plus sign.

Routing rules

On every handover, manual ones included, the server checks the active rules in order; the first match wins. It compares against the handover reason and the conversation title, lower-cased and without word boundaries: the keyword bill also matches “billing”. The rule sets the queue (queue) and, if nobody is assigned yet, its person; if that person no longer belongs to the account, only the queue is set. A conversation that already has a queue keeps it. If no rule matches, the queue stays empty.

In the app the area is called Routing, with the fields Queue and Keywords (several separated by commas). Without rules it shows “No rule yet. Without a rule every handover stays without a queue.” How queues appear in the Inbox is described there.

GET /api/v1/routing-rules

Returns rules in their order and max, the maximum of 50 rules.

Authentication
Session token, any role
Rate limit
no named throttle

POST /api/v1/routing-rules

Adds a rule at the end of the list; without is_active it is active. The response is 201 with rule and the whole list in rules.

Authentication
Session token, Edit or higher
Rate limit
no named throttle
FieldRequiredRuleMeaning
queueyesup to 80 charactersQueue name
keywordsyes1 to 20 entries, each up to 60 charactersKeywords
assigned_user_idnoID, up to 64 charactersPerson to assign
is_activenotrue or falseWhether the rule is checked
StatuserrorMeaning
422unknown_userThe person does not belong to the account.
422too_many_rulesThere are already 50 rules.

PUT /api/v1/routing-rules/{id}

Changes queue, keywords, person and active switch of a rule, using the same fields as for creating. If is_active is omitted, the rule becomes active again. Its position does not change. The response contains rule and the list in rules. An unknown rule gives 404 with not_found, here as well as when moving or deleting.

Authentication
Session token, Edit or higher
Rate limit
no named throttle

POST /api/v1/routing-rules/{id}/up

Moves a rule up by one place. The server assigns positions. The response contains moved, false for the top rule, and the list in rules.

Authentication
Session token, Edit or higher
Rate limit
no named throttle

DELETE /api/v1/routing-rules/{id}

Deletes a rule. The response contains deleted: true and the remaining list in rules.

Authentication
Session token, Edit or higher
Rate limit
no named throttle
FieldContent
id, positionID and place in the order
queue, keywordsQueue and keywords
assigned_user_id, assigned_toPerson and their name
is_activeActive or not

Catalogue and shop connection

Every endpoint in this section requires the plan feature for the shop connection; without it they answer 402 with plan_feature_missing. In the app this is under Catalogue, described in Catalogue and shop.

Products

GET /api/v1/catalog

Returns one page of products sorted by title in products, plus total, page and per_page. total counts every product in the account, even when search is set.

Authentication
Session token, any role
Rate limit
no named throttle
ParameterValuesDefaultEffect
searchup to 120 characters–Title or shop ID starts with the term
pagefrom 11Page
per_page1 to 20050Entries per page

POST /api/v1/catalog

Creates a product. The response is 201 with product. If the shop ID is already taken, you get 422 with identifier_taken.

Authentication
Session token, Edit or higher
Rate limit
catalog, 20 per minute
FieldRequiredRuleMeaning
external_idyesup to 100 charactersID in the shop
titleyesup to 200 charactersTitle
descriptionnoup to 800 charactersDescription
price_cents, list_price_centsnointeger, 0 to 100000000Price and list price in cents; the list price is not checked against the price
currencynoexactly three charactersCurrency, stored in capitals; EUR if omitted
image_url, product_url, cart_urlnoURL, up to 500 charactersImage, product page and cart link
is_availablenotrue or falseIn stock; true if omitted
attributesnoobjectFree-form attributes; the previous value stays if omitted
rating_tenthsno0 to 50Rating in tenths, 50 is the top rating; the output also carries rating, the tenths divided by ten
rating_countno0 to 100000000Number of ratings
delivery_daysno0 to 365Delivery time in days
featuresnoup to 6 entries, each up to 60 charactersFeatures for the product card
actionnonone, product, cartButton on the product card; product if omitted
accessories, also_boughtnoup to 6 shop IDs each, up to 100 charactersAccessories and frequently bought together

A product is returned with the product card fields (id, external_id, title, description, price_cents, currency, price_text, list_price_cents, list_price_text, saving_cents, saving_percent, image_url, product_url, is_available, rating, rating_count, delivery_days, features, action, action_url), plus attributes, action_setting, rating_tenths, cart_url, accessories, also_bought and updated_at.

PUT /api/v1/catalog/{id}

Changes a product using the same fields as for creating, except external_id, which cannot be changed. The endpoint rewrites the product: an omitted field falls back to its default or null; only attributes is kept when omitted. Response with product; an unknown product gives 404 with not_found.

Authentication
Session token, Edit or higher
Rate limit
catalog, 20 per minute

DELETE /api/v1/catalog/{id}

Deletes a product. The response is {"deleted": true}.

Authentication
Session token, Edit or higher
Rate limit
no named throttle

POST /api/v1/catalog/import

Imports up to 500 products per call, in one transaction. Matching is by external_id: a second run with the same IDs creates no duplicates but updates the existing products. The response contains new, changed and total.

Authentication
Session token, Edit or higher
Rate limit
catalog, 20 per minute
FieldRequiredRuleMeaning
productsyes1 to 500 entriesProducts with the fields used for creating

Shop connection

The connection is read-only: nothing is created, changed or ordered in the shop through it. The access key goes in once; every response shows only its last four characters in access_key_masked. Reading, saving and syncing answer with connection, an object with the fields in the following table.

FieldContent
base_urlShop address
access_key_maskedKey ending, prefixed with an ellipsis
currency, is_activeCurrency and whether the connection is active
sync_pending, sync_started_at, sync_requested_atPending sync, its start and when it was requested
last_sync_at, last_sync_ok, last_sync_error, last_success_atLast sync, its result, its error and the last successful one
countsCounts from the last sync or null

GET /api/v1/catalog/shop

Returns the account’s shop connection in connection; if none is saved, connection is null.

Authentication
Session token, any role
Rate limit
no named throttle

PUT /api/v1/catalog/shop

Saves the connection. The address is checked before it is stored, because the server later calls it itself with the key. If a changed address points at a different shop, a new key is needed. is_active changes only if it is sent. The response contains connection.

Authentication
Session token, Administration or higher
Rate limit
catalog, 20 per minute
FieldRequiredRuleMeaning
base_urlyesup to 300 characters, httpsShop address, for example https://shop.example.com
access_keyon first save and for a new shop12 to 200 characters of letters, digits, _ and -Access key
is_activenotrue or falseConnection active
StatuserrorMeaning
422url_unreadableAddress cannot be parsed.
422https_required, scheme_not_allowedThe address does not use https.
422name_unresolvableThe host name cannot be resolved.
422target_not_allowedThe address does not point to the public internet.
422access_key_requiredNew shop without a new key.

POST /api/v1/catalog/shop/test

Tests the saved connection with a sample request. Success: ok: true with currency and has_products. Failure: ok: false with error, for example key_unreadable or a reason as when saving. At most three tests per minute; after that 429 with too_many_tests, retry_after and the Retry-After header. Without a connection: 404 with connection_missing.

Authentication
Session token, Administration or higher
Rate limit
catalog, 20 per minute

POST /api/v1/catalog/shop/sync

Requests a sync, which runs as a background job (see Background jobs). The response is 202 with job_id, already_pending and connection; a second click gets the pending job back, with already_pending: true. Once no job is pending, a new request is accepted 900 seconds after the last one at the earliest; before that, requests get 429 with sync_too_soon, retry_after, connection and the Retry-After header.

Authentication
Session token, Administration or higher
Rate limit
catalog, 20 per minute
StatuserrorMeaning
404connection_missingNo connection saved.
422connection_inactiveThe connection is switched off.

DELETE /api/v1/catalog/shop

Removes the connection. The products stay in the catalogue. The response is {"deleted": true}; without a saved connection you get 404 with connection_missing.

Authentication
Session token, Administration or higher
Rate limit
no named throttle

Analytics

The following endpoints up to and including GET /api/v1/analytics/report require the plan feature for conversation analytics; without it they answer 402 with plan_feature_missing. Any role may read them. In the app this is Analytics, described in Analytics. Test conversations with channel test, that is those from the test dialog and the check conversations of Acceptance, are not included in these figures, as in the app.

ParameterValuesDefaultEffect
period7d, 30d, 90d30dPeriod in days; today counts
zoneTime zone name, up to 64 charactersEurope/BerlinZone in which days are counted. Older names that browsers still report, such as Asia/Calcutta or Europe/Kiev, are accepted and calculated under today’s name (Asia/Kolkata, Europe/Kyiv); responses that carry zone give that name as well. Upper and lower case do not matter; zone gives the usual spelling. The fixed offsets Etc/GMT+1 to Etc/GMT+12 and Etc/GMT-1 to Etc/GMT-14 are accepted with the sign used by the time zone database: Etc/GMT+1 is one hour behind UTC. An unknown zone gives 422 with unknown_timezone, and so does an offset such as +01:00. The Analytics page then shows “The service does not recognise the time zone of this device. The figures cannot be assigned to days and are therefore not shown.”
assistantUUID–Only this assistant; an unknown one gives 404 with unknown_assistant

GET /api/v1/analytics

Returns the period’s 32 metric cards in a single request: period, zone, from, to, days, assistant and cards.

Authentication
Session token, any role
Rate limit
no named throttle

Each card has value, measured and reason. value is null when measured is false; reason then gives the cause. Rates carry value as a percentage with one decimal place, plus counter and denominator. coverage is an average expressed as a percentage and carries only denominator; messages_per_conversation, reply_ms_p50 and reply_ms_p95 carry neither.

GroupCards
Volumeconversations, messages, answered, messages_per_conversation
Handover and frustrationescalations, escalation_rate, self_resolved, frustration_rate
Answer qualityabstentions, unanswered, citation_rate, coverage
Knowledge search, from the latest probe run in the period, for the whole account regardless of assistantretrieval_mrr, retrieval_hit_rate
Rules and blocksenforcement_rate, hard_topic_rate, blocked_rate
Speed and usagereply_ms_p50, reply_ms_p95, units
Toolstool_calls, tool_executed, tool_rejected, tool_errors
Casesleads, appointments, carts, abandoned, abandon_rate
Visitor ratings, counted by rating timevisitor_ratings, visitor_helpful_rate, visitor_rating_coverage
reasonMeaning
no_conversationNo conversation in the period
no_replyNo reply to compute the value from
no_searchNo knowledge search
no_probeNo probe run in the period
no_enforcement_checkNo rule check
no_tool_callNo tool call
no_ratingNo rating

GET /api/v1/analytics/timeseries

Time series per day in the chosen zone. days holds, per day, day, conversations, messages and escalations, plus citation_rate, unanswered, escalation_rate, coverage and retrieval_mrr in the shape of the metric cards; the response also carries period, zone and measured.

Authentication
Session token, any role
Rate limit
no named throttle

GET /api/v1/analytics/topics

The most frequent topics, at most 15. Each topic carries previous, its count in the equally long period immediately before, and direction (up, down or flat); if there was no topic before, previous gives the reason no_previous_question and direction is null. Without a visitor question in the period, measured is false with reason no_question; without a recognisable topic, with no_topic.

Authentication
Session token, any role
Rate limit
no named throttle

GET /api/v1/analytics/distribution

Distribution of conversations by language (languages) and by window width (viewports: handy, tablet, desktop, breit), each group with its own measured. Each entry in entries has key, conversations and share; the share refers to basis, the conversations with this information. Conversations without it are counted separately under without. Without any conversation in the period, reason is no_conversation; if a group has no information at all, it is no_language or no_viewport.

Authentication
Session token, any role
Rate limit
no named throttle

GET /api/v1/analytics/unanswered

Questions the assistant had no answer to: for the latest 50 fallback replies (fallback), the visitor question before each, masked and cut to 300 characters. questions holds conversation_id, question and when per question, plus clusters.

Authentication
Session token, any role
Rate limit
no named throttle

GET /api/v1/analytics/gaps

Detected knowledge gaps, at most 50. Unlike unanswered, an explicit refusal is told apart from a reply without a source. If the question text is no longer available, the gap is still counted; question is then null and question_available is false. Each gap has kind (refused or uncited); refused, uncited and total count all gaps in the period.

Authentication
Session token, any role
Rate limit
no named throttle

GET /api/v1/analytics/blocks

Blocked cases in the period, at most 50: what was turned away and where a limit applied, broken down by topic. Each case in cases has kind (blocked or enforced), topics, hard and when; topics counts per topic, plus checked, enforced, hard, blocked and total.

Authentication
Session token, any role
Rate limit
no named throttle

GET /api/v1/analytics/dropoffs

Drop-off points and goal events with their position in the conversation; the position is the message’s sequence number, the first message being position 1. A drop-off is an open conversation that was not handed over, has at least one visitor message, has had no message for 30 minutes (minutes) and has no enquiry, appointment or cart. The response contains dropoffs, positions, goals with kind lead, appointment or cart, total and goals_total. The distribution covers all cases in the period, the list only the latest 50; total gives the full count.

Authentication
Session token, any role
Rate limit
no named throttle

GET /api/v1/analytics/recommendations

One suggestion per anomaly in the period, with the figure and the area it concerns. It makes no statement about the effect of a change. Each entry in recommendations has kind, count and target (wissen, assistenten or werkzeuge); only kinds with at least one case are listed, the most frequent first. kinds gives the number of possible kinds, total the number listed.

Authentication
Session token, any role
Rate limit
no named throttle

GET /api/v1/analytics/versions

The published versions of an assistant, highest number first, at most 50. The response contains assistant, versions with id, number and published_at, plus count and capped; period and zone have no effect here. Without assistant the endpoint answers 422 with assistant_required.

Authentication
Session token, any role
Rate limit
no named throttle

GET /api/v1/analytics/comparison

Compares a window before a version was published with an equally long one after it, computed over the time and the assistant of that version. The window after ends at the time of the request at the latest; if it is shorter for that reason, after carries partial: true. The response contains version, comparable, before, after and delta; delta gives the difference and direction (up, down or flat) per card, not an effect. If one side has no measured value, the card shows side_not_measured. If the version is not published, comparable is false with reason not_published; if the publication lies in the future, with no_window. The version parameter (UUID) is required; an unknown version gives 404 with unknown_version.

Authentication
Session token, any role
Rate limit
no named throttle

GET /api/v1/analytics/report

Builds a report from metrics, time series, topics, unanswered questions, gaps, blocked cases and drop-offs (metrics, timeseries, topics, unanswered, gaps, blocks, dropoffs) with its timestamp in created. The report is computed fresh, not taken from the cache.

Authentication
Session token, any role
Rate limit
conversations, 30 per minute

GET /api/v1/analytics/subscription

Shows the weekly report: weekly_report (true if not set), period 7d, day monday and recipient, the owner’s email address if it is confirmed, otherwise null.

Authentication
Session token
Rate limit
no named throttle

PUT /api/v1/analytics/subscription

Subscribes to or unsubscribes from the weekly report. Only the owner can switch it, because the email goes to them. Logged as report.subscription.updated, response in the same shape as the read endpoint. Without an audit log entry the setting stays unchanged; you then get 500 with not_logged.

Authentication
Session token, Owner only
Rate limit
no named throttle
FieldRequiredRuleMeaning
weekly_reportyestrue or falseWeekly report on or off

Cases

Cases are enquiries (leads), appointments (appointments) and carts (carts). In the app they are under Cases, described in Appointments and cases. Changing the status of appointments and carts requires the plan feature for appointments and enquiries; without it this endpoint answers 402 with plan_feature_missing. Reading and deleting work in every plan.

GET /api/v1/records

Counts cases in two groups: pending with the open ones, that is leads (new), appointments (requested) and carts (open), and total with all of them, split by the same three kinds. plan_locked lists the kinds without the plan feature, whose status cannot be changed; they are counted as well. available_from names the plan from which all locked kinds are included, available_from_name its name; both are null when nothing is locked or no such plan can be named.

Authentication
Session token, any role
Rate limit
no named throttle

GET /api/v1/records/{type}

Returns one page of a kind, newest first, with entries, total, page and per_page. An unknown kind gives 404 with unknown_kind.

Authentication
Session token, any role
Rate limit
no named throttle
ParameterValuesDefaultEffect
typeleads, appointments, carts–Kind, in the path
statusup to 20 characters–Only this state
pagefrom 11Page
per_page1 to 20050Entries per page

POST /api/v1/records/{type}/{id}/status

Sets the state of a case. The response contains entry. Setting an appointment to confirmed fires the appointment.confirmed webhook with id and starts_at, and the response also contains confirmation_mail. An unknown case gives 404 with not_found. If a cancelled appointment is set to requested or confirmed and its time has been taken in the meantime, the result is 409 with appointment_slot_taken. If the appointment lock of the account is busy while an appointment is set to requested or confirmed, the result is 503 with appointment_lock_busy and Retry-After: 1. In both cases the appointment stays unchanged.

Authentication
Session token, Edit or higher
Rate limit
no named throttle

DELETE /api/v1/records/{type}/{id}

Deletes a single case, for example when a visitor asks for their details to be erased, and logs this beforehand as record.deleted with kind, ID and counts, without name, email address or phone. Without an audit log entry the case stays; you then get 500 with not_logged. There is no plan check here: appointments and baskets can be deleted even without the appointment features. Together with the case, undelivered notifications about it to connected systems (lead.created, appointment.requested, appointment.confirmed) go, and for an appointment any reminders and confirmations not yet sent; their row in the delivery log is then set to failed. Visitor attachments stay with the conversation. With with_conversation=1 the conversation the case came from goes as well, with all messages and attachments. The response contains deleted, kind, id, conversation_deleted and counts. An unknown kind gives 404 with unknown_kind, an unknown case 404 with not_found.

Authentication
Session token, Administration or higher, not during an impersonation
Rate limit
5 per minute and user, shared by export, retention periods, objections and deletion
KindStatesFields in addition to id, conversation_id, status, created_at
leadsnew, seen, donename, email, phone, subject, source, fields
appointmentsrequested, confirmed, cancelledtype, starts_at, duration_min, name, email, phone, subject
cartsopen, claimed, expireditems, subtotal_cents, discount_cents, coupon_code, shipping_cents, total_cents, currency, pickup_token_set (only whether a pickup token is set, not the token)
confirmation_mailMeaning
queuedConfirmation to the visitor queued
unchangedAppointment was already confirmed, no new email
supersededAppointment is no longer confirmed
no_emailNo email address on the appointment
pastAppointment has already started, no confirmation is sent
duplicateA confirmation for this start time already exists
failedQueuing failed

Audit log

GET /api/v1/activity

Returns the audit log, 25 entries per page, newest first. In the app this is Audit log; for entries with who_kind api_key it shows API key “{name}” under Who, with the name from who.

Authentication
Session token, Administration or higher
Rate limit
no named throttle
ParameterValuesEffect
actionup to 80 charactersOnly this action
page1 to 1000Page
FieldContent
entriesEntries with id, time, action, who, who_kind, subject_type, subject_id and data. who is the person’s email address, with who_kind user. If an entry records the triggering API key in data.api_key, for example when a knowledge source is created through the API, it holds the key’s name at the time of the action, or its prefix if there is no name, with who_kind api_key. Otherwise both are null.
actionsThe actions present in the log, for a filter
total, page, pagesNumber of entries, page and page count

Among others, the endpoints on this page write conversation.revealed, conversation.exported, conversation.deleted, attachment.read, retention.updated, visitor.blocked, visitor.unblocked, team.invited, team.invitation_revoked, team.role_changed, team.member_removed, team.invitation_accepted, routing.rule_created, routing.rule_updated, routing.rule_moved, routing.rule_deleted and report.subscription.updated.

Frequent questions

Why are texts masked in the conversation list?

The list and the single view mask email addresses, phone numbers, IBANs and postal addresses, and so does the export unless it is requested with reveal=1. Only the owner can fetch a conversation in plain text, via POST /api/v1/conversations/{id}/reveal with a reason of at least five characters; the access is logged.

What does search_too_broad mean?

Without plain-text rights the search runs on the masked version. If it finds more than 20000 conversations there, the endpoint stops with 422. Set from and to or more filters, then search again.

Why does analytics still show the old figures after a change?

The analytics endpoints keep their response for 60 seconds. After that, the next request computes afresh. The report at GET /api/v1/analytics/report is computed fresh.

Why can I not write into a widget conversation?

Only whoever has taken over a live visitor conversation can write into it. Call POST /api/v1/conversations/{id}/takeover first; without a takeover the send endpoint answers 409 with not_taken_over.

What happens to the products when I remove the shop connection?

They stay in the catalogue. DELETE /api/v1/catalog/shop removes only the connection; delete products one at a time with DELETE /api/v1/catalog/{id}.

Something still unclear?

Tell us which page and which step you mean. We reply personally.

Enquiry in three steps

  1. 1. Topic
  2. 2. Your case
  3. 3. Contact

What is this about?

One click is enough, everything else is optional.

Tell us briefly about your case

Everything in this step is optional. It helps us reply with something concrete straight away instead of asking back.

Enquiries per month, roughly (optional)
When should it start? (optional)
How can we reach you?

Name and email address are enough. We reply personally.

We use your details to handle your enquiry. More on this in the privacy policy.