Skip to content
XICBOT

Settings

Deutsch
Documentation · API reference

Public API endpoints

Sign-up, sign-in, password reset, status, enquiries, demos, invitations and cart pickup: every API endpoint you can call without a session.

In plain language

The endpoints on this page need no sign-in. You use them to create an account and to sign in. You also reset a lost password there. You accept an invitation. Other endpoints show the state of the service and its changes. The contact form and the demo belong here too. Your shop uses one of these endpoints to fetch a cart.

Last updated

All documentation topics

The endpoints on this page work without a session and without a widget key. They cover everything that happens before sign-in – creating an account, confirming the address, signing in, resetting a password, accepting an invitation – and what can be read from outside an account: service status, changelog, accessibility statement, enquiries, demos and the pickup of a cart by your shop. Widget endpoints are described in Widget endpoints, authentication for every other endpoint in API authentication.

Overview

MethodPathPurposeLimit per origin (counter)
POST/api/v1/registerCreate an account and tenant5 per minute, 20 per hour (register)
POST/api/v1/verifyConfirm the email address30 per minute (auth-token)
POST/api/v1/loginSign in, including the second stepFailed attempts: 5 per minute and 20 per hour per address and origin; wrong credentials also count towards 30 per minute and 300 per hour per origin
POST/api/v1/password/forgotRequest a password reset link20 per hour (password-forgot), plus 3 per hour per address and origin
POST/api/v1/password/resetSet a new password30 per minute (auth-token)
GET/api/v1/public/statusService status30 per minute (status)
GET/api/v1/public/changelogChangelog30 per minute (status)
POST/api/v1/public/enquirySend an enquiry5 per minute (enquiry)
POST/api/v1/public/demoCreate a demo2 per minute (demo), at most 3 new demos per IP address within 24 hours
GET/api/v1/public/demo/{id}State of a demo30 per minute (status)
GET/api/v1/public/accessibilityAccessibility statement30 per minute (status)
GET/api/v1/invitation/{token}Look up an invitation20 per minute (invitation)
POST/api/v1/invitation/acceptAccept an invitation with a new account20 per minute (invitation)
GET/api/v1/cart/pickup/{token}Pick up a cart60 per minute (cart-pickup)

Common rules

Every path starts with https://app.xicbot.de/api/v1/; the app calls them from that origin. Send JSON with Content-Type: application/json and Accept: application/json. Errors below /api/ are returned as JSON, unknown paths included. Field validation responds with 422, a text in message and, for each field, a list of messages in errors.

Rate limits

Requests are counted per origin: an IPv4 address on its own, an IPv6 address truncated to its /64 network. An IPv4 address in IPv6 notation such as ::ffff:192.0.2.10 counts as IPv4. The per-minute and per-hour limits are separate counters, and both must have room left. Endpoints with the same counter share it: status covers service status, changelog, demo lookup and accessibility together, auth-token covers confirmation and password reset, invitation covers both invitation endpoints. Once a limit is reached, the endpoint answers 429 with a Retry-After header in seconds.

Sign-in and password requests also keep counters of their own. Their 429 carries {"error": "too_many_attempts", "retry_in": …}, where retry_in gives the seconds until the next attempt is allowed. Limits for all other endpoints are listed in API limits.

Error responses

StatusBodyMeaning
404{"error": "route_unknown"}The path does not exist.
405{"error": "method_not_allowed", "allowed": […]}The path exists but not with this method; allowed lists the permitted methods.
422message and errors, or {"error": "…"}Invalid fields, an invalid token or a request refused on its merits.
429Retry-After header; for sign-in and password requests too_many_attempts with retry_inLimit reached.
500 and abovemessage reads ServerfehlerUnexpected server error. Exception class, file, line and stack trace are removed. Only an existing message is replaced; a response without message does not get one.

Calls from the browser

From a browser on another site, only the endpoints below /api/v1/public/ can be read, and only from the admitted origins; origins, methods, headers and the preflight lifetime are listed in API basics. Sign-up, sign-in, password and invitation endpoints are closed to other origins; cart pickup sets its own rules, as described further down.

Create an account

POST /api/v1/register

Creates an account together with a new tenant named after the company, makes the account its owner and sends a confirmation link to the address. Responds 201 with tenant (id, name, slug), user (id, email, role) confirmation_pending: true and mail_sent (whether the message with the confirmation link is queued). The account gets the role owner, and the tenant's slug is derived from the company name.

Authentication
None
Rate limit
5 per minute and 20 per hour per origin
FieldRequiredRule
emailyesvalid address according to the RFC, at most 190 characters
passwordyesat least 12 characters, including letters and digits
password_confirmationyessame as password
companyyesat most 160 characters; becomes the tenant name
namenoat most 120 characters
localenode or en, de if omitted; sets the language of the confirmation email

The address is normalised before it is stored: surrounding whitespace removed, Unicode normalisation form C, lower case, and without the suffix after a plus sign. For providers that ignore dots in the local part, the dots are removed as well. Where a provider serves the same mailbox under two domains, the address counts as the same under both. Anna+Test@example.com therefore becomes anna@example.com. A quoted local part is left unfolded. If the normalised address is already taken, including by a deleted account, the endpoint responds with 422 and {"error": "validation", "fields": {"email": ["validation.unique"]}}. In the app this step is called Create account, and the password rule reads At least 12 characters, including letters and digits.

POST /api/v1/verify

Confirms the address with the token from the link: {"token": "…"}, at most 120 characters. Responds 200 with {"confirmed": true}. An unknown token, one already used or one older than 48 hours yields 422 with invalid_token. In the app, this is the Confirm your address page.

Authentication
None
Rate limit
30 per minute per origin, counter auth-token

Sign in

POST /api/v1/login

Signs in with address and password and returns a token that you send to the other endpoints as Authorization: Bearer …. The address is normalised the same way as at sign-up. Responds 200 with token, expires_at and user.

Authentication
None
Rate limit
Failed attempts: 5 per minute and 20 per hour per address and origin; wrong credentials also count towards 30 per minute and 300 per hour per origin
FieldRequiredContent
emailyesaddress of the account, at most 190 characters
passwordyes, unless challenge is sentpassword of the account
totpnocode from the authenticator app, at most 10 characters
challengein the second stepchallenge from the 428 response, at most 80 characters
webauthnnoobject with id (at most 1400 characters), client_data_json, authenticator_data and signature (at most 8000 characters each)
localenode or en; on a successful sign-in it becomes the account language, which is used for example for invitations you send and for the email after a password change. Any other value leaves the current language in place; sign-in does not fail because of it

Second factor

  1. Step 1

    Send the password

    If the account has a second factor and totp is missing, the endpoint responds with 428. error is totp_required when an authenticator app is set up, otherwise webauthn_required. The response also carries challenge, expires_in set to 600 seconds and methods, which lists the factors you can use in the second step: totp, webauthn or both. If the account only has security keys and sign-in with keys is not set up on the server, this step already returns 503 webauthn_unconfigured instead of 428. If an authenticator app is set up, you can send totp together with the password and skip the second step; on an account without an app, a code sent along yields bad_totp.

  2. Step 2

    Prepare the security key

    If security keys are registered and sign-in with keys is set up on the server, the response contains a webauthn object with challenge, rp_id, allow (the permitted credential IDs) and timeout, which is at least 30. Use it to request confirmation on the key in the browser.

  3. Step 3

    Send the proof

    Send email, challenge and either totp or webauthn, without the password and from the same IP address as in the first step. The challenge is valid for 600 seconds and is discarded on the fifth failed attempt; a wrong code, a rejected key and a request from a different IP address all count as failed attempts. Such a request gets 422 challenge_expired, but the challenge remains usable from the original address. Once it has expired or been used up, the endpoint also answers 422 challenge_expired, and you start again with the password.

StatuserrorMeaning
200–signed in; token, expires_at and user
422bad_credentialswrong address or password
428totp_required, webauthn_requiredsecond factor required
422bad_totpwrong code
422bad_webauthnkey rejected; reason states why, for example kennung_unbekannt
422challenge_expiredchallenge expired or used up, or request from a different IP address
422webauthn_not_offeredkey signature on a challenge without a key path (methods without webauthn); not a failed attempt
503webauthn_unconfiguredthe service does not accept security keys because sign-in with keys is not set up on the server; for an account with security keys only, already in response to the password
403tenant_paused, tenant_suspended, tenant_deletedtenant paused, suspended or deleted; reported only after valid credentials and second factor
429too_many_attemptstoo many failed attempts; retry_in in seconds
Field in userContent
id, email, name, role, localebasic account data
totp_activeauthenticator app set up
webauthn_keysnumber of registered security keys
email_verifiedaddress confirmed
operatoraccount of the platform operator
available, available_atwhether the account currently accepts handovers from conversations, and since when; if it does not, available_at is null

The token is valid for 30 days; the exact time is in expires_at. It is named after the browser identification in User-Agent, at most 190 characters, or sitzung if none was sent. Every failed attempt counts against the counter for address and origin: wrong credentials, a wrong code, a rejected key, and an expired challenge or one sent from a different IP address. The counter for the origin alone counts only wrong credentials. A successful sign-in resets the counter for address and origin, but not the one for the origin alone. On a 429 the app shows Too many attempts. Please wait a moment., and for a blocked tenant This account is locked. Please write to mail@xicbot.de. Sessions and the second factor are covered in Sign-in and access and Account security.

Reset a password

POST /api/v1/password/forgot

Requests a link for a new password. Fields: email (required, at most 190 characters) and locale (de or en; the account language applies if omitted). The response is 200 with {"ok": true} whether or not the account exists. Only accounts that have a password receive an email.

Authentication
None
Rate limit
20 per hour per origin; 3 per hour per address and origin; at most 10 emails per hour per mailbox

The link points to https://app.xicbot.de/kennwort/neu?token=…&sprache=en and is valid for 60 minutes. Each newly sent link replaces the previous one. At most ten links per hour go out to the same mailbox, whatever network they are requested from; beyond that the response stays 200, but no email is sent, no new link is created and the last link sent remains valid. Only the two per-origin limits answer 429: the one for address and origin combined (3 per hour, counting every request whether or not the account exists) with too_many_attempts and retry_in, and the one for the origin alone (20 per hour) with a Retry-After header. In the app, the entry point and the button are labelled Forgot your password? and Request link.

POST /api/v1/password/reset

Sets the new password. Fields: token (at most 120 characters), password and password_confirmation, following the same rules as at sign-up. Responds 200 with {"ok": true, "sessions_ended": true}: the link has been used up, and every token the account received from sign-in or an invitation has been deleted, which ends its open sessions. The tenant's API keys are separate and stay in place. An unknown or expired token yields 422 invalid_token. The button in the app is labelled Save password.

Authentication
None
Rate limit
30 per minute per origin, counter auth-token

Service status and changelog

GET /api/v1/public/status

Returns state, measured, parts and time. parts normally lists nine parts of the service, each with name and state; if the measurement fails as a whole, it contains a single part total with unknown. The response is cached for up to 20 seconds. The HTTP status follows the overall state in state.

Authentication
None
Rate limit
30 per minute per origin, counter status
stateMeaningHTTP
operationalevery part measured and fine200
degradedall parts measured, at least one impaired503
unknownat least one part not measured503

GET /api/v1/public/changelog

Returns the changelog with measured, reason, version (the newest version) and entries. Each entry has version, date, kind, title and points. kind is feature, fix, security or note; any other value appears as note. Entries lacking a version, date or title are dropped. Sorting is by date, newest first, and by version within the same date. If the changelog cannot be read, the endpoint answers 503 with measured: false, a short German reason in reason and an empty entries array.

Authentication
None
Rate limit
30 per minute per origin, counter status

Enquiry

POST /api/v1/public/enquiry

Accepts an enquiry as sent by the form on xicbot.de. The enquiry is stored and delivered by email. Responds 202 with {"accepted": true}; the sender learns nothing beyond that, neither an ID nor the delivery state.

Authentication
None
Rate limit
5 per minute per origin
FieldRequiredRule
nameyes2 to 120 characters
emailyesvalid address according to the RFC, at most 190 characters
companynoat most 160 characters
topicnoa key from the topic table
packagenostart, pro or custom: the plan whose card in the price band the enquiry came from. It appears in the email as the first line of the “Project” block; any other value yields 422
detailsnoobject with keys from the details table only; other keys yield 422
messageno10 to 5000 characters
languagenode or en, de if omitted
homepagenotrap field for bots, must stay empty; at most 200 characters
topicTopic in the email
website-assistantWebsite assistant
shop-assistantShop assistant
booking-assistantBooking assistant
lead-assistantLead assistant
support-assistantSupport assistant
tool-controlTool control
chatbot-replacementReplace an existing chatbot
otherSomething else
Key in detailsLabel in the emailRule
websiteWebsite or shopat most 190 characters
platformShop systemat most 120 characters
sourcesSources for answersat most 300 characters
calendarAppointments todayat most 300 characters
goalNeeded from a prospectat most 300 characters
ticket_systemSupport requests todayat most 300 characters
systemsSystems to connectat most 300 characters
actionsActions in themat most 300 characters
gapMissing from the current chatat most 300 characters
volumeEnquiries per monthlt-100 (under 100), 100-500 (100 to 500), 500-2000 (500 to 2,000), gt-2000 (over 2,000), unknown (Not sure)
timeframeStartasap (As soon as possible), quarter (In 1–3 months), half-year (In 3–6 months), open (Not decided yet)

Demo

POST /api/v1/public/demo

Creates a time-limited demo for a public website: a tenant with an assistant that answers only from that website's content, and a widget that runs only on that website. The website is read in the background. Fields: url (required, http or https, at most 500 characters), email (at most 190 characters, stored with the demo) and language (de or en, de if omitted; the assistant's language). Responds 201 with a demo object, or 200 if a demo for the same website is already running.

Authentication
None
Rate limit
2 per minute per origin; at most 3 new demos per IP address within 24 hours

What counts is the host of the address: lower case, without www. and without the scheme's default port. https://www.example.com/contact and https://example.com/ therefore lead to the same demo for example.com; a non-default port stays part of the host. If an active demo for this host is already running, you get it back with existing: true; nothing new is created and nothing counts against the daily limit. The demo widget is restricted to the host without its port.

StatuserrorMeaning
201–demo created
200–a demo for this host was already running (existing: true)
422invalid_urladdress without a usable host
422scheme_not_allowed, url_unreadable, name_unresolvable, target_not_allowedscheme other than http or https, unreadable address, name that does not resolve or target not permitted
429demo_ip_limitthree demos from this IP address within 24 hours already
503demo_capacity50 demos are running at the same time
500demo_unconfigureddemos are not set up on the server
Field in demoContent
idID of the demo for the lookup
hosthost the demo applies to
expires_atexpiry, 72 hours after creation
expiredtrue once the period is over
widget_keypublic key of the demo widget
assistantid, name and status of the assistant
knowledgereading progress with status, documents and chunks; new and running mean “still in progress”, not “empty”
existingon creation only: whether the demo existed already
jobwhen a new demo is created only: ID of the job that reads the website

GET /api/v1/public/demo/{id}

Returns the current state as demo with the fields above, without existing and job. An expired demo is not an error: the response stays 200 with expired: true and still states the expiry. 404 not_found if the ID does not belong to a demo or the demo is no longer active.

Authentication
None
Rate limit
30 per minute per origin, counter status

Accessibility statement

GET /api/v1/public/accessibility

Returns the app's accessibility statement as an accessibility object with the text fields standard, conformance, checked_on, version, method, scope and feedback and the lists measures and limitations. The page https://app.xicbot.de/barrierefreiheit reads it without signing in. If no statement is configured, the endpoint answers 503 accessibility_missing instead of returning empty text.

Authentication
None
Rate limit
30 per minute per origin, counter status

Accept an invitation

An invitation from a team arrives as the link https://app.xicbot.de/einladung?token=…&sprache=en and is valid for 168 hours. The token has 48 characters; only its SHA-256 value is stored. How invitations are created and what each role may do is covered in Team and roles.

GET /api/v1/invitation/{token}

Shows what a token stands for: tenant (the tenant name), email, role (viewer, editor or admin), expires_at and account_exists, which tells whether an account for this address exists already; a deleted account does not count. If the invitation is unknown, expired, accepted or withdrawn, the endpoint answers 404 invalid_token.

Authentication
None
Rate limit
20 per minute per origin, counter invitation

POST /api/v1/invitation/accept

Accepts the invitation with a new account. Fields: token (at most 120 characters), password and password_confirmation following the sign-up rules, name (at most 120 characters) and locale (de or en, de if omitted). The address counts as confirmed. Responds 201 with token (valid for 30 days), expires_at, user (id, email, name, role, locale) and tenant (id, name). 422 invalid_token if the invitation is no longer open or the inviting account has been deleted; 409 login_required with email if an account for the address exists already and has not been deleted. A deleted account with this address is restored and starts like a new one: with the password from this request, without two-factor authentication, without security keys and without earlier tokens. Without a log entry neither the account nor the membership is created, the invitation stays open, and the response is 500 with just {"error":"not_logged"}, without message.

Authentication
None
Rate limit
20 per minute per origin, counter invitation

Pick up a cart

When the assistant puts a cart together during a conversation, the cart tool gives the visitor an address for your shop as next_url: the handover address configured there with ?token=… appended, or &token=… if the address already contains a query. Until then, the cart is held by XICBOT. The widget shows the link Continue, which opens next_url in a new tab. Your shop uses the token to read the cart and builds its own cart from it. If the assistant changes the open cart, the token stays the same; if it puts the cart together from scratch, a new cart with a new token is created. Setup is described in Catalog and shop and Tools.

GET /api/v1/cart/pickup/{token}

Returns the cart for the token. The token consists of 40 letters and digits and is the only means of access. The endpoint only reads: it changes neither the cart nor its expiry and can safely be repeated. Responses with status 200, 404 and 410 carry Cache-Control: no-store.

Authentication
None; the token in the path
Rate limit
60 per minute per origin
FieldContent
itemsline items with external_id, title, amount and unit_price_cents; price_source is live (just read from the shop) or stand (taken from the catalogue) and is missing in older carts
subtotal_cents, discount_cents, shipping_cents, total_centsamounts in cents
coupon_coderedeemed coupon code
currencycurrency of the cart
created_at, updated_atcreation and last change
expires_atend of the pickup period
StatuserrorMeaning
200–the cart
404unknown_cartunknown or malformed token, or the tenant is not active
410cart_expiredthe token was valid, but the pickup period is over

The period is 7 days from the last change; whenever the assistant changes the cart during the conversation, it starts again. The response contains no ID of the tenant, the conversation or the handover and nothing about the visitor: holding the token gives access to the cart, not to the conversation.

Shell command bash

curl https://app.xicbot.de/api/v1/cart/pickup/<token> \
  -H 'Accept: application/json'

Frequent questions

Why does the status endpoint return 503 while the app is reachable?

The endpoint answers 200 only when every part was measured and is fine. If one is impaired, state is degraded; if one was not measured, it is unknown. parts shows which part it is.

Does the password request reveal whether an account exists?

No. The response is 200 with {"ok": true} whether or not the account exists and whether or not an email goes out. The limits that answer 429 count every request, regardless of whether the account exists.

How long is a token from sign-in or an invitation valid?

30 days; the exact time is in expires_at. Setting a new password through /api/v1/password/reset deletes every token of the account at once; API keys are not affected.

Can a page pick up the cart directly in the browser?

Only from the origin of the handover address configured in the assistant's cart tool. Any other page is refused. The intended caller is the shop's server.

What happens if the same website is requested as a demo twice?

If an active demo for the host is still running, it is returned with 200 and existing: true. No second demo is created, and the request does not count towards the limit of three demos per IP address.

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.