Chat API

Management API

Last updated: June 1, 2026

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

  1. Open the admin app and go to Account Settings > Management API.
  2. Click + Create Management Key, name it, optionally set IP whitelist and rate limit, then submit.
  3. 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 rateLimitPerMinute and 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 for GET /v1/management/bots/{bot_id}
  • bot_management - required for POST /v1/management/bots and PATCH /v1/management/bots/{bot_id}
  • usage - required for GET /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 multipart avatar part (see PATCH).
  • whiteLabel.whitelabelLogoUrl - fully-qualified public URL of the white-label header logo. Same pattern as avatarUrl. To change it, upload a new file via the multipart whitelabel_logo part (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 automatically
  • name - bot name, injected into the opening sentence
  • role.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 ≈ 200
  • role.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, CUSTOM
  • role.responseLength - Concise, Normal, Detailed
  • role.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 to Auto Detect when omitted on create.
  • advanced.model - see "AI text models" below; the set is gated by your subscription tier and any value outside that tier returns 400 invalid_parameter
  • advanced.chatContextSize - 8000, 16000, 32000. Capped to your tier limit; higher values are silently clamped
  • chatMemory.clientSummaryPromptType - DEFAULT, CUSTOM
  • chatMemory.conversationSummaryPromptType - DEFAULT, CUSTOM
  • appearance.chatAlignment - left, right
  • appearance.minimizedDisplayMode - icon, minified
  • appearance.chatMessageLinkTarget - _blank, _self
  • leadCollection.requireBeforeNewConversation - boolean toggle. true forces the user to fill the lead form before starting a conversation; false lets 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 is 0.0 to 1.0, matching the slider in the admin UI. Values outside this range are rejected with 400 validation_failed.

  • chatMemory.summariesToKnowledgeRatio - integer percent, 10-90 step 10. Controls how much of the chat context is reserved for client historical summaries vs. the rest (knowledge base, current conversation, instructions). Default 50. Values outside 10-90 are rejected with 400 validation_failed. Only applies when chatMemory.enabled=true AND chatMemory.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 a timezone key:

    • each weekday key (monday-sunday) maps to {enabled: boolean, from: "H:MM", to: "H:MM"} in 24-hour time
    • timezone is 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.outOfHoursMessage is 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.

  • conversation.positiveRatingTooltip / conversation.negativeRatingTooltip - short labels shown on the 👍 / 👎 buttons next to each AI reply when conversation.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 when hideRoboAssistLogo=true and a custom logo file is uploaded via the whitelabel_logo multipart part.

  • appearance.simulateHumanTypingDelay - seconds (not milliseconds), integer 0-200. Pause between successive bot bubbles when simulateHumanTyping=true. Default 5.

  • appearance.autoOpenChatDelaySeconds - seconds, integer. Delay before the widget auto-opens when autoOpenChat=true and autoOpenChatDelay=true.

  • advanced.internalLocale - IETF locale-region code in ll_CC form (underscore, NOT ll-CC with 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. Default en_US. This is the locale used for date/number formatting in the widget chrome, distinct from role.language (the bot's conversational output language).

  • security.talkMessagesRateLimit / security.talkMessagesRateLimitDurationSeconds - integers (send as JSON numbers, e.g. 30, not "30"). 0 disables the per-IP rate limit. When non-zero, the widget enforces N messages per duration-in-seconds before showing security.talkMessagesRateLimitHitMessage to the visitor.

  • advanced.botMessagesLimit - integer (JSON number, e.g. 1000). 0 means "no limit"; otherwise must be a multiple of 1000 (1000, 2000, 10000, ...). Values like 100 or 1500 are rejected with 400 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 the POST /v1/management/bots response - 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 1090, 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 0500 Launcher distance from bottom edge (px)
launcherSideMargin int 0500 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 0200 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.01.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 main avatar and whitelabel_logo multipart 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 data wrapper)
  • Files (avatar / logo) can be uploaded later via a second PATCH using mode B

Mode B - multipart/form-data (use when uploading files in the same request):

  • Content-Type: multipart/form-data; boundary=...
  • data JSON part (required, Content-Type: application/json) - bot configuration in the nested shape described above
  • avatar file part (optional) - bot avatar image
  • whitelabel_logo file 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 chars
  • advanced.temperature - between 0.0 and 1.0
  • chatMemory.summariesToKnowledgeRatio - integer between 10 and 90 (percent, step 10)
  • appearance.launcherBottomMargin, appearance.launcherSideMargin - between 0 and 500
  • appearance.footerMarkdown - max 255 chars
  • humanSupport.enabled=true requires humanSupport.email to be set
  • leadCollection.enabled=true requires at least one of leadCollection.emailEnabled or leadCollection.phoneEnabled to be true; whichever channel is on also requires its label, plus leaveDetailsMessage and thankYouMessage
  • 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.avatarUrl and whiteLabel.whitelabelLogoUrl are 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 multipart avatar / whitelabel_logo parts - 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 apiKey returned 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 data wrapper)

Mode B - multipart/form-data (use when uploading files):

  • data JSON part (optional) - the patch. Send only if you want to change fields. Omit entirely if you only want to upload an avatar or logo.
  • avatar file part (optional) - replace the avatar
  • whitelabel_logo file 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}
}
  • subscriptionType is lower-cased (free / basic / standard / premium).
  • messages.used / limit / remaining are the current billing-period message credits.
  • bots.used / limit / remaining count 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 configured rateLimitPerMinute if 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.