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.
- Copy the full key from the success modal. The plaintext is shown only once.
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 11 sections. Each section maps 1:1 to a 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 tab in the admin app.
role- bot persona, raw prompt, response length, language, website / company context (Role tab)conversation- welcome message, query refinement, conversation continuity, rating toggle + tooltips, suggested questions content + dynamic followups (Conversation tab)chatMemory- chat memory toggle, summary prompts, context allocation (Chat 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 Support 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 tab)whiteLabel- hide-logo, custom logo link, custom-domain hosting (White Label tab)security- allowed domains, spam filter, talk rate limits (Security tab)advanced- LLM model, temperature, context size, bot messages limit, internal locale, products view (Advanced tab)
Only name lives at the top level, because it identifies the bot rather than belonging to any one tab.
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 verbatim as the entire system prompt. responseLength, language, websiteAddress, companyDescription are stored but not injected into the prompt - if you want any of them reflected in the bot's behaviour you must include them in your rawPrompt text yourself. Role-specific safety rails and tone instructions are also not added; you own the whole prompt.
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
Several fields accept only a fixed set of string values. Sending anything outside the list is rejected with 400 validation_failed and the field path in error.param. Values are case-sensitive.
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; the set is gated by your subscription tier and any value outside that tier returns400 invalid_parameteradvanced.chatContextSize-8000,16000,32000. Capped to your tier limit; higher values are silently clampedchatMemory.clientSummaryPromptType-DEFAULT,CUSTOMchatMemory.conversationSummaryPromptType-DEFAULT,CUSTOMappearance.chatAlignment-left,rightappearance.minimizedDisplayMode-icon,minifiedappearance.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 validation_failed. -
chatMemory.summariesToKnowledgeRatio- integer percent,10-90step10. Controls how much of the chat context is reserved for client historical summaries vs. the rest (knowledge base, current conversation, instructions). Default50. Values outside10-90are rejected with400 validation_failed. Only applies whenchatMemory.enabled=trueANDchatMemory.summaryConversationsEnabled=true. -
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 👍 / 👎 buttons next to each AI reply whenconversation.conversationRatingEnabled=true. Default copy is "I like the response" / "I don't like the response". Visible to end users. -
whiteLabel.hideRoboAssistLogo- Premium plan only (white-label). Hides the "Powered by ChatLab" footer line. On non-Premium accounts the value is stored but ignored; the footer always renders. -
whiteLabel.whitelabelLogoLink- Premium plan only (white-label). 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").0disables the per-IP rate limit. When non-zero, the widget enforces N messages per duration-in-seconds before showingsecurity.talkMessagesRateLimitHitMessageto the visitor. -
advanced.botMessagesLimit- integer (JSON number, e.g.1000).0means "no limit"; otherwise must be a multiple of 1000 (1000,2000,10000, ...). Values like100or1500are rejected with400 validation_failed. Then silently clamped to the tier cap on top.
AI text models (advanced.model)
Send the exact API value (left-hand backticked column). Display name in the admin UI is in parentheses. Subscription tier determines which subset is selectable; sending a tier-gated model returns 400 invalid_parameter. Default for new bots is 5-MINI.
4-O-MINI(GPT 4-o mini)4-O(GPT 4-o)4.1-MINI(GPT 4.1-mini)4.1(GPT 4.1)5-MINI(GPT 5-mini)5(GPT 5)5.1(GPT 5.1)5.4-MINI(GPT 5.4-mini)5.4(GPT 5.4)5.5(GPT 5.5)GEMINI 2.5 Flash(Gemini 2.5 Flash)GEMINI 2.5 PRO(Gemini 2.5 Pro)GEMINI 3 Flash(Gemini 3 Flash)GEMINI 3 PRO(Gemini 3 Pro)
Field reference (full request schema)
Every field on the wire, with its type, constraint, and one-line description. 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 rating on bot messages | |
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 | |
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, step 10 |
% of the chat context window allocated to summaries vs RAG knowledge |
§ appearance
| Field | Type | Constraint | Description |
|---|---|---|---|
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} |
Minimized state: launcher icon or compact sender bar |
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) | |
welcomeScreenEnabled |
boolean | Show the Welcome Screen instead of going straight to chat | |
welcomeScreenQuestionsLabel |
string | Label above the suggested questions on the welcome screen | |
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 | |
emailWithConversationContent |
boolean | If true, include the conversation transcript in the email body |
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 |
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 |
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 | Premium plan only | Hide the default ChatLab logo in the footer |
whitelabelLogoLink |
string | Premium plan only | 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 | |
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 |
§ advanced
| Field | Type | Constraint | Description |
|---|---|---|---|
model |
string | tier-gated; see "AI text models" above | LLM identifier (e.g. 5-MINI) |
temperature |
decimal | 0.0–1.0 |
Sampling temperature (matches the UI slider) |
chatContextSize |
int | ∈ {8000, 16000, 32000}; silently clamped to your tier cap |
Token window for chat history |
botMessagesLimit |
long | 0 or multiple of 1000 (e.g. 1000, 2000, 10000) |
Max bot replies per conversation (0 = no limit) |
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 products view inside chat |
Out of API scope
The admin UI surfaces a few areas that are intentionally not exposed in this version of the Management API:
- Voice tab - the entire voice configuration (toggle, voice id, voice model, daily cap, etc.). Voice is not yet on prod.
- Actions tab - managed e-commerce / booking integrations and custom API functions. Tool calling has never been part of the Management API.
- Custom open / close chat icons -
customLauncherIconVisible,openChatIcon,closeChatIcon. The API exposes only the mainavatarandwhitelabel_logomultipart parts. - IP and country blacklists - admin-only features, not exposed via Management API.
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 (Premium only)
Only name is required in the JSON; every other field falls back to the same default the admin UI wizard would set.
Full 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",
"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?",
"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-MINI",
"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, plusleaveDetailsMessageandthankYouMessage- Subscription-capped fields (
advanced.chatContextSize,advanced.botMessagesLimit, etc.) are silently clamped to your tier limit
Fields whose value is null on the server are omitted from the JSON body - the wire only carries fields with non-null values.
Full 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",
"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?",
"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-MINI",
"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_..."
Full 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",
"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?",
"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-MINI",
"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.
Two 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 (Premium only)
All three parts are optional on PATCH, but at least one must be present for the call to be meaningful.
Full request body (maximal surface)
Any field accepted by POST /v1/management/bots may also be sent here. The example below is the full surface; 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",
"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?",
"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 (maximal surface) 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",
"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?",
"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": "standard",
"messages": {"used": 4123, "limit": 11000, "remaining": 6877},
"bots": {"used": 3, "limit": 5, "remaining": 2}
}
subscriptionTypeis lower-cased (free/basic/standard/premium).messages.used/limit/remainingare the current billing-period message credits.bots.used/limit/remainingcount active bots against the plan cap.
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"
}
}
Related
For conversation endpoints and SSE streaming, see Bot Talk API.