Management API overview
The Management API is for back-office work that does not involve sending chat messages:
- create a bot programmatically with
POST /v1/management/bots - read a specific bot you own with
GET /v1/management/bots/{bot_id} - update a specific bot with
PATCH /v1/management/bots/{bot_id} - read subscription usage with
GET /v1/usage
Management keys are tied to your account, not to any particular bot. They are deliberately kept separate from Bot Talk keys so that a compromised chat key cannot mutate your bots or read your billing data.
Base URL
https://api.chatlab.com/aichat
All endpoints in this article are relative to this base URL.
Getting started
- Open the admin app and go to Account Settings > Management API.
- Click Create Management Key, name it, optionally set IP whitelist and rate limit, then submit.
- Select only the permissions your integration needs and copy the full key from the success modal. The plaintext is shown only once.
- Store it on your server, not in browser code or a public repository. Make a read-only request such as
GET /v1/usagewith theusagepermission to verify access. - Revoke unused or compromised keys from the Management API page.
A key looks like mk_abcdefghijklmnopqrstuvwxyz012345. The mk_ prefix distinguishes it from Bot Talk keys (ck_).
Authentication
Authorization: Bearer mk_abcdefghijklmnopqrstuvwxyz012345
Sending a mk_ key to /v1/chat (or any other Bot Talk endpoint) returns 403 key_type_not_allowed. Sending a ck_ key to /v1/management/* returns the same error.
Limits
- Max 5 active Management API keys per user
- Max 10 requests per minute per key (token bucket, capacity 10, smooth refill at ~1 token every 6 seconds). Configurable downward at create time - set a lower
rateLimitPerMinuteand the cap drops, refill rate scales with it.
Permissions
Each Management key carries any subset of the three permissions below. At least one must be selected at create time; otherwise the request is rejected with 400 invalid_request_error. Calling an endpoint with a key that lacks the required permission returns 403 insufficient_permissions.
bot_read- required forGET /v1/management/bots/{bot_id}bot_management- required forPOST /v1/management/botsandPATCH /v1/management/bots/{bot_id}usage- required forGET /v1/usage
Body shape: nested sections that mirror the admin UI tabs
POST and PATCH accept a JSON body grouped into 13 sections. Each section maps to a sub-tab in the admin app's Bot Settings sidebar, so the JSON keys and the visible tabs line up: if you change consent.humanSupportRequirePolicyAccept via the API, you will see the same toggle flip on the Consent & Privacy tab in the admin app.
role- bot persona, raw prompt, response length, language, website / company context (Role & Behavior tab)conversation- welcome message, query refinement, conversation continuity, rating toggle + tooltips, suggested questions content + dynamic followups (Chat Conversation tab)chatMemory- chat memory toggle, summary prompts, context allocation (Summaries & Memory tab)appearance- colors, copy, dimensions, custom CSS, welcome screen, suggested-question styling, auto-open behaviour, human-typing simulation, footer markdown (Appearance tab)humanSupport- human contact form (Human Contact Form tab)leadCollection- lead capture form (Lead Collection tab)liveChat- live chat handoff (Live Chat tab)consent- all four privacy-policy consent toggles plus the consent screen copy (Consent & Privacy tab)whiteLabel- hide-logo, custom logo link, custom-domain hosting (Whitelabel tab)security- allowed domains, spam filter, talk rate limits (Security tab)voice- voice input and voice conversations: model, voice, languages, prompt, duration cap (Voice Conversation tab)multilingual- multilingual mode, base language, offered languages, knowledge-language handling (Languages tab)advanced- LLM model, temperature, context size, bot messages limit, internal locale, Offer Cards (Model & Advanced tab)
Of the writable settings, only name lives at the top level, because it identifies the bot rather than belonging to any one tab.
The Bot Settings sidebar currently has 16 sub-tabs, and 13 of them map to the sections above. The three sub-tabs with no corresponding section are Flow, Actions, and Forms - all three covered under "Out of API scope" below. The 13 that do map are Appearance, Chat Conversation, Role & Behavior, Human Contact Form, Lead Collection, Live Chat, Whitelabel, Consent & Privacy, Security, Model & Advanced, Summaries & Memory, Voice Conversation, and Languages.
Request body and response body share the same shape. The response adds two extras:
meta- read-only: bot id and timestamps. Strip it to turn a GET response into a valid POST body.apiKey- present only on create - the freshly minted Bot Talk API key for the new bot.
Two fields inside the shared shape are read-only - returned in the response, ignored if you try to send them on POST/PATCH:
appearance.avatarUrl- fully-qualified public URL of the bot avatar image (e.g.https://api.chatlab.com/aichat/content/avatar_xyz.png). GET it directly to download the bytes. To change it, upload a new file via the multipartavatarpart (see PATCH).whiteLabel.whitelabelLogoUrl- fully-qualified public URL of the white-label header logo. Same pattern asavatarUrl. To change it, upload a new file via the multipartwhitelabel_logopart (see PATCH).
Both URLs use the current request's scheme + host + context path, so on a white-label custom domain they come back rooted at that domain (e.g. https://api.acme.com/aichat/content/...).
Send null for a section to skip it on PATCH; send null for a field within a section to skip that single field. Field-level null never clears a stored value - it only means "don't touch".
Role and prompt construction
The system prompt the LLM actually receives is built one of two ways depending on role.role. Knowing which branch you are on tells you which fields matter and which are stored-but-ignored.
Branch A - role.role is CUSTOMER_SUPPORT, SALES, or LEAD_COLLECTION_AGENT (template-driven)
Backend assembles the prompt from a built-in template and ignores role.rawPrompt entirely (the value is still stored on the bot, just not used). The template incorporates:
role.role- role label (e.g. "Customer Support") and role-specific instructions appended automaticallyname- bot name, injected into the opening sentencerole.language-"Auto Detect"switches the bot to follow the user's language; any other value (e.g."English","Polish") becomes "Output in {language}, unless user uses another language"role.responseLength- mapped to a target word count:Concise≈ 50 words,Normal≈ 100,Detailed≈ 200role.websiteAddress- optional; when non-blank, appended as "for the users of the website {url}"role.companyDescription- optional; when non-blank, prepended as an extra paragraph before the role instructions
This is the recommended branch for most bots - you get role-tuned behaviour and safety rails for free.
Branch B - role.role is CUSTOM (caller-supplied prompt)
Backend uses role.rawPrompt as the role prompt and appends the platform's fixed response-length guidance. This is separate from role.responseLength: the optional Concise/Normal/Detailed setting is not injected for a CUSTOM role. Likewise, language, websiteAddress, and companyDescription are stored but not injected into this role prompt. Include any desired language, response-length target, or company context in rawPrompt yourself. Additional runtime instructions and retrieved context can also be supplied, so rawPrompt is not the entire model input. The predefined role's tone instructions are not added, but choosing CUSTOM does not remove all platform instructions.
Use CUSTOM only when the template-driven prompt does not fit your use case (e.g. you need a very domain-specific persona, your own safety constraints, a non-standard output format).
Enum / closed-set fields
Enum and constrained fields reject invalid values with 400 invalid_parameter. Other fields are plain strings, even where the UI offers a fixed list. Use the documented spelling and validate your payload rather than assuming every unsupported string is rejected.
role.role-CUSTOMER_SUPPORT,SALES,LEAD_COLLECTION_AGENT,CUSTOMrole.responseLength-Concise,Normal,Detailedrole.language- full English language name from the admin dropdown, e.g.Auto Detect,English,Polish,Spanish,German,French,Italian,Portuguese,Dutch,Russian,Chinese (Simplified),Japanese,Arabic,Hindi, and ~80 others. The value is stored verbatim and substituted into the prompt template, so two-letter ISO codes (en,pl) and other off-list values are not rejected by the API but produce a garbled instruction like "Output in en, unless...". Defaults toAuto Detectwhen omitted on create.advanced.model- see "AI text models" below. This is a string, not a validated enum in the request schema; use a supported value, not an arbitrary model name.advanced.chatContextSize- use8000,16000, or32000, matching the UI. Values above the account's maximum are silently clamped; the DTO does not enforce this three-value set.chatMemory.clientSummaryPromptType-DEFAULT,CUSTOMchatMemory.conversationSummaryPromptType-DEFAULT,CUSTOMappearance.chatAlignment-left,rightappearance.minimizedDisplayMode-icon,minified,pillappearance.chatMessageLinkTarget-_blank,_selfleadCollection.requireBeforeNewConversation- boolean toggle.trueforces the user to fill the lead form before starting a conversation;falselets the AI decide when to surface the form (default).
Structured fields and ranges
Fields that look like simple strings or numbers but actually have specific shapes, ranges, or admin-UI quirks worth knowing about.
-
advanced.temperature- accepted range is0.0to1.0, matching the slider in the admin UI. Values outside this range are rejected with400 invalid_parameter. -
chatMemory.summariesToKnowledgeRatio- integer percent,10-90; the UI uses steps of10, but the API accepts integers throughout the range. Default50. With memory enabled and context larger than 8k, it splits the knowledge-and-memory budget after the prompt/recent-conversation reservation, not the entire model context. At 8k no summary budget is allocated by this setting. -
liveChat.schedule- JSON encoded as a string, not a nested JSON object on the wire. The server stores the raw string verbatim; the admin UI parses it client-side when rendering the schedule editor. Once parsed, the string is shaped as one entry per weekday plus atimezonekey:- each weekday key (
monday-sunday) maps to{enabled: boolean, from: "H:MM", to: "H:MM"}in 24-hour time timezoneis an IANA zone name (e.g."Europe/Warsaw","America/New_York")
Example value (note the outer quotes and escaped inner quotes - it is one string field, not a nested object):
"{\"monday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"tuesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"wednesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"thursday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"friday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"saturday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"sunday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"timezone\":\"Europe/Warsaw\"}"Outside the listed hours,
liveChat.outOfHoursMessageis shown to the visitor and the live-chat handoff is suppressed. Inner-shape validation runs only client-side in the admin UI - malformed JSON or unrecognized keys are accepted by the API as just-a-string and will surface as a render error when a human later opens the bot in admin. Validate the structure on your side before sending. - each weekday key (
-
conversation.positiveRatingTooltip/conversation.negativeRatingTooltip- short labels shown on the 👍 / 👎 conversation-rating controls whenconversation.conversationRatingEnabled=true. Default copy is "I like the response" / "I don't like the response". Visible to end users. -
whiteLabel.hideRoboAssistLogo- white-label capability, subject to your account limits. Hides the "Powered by ChatLab" footer line. If your account does not include white-labelling, the value is stored but ignored and the footer always renders. -
whiteLabel.whitelabelLogoLink- white-label capability, subject to your account limits. Click target URL for the custom logo whenhideRoboAssistLogo=trueand a custom logo file is uploaded via thewhitelabel_logomultipart part. -
appearance.simulateHumanTypingDelay- seconds (not milliseconds), integer0-200. Pause between successive bot bubbles whensimulateHumanTyping=true. Default5. -
appearance.autoOpenChatDelaySeconds- seconds, integer. Delay before the widget auto-opens whenautoOpenChat=trueandautoOpenChatDelay=true. -
advanced.internalLocale- IETF locale-region code inll_CCform (underscore, NOTll-CCwith a dash). Accepted values come from a fixed list of ~95 locales:en_US,pl_PL,de_DE,fr_FR,es_ES,it_IT,pt_PT,nl_NL,ru_RU,zh_CN,zh_TW,ja_JP,ko_KR,ar_SA,hi_IN,tr_TR,cs_CZ,da_DK,fi_FI,sv_SE,no_NO,el_GR,he_IL, and many more. Sending a two-letter code alone ("en") or BCP-47 ("en-US") is not in the allowed list. Defaulten_US. This is the locale used for date/number formatting in the widget chrome, distinct fromrole.language(the bot's conversational output language). -
security.talkMessagesRateLimit/security.talkMessagesRateLimitDurationSeconds- integers (send as JSON numbers, e.g.30, not"30"). set the message limit to0to disable this session-based limit. When enabling it, provide a positive duration. The backend enforces N messages per session per duration-in-seconds before showingsecurity.talkMessagesRateLimitHitMessageto the visitor. -
advanced.botMessagesLimit- integer (JSON number, e.g.1000).0means no additional per-bot cap, while the account credit budget still applies; otherwise must be a multiple of 1000 (1000,2000,10000, ...). Values like100or1500are rejected with400 invalid_parameter. This is a monthly bot credit cap, not a per-conversation message count. The value is checked against your plan on every create and update: the sum ofbotMessagesLimitacross all your bots (with the bot being created or updated counted at its new value) must not exceed the monthly message credits of your subscription, otherwise the request is rejected with402 quota_exceededand nothing is saved. The account's budget still constrains actual use.
AI text models (advanced.model)
Use the exact model identifier returned in advanced.model when reading a bot configured with your intended model in Settings > Model & Advanced. The current create default is 5.6-LUNA. Identifiers are case-sensitive and differ from display names; for example 5.6-LUNA is displayed as GPT 5.6-luna.
The model catalog changes over time. Do not infer that an arbitrary string or a provider's public model ID is supported simply because the request schema accepts a string. Read back the saved configuration and test the resulting chatbot before deploying it.
Field reference (full request schema)
The supported sections and fields, with their types and important constraints. PATCH semantics: any field omitted (or sent as null) leaves the persisted value untouched. The same shape is used for the response (minus multipart binary content; plus the read-only meta block on every response and apiKey only on the create response).
Top level
| Field | Type | Constraint | Description |
|---|---|---|---|
name |
string | max 150, required on create | Bot display name |
role |
object | See § role | |
conversation |
object | See § conversation | |
chatMemory |
object | See § chatMemory | |
appearance |
object | See § appearance | |
humanSupport |
object | See § humanSupport | |
leadCollection |
object | See § leadCollection | |
liveChat |
object | See § liveChat | |
consent |
object | See § consent | |
whiteLabel |
object | See § whiteLabel | |
security |
object | See § security | |
advanced |
object | See § advanced |
Response-only additions:
meta: { id, createdAt, updatedAt }- read-only.apiKey- string, present only on thePOST /v1/management/botsresponse - the freshly-minted Bot Talk key for the new bot, returned exactly once.
§ role
| Field | Type | Constraint | Description |
|---|---|---|---|
role |
string (enum) | CUSTOMER_SUPPORT, SALES, LEAD_COLLECTION_AGENT, CUSTOM |
Persona preset; selects the prompt template (see "Role and prompt construction") |
language |
string | full English language name (English, Polish, ...) or Auto Detect |
Primary language fed into the prompt template |
responseLength |
string | ∈ {Concise, Normal, Detailed} |
Desired AI response verbosity |
websiteAddress |
string | Website used for prompt context | |
companyDescription |
string | Company description used for prompt context | |
rawPrompt |
string | Custom system prompt - used verbatim only when role=CUSTOM |
§ conversation
| Field | Type | Constraint | Description |
|---|---|---|---|
welcomeMessage |
string | First message shown to the visitor on open | |
queryRefinementEnabled |
boolean | If true, refine the visitor's question before RAG retrieval | |
conversationContinuityEnabled |
boolean | If true, returning visitors resume their last conversation | |
conversationRatingEnabled |
boolean | If true, show thumbs up/down conversation-rating controls | |
positiveRatingTooltip |
string | Tooltip on the positive rating button | |
negativeRatingTooltip |
string | Tooltip on the negative rating button | |
suggestedQuestions |
string | Newline-separated suggested questions / conversation openers | |
dynamicSuggestedFollowups |
boolean | If true, AI proposes follow-up suggestions after each reply | |
positiveRatingMessage |
string | Message shown after positive feedback | |
negativeRatingMessage |
string | Message shown after negative feedback | |
hideSuggestedQuestionsAfterFirstUse |
boolean | Hide static suggestions after use | |
dynamicFollowupsAutoIcons |
boolean | If true, AI auto-picks emoji icons for the dynamic follow-ups |
§ chatMemory
| Field | Type | Constraint | Description |
|---|---|---|---|
enabled |
boolean | Master toggle for the chat-memory feature | |
summaryConversationsEnabled |
boolean | Persist per-conversation summaries | |
conversationSummaryPrompt |
string | Custom prompt used to summarize each conversation | |
conversationSummaryPromptType |
string (enum) | ∈ {DEFAULT, CUSTOM} |
Whether to use the default or the custom summary prompt |
clientSummaryPrompt |
string | Custom prompt used to summarize the client across conversations | |
clientSummaryPromptType |
string (enum) | ∈ {DEFAULT, CUSTOM} |
Default vs custom client-profile prompt |
summariesToKnowledgeRatio |
int | 10-90 (UI step 10) |
% of the available knowledge/memory budget allocated to summaries at contexts above 8k |
§ appearance
| Field | Type | Constraint | Description |
|---|---|---|---|
launcherLabelLine1 |
string | max 60 | First line of launcher text |
launcherLabelLine2 |
string | max 60 | Second line of launcher text |
launcherTextColor |
string | Launcher text color | |
launcherDismissEnabled |
boolean | Allow dismissing the launcher | |
widgetFontFamily |
string | inter, system, nunito, lato, montserrat, poppins, source-serif, jetbrains-mono |
Widget font family |
chatDrawerEnabled |
boolean | Enable drawer presentation | |
launcherColor |
string (hex) | Launcher (chat icon) background color | |
headerColor |
string (hex) | Chat header background color | |
titleColor |
string (hex) | Chat header title color | |
subtitleColor |
string (hex) | Chat header subtitle color | |
clientMessageBubbleColor |
string (hex) | Visitor message bubble color | |
clientMessageTextColor |
string (hex) | Visitor message text color | |
responseMessageBubbleColor |
string (hex) | Bot reply bubble color | |
responseMessageTextColor |
string (hex) | Bot reply text color | |
chatSubheader |
string | Tagline shown below the chat title | |
senderPlaceholder |
string | Placeholder text in the message input | |
resetConversationTooltip |
string | Tooltip on the "reset conversation" button | |
chatAlignment |
string (enum) | ∈ {left, right} |
Which side of the screen the chat anchors to |
launcherBottomMargin |
int | 0-500 |
Launcher distance from bottom edge (px) |
launcherSideMargin |
int | 0-500 |
Launcher distance from the side edge (px) |
displayShadow |
boolean | Drop-shadow under the widget | |
customCss |
string | Raw CSS injected into the widget iframe | |
chatMessageLinkTarget |
string (enum) | ∈ {_blank, _self} |
How links inside bot messages open |
minimizedDisplayMode |
string (enum) | ∈ {icon, minified, pill} |
Minimized state: launcher icon, compact sender bar, or pill |
chatDesktopWidthPx |
int | Desktop widget width | |
chatDesktopHeightPx |
int | Desktop widget height | |
chatMobileSizePercent |
int | Mobile widget size as % of viewport | |
messageFontSize |
int | Message text font size (px) | |
showChatbotBubblesDesktop |
boolean | Show the floating tease bubbles on desktop | |
showChatbotBubblesMobile |
boolean | Show the floating tease bubbles on mobile | |
chatbotBubblesDelaySeconds |
int | Delay before the tease bubbles appear (seconds) | |
launcherIconFullSize |
boolean | Render the custom launcher icon edge-to-edge instead of inset | |
welcomeScreenEnabled |
boolean | Show the Welcome Screen instead of going straight to chat | |
welcomeScreenQuestionsLabel |
string | Label above the suggested questions on the welcome screen | |
welcomeScreenHideHumanContactForm |
boolean | Hide the human contact form action in the header while the Welcome Screen is showing. It reappears after the visitor's first message. Bots created before 2026-09-02 default to true |
|
welcomeScreenHideLiveChat |
boolean | Hide the live chat action in the header while the Welcome Screen is showing. It reappears after the visitor's first message. Bots created before 2026-09-02 default to true |
|
headerActionsLayout |
string | ICONS or DROPDOWN |
How live chat and the human contact form are offered in the chat header: ICONS (a separate icon each) or DROPDOWN (grouped in the header menu). Bots created before 2026-09-02 default to ICONS |
stackSuggestedQuestions |
boolean | Stack suggested questions vertically (vs side-by-side) | |
suggestedQuestionsFontSize |
int | Font size of suggested-question chips (px) | |
suggestedQuestionsTextColor |
string (hex) | Suggested-question chip text color | |
suggestedQuestionsBackgroundColor |
string (hex) | Suggested-question chip background color | |
autoOpenChat |
boolean | Auto-open the chat on desktop | |
autoOpenChatOnMobiles |
boolean | Auto-open the chat on mobile | |
autoOpenChatDelay |
boolean | Use a delay before auto-opening | |
autoOpenChatDelaySeconds |
int | Auto-open delay (seconds) | |
simulateHumanTyping |
boolean | Split bot reply into bubbles with typing animation | |
simulateHumanTypingDelay |
int | 0-200 |
Delay between bubble messages (seconds) |
footerMarkdown |
string | max 255 | Custom footer markdown shown under the chat |
avatarUrl |
string | read-only | Fully-qualified public URL of the avatar; to change it, upload via the multipart avatar part |
Multipart on POST/PATCH: avatar (file part). GET / response bodies omit the file content - only the URL is on the wire.
§ humanSupport
| Field | Type | Constraint | Description |
|---|---|---|---|
enabled |
boolean | Human Support flow toggle | |
email |
string | required (create-strict) when enabled=true |
Address that receives human-support emails |
dialogMessage |
string | Encouragement message shown above the form | |
thankYouMessage |
string | Confirmation shown after submission | |
emailMessageSubjectTemplate |
string | Subject template for the email sent to the agent | |
emailMessageContentTemplate |
string | Body template for the email sent to the agent | |
emailPlaceholder |
string | Placeholder on the email input | |
messagePlaceholder |
string | Placeholder on the message textarea | |
phoneEnabled |
boolean | default false |
Show an optional phone input in the built-in contact form. Visitors can leave it empty. Ignored when a custom form is attached via customFormId |
phonePlaceholder |
string | max 50, default Your phone |
Placeholder on the phone input. Translated for multilingual bots like the other placeholders |
emailWithConversationContent |
boolean | If true, include the conversation transcript in the email body | |
customFormId |
long | id of an existing custom form | Replace the built-in contact form with a custom form. On create, omission uses the built-in form; on PATCH, null leaves the current selection unchanged |
customFormMapping |
string | JSON-encoded string | Maps custom-form fields onto the human-support email fields |
requirePolicyAccept is on consent.humanSupportRequirePolicyAccept, not here.
§ leadCollection
| Field | Type | Constraint | Description |
|---|---|---|---|
enabled |
boolean | Lead form toggle | |
nameEnabled |
boolean | Collect name | |
nameLabel |
string | Label on the name input | |
emailEnabled |
boolean | Collect email | |
emailLabel |
string | required (create-strict) when enabled=true AND emailEnabled=true |
Label on the email input |
phoneEnabled |
boolean | Collect phone | |
phoneLabel |
string | required (create-strict) when enabled=true AND phoneEnabled=true |
Label on the phone input |
leaveDetailsMessage |
string | required (create-strict) when enabled=true |
Message encouraging the visitor to leave their details |
thankYouMessage |
string | required (create-strict) when enabled=true |
Confirmation shown after submission |
requireBeforeNewConversation |
boolean | If true, form must be submitted before chat starts; if false, AI decides when to surface the form |
|
emailNotificationEnabled |
boolean | Email the owner each time a lead is collected | |
emailNotificationAddress |
string | Notification recipient (defaults to account email) | |
emailWithConversationContent |
boolean | If true, include the conversation transcript in the notification |
| customFormId | long | id of an existing custom form | Replace the built-in lead form with a custom form. On create, omission uses the built-in form; on PATCH, null leaves the current selection unchanged |
| customFormMapping | string | JSON-encoded string | Maps custom-form fields onto name / email / phone |
Cross-field create-strict rule: enabled=true requires at least one of emailEnabled or phoneEnabled. requirePolicyAccept is on consent.leadCollectionRequirePolicyAccept, not here.
§ liveChat
| Field | Type | Constraint | Description |
|---|---|---|---|
enabled |
boolean | Live Chat feature toggle | |
infoMessage |
string | Pre-handoff explanatory message | |
startMessage |
string | Message shown when the live session begins | |
endMessage |
string | Message shown when the live session ends | |
nameLabel |
string | Label on the name input in the live-chat pre-form | |
emailLabel |
string | Label on the email input in the live-chat pre-form | |
schedule |
string | JSON-encoded string (weekday toggles + from/to + timezone) |
Live-chat operating schedule - see "Structured fields and ranges" for the exact shape |
outOfHoursMessage |
string | Message shown when the schedule says we're off | |
closeModalMessage |
string | Title of the "close live chat?" modal | |
closeModalConfirmLabel |
string | Confirm button label on the close modal | |
closeModalCancelLabel |
string | Cancel button label on the close modal | |
closeModalTooltipText |
string | Tooltip on the close-chat affordance | |
operatorHasJoinedLabel |
string | Label shown when an operator joins | |
operatorDidNotJoinInTimeLabel |
string | Label shown when no operator joins within the timeout | |
waitingForOperatorToJoinLabel |
string | Label shown while waiting for an operator | |
waitingForOperatorSeconds |
int | Timeout for an operator to pick up (seconds) | |
redirectToHumanSupportForm |
boolean | If true, fall through to the Human Support form when no operator picks up | |
requireAdminOnline |
boolean | Require an available operator according to presence settings | |
presenceMode |
string | Presence mode; use the value from a bot configured in the UI | |
hideHumanSupportWhenLiveChatAvailable |
boolean | Hide the contact form when live chat is available | |
missedEmailEnabled |
boolean | default true |
Email the bot owner when a live chat request went unanswered. Left unset on legacy bots, which reads as enabled |
requirePolicyAccept is on consent.liveChatRequirePolicyAccept, not here.
§ consent
| Field | Type | Constraint | Description |
|---|---|---|---|
newConversationRequirePolicyAccept |
boolean | Require privacy-policy consent before starting a new conversation | |
humanSupportRequirePolicyAccept |
boolean | Require privacy-policy consent before submitting the human-support form | |
leadCollectionRequirePolicyAccept |
boolean | Require privacy-policy consent before submitting the lead-collection form | |
liveChatRequirePolicyAccept |
boolean | Require privacy-policy consent before starting a live-chat session | |
newConversationConsentDescription |
string | Intro text for the consent screen at conversation start | |
privacyPolicyConsentCheckboxLabel |
string | Label next to the consent checkbox (usually contains a link to the privacy policy) |
§ whiteLabel
| Field | Type | Constraint | Description |
|---|---|---|---|
hideRoboAssistLogo |
boolean | white-label capability; subject to account limits | Hide the default ChatLab logo in the footer |
whitelabelLogoLink |
string | white-label capability; subject to account limits | URL the custom footer logo links to |
assignToCustomDomain |
boolean | gated by CUSTOM_DOMAIN feature |
Host the chat on the configured custom domain |
whitelabelLogoUrl |
string | read-only | Fully-qualified public URL of the white-label logo; to change it, upload via the multipart whitelabel_logo part |
Multipart on POST/PATCH: whitelabel_logo (file part). GET / response bodies omit the file content - only the URL is on the wire.
§ security
| Field | Type | Constraint | Description |
|---|---|---|---|
allowedDomains |
string | Comma-separated list of domains allowed to embed the widget (empty = no whitelist) | |
spamFilterEnabled |
boolean | Enable the per-bot spam filter on incoming messages | |
countryFilterMode |
string | BLACKLIST or WHITELIST |
How the country lists are interpreted. The lists themselves stay admin-only |
talkMessagesRateLimit |
int | >= 0; 0 disables |
Max user messages allowed in the rate-limit window |
talkMessagesRateLimitDurationSeconds |
int | >= 0 |
Rate-limit window length (seconds) |
talkMessagesRateLimitHitMessage |
string | Message shown to the visitor when the rate limit is hit |
§ voice
| Field | Type | Constraint | Description |
|---|---|---|---|
inputEnabled |
boolean | Allow the visitor to dictate messages (speech to text) | |
conversationEnabled |
boolean | requires the voice feature on the plan | Enable full voice conversations |
voiceId |
string | provider-specific voice id (e.g. alloy) |
Which synthetic voice speaks |
model |
string | e.g. GPT-REALTIME-MINI, GEMINI-LIVE, ELEVENLABS-* |
Voice model. Billed per minute, rates differ per model |
turnDetection |
string | provider-specific | Turn-taking mode |
audioPrompt |
string | Extra system prompt used only for voice turns | |
welcomeMessage |
string | Spoken opening line | |
language |
string | language code | Primary voice language |
additionalLanguages |
string | comma-separated language codes | Extra languages the voice agent accepts |
maxDurationSeconds |
int | Hard cap on a single voice conversation | |
maxDurationMessage |
string | Message shown when the cap is reached |
§ multilingual
| Field | Type | Constraint | Description |
|---|---|---|---|
enabled |
boolean | Multilingual mode toggle | |
mode |
string | AUTODETECT or FIXED_LIST |
How the bot picks the reply language |
baseLanguage |
string | language code | Language the bot's own copy is authored in |
languages |
string | comma-separated language codes | Languages offered to the visitor |
knowledgeLanguageMode |
string | NONE, MAIN, or DYNAMIC |
How knowledge in other languages is treated |
knowledgeLanguageFallback |
string | language code | Language used when no match is found |
§ advanced
| Field | Type | Constraint | Description |
|---|---|---|---|
model |
string | subject to account limits; see "AI text models" above | LLM identifier (e.g. 5.6-LUNA) |
temperature |
decimal | 0.0-1.0 |
Sampling temperature (matches the UI slider) |
chatContextSize |
int | ∈ {8000, 16000, 32000}; silently clamped to your account limit |
Configured context budget, including instructions, history, and knowledge |
botMessagesLimit |
long | 0 or multiple of 1000 (e.g. 1000, 2000, 10000); sum across all bots must fit the plan's monthly credits |
Monthly bot message-credit cap (0 = no additional bot cap; account budget still applies) |
internalLocale |
string | locale code in ll_CC form |
Locale for widget chrome labels (distinct from role.language) |
productsViewEnabled |
boolean | If true, expose the e-commerce Offer Cards inside chat | |
includeProductsInKnowledgeBase |
boolean | If true, index the product catalog as part of the knowledge base |
Out of API scope
The admin UI surfaces a few areas that are intentionally not exposed in this version of the Management API:
- Training and deletion - these endpoints do not list all bots, manage training datasets, or delete bots. Use the app for those workflows.
- Flow tab - the visual Conversation Flow editor (stages and transitions). Not exposed via the Management API.
- Actions tab - managed e-commerce / booking integrations, AI Search, and custom API functions. Tool calling has never been part of the Management API.
- The custom form builder itself - creating and editing custom forms is not exposed. You can, however, attach an existing form to a bot through
leadCollection.customFormIdandhumanSupport.customFormId. - Custom open / close chat icons -
customLauncherIconVisible,openChatIcon,closeChatIcon. The API exposes only the mainavatarandwhitelabel_logomultipart parts. - IP and country lists - the entries themselves are admin-only. Only the interpretation mode is exposed, via
security.countryFilterMode.
Endpoints
POST /v1/management/bots
Create a new bot. Two equivalent Content-Types are accepted; pick whichever is more convenient.
Mode A - plain JSON (recommended when you don't need to upload an avatar / logo in the same request):
Content-Type: application/json- Request body is the bot configuration JSON (no
datawrapper) - Files (avatar / logo) can be uploaded later via a second
PATCHusing mode B
Mode B - multipart/form-data (use when uploading files in the same request):
Content-Type: multipart/form-data; boundary=...dataJSON part (required,Content-Type: application/json) - bot configuration in the nested shape described aboveavatarfile part (optional) - bot avatar imagewhitelabel_logofile part (optional) - white-label logo (applies only if your account includes white-labelling)
Only name is required in the JSON; every other field falls back to the same default the admin UI wizard would set.
Example request body
This is the maximal data JSON - every section populated. Send only the sections you care about; everything else takes default values.
{
"name": "Helpdesk Bot",
"role": {
"rawPrompt": "You are a friendly support assistant for Acme Inc.",
"role": "CUSTOMER_SUPPORT",
"language": "English",
"responseLength": "Normal",
"websiteAddress": "https://acme.com",
"companyDescription": "Acme sells industrial widgets."
},
"conversation": {
"welcomeMessage": "Hi! How can I help today?",
"queryRefinementEnabled": true,
"conversationContinuityEnabled": true,
"conversationRatingEnabled": true,
"positiveRatingTooltip": "Helpful",
"negativeRatingTooltip": "Not helpful",
"suggestedQuestions": "What are your hours?\nHow do I cancel?\nWhere is my order?",
"dynamicSuggestedFollowups": true,
"dynamicFollowupsAutoIcons": true
},
"chatMemory": {
"enabled": true,
"summaryConversationsEnabled": true,
"conversationSummaryPrompt": "Summarize this conversation in 3 sentences.",
"clientSummaryPrompt": "Summarize what we know about this customer.",
"clientSummaryPromptType": "DEFAULT",
"conversationSummaryPromptType": "DEFAULT",
"summariesToKnowledgeRatio": 50
},
"appearance": {
"launcherColor": "#1A73E8",
"headerColor": "#1A73E8",
"titleColor": "#FFFFFF",
"subtitleColor": "#FFFFFF",
"clientMessageBubbleColor": "#000000",
"clientMessageTextColor": "#FFFFFF",
"responseMessageBubbleColor": "#F4F4F4",
"responseMessageTextColor": "#000000",
"chatSubheader": "AI support assistant",
"senderPlaceholder": "Type a message...",
"resetConversationTooltip": "Restart conversation",
"chatAlignment": "right",
"launcherBottomMargin": 20,
"launcherSideMargin": 20,
"displayShadow": true,
"customCss": ".rcw-conversation-container { border-radius: 16px; }",
"chatMessageLinkTarget": "_blank",
"minimizedDisplayMode": "icon",
"chatDesktopWidthPx": 400,
"chatDesktopHeightPx": 600,
"chatMobileSizePercent": 100,
"messageFontSize": 14,
"showChatbotBubblesDesktop": true,
"showChatbotBubblesMobile": false,
"chatbotBubblesDelaySeconds": 5,
"welcomeScreenEnabled": false,
"welcomeScreenQuestionsLabel": "Quick start",
"welcomeScreenHideHumanContactForm": false,
"welcomeScreenHideLiveChat": false,
"headerActionsLayout": "DROPDOWN",
"stackSuggestedQuestions": false,
"suggestedQuestionsFontSize": 14,
"suggestedQuestionsTextColor": "#000000",
"suggestedQuestionsBackgroundColor": "#F4F4F4",
"autoOpenChat": false,
"autoOpenChatOnMobiles": false,
"autoOpenChatDelay": false,
"autoOpenChatDelaySeconds": 5,
"simulateHumanTyping": true,
"simulateHumanTypingDelay": 5,
"footerMarkdown": "Powered by Acme"
},
"humanSupport": {
"enabled": true,
"email": "support@acme.com",
"dialogMessage": "Leave us a message and we will get back to you.",
"thankYouMessage": "Thanks - we received your message.",
"emailMessageSubjectTemplate": "[Acme support] New message from {customerName}",
"emailMessageContentTemplate": "{message}\n\n - \n{conversationTranscript}",
"emailPlaceholder": "your@email.com",
"messagePlaceholder": "How can we help?",
"phoneEnabled": true,
"phonePlaceholder": "Your phone number",
"emailWithConversationContent": true
},
"leadCollection": {
"enabled": true,
"nameEnabled": true,
"nameLabel": "Your name",
"emailEnabled": true,
"emailLabel": "Email",
"phoneEnabled": false,
"phoneLabel": "Phone",
"leaveDetailsMessage": "Please leave your details and we will get in touch.",
"thankYouMessage": "Thanks - we will be in touch shortly.",
"requireBeforeNewConversation": false,
"emailNotificationEnabled": true,
"emailNotificationAddress": "leads@acme.com",
"emailWithConversationContent": true
},
"liveChat": {
"enabled": false,
"infoMessage": "Connecting you with a human agent...",
"startMessage": "You are now chatting with our team.",
"endMessage": "Live chat has ended.",
"nameLabel": "Your name",
"emailLabel": "Email",
"schedule": "{\"monday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"tuesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"wednesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"thursday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"friday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"saturday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"sunday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"timezone\":\"Europe/Warsaw\"}",
"outOfHoursMessage": "We are currently offline.",
"closeModalMessage": "End the live chat session?",
"closeModalConfirmLabel": "Yes, end",
"closeModalCancelLabel": "Cancel",
"closeModalTooltipText": "End live chat",
"operatorHasJoinedLabel": "An agent has joined.",
"operatorDidNotJoinInTimeLabel": "No agent available right now.",
"waitingForOperatorToJoinLabel": "Waiting for an agent...",
"waitingForOperatorSeconds": 60,
"redirectToHumanSupportForm": true
},
"consent": {
"newConversationRequirePolicyAccept": false,
"humanSupportRequirePolicyAccept": false,
"leadCollectionRequirePolicyAccept": true,
"liveChatRequirePolicyAccept": false,
"newConversationConsentDescription": "By starting a conversation you agree to our terms.",
"privacyPolicyConsentCheckboxLabel": "I have read and accepted the privacy policy."
},
"whiteLabel": {
"hideRoboAssistLogo": false,
"whitelabelLogoLink": "https://acme.com",
"assignToCustomDomain": false
},
"security": {
"allowedDomains": "acme.com,support.acme.com",
"spamFilterEnabled": true,
"talkMessagesRateLimit": 30,
"talkMessagesRateLimitDurationSeconds": 60,
"talkMessagesRateLimitHitMessage": "Please slow down."
},
"advanced": {
"model": "5.6-LUNA",
"temperature": 0.4,
"chatContextSize": 16000,
"botMessagesLimit": 1000,
"internalLocale": "en_US",
"productsViewEnabled": false
}
}
Validation rules with their own error messages:
name- required, max 150 charsadvanced.temperature- between0.0and1.0chatMemory.summariesToKnowledgeRatio- integer between10and90(percent, step10)appearance.launcherBottomMargin,appearance.launcherSideMargin- between0and500appearance.footerMarkdown- max 255 charshumanSupport.enabled=truerequireshumanSupport.emailto be setleadCollection.enabled=truerequires at least one ofleadCollection.emailEnabledorleadCollection.phoneEnabledto be true; whichever channel is on also requires its label, plusleaveDetailsMessageandthankYouMessageadvanced.chatContextSizeis clamped to the account's maximum supported context size. Do not assume other numeric fields are automatically clamped.
Fields whose value is null on the server are omitted from the JSON body - the wire only carries fields with non-null values.
Example response body (201)
Same shape as the request, plus the read-only meta block and the one-shot apiKey at the top level. Read-only file URLs (appearance.avatarUrl, whiteLabel.whitelabelLogoUrl) are populated by the server when the corresponding multipart parts were uploaded.
{
"name": "Helpdesk Bot",
"role": {
"rawPrompt": "You are a friendly support assistant for Acme Inc.",
"role": "CUSTOMER_SUPPORT",
"language": "English",
"responseLength": "Normal",
"websiteAddress": "https://acme.com",
"companyDescription": "Acme sells industrial widgets."
},
"conversation": {
"welcomeMessage": "Hi! How can I help today?",
"queryRefinementEnabled": true,
"conversationContinuityEnabled": true,
"conversationRatingEnabled": true,
"positiveRatingTooltip": "Helpful",
"negativeRatingTooltip": "Not helpful",
"suggestedQuestions": "What are your hours?\nHow do I cancel?\nWhere is my order?",
"dynamicSuggestedFollowups": true,
"dynamicFollowupsAutoIcons": true
},
"chatMemory": {
"enabled": true,
"summaryConversationsEnabled": true,
"conversationSummaryPrompt": "Summarize this conversation in 3 sentences.",
"clientSummaryPrompt": "Summarize what we know about this customer.",
"clientSummaryPromptType": "DEFAULT",
"conversationSummaryPromptType": "DEFAULT",
"summariesToKnowledgeRatio": 50
},
"appearance": {
"launcherColor": "#1A73E8",
"headerColor": "#1A73E8",
"titleColor": "#FFFFFF",
"subtitleColor": "#FFFFFF",
"clientMessageBubbleColor": "#000000",
"clientMessageTextColor": "#FFFFFF",
"responseMessageBubbleColor": "#F4F4F4",
"responseMessageTextColor": "#000000",
"chatSubheader": "AI support assistant",
"senderPlaceholder": "Type a message...",
"resetConversationTooltip": "Restart conversation",
"chatAlignment": "right",
"launcherBottomMargin": 20,
"launcherSideMargin": 20,
"displayShadow": true,
"customCss": ".rcw-conversation-container { border-radius: 16px; }",
"chatMessageLinkTarget": "_blank",
"minimizedDisplayMode": "icon",
"chatDesktopWidthPx": 400,
"chatDesktopHeightPx": 600,
"chatMobileSizePercent": 100,
"messageFontSize": 14,
"showChatbotBubblesDesktop": true,
"showChatbotBubblesMobile": false,
"chatbotBubblesDelaySeconds": 5,
"welcomeScreenEnabled": false,
"welcomeScreenQuestionsLabel": "Quick start",
"welcomeScreenHideHumanContactForm": false,
"welcomeScreenHideLiveChat": false,
"headerActionsLayout": "DROPDOWN",
"stackSuggestedQuestions": false,
"suggestedQuestionsFontSize": 14,
"suggestedQuestionsTextColor": "#000000",
"suggestedQuestionsBackgroundColor": "#F4F4F4",
"autoOpenChat": false,
"autoOpenChatOnMobiles": false,
"autoOpenChatDelay": false,
"autoOpenChatDelaySeconds": 5,
"simulateHumanTyping": true,
"simulateHumanTypingDelay": 5,
"footerMarkdown": "Powered by Acme",
"avatarUrl": "https://api.chatlab.com/aichat/content/avatar_a8f3b2c1_2026060110.png"
},
"humanSupport": {
"enabled": true,
"email": "support@acme.com",
"dialogMessage": "Leave us a message and we will get back to you.",
"thankYouMessage": "Thanks - we received your message.",
"emailMessageSubjectTemplate": "[Acme support] New message from {customerName}",
"emailMessageContentTemplate": "{message}\n\n - \n{conversationTranscript}",
"emailPlaceholder": "your@email.com",
"messagePlaceholder": "How can we help?",
"phoneEnabled": true,
"phonePlaceholder": "Your phone number",
"emailWithConversationContent": true
},
"leadCollection": {
"enabled": true,
"nameEnabled": true,
"nameLabel": "Your name",
"emailEnabled": true,
"emailLabel": "Email",
"phoneEnabled": false,
"phoneLabel": "Phone",
"leaveDetailsMessage": "Please leave your details and we will get in touch.",
"thankYouMessage": "Thanks - we will be in touch shortly.",
"requireBeforeNewConversation": false,
"emailNotificationEnabled": true,
"emailNotificationAddress": "leads@acme.com",
"emailWithConversationContent": true
},
"liveChat": {
"enabled": false,
"infoMessage": "Connecting you with a human agent...",
"startMessage": "You are now chatting with our team.",
"endMessage": "Live chat has ended.",
"nameLabel": "Your name",
"emailLabel": "Email",
"schedule": "{\"monday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"tuesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"wednesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"thursday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"friday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"saturday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"sunday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"timezone\":\"Europe/Warsaw\"}",
"outOfHoursMessage": "We are currently offline.",
"closeModalMessage": "End the live chat session?",
"closeModalConfirmLabel": "Yes, end",
"closeModalCancelLabel": "Cancel",
"closeModalTooltipText": "End live chat",
"operatorHasJoinedLabel": "An agent has joined.",
"operatorDidNotJoinInTimeLabel": "No agent available right now.",
"waitingForOperatorToJoinLabel": "Waiting for an agent...",
"waitingForOperatorSeconds": 60,
"redirectToHumanSupportForm": true
},
"consent": {
"newConversationRequirePolicyAccept": false,
"humanSupportRequirePolicyAccept": false,
"leadCollectionRequirePolicyAccept": true,
"liveChatRequirePolicyAccept": false,
"newConversationConsentDescription": "By starting a conversation you agree to our terms.",
"privacyPolicyConsentCheckboxLabel": "I have read and accepted the privacy policy."
},
"whiteLabel": {
"hideRoboAssistLogo": false,
"whitelabelLogoLink": "https://acme.com",
"assignToCustomDomain": false,
"whitelabelLogoUrl": "https://api.chatlab.com/aichat/content/custom_logo_4287_2026060110.png"
},
"security": {
"allowedDomains": "acme.com,support.acme.com",
"spamFilterEnabled": true,
"talkMessagesRateLimit": 30,
"talkMessagesRateLimitDurationSeconds": 60,
"talkMessagesRateLimitHitMessage": "Please slow down."
},
"advanced": {
"model": "5.6-LUNA",
"temperature": 0.4,
"chatContextSize": 16000,
"botMessagesLimit": 1000,
"internalLocale": "en_US",
"productsViewEnabled": false
},
"meta": {
"id": 4287,
"createdAt": "2026-06-01T10:11:02Z",
"updatedAt": "2026-06-01T10:11:02Z"
},
"apiKey": "ck_freshly_minted_bot_talk_key_here"
}
The apiKey field appears only on create - it is the freshly minted Bot Talk key bound to the new bot. The plaintext is shown once and cannot be retrieved later from the API; persist it on your side immediately.
The Location response header carries the URL of the new bot (/v1/management/bots/{id}).
Curl examples
Mode A - plain JSON (simplest):
curl -X POST https://api.chatlab.com/aichat/v1/management/bots \
-H "Authorization: Bearer mk_..." \
-H "Content-Type: application/json" \
-d '{"name":"Helpdesk Bot","conversation":{"welcomeMessage":"Hi!"}}'
Mode B - multipart with avatar:
curl -X POST https://api.chatlab.com/aichat/v1/management/bots \
-H "Authorization: Bearer mk_..." \
-F 'data={"name":"Helpdesk Bot","conversation":{"welcomeMessage":"Hi!"}};type=application/json' \
-F 'avatar=@./avatar.png'
GET /v1/management/bots/{bot_id}
Return the current configuration of a bot you own.
Curl example
curl https://api.chatlab.com/aichat/v1/management/bots/4287 \
-H "Authorization: Bearer mk_..."
Example response body (200)
Same shape as the POST response, minus the one-shot apiKey. The meta block is included. Returns 404 not_found_error if the bot does not exist or does not belong to your account.
The current avatar and white-label logo are surfaced as fully-qualified read-only URLs (appearance.avatarUrl, whiteLabel.whitelabelLogoUrl) - rooted at the same scheme + host + context path that served this request. Fetch the bytes by GETting those URLs directly; to replace either file, upload a new one via the multipart avatar / whitelabel_logo part on PATCH. These URL fields are ignored if sent in a request body.
{
"name": "Helpdesk Bot",
"role": {
"rawPrompt": "You are a friendly support assistant for Acme Inc.",
"role": "CUSTOMER_SUPPORT",
"language": "English",
"responseLength": "Normal",
"websiteAddress": "https://acme.com",
"companyDescription": "Acme sells industrial widgets."
},
"conversation": {
"welcomeMessage": "Hi! How can I help today?",
"queryRefinementEnabled": true,
"conversationContinuityEnabled": true,
"conversationRatingEnabled": true,
"positiveRatingTooltip": "Helpful",
"negativeRatingTooltip": "Not helpful",
"suggestedQuestions": "What are your hours?\nHow do I cancel?\nWhere is my order?",
"dynamicSuggestedFollowups": true,
"dynamicFollowupsAutoIcons": true
},
"chatMemory": {
"enabled": true,
"summaryConversationsEnabled": true,
"conversationSummaryPrompt": "Summarize this conversation in 3 sentences.",
"clientSummaryPrompt": "Summarize what we know about this customer.",
"clientSummaryPromptType": "DEFAULT",
"conversationSummaryPromptType": "DEFAULT",
"summariesToKnowledgeRatio": 50
},
"appearance": {
"launcherColor": "#1A73E8",
"headerColor": "#1A73E8",
"titleColor": "#FFFFFF",
"subtitleColor": "#FFFFFF",
"clientMessageBubbleColor": "#000000",
"clientMessageTextColor": "#FFFFFF",
"responseMessageBubbleColor": "#F4F4F4",
"responseMessageTextColor": "#000000",
"chatSubheader": "AI support assistant",
"senderPlaceholder": "Type a message...",
"resetConversationTooltip": "Restart conversation",
"chatAlignment": "right",
"launcherBottomMargin": 20,
"launcherSideMargin": 20,
"displayShadow": true,
"customCss": ".rcw-conversation-container { border-radius: 16px; }",
"chatMessageLinkTarget": "_blank",
"minimizedDisplayMode": "icon",
"chatDesktopWidthPx": 400,
"chatDesktopHeightPx": 600,
"chatMobileSizePercent": 100,
"messageFontSize": 14,
"showChatbotBubblesDesktop": true,
"showChatbotBubblesMobile": false,
"chatbotBubblesDelaySeconds": 5,
"welcomeScreenEnabled": false,
"welcomeScreenQuestionsLabel": "Quick start",
"welcomeScreenHideHumanContactForm": false,
"welcomeScreenHideLiveChat": false,
"headerActionsLayout": "DROPDOWN",
"stackSuggestedQuestions": false,
"suggestedQuestionsFontSize": 14,
"suggestedQuestionsTextColor": "#000000",
"suggestedQuestionsBackgroundColor": "#F4F4F4",
"autoOpenChat": false,
"autoOpenChatOnMobiles": false,
"autoOpenChatDelay": false,
"autoOpenChatDelaySeconds": 5,
"simulateHumanTyping": true,
"simulateHumanTypingDelay": 5,
"footerMarkdown": "Powered by Acme",
"avatarUrl": "https://api.chatlab.com/aichat/content/avatar_a8f3b2c1_2026060110.png"
},
"humanSupport": {
"enabled": true,
"email": "support@acme.com",
"dialogMessage": "Leave us a message and we will get back to you.",
"thankYouMessage": "Thanks - we received your message.",
"emailMessageSubjectTemplate": "[Acme support] New message from {customerName}",
"emailMessageContentTemplate": "{message}\n\n - \n{conversationTranscript}",
"emailPlaceholder": "your@email.com",
"messagePlaceholder": "How can we help?",
"phoneEnabled": true,
"phonePlaceholder": "Your phone number",
"emailWithConversationContent": true
},
"leadCollection": {
"enabled": true,
"nameEnabled": true,
"nameLabel": "Your name",
"emailEnabled": true,
"emailLabel": "Email",
"phoneEnabled": false,
"phoneLabel": "Phone",
"leaveDetailsMessage": "Please leave your details and we will get in touch.",
"thankYouMessage": "Thanks - we will be in touch shortly.",
"requireBeforeNewConversation": false,
"emailNotificationEnabled": true,
"emailNotificationAddress": "leads@acme.com",
"emailWithConversationContent": true
},
"liveChat": {
"enabled": false,
"infoMessage": "Connecting you with a human agent...",
"startMessage": "You are now chatting with our team.",
"endMessage": "Live chat has ended.",
"nameLabel": "Your name",
"emailLabel": "Email",
"schedule": "{\"monday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"tuesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"wednesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"thursday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"friday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"saturday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"sunday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"timezone\":\"Europe/Warsaw\"}",
"outOfHoursMessage": "We are currently offline.",
"closeModalMessage": "End the live chat session?",
"closeModalConfirmLabel": "Yes, end",
"closeModalCancelLabel": "Cancel",
"closeModalTooltipText": "End live chat",
"operatorHasJoinedLabel": "An agent has joined.",
"operatorDidNotJoinInTimeLabel": "No agent available right now.",
"waitingForOperatorToJoinLabel": "Waiting for an agent...",
"waitingForOperatorSeconds": 60,
"redirectToHumanSupportForm": true
},
"consent": {
"newConversationRequirePolicyAccept": false,
"humanSupportRequirePolicyAccept": false,
"leadCollectionRequirePolicyAccept": true,
"liveChatRequirePolicyAccept": false,
"newConversationConsentDescription": "By starting a conversation you agree to our terms.",
"privacyPolicyConsentCheckboxLabel": "I have read and accepted the privacy policy."
},
"whiteLabel": {
"hideRoboAssistLogo": false,
"whitelabelLogoLink": "https://acme.com",
"assignToCustomDomain": false,
"whitelabelLogoUrl": "https://api.chatlab.com/aichat/content/custom_logo_4287_2026060110.png"
},
"security": {
"allowedDomains": "acme.com,support.acme.com",
"spamFilterEnabled": true,
"talkMessagesRateLimit": 30,
"talkMessagesRateLimitDurationSeconds": 60,
"talkMessagesRateLimitHitMessage": "Please slow down."
},
"advanced": {
"model": "5.6-LUNA",
"temperature": 0.4,
"chatContextSize": 16000,
"botMessagesLimit": 1000,
"internalLocale": "en_US",
"productsViewEnabled": false
},
"meta": {
"id": 4287,
"createdAt": "2026-06-01T10:11:02Z",
"updatedAt": "2026-06-01T11:02:19Z"
}
}
Clone a bot
The request body of POST /v1/management/bots and the response body of GET /v1/management/bots/{bot_id} share the same shape, so cloning is a three-step pipeline: GET the source, strip server-managed identity fields, POST the result.
1. GET the source bot.
curl https://api.chatlab.com/aichat/v1/management/bots/4287 \
-H "Authorization: Bearer mk_..." \
-o source-bot.json
2. Strip the top-level meta block. The meta object (id, createdAt, updatedAt) is server-managed and read-only - leaving it in the POST body does no harm (the server ignores it) but removing it makes the intent explicit and keeps the payload clean. Optionally edit name so the clone is distinguishable from the source.
jq 'del(.meta) | .name = "Helpdesk Bot (clone)"' source-bot.json > clone-body.json
3. POST the stripped body to create the clone. See the POST /v1/management/bots reference above for the full body shape and validation rules.
curl -X POST https://api.chatlab.com/aichat/v1/management/bots \
-H "Authorization: Bearer mk_..." \
-H "Content-Type: application/json" \
-d @clone-body.json
The response contains the new bot's meta.id plus a freshly minted apiKey (the Bot Talk key for the clone). The apiKey plaintext is returned only on this create response - copy it before discarding the response body; it cannot be retrieved later.
This copies supported settings, not a fully trained chatbot. Training content, conversation history, integrations/actions, Flow, and form definitions are not cloned by these endpoints. Configure those separately in the app.
Additional caveats:
- Files are not cloned.
appearance.avatarUrlandwhiteLabel.whitelabelLogoUrlare read-only and point at the source bot's files. If you need the same avatar or white-label logo on the clone, download the bytes from the source URLs and upload them as multipartavatar/whitelabel_logoparts - either on the create POST (Mode B) or on a follow-up PATCH. - Bot Talk keys are not cloned. Each bot has its own pool of Bot Talk keys. The single
apiKeyreturned by the create POST is the only one minted automatically; create additional keys from the bot's API tab if needed.
PATCH /v1/management/bots/{bot_id}
Update one or more fields on a bot you own. Only sections / fields present in the JSON are modified; everything omitted (or sent as null) is left untouched. Partial-update semantics apply per field within a sent section.
Two equivalent Content-Types are accepted (same as POST):
Mode A - plain JSON (recommended when only updating settings):
Content-Type: application/json- Request body is the patch JSON (no
datawrapper)
Mode B - multipart/form-data (use when uploading files):
dataJSON part (optional) - the patch. Send only if you want to change fields. Omit entirely if you only want to upload an avatar or logo.avatarfile part (optional) - replace the avatarwhitelabel_logofile part (optional) - replace the white-label logo (applies only if your account includes white-labelling)
All three parts are optional on PATCH, but at least one must be present for the call to be meaningful.
Example request body (multiple sections)
Any field accepted by POST /v1/management/bots may also be sent here. The example below illustrates several sections, not every supported field; in practice you send only the keys you want to change (see "Minimal partial update" further down) - every key omitted (or sent as null) leaves the persisted value untouched.
{
"name": "Helpdesk Bot",
"role": {
"rawPrompt": "You are a friendly support assistant for Acme Inc. Be concise.",
"role": "CUSTOMER_SUPPORT",
"language": "English",
"responseLength": "Concise",
"websiteAddress": "https://acme.com",
"companyDescription": "Acme sells industrial widgets."
},
"conversation": {
"welcomeMessage": "Hello there!",
"queryRefinementEnabled": true,
"conversationContinuityEnabled": true,
"conversationRatingEnabled": true,
"positiveRatingTooltip": "Helpful",
"negativeRatingTooltip": "Not helpful",
"suggestedQuestions": "Pricing\nShipping times\nReturns policy",
"dynamicSuggestedFollowups": true,
"dynamicFollowupsAutoIcons": true
},
"chatMemory": {
"enabled": true,
"summaryConversationsEnabled": true,
"conversationSummaryPrompt": "Summarize the conversation in 3 sentences.",
"clientSummaryPrompt": "Summarize what we know about the customer.",
"clientSummaryPromptType": "DEFAULT",
"conversationSummaryPromptType": "DEFAULT",
"summariesToKnowledgeRatio": 50
},
"appearance": {
"launcherColor": "#abcdef",
"headerColor": "#abcdef",
"titleColor": "#FFFFFF",
"subtitleColor": "#FFFFFF",
"clientMessageBubbleColor": "#000000",
"clientMessageTextColor": "#FFFFFF",
"responseMessageBubbleColor": "#F4F4F4",
"responseMessageTextColor": "#000000",
"chatSubheader": "AI assistant",
"senderPlaceholder": "Type a message",
"resetConversationTooltip": "Restart conversation",
"chatAlignment": "right",
"launcherBottomMargin": 20,
"launcherSideMargin": 20,
"displayShadow": true,
"customCss": ".rcw-conversation-container { border-radius: 16px; }",
"chatMessageLinkTarget": "_blank",
"minimizedDisplayMode": "icon",
"chatDesktopWidthPx": 400,
"chatDesktopHeightPx": 600,
"chatMobileSizePercent": 100,
"messageFontSize": 14,
"showChatbotBubblesDesktop": true,
"showChatbotBubblesMobile": false,
"chatbotBubblesDelaySeconds": 5,
"welcomeScreenEnabled": false,
"welcomeScreenQuestionsLabel": "Quick start",
"welcomeScreenHideHumanContactForm": false,
"welcomeScreenHideLiveChat": false,
"headerActionsLayout": "DROPDOWN",
"stackSuggestedQuestions": false,
"suggestedQuestionsFontSize": 14,
"suggestedQuestionsTextColor": "#000000",
"suggestedQuestionsBackgroundColor": "#F4F4F4",
"autoOpenChat": false,
"autoOpenChatOnMobiles": false,
"autoOpenChatDelay": false,
"autoOpenChatDelaySeconds": 5,
"simulateHumanTyping": true,
"simulateHumanTypingDelay": 5,
"footerMarkdown": "Powered by Acme"
},
"humanSupport": {
"enabled": true,
"email": "support@acme.com",
"dialogMessage": "Leave us a message and we'll get back to you.",
"thankYouMessage": "Thanks!",
"emailMessageSubjectTemplate": "[Acme support] New message from {customerName}",
"emailMessageContentTemplate": "{message}\n\n - \n{conversationTranscript}",
"emailPlaceholder": "your@email.com",
"messagePlaceholder": "How can we help?",
"phoneEnabled": true,
"phonePlaceholder": "Your phone number",
"emailWithConversationContent": true
},
"leadCollection": {
"enabled": true,
"nameEnabled": true,
"nameLabel": "Your name",
"emailEnabled": true,
"emailLabel": "Email",
"phoneEnabled": false,
"phoneLabel": "Phone",
"leaveDetailsMessage": "Please leave your details.",
"thankYouMessage": "Thanks!",
"requireBeforeNewConversation": true,
"emailNotificationEnabled": true,
"emailNotificationAddress": "leads@acme.com",
"emailWithConversationContent": true
},
"liveChat": {
"enabled": false,
"infoMessage": "Connecting you with a human agent...",
"startMessage": "You are now chatting with our team.",
"endMessage": "Live chat has ended.",
"nameLabel": "Your name",
"emailLabel": "Email",
"schedule": "{\"monday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"tuesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"wednesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"thursday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"friday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"saturday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"sunday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"timezone\":\"Europe/Warsaw\"}",
"outOfHoursMessage": "We are currently offline.",
"closeModalMessage": "End the live chat session?",
"closeModalConfirmLabel": "Yes, end",
"closeModalCancelLabel": "Cancel",
"closeModalTooltipText": "End live chat",
"operatorHasJoinedLabel": "An agent has joined.",
"operatorDidNotJoinInTimeLabel": "No agent available right now.",
"waitingForOperatorToJoinLabel": "Waiting for an agent...",
"waitingForOperatorSeconds": 60,
"redirectToHumanSupportForm": true
},
"consent": {
"newConversationRequirePolicyAccept": false,
"humanSupportRequirePolicyAccept": false,
"leadCollectionRequirePolicyAccept": true,
"liveChatRequirePolicyAccept": false,
"newConversationConsentDescription": "By starting a conversation you agree to our terms.",
"privacyPolicyConsentCheckboxLabel": "I have read and accepted the privacy policy."
},
"whiteLabel": {
"hideRoboAssistLogo": false,
"whitelabelLogoLink": "https://acme.com",
"assignToCustomDomain": false
},
"security": {
"allowedDomains": "acme.com,support.acme.com",
"spamFilterEnabled": true,
"talkMessagesRateLimit": 30,
"talkMessagesRateLimitDurationSeconds": 60,
"talkMessagesRateLimitHitMessage": "Please slow down."
},
"advanced": {
"model": "5",
"temperature": 0.2,
"chatContextSize": 32000,
"botMessagesLimit": 2000,
"internalLocale": "en_US",
"productsViewEnabled": false
}
}
Minimal partial update
PATCH a single field by sending exactly the keys you want changed - everything else is preserved.
{
"appearance": {
"launcherColor": "#abcdef"
}
}
Curl examples
Mode A - plain JSON (simplest):
curl -X PATCH https://api.chatlab.com/aichat/v1/management/bots/4287 \
-H "Authorization: Bearer mk_..." \
-H "Content-Type: application/json" \
-d '{"appearance":{"launcherColor":"#abcdef"}}'
Mode B - multipart (when replacing avatar / logo):
curl -X PATCH https://api.chatlab.com/aichat/v1/management/bots/4287 \
-H "Authorization: Bearer mk_..." \
-F 'data={"appearance":{"launcherColor":"#abcdef"}};type=application/json' \
-F 'avatar=@./new-avatar.png'
Mode B - replace only the avatar (no field changes):
curl -X PATCH https://api.chatlab.com/aichat/v1/management/bots/4287 \
-H "Authorization: Bearer mk_..." \
-F 'avatar=@./new-avatar.png'
Response body (200)
Same shape as GET /v1/management/bots/{bot_id} - the bot's full configuration after the patch was applied, including the meta block. No apiKey field. Returns 404 not_found_error if the bot does not exist or does not belong to your account.
The example below shows the response after applying the Full request body (multiple sections) patch above to the bot from the GET example - changed fields reflect the new values, untouched fields are preserved, and meta.updatedAt advances.
{
"name": "Helpdesk Bot",
"role": {
"rawPrompt": "You are a friendly support assistant for Acme Inc. Be concise.",
"role": "CUSTOMER_SUPPORT",
"language": "English",
"responseLength": "Concise",
"websiteAddress": "https://acme.com",
"companyDescription": "Acme sells industrial widgets."
},
"conversation": {
"welcomeMessage": "Hello there!",
"queryRefinementEnabled": true,
"conversationContinuityEnabled": true,
"conversationRatingEnabled": true,
"positiveRatingTooltip": "Helpful",
"negativeRatingTooltip": "Not helpful",
"suggestedQuestions": "Pricing\nShipping times\nReturns policy",
"dynamicSuggestedFollowups": true,
"dynamicFollowupsAutoIcons": true
},
"chatMemory": {
"enabled": true,
"summaryConversationsEnabled": true,
"conversationSummaryPrompt": "Summarize the conversation in 3 sentences.",
"clientSummaryPrompt": "Summarize what we know about the customer.",
"clientSummaryPromptType": "DEFAULT",
"conversationSummaryPromptType": "DEFAULT",
"summariesToKnowledgeRatio": 50
},
"appearance": {
"launcherColor": "#abcdef",
"headerColor": "#abcdef",
"titleColor": "#FFFFFF",
"subtitleColor": "#FFFFFF",
"clientMessageBubbleColor": "#000000",
"clientMessageTextColor": "#FFFFFF",
"responseMessageBubbleColor": "#F4F4F4",
"responseMessageTextColor": "#000000",
"chatSubheader": "AI assistant",
"senderPlaceholder": "Type a message",
"resetConversationTooltip": "Restart conversation",
"chatAlignment": "right",
"launcherBottomMargin": 20,
"launcherSideMargin": 20,
"displayShadow": true,
"customCss": ".rcw-conversation-container { border-radius: 16px; }",
"chatMessageLinkTarget": "_blank",
"minimizedDisplayMode": "icon",
"chatDesktopWidthPx": 400,
"chatDesktopHeightPx": 600,
"chatMobileSizePercent": 100,
"messageFontSize": 14,
"showChatbotBubblesDesktop": true,
"showChatbotBubblesMobile": false,
"chatbotBubblesDelaySeconds": 5,
"welcomeScreenEnabled": false,
"welcomeScreenQuestionsLabel": "Quick start",
"welcomeScreenHideHumanContactForm": false,
"welcomeScreenHideLiveChat": false,
"headerActionsLayout": "DROPDOWN",
"stackSuggestedQuestions": false,
"suggestedQuestionsFontSize": 14,
"suggestedQuestionsTextColor": "#000000",
"suggestedQuestionsBackgroundColor": "#F4F4F4",
"autoOpenChat": false,
"autoOpenChatOnMobiles": false,
"autoOpenChatDelay": false,
"autoOpenChatDelaySeconds": 5,
"simulateHumanTyping": true,
"simulateHumanTypingDelay": 5,
"footerMarkdown": "Powered by Acme",
"avatarUrl": "https://api.chatlab.com/aichat/content/avatar_a8f3b2c1_2026060110.png"
},
"humanSupport": {
"enabled": true,
"email": "support@acme.com",
"dialogMessage": "Leave us a message and we'll get back to you.",
"thankYouMessage": "Thanks!",
"emailMessageSubjectTemplate": "[Acme support] New message from {customerName}",
"emailMessageContentTemplate": "{message}\n\n - \n{conversationTranscript}",
"emailPlaceholder": "your@email.com",
"messagePlaceholder": "How can we help?",
"phoneEnabled": true,
"phonePlaceholder": "Your phone number",
"emailWithConversationContent": true
},
"leadCollection": {
"enabled": true,
"nameEnabled": true,
"nameLabel": "Your name",
"emailEnabled": true,
"emailLabel": "Email",
"phoneEnabled": false,
"phoneLabel": "Phone",
"leaveDetailsMessage": "Please leave your details.",
"thankYouMessage": "Thanks!",
"requireBeforeNewConversation": true,
"emailNotificationEnabled": true,
"emailNotificationAddress": "leads@acme.com",
"emailWithConversationContent": true
},
"liveChat": {
"enabled": false,
"infoMessage": "Connecting you with a human agent...",
"startMessage": "You are now chatting with our team.",
"endMessage": "Live chat has ended.",
"nameLabel": "Your name",
"emailLabel": "Email",
"schedule": "{\"monday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"tuesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"wednesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"thursday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"friday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"saturday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"sunday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"timezone\":\"Europe/Warsaw\"}",
"outOfHoursMessage": "We are currently offline.",
"closeModalMessage": "End the live chat session?",
"closeModalConfirmLabel": "Yes, end",
"closeModalCancelLabel": "Cancel",
"closeModalTooltipText": "End live chat",
"operatorHasJoinedLabel": "An agent has joined.",
"operatorDidNotJoinInTimeLabel": "No agent available right now.",
"waitingForOperatorToJoinLabel": "Waiting for an agent...",
"waitingForOperatorSeconds": 60,
"redirectToHumanSupportForm": true
},
"consent": {
"newConversationRequirePolicyAccept": false,
"humanSupportRequirePolicyAccept": false,
"leadCollectionRequirePolicyAccept": true,
"liveChatRequirePolicyAccept": false,
"newConversationConsentDescription": "By starting a conversation you agree to our terms.",
"privacyPolicyConsentCheckboxLabel": "I have read and accepted the privacy policy."
},
"whiteLabel": {
"hideRoboAssistLogo": false,
"whitelabelLogoLink": "https://acme.com",
"assignToCustomDomain": false,
"whitelabelLogoUrl": "https://api.chatlab.com/aichat/content/custom_logo_4287_2026060110.png"
},
"security": {
"allowedDomains": "acme.com,support.acme.com",
"spamFilterEnabled": true,
"talkMessagesRateLimit": 30,
"talkMessagesRateLimitDurationSeconds": 60,
"talkMessagesRateLimitHitMessage": "Please slow down."
},
"advanced": {
"model": "5",
"temperature": 0.2,
"chatContextSize": 32000,
"botMessagesLimit": 2000,
"internalLocale": "en_US",
"productsViewEnabled": false
},
"meta": {
"id": 4287,
"createdAt": "2026-06-01T10:11:02Z",
"updatedAt": "2026-06-01T12:45:08Z"
}
}
GET /v1/usage
Read current subscription usage for the account that owns the Management key.
Response body (200)
{
"subscriptionType": "your-plan-id",
"messages": {"used": 4123, "limit": 11000, "remaining": 6877},
"bots": {"used": 3, "limit": 5, "remaining": 2}
}
subscriptionTypeis a lower-cased identifier of the account's current plan (your-plan-idis an illustrative placeholder). Plans come from a dynamic catalog, so the exact set of identifiers can change over time as plans are renamed or added - treat this as an opaque string, not a fixed enum.messages.used/limit/remainingare the current calendar-month message credits, resetting on the first day of the month, not the subscription renewal day. They are credit-weighted, not raw reply counts.bots.used/limit/remainingcount owned bots, including paused bots, against your account's total bot limit.
Rate limit headers
Responses that reach the rate-limit stage (i.e. auth and IP whitelist passed) include:
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 7
X-RateLimit-Reset: 1715430000
X-RateLimit-Limit- the per-key cap actually applied to this call (10 by default, or your configuredrateLimitPerMinuteif lower).X-RateLimit-Remaining- tokens left in the bucket right after this call.X-RateLimit-Reset- Unix epoch seconds at which the next token becomes available (not a full bucket reset; the bucket refills continuously). When the bucket is full, this is the current time.
On 429 rate_limit_exceeded responses, Retry-After is also set, expressed in whole seconds until at least one token frees up.
Pre-auth errors (401 missing_api_key, 401 invalid_api_key, 403 ip_blocked) and 403 ip_not_whitelisted do not carry the X-RateLimit-* headers - the limiter is only consulted after authentication and IP checks succeed.
Error format
Same envelope as Bot Talk API:
{
"error": {
"type": "permission_error",
"code": "key_type_not_allowed",
"message": "This endpoint requires a MANAGEMENT API key.",
"param": null
}
}
Validation errors use code: "invalid_parameter" and prepend the failing field path to the message so the failing section is easy to spot:
{
"error": {
"type": "invalid_request_error",
"code": "invalid_parameter",
"message": "humanSupport.email Human support email is required when humanSupport.enabled=true"
}
}
Bad values for enum / closed-set fields (e.g. chatMemory.clientSummaryPromptType = "BOGUS") include the field path, the rejected value, and the list of allowed values:
{
"error": {
"type": "invalid_request_error",
"code": "invalid_parameter",
"message": "chatMemory.clientSummaryPromptType 'BOGUS' is not a valid value. Allowed: CUSTOM, DEFAULT"
}
}
Plan limits use 402 with code: "quota_exceeded". This covers the bot count cap on create and the advanced.botMessagesLimit sum check on create and update:
{
"error": {
"type": "quota_exceeded_error",
"code": "quota_exceeded",
"message": "The sum of per-bot message limits across your bots would exceed the monthly message credits of your subscription plan."
}
}
Related
For conversation endpoints and SSE streaming, see Bot Talk API.