Skip to content
XICBOT

Settings

Deutsch
Documentation · API reference

API authentication: sessions, keys and permissions

Session tokens, API keys and the chat window key: how to authenticate against the API, which scopes, roles and caps apply, and which errors you get back.

In plain language

Anyone who uses the API must prove who they are. There are several ways to do this. The app itself uses a token from the sign-in. Your own systems use a key. The chat window has its own public key. Each way only opens its own part. After that, the role and the plan also count. The page explains each step and each refusal.

Last updated

All documentation topics

The API at https://app.xicbot.de/api/v1/ accepts three kinds of credentials, and each one opens only its own branch: the session token you get from signing in covers everything the application itself does, an API key connects your own systems under /api/v1/integration/, and the chat window uses its own public key. After that, the account status, a confirmed email address, the role, the plan and the key's scopes decide whether a request gets through. This page walks through each of these checks and the rejections they produce.

The three credentials at a glance

CredentialHow it is sentOpensValid for
Session tokenHeader Authorization: Bearer <token>the application routes under /api/v1/30 days from sign-in; an operator impersonation 4 hours
API keyHeader X-Api-Key: xbot_…only /api/v1/integration/…until the chosen expiry or until revoked; without an expiry it stays valid
Chat window public keyin the path: /api/v1/public/widget/{key}/…only the chat window routes of that one assistantuntil a new key is generated

The credentials are not interchangeable. Under /api/v1/integration/ only the X-Api-Key header counts; a session token sent there gets 401 with key_missing. Conversely, an API key is not a session token: sent in the Authorization header to an application route it gets 401. Routes that need no credentials at all (sign-in, status page, enquiry form, demo, legal texts, invitation, cart) are covered in Public endpoints.

There is one version of the API, v1, and it is part of the path. An address such as /api/v2/… gets the same answer as a made-up one: 404 with route_unknown. If the path exists but not for the method you used, the answer is 405 with method_not_allowed, and allowed lists the methods the path accepts. You do not need an Accept header; under /api/ the API answers in JSON, including the 401 for missing credentials. Response and error format: API basics.

Session token from signing in

POST /api/v1/login with email address and password returns token, expires_at and user on success. Every sign-in gets its own token, valid for 30 days. It is named after the browser's or program's identifier (User-Agent, up to 190 characters), or sitzung if there is none. Send the token with every further request as Authorization: Bearer <token>. The individual sign-in fields are listed under Public endpoints.

  • 422 with bad_credentials: the address or the password is wrong.
  • 428 with totp_required or webauthn_required: a second factor is set up. The response carries challenge, expires_in (600 seconds) and methods, and if a security key is registered also a webauthn object with challenge, rp_id, allow and timeout; see Account security.
  • 422 with bad_totp: the second-factor code is wrong.
  • 422 with challenge_expired: the challenge from the 428 response is no longer valid, because the 600 seconds have passed, because the second step comes from a different IP address than the first, or because the user no longer has a second factor.
  • 422 with bad_webauthn and reason: the sign-in with the security key was not accepted, and reason says why. 422 with webauthn_not_offered: the second call sends a key signature although the 428 response had no webauthn in methods. 503 with webauthn_unconfigured: signing in with a security key is currently not available.
  • 429 with too_many_attempts and retry_in: too many attempts, see API limits.
  • 403 with tenant_paused, tenant_suspended or tenant_deleted: the account is not active and the user belongs to no other active account; no token is issued. If the user belongs to another active account, sign-in logs them in there, an account they own first, otherwise the one they have belonged to longest; the switch is logged there with cause login, and a running impersonation ends. This answer only comes after correct credentials and a passed second factor, and unlike on later requests it has no message field.

GET /api/v1/me shows what a token is allowed to do. What counts are user.role, user.email_verified and user.operator together with tenant.status, tenant.plan and tenant.features; a user without an assigned account gets tenant as null. The route and all of its fields are described in Account API.

Shell command bash

curl -s https://app.xicbot.de/api/v1/me \
  -H "Authorization: Bearer <token>"

If the token is missing, expired or deleted, every route that needs credentials answers 401. The application keeps its token in the browser's local storage; on a 401 it ends the session and the sign-in page shows Your session has expired. Please sign in again. POST /api/v1/logout deletes exactly the token it is called with. Called with an impersonation token, this counts as the end of the impersonation: the log shows impersonation.end, not a sign-out. Sign-ins on other devices stay in place.

POST /api/v1/invitation/accept accepts an invitation as long as there is no user account for the invited address yet; a deleted account does not count. It 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. You set a password of at least 12 characters with letters and digits, confirmed in password_confirmation. The response comes with 201 and a token valid for 30 days, named einladung; it carries tenant in addition to token, expires_at and user. Without a log entry neither the account nor the membership is created, the invitation stays open, and the response is 500 with not_logged. If a user account that has not been deleted already exists for the address, the answer is 409 with login_required and email. In that case, sign in and accept the invitation with POST /api/v1/team/invitations/accept; this route does not require a confirmed email address, and an address that is still unconfirmed counts as confirmed once the invitation is accepted. It answers 403 with wrong_account if you are signed in with a different address than the invited one. Without a log entry the account does not join, and the response is 500 with not_logged. Inviting people and assigning roles: Team and roles.

API keys for your own systems

An API key belongs to the account and opens only /api/v1/integration/. It consists of xbot_ followed by 48 random letters and digits and goes into the X-Api-Key header; keys cannot be sent as Authorization: Bearer. The application puts it as A key is shown in plain text exactly once. Only its fingerprint is stored. What is kept is the SHA-256 fingerprint and the first twelve characters, which remain visible as Identifier so you can tell keys apart in the list.

The API checks a key in a fixed order; the first step that fails produces the answer:

StepCaseStatusError
1api_key or key in the address400key_in_url
2X-Api-Key header missing, empty or not starting with xbot_401key_missing
3key unknown, revoked or expired; all three get the same answer401invalid_key
4account not active, whether paused, suspended or deleted403tenant_suspended
5the account's plan lacks the feature for keys and webhooks, for example after a plan change402plan_feature_missing
6per-minute or per-day cap reached429too_many_requests
7the route needs a scope the key does not carry403read_only or scope_missing

Per-minute and per-day caps

Every key has a per-minute cap taken from Requests per minute and a daily cap whose value is set on the account; both are counted per key. Once either is reached, the API answers 429 with too_many_requests and gives the wait time in retry_after and in the Retry-After header. Values, time windows, how requests are counted and a sample response are in API limits.

To try out a new key, call GET /api/v1/integration/status. It needs no scope, reads no data and returns the account, the scopes and the per-minute cap of the key. Response and example are in Integration endpoints.

Key scopes

The scopes decide which routes a key can reach. Each scope means exactly what it says: leads.write does not include leads. To create and read enquiries you need both. The application puts it as Writing is a separate scope. Without it a key only reads. Paths are relative to /api/v1/integration/:

ScopeIn the applicationRoutes
conversationsRead conversationsGET conversations, GET conversations/{id}
leadsRead enquiriesGET leads
leads.writeCreate enquiriesPOST leads
appointmentsRead appointmentsGET appointments
analyticsRead analyticsGET analytics
knowledge.writeCreate knowledge sourcesPOST knowledge/sources
chatUse chatPOST chat
no scope needed-GET status

If the key lacks the required scope, the route answers 403 and required names the scope. If it ends in .write the error is read_only, otherwise scope_missing.

A key's scopes, name and per-minute cap cannot be changed once it exists; there is no route for that. For different scopes, create a new key and revoke the old one.

Managing API keys in the application

Keys are created, listed and revoked on the Interfaces page at https://app.xicbot.de/schnittstellen, from the Administration role upwards. Creating a key requires the plan to include the feature; otherwise POST /api/v1/keys answers 402 with plan_feature_missing. Listing and revoking work without the feature, so that a key from before a plan change can still be revoked; it cannot be used without the feature. The routes GET /api/v1/keys, POST /api/v1/keys and DELETE /api/v1/keys/{id} require the same role but no confirmed email address. At most ten keys can be valid at the same time, and only the response to the create call contains the plain-text key. Fields, steps, the list and revoking are covered in Keys and webhooks; which plans include the feature is shown in Plans and quotas and on the pricing page.

The chat window's public key

Every assistant gets a public key of 32 hexadecimal characters when it is created. It is not sent in a header but in the path: /api/v1/public/widget/{key}/…. This branch needs no session. On the assistant, the application shows it under Public key with the note It appears in plain text on every foreign page. It only permits using this one chat window.

Four cases get the same answer, 404 with unknown_widget: the key is unknown, Enable chat window is off, the assistant is not active (that is, it is set to Draft or paused), or the account is not active. From the outside there is no way to tell which case applies. A new assistant starts with Enable chat window switched off. If none of the four cases applies and the assistant belongs to a demo account whose period has run out, the answer is 404 with demo_expired.

Generate a new key replaces the key after the prompt Generate a new key? The current embed code stops working immediately. Then The new key is active. Please replace the embed code everywhere. is shown. The Administration and Owner roles can do this; everyone else sees Generating a new key needs the Administration or Owner role. The embed code is covered in Embed the widget, the routes of this branch in Widget endpoints.

Through the API, POST /api/v1/assistants/{id}/key swaps the key. The route's requirements, response and errors are described in Knowledge and assistant endpoints.

What a route additionally requires

After the credential itself, the API checks further conditions per route group, on every request:

Routes under /api/v1/Requires
Sign-in and registration, public/status, enquiry form, demo, legal texts, invitation, cartno credentials
public/widget/{key}/…public key of an enabled, active assistant in an active account
integration/…API key and plan feature; every route except status needs a scope
Account routes such as me, logout, password, sessions, second factorsession token, active account
keys, webhookssession token, active account, Administration role; plan feature for creating, changing and testing
impersonation/stopsession token, active account
knowledge, einrichtung, assistants, billing, privacy, conversations, blocks, team, routing, tools, catalog, analytics, recordssession token, active account, confirmed email address
activityadditionally the Administration role
admin/…session token, active account (an operator without an account of their own passes as well), confirmed email address, operator access, no impersonation in progress

Individual routes can also require a role or a plan feature; that is noted with the route in Knowledge and assistants and Conversations and management. The rejections on session routes and what the application shows for them:

StatusErrorMeaningMessage in the application
401-token missing, expired or deletedsign-out, then Your session has expired. Please sign in again.
403no_tenantthe user has no accountYour account does not have access to this area.
403tenant_paused, tenant_suspended, tenant_deletedthe account is not active; message reads „Das Konto ist derzeit nicht aktiv.“This account is locked. Please write to mail@xicbot.de.
403email_unverifiedthe email address is not confirmedThis area stays locked until the email address is confirmed.
403role_too_low with requiredthe role is not high enoughfor editor: With the Read role this area is view-only. Changes need at least the Edit role. For owner: Only the account owner can do this. Otherwise: This needs at least the Administration role.
402plan_feature_missing with feature, plan, plan_name, available_inthe plan does not include the featureYour {tarif} plan does not include this feature. See Billing for the overview.
403not_operatorno operator accessThis area is restricted to the operator.
403in_impersonationlocked during an impersonationThis area is locked during an impersonation session.

The account status (active, paused, suspended or deleted) is checked on every request, not only at sign-in. So once an account is paused, even a token that has not expired gets 403. Exempt are POST /api/v1/tenant/switch, so that you can switch from an inactive account into another one, and POST /api/v1/logout and POST /api/v1/impersonation/stop, which only end the token they are called with. Without a confirmed email address the account routes, key management and impersonation/stop remain reachable, the functional areas do not; how to confirm the address is explained in Sign-in and access.

Roles

Role in the applicationValue in roleRank
Readviewer10
Editeditor20
Administrationadmin30
Ownerowner40

A route with a role condition requires at least the named rank; higher roles get through as well. An unknown role on your side counts as 0, an unknown required role as 99, so both end in a rejection: 403 with role_too_low and required. Some routes require the Owner role, for example the data export with GET /api/v1/privacy/export. What each role can do in the application is covered in Team and roles.

The role belongs to the combination of user and account. Switching into another account with POST /api/v1/tenant/switch gives you the role you hold there. An account the user does not belong to gets the same answer as an unknown one: 404 with not_found. If the target is not active, the answer is 403 with tenant_<status> and „Das Konto ist derzeit nicht aktiv.“ During an impersonation, switching is blocked (403, in_impersonation).

Operator access and impersonation

Operator is not a role but a separate flag on the user (user.operator). An Owner is therefore not an operator. The routes under /api/v1/admin/ require this flag and a confirmed address; without the flag they answer 403 with not_operator, during an impersonation 403 with in_impersonation. In the navigation, Operations in the Operator group appears only for operators and not during an impersonation.

For troubleshooting, the operator can take over an account: in the application with Take over account and the Reason for the takeover field, through the API with POST /api/v1/admin/tenants/{id}/impersonate and an optional reason. The reason is cut off after 200 characters. The takeover creates a separate token for the highest-ranking user currently working in this account, and among equal ranks for the one registered first; a member currently working in another account is not considered. It is valid for 4 hours, its name starts with impersonation:, and no password is read or set in the process. The start is logged together with the reason, the end without it.

  • The response carries token, expires_at, user (id, email, name) and tenant (id, name). An unknown account gives 404 with unknown, an inactive one 409 with tenant_inactive, an account in which no one is currently working 422 with tenant_without_user.
  • With the impersonation token, GET /api/v1/me returns the operator's address (operator) and expires_at under impersonation. The application then shows Takeover in the status bar, with that address and the End takeover button; screen readers hear You are working inside a customer account. there.
  • Sign out during an impersonation ends it like End takeover; in the application the operator is then signed out of their own account as well.
  • In the session list under Account in Signed-in devices, the takeover appears as Takeover by the operator. End all others (DELETE /api/v1/sessions) leaves it and the account's API keys in place; the customer ends it with End in its row (DELETE /api/v1/sessions/{id}).
  • Blocked with 403 and in_impersonation: changing the password (in the application The password cannot be changed during a support session.), ending all other sessions at once, setting up, confirming or removing the second factor, adding or removing security keys, switching accounts and the routes under /api/v1/admin/. Of the single sessions (DELETE /api/v1/sessions/{id}), an impersonation can only end itself; any other session also returns 403 with in_impersonation.
  • The per-minute caps per user, for example for assistants or for creating keys, do not apply during an impersonation, because the requests would otherwise count against the customer's counter. The cap for accepting the data processing agreement (POST /api/v1/privacy/dpa) still counts.

POST /api/v1/impersonation/stop

Ends the impersonation: the impersonation token is deleted, the end is normally logged and the response is {"ok": true}. If the operator and the customer end the impersonation at the same time, only the call that actually deleted the token writes the end, so it is not logged twice. Called with an ordinary session token, the route answers 400 with no_impersonation. It also works when the account has since become inactive. It sits outside /api/v1/admin/ because that branch is locked during an impersonation.

Authentication
Impersonation token

If someone has lost the only device for two-factor authentication, the operator can reset it. In the application, under Operations on a tenant's page, the number next to Users is a toggle that expands the people of the tenant with Name, Email address, Role, Two-factor authentication and Action. Counted and listed are everyone who belongs to the tenant, even if they are currently working in another tenant; Role is their role in this tenant. For customer accounts with two-factor authentication set up, the row offers Reset two-factor authentication; operator accounts show Operator account instead. The button opens the question Remove this person's authenticator code and security keys and end all of their sessions? with the field Reason (recorded in the operator log) (10 to 500 characters) and the buttons Reset and Cancel. Through the API the call is POST /api/v1/admin/users/{id}/second-factor/reset with reason. The authenticator code and the security keys are removed, all of the person's sessions end, and the response carries ok, sessions_ended and mail_queued. If mail_queued is false, the security notice to the account's email address could not be queued; the application then asks you to notify the person yourself. An operator account gives 403 with operator_account, a person without two-factor authentication 409 with no_second_factor, an unknown ID 404 with unknown. If the entry in the operator log or in the account's activity log cannot be written, the reset does not happen and the response is 500 with not_logged.

Frequent questions

Can I use an API key on the application routes?

No. An API key only opens /api/v1/integration/. The other routes that need credentials accept the session token in the Authorization header; a key sent there gets 401.

Why does the chat window answer 404 with unknown_widget?

One of four cases applies: the key is unknown or has been replaced, Enable chat window is off, the assistant is not active, or the account is not active. The answer does not tell these apart; check them one by one on the assistant.

How long is a session token valid?

30 days from sign-in, the same for a token from an invitation, 4 hours for an impersonation token. POST /api/v1/logout deletes it immediately. If the account is not active, requests with it fail with 403 even though it has not expired; signing out with it still works.

Can I change a key's scopes later?

No, there is no route for that. Create a new key with the scopes you need and revoke the old one with Revoke.

What happens to tokens and keys when the account is paused?

They remain, but are rejected: session tokens with 403 and tenant_paused, API keys with 403 and tenant_suspended, the chat window's public key with 404 and unknown_widget. Once the account is active again they work again, unless they have expired in the meantime.

How can I tell that the operator has taken over my account?

Your session list under Account in Signed-in devices then shows an entry Takeover by the operator. It disappears when the takeover is ended or when it expires after 4 hours. You can end the takeover yourself with End in that row.

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.