Help Center
Chat API

Management API

Last updated:

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. Select only the permissions your integration needs and copy the full key from the success modal. The plaintext is shown only once.
  4. Store it on your server, not in browser code or a public repository. Make a read-only request such as GET /v1/usage with the usage permission to verify access.
  5. Revoke unused or compromised keys from the Management API page.

A key looks like mk_abcdefghijklmnopqrstuvwxyz012345. The mk_ prefix distinguishes it from Bot Talk keys (ck_).

Authentication

Authorization: Bearer mk_abcdefghijklmnopqrstuvwxyz012345

Sending a mk_ key to /v1/chat (or any other Bot Talk endpoint) returns 403 key_type_not_allowed. Sending a ck_ key to /v1/management/* returns the same error.

Limits

  • Max 5 active Management API keys per user
  • Max 10 requests per minute per key (token bucket, capacity 10, smooth refill at ~1 token every 6 seconds). Configurable downward at create time - set a lower 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 13 sections. Each section maps to a sub-tab in the admin app's Bot Settings sidebar, so the JSON keys and the visible tabs line up: if you change consent.humanSupportRequirePolicyAccept via the API, you will see the same toggle flip on the Consent & Privacy tab in the admin app.

  • role - bot persona, raw prompt, response length, language, website / company context (Role & Behavior tab)
  • conversation - welcome message, query refinement, conversation continuity, rating toggle + tooltips, suggested questions content + dynamic followups (Chat Conversation tab)
  • chatMemory - chat memory toggle, summary prompts, context allocation (Summaries & Memory tab)
  • appearance - colors, copy, dimensions, custom CSS, welcome screen, suggested-question styling, auto-open behaviour, human-typing simulation, footer markdown (Appearance tab)
  • humanSupport - human contact form (Human Contact Form tab)
  • leadCollection - lead capture form (Lead Collection tab)
  • liveChat - live chat handoff (Live Chat tab)
  • consent - all four privacy-policy consent toggles plus the consent screen copy (Consent & Privacy tab)
  • whiteLabel - hide-logo, custom logo link, custom-domain hosting (Whitelabel tab)
  • security - allowed domains, spam filter, talk rate limits (Security tab)
  • voice - voice input and voice conversations: model, voice, languages, prompt, duration cap (Voice Conversation tab)
  • multilingual - multilingual mode, base language, offered languages, knowledge-language handling (Languages tab)
  • advanced - LLM model, temperature, context size, bot messages limit, internal locale, Offer Cards (Model & Advanced tab)

Of the writable settings, only name lives at the top level, because it identifies the bot rather than belonging to any one tab.

The Bot Settings sidebar currently has 16 sub-tabs, and 13 of them map to the sections above. The three sub-tabs with no corresponding section are Flow, Actions, and Forms - all three covered under "Out of API scope" below. The 13 that do map are Appearance, Chat Conversation, Role & Behavior, Human Contact Form, Lead Collection, Live Chat, Whitelabel, Consent & Privacy, Security, Model & Advanced, Summaries & Memory, Voice Conversation, and Languages.

Request body and response body share the same shape. The response adds two extras:

  • meta - read-only: bot id and timestamps. Strip it to turn a GET response into a valid POST body.
  • apiKey - present only on create - the freshly minted Bot Talk API key for the new bot.

Two fields inside the shared shape are read-only - returned in the response, ignored if you try to send them on POST/PATCH:

  • appearance.avatarUrl - fully-qualified public URL of the bot avatar image (e.g. https://api.chatlab.com/aichat/content/avatar_xyz.png). GET it directly to download the bytes. To change it, upload a new file via the 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 as the role prompt and appends the platform's fixed response-length guidance. This is separate from role.responseLength: the optional Concise/Normal/Detailed setting is not injected for a CUSTOM role. Likewise, language, websiteAddress, and companyDescription are stored but not injected into this role prompt. Include any desired language, response-length target, or company context in rawPrompt yourself. Additional runtime instructions and retrieved context can also be supplied, so rawPrompt is not the entire model input. The predefined role's tone instructions are not added, but choosing CUSTOM does not remove all platform instructions.

Use CUSTOM only when the template-driven prompt does not fit your use case (e.g. you need a very domain-specific persona, your own safety constraints, a non-standard output format).

Enum / closed-set fields

Enum and constrained fields reject invalid values with 400 invalid_parameter. Other fields are plain strings, even where the UI offers a fixed list. Use the documented spelling and validate your payload rather than assuming every unsupported string is rejected.

  • role.role - CUSTOMER_SUPPORT, SALES, LEAD_COLLECTION_AGENT, 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. This is a string, not a validated enum in the request schema; use a supported value, not an arbitrary model name.
  • advanced.chatContextSize - use 8000, 16000, or 32000, matching the UI. Values above the account's maximum are silently clamped; the DTO does not enforce this three-value set.
  • chatMemory.clientSummaryPromptType - DEFAULT, CUSTOM
  • chatMemory.conversationSummaryPromptType - DEFAULT, CUSTOM
  • appearance.chatAlignment - left, right
  • appearance.minimizedDisplayMode - icon, minified, pill
  • 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 invalid_parameter.

  • chatMemory.summariesToKnowledgeRatio - integer percent, 10-90; the UI uses steps of 10, but the API accepts integers throughout the range. Default 50. With memory enabled and context larger than 8k, it splits the knowledge-and-memory budget after the prompt/recent-conversation reservation, not the entire model context. At 8k no summary budget is allocated by this setting.

  • liveChat.schedule - JSON encoded as a string, not a nested JSON object on the wire. The server stores the raw string verbatim; the admin UI parses it client-side when rendering the schedule editor. Once parsed, the string is shaped as one entry per weekday plus 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 👍 / 👎 conversation-rating controls when conversation.conversationRatingEnabled=true. Default copy is "I like the response" / "I don't like the response". Visible to end users.

  • whiteLabel.hideRoboAssistLogo - white-label capability, subject to your account limits. Hides the "Powered by ChatLab" footer line. If your account does not include white-labelling, the value is stored but ignored and the footer always renders.

  • whiteLabel.whitelabelLogoLink - white-label capability, subject to your account limits. Click target URL for the custom logo 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"). set the message limit to 0 to disable this session-based limit. When enabling it, provide a positive duration. The backend enforces N messages per session per duration-in-seconds before showing security.talkMessagesRateLimitHitMessage to the visitor.

  • advanced.botMessagesLimit - integer (JSON number, e.g. 1000). 0 means no additional per-bot cap, while the account credit budget still applies; otherwise must be a multiple of 1000 (1000, 2000, 10000, ...). Values like 100 or 1500 are rejected with 400 invalid_parameter. This is a monthly bot credit cap, not a per-conversation message count. The value is checked against your plan on every create and update: the sum of botMessagesLimit across all your bots (with the bot being created or updated counted at its new value) must not exceed the monthly message credits of your subscription, otherwise the request is rejected with 402 quota_exceeded and nothing is saved. The account's budget still constrains actual use.

AI text models (advanced.model)

Use the exact model identifier returned in advanced.model when reading a bot configured with your intended model in Settings > Model & Advanced. The current create default is 5.6-LUNA. Identifiers are case-sensitive and differ from display names; for example 5.6-LUNA is displayed as GPT 5.6-luna.

The model catalog changes over time. Do not infer that an arbitrary string or a provider's public model ID is supported simply because the request schema accepts a string. Read back the saved configuration and test the resulting chatbot before deploying it.

Field reference (full request schema)

The supported sections and fields, with their types and important constraints. PATCH semantics: any field omitted (or sent as null) leaves the persisted value untouched. The same shape is used for the response (minus multipart binary content; plus the read-only meta block on every response and apiKey only on the create response).

Top level

Field Type Constraint Description
name string max 150, required on create Bot display name
role object See § role
conversation object See § conversation
chatMemory object See § chatMemory
appearance object See § appearance
humanSupport object See § humanSupport
leadCollection object See § leadCollection
liveChat object See § liveChat
consent object See § consent
whiteLabel object See § whiteLabel
security object See § security
advanced object See § advanced

Response-only additions:

  • meta: { id, createdAt, updatedAt } - read-only.
  • apiKey - string, present only on 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 conversation-rating controls
positiveRatingTooltip string Tooltip on the positive rating button
negativeRatingTooltip string Tooltip on the negative rating button
suggestedQuestions string Newline-separated suggested questions / conversation openers
dynamicSuggestedFollowups boolean If true, AI proposes follow-up suggestions after each reply
positiveRatingMessage string Message shown after positive feedback
negativeRatingMessage string Message shown after negative feedback
hideSuggestedQuestionsAfterFirstUse boolean Hide static suggestions after use
dynamicFollowupsAutoIcons boolean If true, AI auto-picks emoji icons for the dynamic follow-ups

§ chatMemory

Field Type Constraint Description
enabled boolean Master toggle for the chat-memory feature
summaryConversationsEnabled boolean Persist per-conversation summaries
conversationSummaryPrompt string Custom prompt used to summarize each conversation
conversationSummaryPromptType string (enum) ∈ {DEFAULT, CUSTOM} Whether to use the default or the custom summary prompt
clientSummaryPrompt string Custom prompt used to summarize the client across conversations
clientSummaryPromptType string (enum) ∈ {DEFAULT, CUSTOM} Default vs custom client-profile prompt
summariesToKnowledgeRatio int 10-90 (UI step 10) % of the available knowledge/memory budget allocated to summaries at contexts above 8k

§ appearance

Field Type Constraint Description
launcherLabelLine1 string max 60 First line of launcher text
launcherLabelLine2 string max 60 Second line of launcher text
launcherTextColor string Launcher text color
launcherDismissEnabled boolean Allow dismissing the launcher
widgetFontFamily string inter, system, nunito, lato, montserrat, poppins, source-serif, jetbrains-mono Widget font family
chatDrawerEnabled boolean Enable drawer presentation
launcherColor string (hex) Launcher (chat icon) background color
headerColor string (hex) Chat header background color
titleColor string (hex) Chat header title color
subtitleColor string (hex) Chat header subtitle color
clientMessageBubbleColor string (hex) Visitor message bubble color
clientMessageTextColor string (hex) Visitor message text color
responseMessageBubbleColor string (hex) Bot reply bubble color
responseMessageTextColor string (hex) Bot reply text color
chatSubheader string Tagline shown below the chat title
senderPlaceholder string Placeholder text in the message input
resetConversationTooltip string Tooltip on the "reset conversation" button
chatAlignment string (enum) ∈ {left, right} Which side of the screen the chat anchors to
launcherBottomMargin int 0-500 Launcher distance from bottom edge (px)
launcherSideMargin int 0-500 Launcher distance from the side edge (px)
displayShadow boolean Drop-shadow under the widget
customCss string Raw CSS injected into the widget iframe
chatMessageLinkTarget string (enum) ∈ {_blank, _self} How links inside bot messages open
minimizedDisplayMode string (enum) ∈ {icon, minified, pill} Minimized state: launcher icon, compact sender bar, or pill
chatDesktopWidthPx int Desktop widget width
chatDesktopHeightPx int Desktop widget height
chatMobileSizePercent int Mobile widget size as % of viewport
messageFontSize int Message text font size (px)
showChatbotBubblesDesktop boolean Show the floating tease bubbles on desktop
showChatbotBubblesMobile boolean Show the floating tease bubbles on mobile
chatbotBubblesDelaySeconds int Delay before the tease bubbles appear (seconds)
launcherIconFullSize boolean Render the custom launcher icon edge-to-edge instead of inset
welcomeScreenEnabled boolean Show the Welcome Screen instead of going straight to chat
welcomeScreenQuestionsLabel string Label above the suggested questions on the welcome screen
welcomeScreenHideHumanContactForm boolean Hide the human contact form action in the header while the Welcome Screen is showing. It reappears after the visitor's first message. Bots created before 2026-09-02 default to true
welcomeScreenHideLiveChat boolean Hide the live chat action in the header while the Welcome Screen is showing. It reappears after the visitor's first message. Bots created before 2026-09-02 default to true
headerActionsLayout string ICONS or DROPDOWN How live chat and the human contact form are offered in the chat header: ICONS (a separate icon each) or DROPDOWN (grouped in the header menu). Bots created before 2026-09-02 default to ICONS
stackSuggestedQuestions boolean Stack suggested questions vertically (vs side-by-side)
suggestedQuestionsFontSize int Font size of suggested-question chips (px)
suggestedQuestionsTextColor string (hex) Suggested-question chip text color
suggestedQuestionsBackgroundColor string (hex) Suggested-question chip background color
autoOpenChat boolean Auto-open the chat on desktop
autoOpenChatOnMobiles boolean Auto-open the chat on mobile
autoOpenChatDelay boolean Use a delay before auto-opening
autoOpenChatDelaySeconds int Auto-open delay (seconds)
simulateHumanTyping boolean Split bot reply into bubbles with typing animation
simulateHumanTypingDelay int 0-200 Delay between bubble messages (seconds)
footerMarkdown string max 255 Custom footer markdown shown under the chat
avatarUrl string read-only Fully-qualified public URL of the avatar; to change it, upload via the multipart avatar part

Multipart on POST/PATCH: avatar (file part). GET / response bodies omit the file content - only the URL is on the wire.

§ humanSupport

Field Type Constraint Description
enabled boolean Human Support flow toggle
email string required (create-strict) when enabled=true Address that receives human-support emails
dialogMessage string Encouragement message shown above the form
thankYouMessage string Confirmation shown after submission
emailMessageSubjectTemplate string Subject template for the email sent to the agent
emailMessageContentTemplate string Body template for the email sent to the agent
emailPlaceholder string Placeholder on the email input
messagePlaceholder string Placeholder on the message textarea
phoneEnabled boolean default false Show an optional phone input in the built-in contact form. Visitors can leave it empty. Ignored when a custom form is attached via customFormId
phonePlaceholder string max 50, default Your phone Placeholder on the phone input. Translated for multilingual bots like the other placeholders
emailWithConversationContent boolean If true, include the conversation transcript in the email body
customFormId long id of an existing custom form Replace the built-in contact form with a custom form. On create, omission uses the built-in form; on PATCH, null leaves the current selection unchanged
customFormMapping string JSON-encoded string Maps custom-form fields onto the human-support email fields

requirePolicyAccept is on consent.humanSupportRequirePolicyAccept, not here.

§ leadCollection

Field Type Constraint Description
enabled boolean Lead form toggle
nameEnabled boolean Collect name
nameLabel string Label on the name input
emailEnabled boolean Collect email
emailLabel string required (create-strict) when enabled=true AND emailEnabled=true Label on the email input
phoneEnabled boolean Collect phone
phoneLabel string required (create-strict) when enabled=true AND phoneEnabled=true Label on the phone input
leaveDetailsMessage string required (create-strict) when enabled=true Message encouraging the visitor to leave their details
thankYouMessage string required (create-strict) when enabled=true Confirmation shown after submission
requireBeforeNewConversation boolean If true, form must be submitted before chat starts; if false, AI decides when to surface the form
emailNotificationEnabled boolean Email the owner each time a lead is collected
emailNotificationAddress string Notification recipient (defaults to account email)
emailWithConversationContent boolean If true, include the conversation transcript in the notification

| customFormId | long | id of an existing custom form | Replace the built-in lead form with a custom form. On create, omission uses the built-in form; on PATCH, null leaves the current selection unchanged | | customFormMapping | string | JSON-encoded string | Maps custom-form fields onto name / email / phone |

Cross-field create-strict rule: enabled=true requires at least one of emailEnabled or phoneEnabled. requirePolicyAccept is on consent.leadCollectionRequirePolicyAccept, not here.

§ liveChat

Field Type Constraint Description
enabled boolean Live Chat feature toggle
infoMessage string Pre-handoff explanatory message
startMessage string Message shown when the live session begins
endMessage string Message shown when the live session ends
nameLabel string Label on the name input in the live-chat pre-form
emailLabel string Label on the email input in the live-chat pre-form
schedule string JSON-encoded string (weekday toggles + from/to + timezone) Live-chat operating schedule - see "Structured fields and ranges" for the exact shape
outOfHoursMessage string Message shown when the schedule says we're off
closeModalMessage string Title of the "close live chat?" modal
closeModalConfirmLabel string Confirm button label on the close modal
closeModalCancelLabel string Cancel button label on the close modal
closeModalTooltipText string Tooltip on the close-chat affordance
operatorHasJoinedLabel string Label shown when an operator joins
operatorDidNotJoinInTimeLabel string Label shown when no operator joins within the timeout
waitingForOperatorToJoinLabel string Label shown while waiting for an operator
waitingForOperatorSeconds int Timeout for an operator to pick up (seconds)
redirectToHumanSupportForm boolean If true, fall through to the Human Support form when no operator picks up
requireAdminOnline boolean Require an available operator according to presence settings
presenceMode string Presence mode; use the value from a bot configured in the UI
hideHumanSupportWhenLiveChatAvailable boolean Hide the contact form when live chat is available
missedEmailEnabled boolean default true Email the bot owner when a live chat request went unanswered. Left unset on legacy bots, which reads as enabled

requirePolicyAccept is on consent.liveChatRequirePolicyAccept, not here.

§ consent

Field Type Constraint Description
newConversationRequirePolicyAccept boolean Require privacy-policy consent before starting a new conversation
humanSupportRequirePolicyAccept boolean Require privacy-policy consent before submitting the human-support form
leadCollectionRequirePolicyAccept boolean Require privacy-policy consent before submitting the lead-collection form
liveChatRequirePolicyAccept boolean Require privacy-policy consent before starting a live-chat session
newConversationConsentDescription string Intro text for the consent screen at conversation start
privacyPolicyConsentCheckboxLabel string Label next to the consent checkbox (usually contains a link to the privacy policy)

§ whiteLabel

Field Type Constraint Description
hideRoboAssistLogo boolean white-label capability; subject to account limits Hide the default ChatLab logo in the footer
whitelabelLogoLink string white-label capability; subject to account limits URL the custom footer logo links to
assignToCustomDomain boolean gated by CUSTOM_DOMAIN feature Host the chat on the configured custom domain
whitelabelLogoUrl string read-only Fully-qualified public URL of the white-label logo; to change it, upload via the multipart whitelabel_logo part

Multipart on POST/PATCH: whitelabel_logo (file part). GET / response bodies omit the file content - only the URL is on the wire.

§ security

Field Type Constraint Description
allowedDomains string Comma-separated list of domains allowed to embed the widget (empty = no whitelist)
spamFilterEnabled boolean Enable the per-bot spam filter on incoming messages
countryFilterMode string BLACKLIST or WHITELIST How the country lists are interpreted. The lists themselves stay admin-only
talkMessagesRateLimit int >= 0; 0 disables Max user messages allowed in the rate-limit window
talkMessagesRateLimitDurationSeconds int >= 0 Rate-limit window length (seconds)
talkMessagesRateLimitHitMessage string Message shown to the visitor when the rate limit is hit

§ voice

Field Type Constraint Description
inputEnabled boolean Allow the visitor to dictate messages (speech to text)
conversationEnabled boolean requires the voice feature on the plan Enable full voice conversations
voiceId string provider-specific voice id (e.g. alloy) Which synthetic voice speaks
model string e.g. GPT-REALTIME-MINI, GEMINI-LIVE, ELEVENLABS-* Voice model. Billed per minute, rates differ per model
turnDetection string provider-specific Turn-taking mode
audioPrompt string Extra system prompt used only for voice turns
welcomeMessage string Spoken opening line
language string language code Primary voice language
additionalLanguages string comma-separated language codes Extra languages the voice agent accepts
maxDurationSeconds int Hard cap on a single voice conversation
maxDurationMessage string Message shown when the cap is reached

§ multilingual

Field Type Constraint Description
enabled boolean Multilingual mode toggle
mode string AUTODETECT or FIXED_LIST How the bot picks the reply language
baseLanguage string language code Language the bot's own copy is authored in
languages string comma-separated language codes Languages offered to the visitor
knowledgeLanguageMode string NONE, MAIN, or DYNAMIC How knowledge in other languages is treated
knowledgeLanguageFallback string language code Language used when no match is found

§ advanced

Field Type Constraint Description
model string subject to account limits; see "AI text models" above LLM identifier (e.g. 5.6-LUNA)
temperature decimal 0.0-1.0 Sampling temperature (matches the UI slider)
chatContextSize int ∈ {8000, 16000, 32000}; silently clamped to your account limit Configured context budget, including instructions, history, and knowledge
botMessagesLimit long 0 or multiple of 1000 (e.g. 1000, 2000, 10000); sum across all bots must fit the plan's monthly credits Monthly bot message-credit cap (0 = no additional bot cap; account budget still applies)
internalLocale string locale code in ll_CC form Locale for widget chrome labels (distinct from role.language)
productsViewEnabled boolean If true, expose the e-commerce Offer Cards inside chat
includeProductsInKnowledgeBase boolean If true, index the product catalog as part of the knowledge base

Out of API scope

The admin UI surfaces a few areas that are intentionally not exposed in this version of the Management API:

  • Training and deletion - these endpoints do not list all bots, manage training datasets, or delete bots. Use the app for those workflows.
  • Flow tab - the visual Conversation Flow editor (stages and transitions). Not exposed via the Management API.
  • Actions tab - managed e-commerce / booking integrations, AI Search, and custom API functions. Tool calling has never been part of the Management API.
  • The custom form builder itself - creating and editing custom forms is not exposed. You can, however, attach an existing form to a bot through leadCollection.customFormId and humanSupport.customFormId.
  • Custom open / close chat icons - customLauncherIconVisible, openChatIcon, closeChatIcon. The API exposes only the main avatar and whitelabel_logo multipart parts.
  • IP and country lists - the entries themselves are admin-only. Only the interpretation mode is exposed, via security.countryFilterMode.

Endpoints

POST /v1/management/bots

Create a new bot. Two equivalent Content-Types are accepted; pick whichever is more convenient.

Mode A - plain JSON (recommended when you don't need to upload an avatar / logo in the same request):

  • Content-Type: application/json
  • Request body is the bot configuration JSON (no 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 (applies only if your account includes white-labelling)

Only name is required in the JSON; every other field falls back to the same default the admin UI wizard would set.

Example request body

This is the maximal data JSON - every section populated. Send only the sections you care about; everything else takes default values.

{
  "name": "Helpdesk Bot",
  "role": {
    "rawPrompt": "You are a friendly support assistant for Acme Inc.",
    "role": "CUSTOMER_SUPPORT",
    "language": "English",
    "responseLength": "Normal",
    "websiteAddress": "https://acme.com",
    "companyDescription": "Acme sells industrial widgets."
  },
  "conversation": {
    "welcomeMessage": "Hi! How can I help today?",
    "queryRefinementEnabled": true,
    "conversationContinuityEnabled": true,
    "conversationRatingEnabled": true,
    "positiveRatingTooltip": "Helpful",
    "negativeRatingTooltip": "Not helpful",
    "suggestedQuestions": "What are your hours?\nHow do I cancel?\nWhere is my order?",
    "dynamicSuggestedFollowups": true,
    "dynamicFollowupsAutoIcons": true
  },
  "chatMemory": {
    "enabled": true,
    "summaryConversationsEnabled": true,
    "conversationSummaryPrompt": "Summarize this conversation in 3 sentences.",
    "clientSummaryPrompt": "Summarize what we know about this customer.",
    "clientSummaryPromptType": "DEFAULT",
    "conversationSummaryPromptType": "DEFAULT",
    "summariesToKnowledgeRatio": 50
  },
  "appearance": {
    "launcherColor": "#1A73E8",
    "headerColor": "#1A73E8",
    "titleColor": "#FFFFFF",
    "subtitleColor": "#FFFFFF",
    "clientMessageBubbleColor": "#000000",
    "clientMessageTextColor": "#FFFFFF",
    "responseMessageBubbleColor": "#F4F4F4",
    "responseMessageTextColor": "#000000",
    "chatSubheader": "AI support assistant",
    "senderPlaceholder": "Type a message...",
    "resetConversationTooltip": "Restart conversation",
    "chatAlignment": "right",
    "launcherBottomMargin": 20,
    "launcherSideMargin": 20,
    "displayShadow": true,
    "customCss": ".rcw-conversation-container { border-radius: 16px; }",
    "chatMessageLinkTarget": "_blank",
    "minimizedDisplayMode": "icon",
    "chatDesktopWidthPx": 400,
    "chatDesktopHeightPx": 600,
    "chatMobileSizePercent": 100,
    "messageFontSize": 14,
    "showChatbotBubblesDesktop": true,
    "showChatbotBubblesMobile": false,
    "chatbotBubblesDelaySeconds": 5,
    "welcomeScreenEnabled": false,
    "welcomeScreenQuestionsLabel": "Quick start",
    "welcomeScreenHideHumanContactForm": false,
    "welcomeScreenHideLiveChat": false,
    "headerActionsLayout": "DROPDOWN",
    "stackSuggestedQuestions": false,
    "suggestedQuestionsFontSize": 14,
    "suggestedQuestionsTextColor": "#000000",
    "suggestedQuestionsBackgroundColor": "#F4F4F4",
    "autoOpenChat": false,
    "autoOpenChatOnMobiles": false,
    "autoOpenChatDelay": false,
    "autoOpenChatDelaySeconds": 5,
    "simulateHumanTyping": true,
    "simulateHumanTypingDelay": 5,
    "footerMarkdown": "Powered by Acme"
  },
  "humanSupport": {
    "enabled": true,
    "email": "support@acme.com",
    "dialogMessage": "Leave us a message and we will get back to you.",
    "thankYouMessage": "Thanks - we received your message.",
    "emailMessageSubjectTemplate": "[Acme support] New message from {customerName}",
    "emailMessageContentTemplate": "{message}\n\n - \n{conversationTranscript}",
    "emailPlaceholder": "your@email.com",
    "messagePlaceholder": "How can we help?",
    "phoneEnabled": true,
    "phonePlaceholder": "Your phone number",
    "emailWithConversationContent": true
  },
  "leadCollection": {
    "enabled": true,
    "nameEnabled": true,
    "nameLabel": "Your name",
    "emailEnabled": true,
    "emailLabel": "Email",
    "phoneEnabled": false,
    "phoneLabel": "Phone",
    "leaveDetailsMessage": "Please leave your details and we will get in touch.",
    "thankYouMessage": "Thanks - we will be in touch shortly.",
    "requireBeforeNewConversation": false,
    "emailNotificationEnabled": true,
    "emailNotificationAddress": "leads@acme.com",
    "emailWithConversationContent": true
  },
  "liveChat": {
    "enabled": false,
    "infoMessage": "Connecting you with a human agent...",
    "startMessage": "You are now chatting with our team.",
    "endMessage": "Live chat has ended.",
    "nameLabel": "Your name",
    "emailLabel": "Email",
    "schedule": "{\"monday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"tuesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"wednesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"thursday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"friday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"saturday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"sunday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"timezone\":\"Europe/Warsaw\"}",
    "outOfHoursMessage": "We are currently offline.",
    "closeModalMessage": "End the live chat session?",
    "closeModalConfirmLabel": "Yes, end",
    "closeModalCancelLabel": "Cancel",
    "closeModalTooltipText": "End live chat",
    "operatorHasJoinedLabel": "An agent has joined.",
    "operatorDidNotJoinInTimeLabel": "No agent available right now.",
    "waitingForOperatorToJoinLabel": "Waiting for an agent...",
    "waitingForOperatorSeconds": 60,
    "redirectToHumanSupportForm": true
  },
  "consent": {
    "newConversationRequirePolicyAccept": false,
    "humanSupportRequirePolicyAccept": false,
    "leadCollectionRequirePolicyAccept": true,
    "liveChatRequirePolicyAccept": false,
    "newConversationConsentDescription": "By starting a conversation you agree to our terms.",
    "privacyPolicyConsentCheckboxLabel": "I have read and accepted the privacy policy."
  },
  "whiteLabel": {
    "hideRoboAssistLogo": false,
    "whitelabelLogoLink": "https://acme.com",
    "assignToCustomDomain": false
  },
  "security": {
    "allowedDomains": "acme.com,support.acme.com",
    "spamFilterEnabled": true,
    "talkMessagesRateLimit": 30,
    "talkMessagesRateLimitDurationSeconds": 60,
    "talkMessagesRateLimitHitMessage": "Please slow down."
  },
  "advanced": {
    "model": "5.6-LUNA",
    "temperature": 0.4,
    "chatContextSize": 16000,
    "botMessagesLimit": 1000,
    "internalLocale": "en_US",
    "productsViewEnabled": false
  }
}

Validation rules with their own error messages:

  • name - required, max 150 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
  • advanced.chatContextSize is clamped to the account's maximum supported context size. Do not assume other numeric fields are automatically clamped.

Fields whose value is null on the server are omitted from the JSON body - the wire only carries fields with non-null values.

Example response body (201)

Same shape as the request, plus the read-only meta block and the one-shot apiKey at the top level. Read-only file URLs (appearance.avatarUrl, whiteLabel.whitelabelLogoUrl) are populated by the server when the corresponding multipart parts were uploaded.

{
  "name": "Helpdesk Bot",
  "role": {
    "rawPrompt": "You are a friendly support assistant for Acme Inc.",
    "role": "CUSTOMER_SUPPORT",
    "language": "English",
    "responseLength": "Normal",
    "websiteAddress": "https://acme.com",
    "companyDescription": "Acme sells industrial widgets."
  },
  "conversation": {
    "welcomeMessage": "Hi! How can I help today?",
    "queryRefinementEnabled": true,
    "conversationContinuityEnabled": true,
    "conversationRatingEnabled": true,
    "positiveRatingTooltip": "Helpful",
    "negativeRatingTooltip": "Not helpful",
    "suggestedQuestions": "What are your hours?\nHow do I cancel?\nWhere is my order?",
    "dynamicSuggestedFollowups": true,
    "dynamicFollowupsAutoIcons": true
  },
  "chatMemory": {
    "enabled": true,
    "summaryConversationsEnabled": true,
    "conversationSummaryPrompt": "Summarize this conversation in 3 sentences.",
    "clientSummaryPrompt": "Summarize what we know about this customer.",
    "clientSummaryPromptType": "DEFAULT",
    "conversationSummaryPromptType": "DEFAULT",
    "summariesToKnowledgeRatio": 50
  },
  "appearance": {
    "launcherColor": "#1A73E8",
    "headerColor": "#1A73E8",
    "titleColor": "#FFFFFF",
    "subtitleColor": "#FFFFFF",
    "clientMessageBubbleColor": "#000000",
    "clientMessageTextColor": "#FFFFFF",
    "responseMessageBubbleColor": "#F4F4F4",
    "responseMessageTextColor": "#000000",
    "chatSubheader": "AI support assistant",
    "senderPlaceholder": "Type a message...",
    "resetConversationTooltip": "Restart conversation",
    "chatAlignment": "right",
    "launcherBottomMargin": 20,
    "launcherSideMargin": 20,
    "displayShadow": true,
    "customCss": ".rcw-conversation-container { border-radius: 16px; }",
    "chatMessageLinkTarget": "_blank",
    "minimizedDisplayMode": "icon",
    "chatDesktopWidthPx": 400,
    "chatDesktopHeightPx": 600,
    "chatMobileSizePercent": 100,
    "messageFontSize": 14,
    "showChatbotBubblesDesktop": true,
    "showChatbotBubblesMobile": false,
    "chatbotBubblesDelaySeconds": 5,
    "welcomeScreenEnabled": false,
    "welcomeScreenQuestionsLabel": "Quick start",
    "welcomeScreenHideHumanContactForm": false,
    "welcomeScreenHideLiveChat": false,
    "headerActionsLayout": "DROPDOWN",
    "stackSuggestedQuestions": false,
    "suggestedQuestionsFontSize": 14,
    "suggestedQuestionsTextColor": "#000000",
    "suggestedQuestionsBackgroundColor": "#F4F4F4",
    "autoOpenChat": false,
    "autoOpenChatOnMobiles": false,
    "autoOpenChatDelay": false,
    "autoOpenChatDelaySeconds": 5,
    "simulateHumanTyping": true,
    "simulateHumanTypingDelay": 5,
    "footerMarkdown": "Powered by Acme",
    "avatarUrl": "https://api.chatlab.com/aichat/content/avatar_a8f3b2c1_2026060110.png"
  },
  "humanSupport": {
    "enabled": true,
    "email": "support@acme.com",
    "dialogMessage": "Leave us a message and we will get back to you.",
    "thankYouMessage": "Thanks - we received your message.",
    "emailMessageSubjectTemplate": "[Acme support] New message from {customerName}",
    "emailMessageContentTemplate": "{message}\n\n - \n{conversationTranscript}",
    "emailPlaceholder": "your@email.com",
    "messagePlaceholder": "How can we help?",
    "phoneEnabled": true,
    "phonePlaceholder": "Your phone number",
    "emailWithConversationContent": true
  },
  "leadCollection": {
    "enabled": true,
    "nameEnabled": true,
    "nameLabel": "Your name",
    "emailEnabled": true,
    "emailLabel": "Email",
    "phoneEnabled": false,
    "phoneLabel": "Phone",
    "leaveDetailsMessage": "Please leave your details and we will get in touch.",
    "thankYouMessage": "Thanks - we will be in touch shortly.",
    "requireBeforeNewConversation": false,
    "emailNotificationEnabled": true,
    "emailNotificationAddress": "leads@acme.com",
    "emailWithConversationContent": true
  },
  "liveChat": {
    "enabled": false,
    "infoMessage": "Connecting you with a human agent...",
    "startMessage": "You are now chatting with our team.",
    "endMessage": "Live chat has ended.",
    "nameLabel": "Your name",
    "emailLabel": "Email",
    "schedule": "{\"monday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"tuesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"wednesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"thursday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"friday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"saturday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"sunday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"timezone\":\"Europe/Warsaw\"}",
    "outOfHoursMessage": "We are currently offline.",
    "closeModalMessage": "End the live chat session?",
    "closeModalConfirmLabel": "Yes, end",
    "closeModalCancelLabel": "Cancel",
    "closeModalTooltipText": "End live chat",
    "operatorHasJoinedLabel": "An agent has joined.",
    "operatorDidNotJoinInTimeLabel": "No agent available right now.",
    "waitingForOperatorToJoinLabel": "Waiting for an agent...",
    "waitingForOperatorSeconds": 60,
    "redirectToHumanSupportForm": true
  },
  "consent": {
    "newConversationRequirePolicyAccept": false,
    "humanSupportRequirePolicyAccept": false,
    "leadCollectionRequirePolicyAccept": true,
    "liveChatRequirePolicyAccept": false,
    "newConversationConsentDescription": "By starting a conversation you agree to our terms.",
    "privacyPolicyConsentCheckboxLabel": "I have read and accepted the privacy policy."
  },
  "whiteLabel": {
    "hideRoboAssistLogo": false,
    "whitelabelLogoLink": "https://acme.com",
    "assignToCustomDomain": false,
    "whitelabelLogoUrl": "https://api.chatlab.com/aichat/content/custom_logo_4287_2026060110.png"
  },
  "security": {
    "allowedDomains": "acme.com,support.acme.com",
    "spamFilterEnabled": true,
    "talkMessagesRateLimit": 30,
    "talkMessagesRateLimitDurationSeconds": 60,
    "talkMessagesRateLimitHitMessage": "Please slow down."
  },
  "advanced": {
    "model": "5.6-LUNA",
    "temperature": 0.4,
    "chatContextSize": 16000,
    "botMessagesLimit": 1000,
    "internalLocale": "en_US",
    "productsViewEnabled": false
  },
  "meta": {
    "id": 4287,
    "createdAt": "2026-06-01T10:11:02Z",
    "updatedAt": "2026-06-01T10:11:02Z"
  },
  "apiKey": "ck_freshly_minted_bot_talk_key_here"
}

The apiKey field appears only on create - it is the freshly minted Bot Talk key bound to the new bot. The plaintext is shown once and cannot be retrieved later from the API; persist it on your side immediately.

The Location response header carries the URL of the new bot (/v1/management/bots/{id}).

Curl examples

Mode A - plain JSON (simplest):

curl -X POST https://api.chatlab.com/aichat/v1/management/bots \
  -H "Authorization: Bearer mk_..." \
  -H "Content-Type: application/json" \
  -d '{"name":"Helpdesk Bot","conversation":{"welcomeMessage":"Hi!"}}'

Mode B - multipart with avatar:

curl -X POST https://api.chatlab.com/aichat/v1/management/bots \
  -H "Authorization: Bearer mk_..." \
  -F 'data={"name":"Helpdesk Bot","conversation":{"welcomeMessage":"Hi!"}};type=application/json' \
  -F 'avatar=@./avatar.png'

GET /v1/management/bots/{bot_id}

Return the current configuration of a bot you own.

Curl example

curl https://api.chatlab.com/aichat/v1/management/bots/4287 \
  -H "Authorization: Bearer mk_..."

Example response body (200)

Same shape as the POST response, minus the one-shot apiKey. The meta block is included. Returns 404 not_found_error if the bot does not exist or does not belong to your account.

The current avatar and white-label logo are surfaced as fully-qualified read-only URLs (appearance.avatarUrl, whiteLabel.whitelabelLogoUrl) - rooted at the same scheme + host + context path that served this request. Fetch the bytes by GETting those URLs directly; to replace either file, upload a new one via the multipart avatar / whitelabel_logo part on PATCH. These URL fields are ignored if sent in a request body.

{
  "name": "Helpdesk Bot",
  "role": {
    "rawPrompt": "You are a friendly support assistant for Acme Inc.",
    "role": "CUSTOMER_SUPPORT",
    "language": "English",
    "responseLength": "Normal",
    "websiteAddress": "https://acme.com",
    "companyDescription": "Acme sells industrial widgets."
  },
  "conversation": {
    "welcomeMessage": "Hi! How can I help today?",
    "queryRefinementEnabled": true,
    "conversationContinuityEnabled": true,
    "conversationRatingEnabled": true,
    "positiveRatingTooltip": "Helpful",
    "negativeRatingTooltip": "Not helpful",
    "suggestedQuestions": "What are your hours?\nHow do I cancel?\nWhere is my order?",
    "dynamicSuggestedFollowups": true,
    "dynamicFollowupsAutoIcons": true
  },
  "chatMemory": {
    "enabled": true,
    "summaryConversationsEnabled": true,
    "conversationSummaryPrompt": "Summarize this conversation in 3 sentences.",
    "clientSummaryPrompt": "Summarize what we know about this customer.",
    "clientSummaryPromptType": "DEFAULT",
    "conversationSummaryPromptType": "DEFAULT",
    "summariesToKnowledgeRatio": 50
  },
  "appearance": {
    "launcherColor": "#1A73E8",
    "headerColor": "#1A73E8",
    "titleColor": "#FFFFFF",
    "subtitleColor": "#FFFFFF",
    "clientMessageBubbleColor": "#000000",
    "clientMessageTextColor": "#FFFFFF",
    "responseMessageBubbleColor": "#F4F4F4",
    "responseMessageTextColor": "#000000",
    "chatSubheader": "AI support assistant",
    "senderPlaceholder": "Type a message...",
    "resetConversationTooltip": "Restart conversation",
    "chatAlignment": "right",
    "launcherBottomMargin": 20,
    "launcherSideMargin": 20,
    "displayShadow": true,
    "customCss": ".rcw-conversation-container { border-radius: 16px; }",
    "chatMessageLinkTarget": "_blank",
    "minimizedDisplayMode": "icon",
    "chatDesktopWidthPx": 400,
    "chatDesktopHeightPx": 600,
    "chatMobileSizePercent": 100,
    "messageFontSize": 14,
    "showChatbotBubblesDesktop": true,
    "showChatbotBubblesMobile": false,
    "chatbotBubblesDelaySeconds": 5,
    "welcomeScreenEnabled": false,
    "welcomeScreenQuestionsLabel": "Quick start",
    "welcomeScreenHideHumanContactForm": false,
    "welcomeScreenHideLiveChat": false,
    "headerActionsLayout": "DROPDOWN",
    "stackSuggestedQuestions": false,
    "suggestedQuestionsFontSize": 14,
    "suggestedQuestionsTextColor": "#000000",
    "suggestedQuestionsBackgroundColor": "#F4F4F4",
    "autoOpenChat": false,
    "autoOpenChatOnMobiles": false,
    "autoOpenChatDelay": false,
    "autoOpenChatDelaySeconds": 5,
    "simulateHumanTyping": true,
    "simulateHumanTypingDelay": 5,
    "footerMarkdown": "Powered by Acme",
    "avatarUrl": "https://api.chatlab.com/aichat/content/avatar_a8f3b2c1_2026060110.png"
  },
  "humanSupport": {
    "enabled": true,
    "email": "support@acme.com",
    "dialogMessage": "Leave us a message and we will get back to you.",
    "thankYouMessage": "Thanks - we received your message.",
    "emailMessageSubjectTemplate": "[Acme support] New message from {customerName}",
    "emailMessageContentTemplate": "{message}\n\n - \n{conversationTranscript}",
    "emailPlaceholder": "your@email.com",
    "messagePlaceholder": "How can we help?",
    "phoneEnabled": true,
    "phonePlaceholder": "Your phone number",
    "emailWithConversationContent": true
  },
  "leadCollection": {
    "enabled": true,
    "nameEnabled": true,
    "nameLabel": "Your name",
    "emailEnabled": true,
    "emailLabel": "Email",
    "phoneEnabled": false,
    "phoneLabel": "Phone",
    "leaveDetailsMessage": "Please leave your details and we will get in touch.",
    "thankYouMessage": "Thanks - we will be in touch shortly.",
    "requireBeforeNewConversation": false,
    "emailNotificationEnabled": true,
    "emailNotificationAddress": "leads@acme.com",
    "emailWithConversationContent": true
  },
  "liveChat": {
    "enabled": false,
    "infoMessage": "Connecting you with a human agent...",
    "startMessage": "You are now chatting with our team.",
    "endMessage": "Live chat has ended.",
    "nameLabel": "Your name",
    "emailLabel": "Email",
    "schedule": "{\"monday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"tuesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"wednesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"thursday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"friday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"saturday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"sunday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"timezone\":\"Europe/Warsaw\"}",
    "outOfHoursMessage": "We are currently offline.",
    "closeModalMessage": "End the live chat session?",
    "closeModalConfirmLabel": "Yes, end",
    "closeModalCancelLabel": "Cancel",
    "closeModalTooltipText": "End live chat",
    "operatorHasJoinedLabel": "An agent has joined.",
    "operatorDidNotJoinInTimeLabel": "No agent available right now.",
    "waitingForOperatorToJoinLabel": "Waiting for an agent...",
    "waitingForOperatorSeconds": 60,
    "redirectToHumanSupportForm": true
  },
  "consent": {
    "newConversationRequirePolicyAccept": false,
    "humanSupportRequirePolicyAccept": false,
    "leadCollectionRequirePolicyAccept": true,
    "liveChatRequirePolicyAccept": false,
    "newConversationConsentDescription": "By starting a conversation you agree to our terms.",
    "privacyPolicyConsentCheckboxLabel": "I have read and accepted the privacy policy."
  },
  "whiteLabel": {
    "hideRoboAssistLogo": false,
    "whitelabelLogoLink": "https://acme.com",
    "assignToCustomDomain": false,
    "whitelabelLogoUrl": "https://api.chatlab.com/aichat/content/custom_logo_4287_2026060110.png"
  },
  "security": {
    "allowedDomains": "acme.com,support.acme.com",
    "spamFilterEnabled": true,
    "talkMessagesRateLimit": 30,
    "talkMessagesRateLimitDurationSeconds": 60,
    "talkMessagesRateLimitHitMessage": "Please slow down."
  },
  "advanced": {
    "model": "5.6-LUNA",
    "temperature": 0.4,
    "chatContextSize": 16000,
    "botMessagesLimit": 1000,
    "internalLocale": "en_US",
    "productsViewEnabled": false
  },
  "meta": {
    "id": 4287,
    "createdAt": "2026-06-01T10:11:02Z",
    "updatedAt": "2026-06-01T11:02:19Z"
  }
}

Clone a bot

The request body of POST /v1/management/bots and the response body of GET /v1/management/bots/{bot_id} share the same shape, so cloning is a three-step pipeline: GET the source, strip server-managed identity fields, POST the result.

1. GET the source bot.

curl https://api.chatlab.com/aichat/v1/management/bots/4287 \
  -H "Authorization: Bearer mk_..." \
  -o source-bot.json

2. Strip the top-level meta block. The meta object (id, createdAt, updatedAt) is server-managed and read-only - leaving it in the POST body does no harm (the server ignores it) but removing it makes the intent explicit and keeps the payload clean. Optionally edit name so the clone is distinguishable from the source.

jq 'del(.meta) | .name = "Helpdesk Bot (clone)"' source-bot.json > clone-body.json

3. POST the stripped body to create the clone. See the POST /v1/management/bots reference above for the full body shape and validation rules.

curl -X POST https://api.chatlab.com/aichat/v1/management/bots \
  -H "Authorization: Bearer mk_..." \
  -H "Content-Type: application/json" \
  -d @clone-body.json

The response contains the new bot's meta.id plus a freshly minted apiKey (the Bot Talk key for the clone). The apiKey plaintext is returned only on this create response - copy it before discarding the response body; it cannot be retrieved later.

This copies supported settings, not a fully trained chatbot. Training content, conversation history, integrations/actions, Flow, and form definitions are not cloned by these endpoints. Configure those separately in the app.

Additional caveats:

  • Files are not cloned. appearance.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 (applies only if your account includes white-labelling)

All three parts are optional on PATCH, but at least one must be present for the call to be meaningful.

Example request body (multiple sections)

Any field accepted by POST /v1/management/bots may also be sent here. The example below illustrates several sections, not every supported field; in practice you send only the keys you want to change (see "Minimal partial update" further down) - every key omitted (or sent as null) leaves the persisted value untouched.

{
  "name": "Helpdesk Bot",
  "role": {
    "rawPrompt": "You are a friendly support assistant for Acme Inc. Be concise.",
    "role": "CUSTOMER_SUPPORT",
    "language": "English",
    "responseLength": "Concise",
    "websiteAddress": "https://acme.com",
    "companyDescription": "Acme sells industrial widgets."
  },
  "conversation": {
    "welcomeMessage": "Hello there!",
    "queryRefinementEnabled": true,
    "conversationContinuityEnabled": true,
    "conversationRatingEnabled": true,
    "positiveRatingTooltip": "Helpful",
    "negativeRatingTooltip": "Not helpful",
    "suggestedQuestions": "Pricing\nShipping times\nReturns policy",
    "dynamicSuggestedFollowups": true,
    "dynamicFollowupsAutoIcons": true
  },
  "chatMemory": {
    "enabled": true,
    "summaryConversationsEnabled": true,
    "conversationSummaryPrompt": "Summarize the conversation in 3 sentences.",
    "clientSummaryPrompt": "Summarize what we know about the customer.",
    "clientSummaryPromptType": "DEFAULT",
    "conversationSummaryPromptType": "DEFAULT",
    "summariesToKnowledgeRatio": 50
  },
  "appearance": {
    "launcherColor": "#abcdef",
    "headerColor": "#abcdef",
    "titleColor": "#FFFFFF",
    "subtitleColor": "#FFFFFF",
    "clientMessageBubbleColor": "#000000",
    "clientMessageTextColor": "#FFFFFF",
    "responseMessageBubbleColor": "#F4F4F4",
    "responseMessageTextColor": "#000000",
    "chatSubheader": "AI assistant",
    "senderPlaceholder": "Type a message",
    "resetConversationTooltip": "Restart conversation",
    "chatAlignment": "right",
    "launcherBottomMargin": 20,
    "launcherSideMargin": 20,
    "displayShadow": true,
    "customCss": ".rcw-conversation-container { border-radius: 16px; }",
    "chatMessageLinkTarget": "_blank",
    "minimizedDisplayMode": "icon",
    "chatDesktopWidthPx": 400,
    "chatDesktopHeightPx": 600,
    "chatMobileSizePercent": 100,
    "messageFontSize": 14,
    "showChatbotBubblesDesktop": true,
    "showChatbotBubblesMobile": false,
    "chatbotBubblesDelaySeconds": 5,
    "welcomeScreenEnabled": false,
    "welcomeScreenQuestionsLabel": "Quick start",
    "welcomeScreenHideHumanContactForm": false,
    "welcomeScreenHideLiveChat": false,
    "headerActionsLayout": "DROPDOWN",
    "stackSuggestedQuestions": false,
    "suggestedQuestionsFontSize": 14,
    "suggestedQuestionsTextColor": "#000000",
    "suggestedQuestionsBackgroundColor": "#F4F4F4",
    "autoOpenChat": false,
    "autoOpenChatOnMobiles": false,
    "autoOpenChatDelay": false,
    "autoOpenChatDelaySeconds": 5,
    "simulateHumanTyping": true,
    "simulateHumanTypingDelay": 5,
    "footerMarkdown": "Powered by Acme"
  },
  "humanSupport": {
    "enabled": true,
    "email": "support@acme.com",
    "dialogMessage": "Leave us a message and we'll get back to you.",
    "thankYouMessage": "Thanks!",
    "emailMessageSubjectTemplate": "[Acme support] New message from {customerName}",
    "emailMessageContentTemplate": "{message}\n\n - \n{conversationTranscript}",
    "emailPlaceholder": "your@email.com",
    "messagePlaceholder": "How can we help?",
    "phoneEnabled": true,
    "phonePlaceholder": "Your phone number",
    "emailWithConversationContent": true
  },
  "leadCollection": {
    "enabled": true,
    "nameEnabled": true,
    "nameLabel": "Your name",
    "emailEnabled": true,
    "emailLabel": "Email",
    "phoneEnabled": false,
    "phoneLabel": "Phone",
    "leaveDetailsMessage": "Please leave your details.",
    "thankYouMessage": "Thanks!",
    "requireBeforeNewConversation": true,
    "emailNotificationEnabled": true,
    "emailNotificationAddress": "leads@acme.com",
    "emailWithConversationContent": true
  },
  "liveChat": {
    "enabled": false,
    "infoMessage": "Connecting you with a human agent...",
    "startMessage": "You are now chatting with our team.",
    "endMessage": "Live chat has ended.",
    "nameLabel": "Your name",
    "emailLabel": "Email",
    "schedule": "{\"monday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"tuesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"wednesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"thursday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"friday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"saturday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"sunday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"timezone\":\"Europe/Warsaw\"}",
    "outOfHoursMessage": "We are currently offline.",
    "closeModalMessage": "End the live chat session?",
    "closeModalConfirmLabel": "Yes, end",
    "closeModalCancelLabel": "Cancel",
    "closeModalTooltipText": "End live chat",
    "operatorHasJoinedLabel": "An agent has joined.",
    "operatorDidNotJoinInTimeLabel": "No agent available right now.",
    "waitingForOperatorToJoinLabel": "Waiting for an agent...",
    "waitingForOperatorSeconds": 60,
    "redirectToHumanSupportForm": true
  },
  "consent": {
    "newConversationRequirePolicyAccept": false,
    "humanSupportRequirePolicyAccept": false,
    "leadCollectionRequirePolicyAccept": true,
    "liveChatRequirePolicyAccept": false,
    "newConversationConsentDescription": "By starting a conversation you agree to our terms.",
    "privacyPolicyConsentCheckboxLabel": "I have read and accepted the privacy policy."
  },
  "whiteLabel": {
    "hideRoboAssistLogo": false,
    "whitelabelLogoLink": "https://acme.com",
    "assignToCustomDomain": false
  },
  "security": {
    "allowedDomains": "acme.com,support.acme.com",
    "spamFilterEnabled": true,
    "talkMessagesRateLimit": 30,
    "talkMessagesRateLimitDurationSeconds": 60,
    "talkMessagesRateLimitHitMessage": "Please slow down."
  },
  "advanced": {
    "model": "5",
    "temperature": 0.2,
    "chatContextSize": 32000,
    "botMessagesLimit": 2000,
    "internalLocale": "en_US",
    "productsViewEnabled": false
  }
}

Minimal partial update

PATCH a single field by sending exactly the keys you want changed - everything else is preserved.

{
  "appearance": {
    "launcherColor": "#abcdef"
  }
}

Curl examples

Mode A - plain JSON (simplest):

curl -X PATCH https://api.chatlab.com/aichat/v1/management/bots/4287 \
  -H "Authorization: Bearer mk_..." \
  -H "Content-Type: application/json" \
  -d '{"appearance":{"launcherColor":"#abcdef"}}'

Mode B - multipart (when replacing avatar / logo):

curl -X PATCH https://api.chatlab.com/aichat/v1/management/bots/4287 \
  -H "Authorization: Bearer mk_..." \
  -F 'data={"appearance":{"launcherColor":"#abcdef"}};type=application/json' \
  -F 'avatar=@./new-avatar.png'

Mode B - replace only the avatar (no field changes):

curl -X PATCH https://api.chatlab.com/aichat/v1/management/bots/4287 \
  -H "Authorization: Bearer mk_..." \
  -F 'avatar=@./new-avatar.png'

Response body (200)

Same shape as GET /v1/management/bots/{bot_id} - the bot's full configuration after the patch was applied, including the meta block. No apiKey field. Returns 404 not_found_error if the bot does not exist or does not belong to your account.

The example below shows the response after applying the Full request body (multiple sections) patch above to the bot from the GET example - changed fields reflect the new values, untouched fields are preserved, and meta.updatedAt advances.

{
  "name": "Helpdesk Bot",
  "role": {
    "rawPrompt": "You are a friendly support assistant for Acme Inc. Be concise.",
    "role": "CUSTOMER_SUPPORT",
    "language": "English",
    "responseLength": "Concise",
    "websiteAddress": "https://acme.com",
    "companyDescription": "Acme sells industrial widgets."
  },
  "conversation": {
    "welcomeMessage": "Hello there!",
    "queryRefinementEnabled": true,
    "conversationContinuityEnabled": true,
    "conversationRatingEnabled": true,
    "positiveRatingTooltip": "Helpful",
    "negativeRatingTooltip": "Not helpful",
    "suggestedQuestions": "Pricing\nShipping times\nReturns policy",
    "dynamicSuggestedFollowups": true,
    "dynamicFollowupsAutoIcons": true
  },
  "chatMemory": {
    "enabled": true,
    "summaryConversationsEnabled": true,
    "conversationSummaryPrompt": "Summarize the conversation in 3 sentences.",
    "clientSummaryPrompt": "Summarize what we know about the customer.",
    "clientSummaryPromptType": "DEFAULT",
    "conversationSummaryPromptType": "DEFAULT",
    "summariesToKnowledgeRatio": 50
  },
  "appearance": {
    "launcherColor": "#abcdef",
    "headerColor": "#abcdef",
    "titleColor": "#FFFFFF",
    "subtitleColor": "#FFFFFF",
    "clientMessageBubbleColor": "#000000",
    "clientMessageTextColor": "#FFFFFF",
    "responseMessageBubbleColor": "#F4F4F4",
    "responseMessageTextColor": "#000000",
    "chatSubheader": "AI assistant",
    "senderPlaceholder": "Type a message",
    "resetConversationTooltip": "Restart conversation",
    "chatAlignment": "right",
    "launcherBottomMargin": 20,
    "launcherSideMargin": 20,
    "displayShadow": true,
    "customCss": ".rcw-conversation-container { border-radius: 16px; }",
    "chatMessageLinkTarget": "_blank",
    "minimizedDisplayMode": "icon",
    "chatDesktopWidthPx": 400,
    "chatDesktopHeightPx": 600,
    "chatMobileSizePercent": 100,
    "messageFontSize": 14,
    "showChatbotBubblesDesktop": true,
    "showChatbotBubblesMobile": false,
    "chatbotBubblesDelaySeconds": 5,
    "welcomeScreenEnabled": false,
    "welcomeScreenQuestionsLabel": "Quick start",
    "welcomeScreenHideHumanContactForm": false,
    "welcomeScreenHideLiveChat": false,
    "headerActionsLayout": "DROPDOWN",
    "stackSuggestedQuestions": false,
    "suggestedQuestionsFontSize": 14,
    "suggestedQuestionsTextColor": "#000000",
    "suggestedQuestionsBackgroundColor": "#F4F4F4",
    "autoOpenChat": false,
    "autoOpenChatOnMobiles": false,
    "autoOpenChatDelay": false,
    "autoOpenChatDelaySeconds": 5,
    "simulateHumanTyping": true,
    "simulateHumanTypingDelay": 5,
    "footerMarkdown": "Powered by Acme",
    "avatarUrl": "https://api.chatlab.com/aichat/content/avatar_a8f3b2c1_2026060110.png"
  },
  "humanSupport": {
    "enabled": true,
    "email": "support@acme.com",
    "dialogMessage": "Leave us a message and we'll get back to you.",
    "thankYouMessage": "Thanks!",
    "emailMessageSubjectTemplate": "[Acme support] New message from {customerName}",
    "emailMessageContentTemplate": "{message}\n\n - \n{conversationTranscript}",
    "emailPlaceholder": "your@email.com",
    "messagePlaceholder": "How can we help?",
    "phoneEnabled": true,
    "phonePlaceholder": "Your phone number",
    "emailWithConversationContent": true
  },
  "leadCollection": {
    "enabled": true,
    "nameEnabled": true,
    "nameLabel": "Your name",
    "emailEnabled": true,
    "emailLabel": "Email",
    "phoneEnabled": false,
    "phoneLabel": "Phone",
    "leaveDetailsMessage": "Please leave your details.",
    "thankYouMessage": "Thanks!",
    "requireBeforeNewConversation": true,
    "emailNotificationEnabled": true,
    "emailNotificationAddress": "leads@acme.com",
    "emailWithConversationContent": true
  },
  "liveChat": {
    "enabled": false,
    "infoMessage": "Connecting you with a human agent...",
    "startMessage": "You are now chatting with our team.",
    "endMessage": "Live chat has ended.",
    "nameLabel": "Your name",
    "emailLabel": "Email",
    "schedule": "{\"monday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"tuesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"wednesday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"thursday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"friday\":{\"enabled\":true,\"from\":\"9:00\",\"to\":\"17:00\"},\"saturday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"sunday\":{\"enabled\":false,\"from\":\"9:00\",\"to\":\"17:00\"},\"timezone\":\"Europe/Warsaw\"}",
    "outOfHoursMessage": "We are currently offline.",
    "closeModalMessage": "End the live chat session?",
    "closeModalConfirmLabel": "Yes, end",
    "closeModalCancelLabel": "Cancel",
    "closeModalTooltipText": "End live chat",
    "operatorHasJoinedLabel": "An agent has joined.",
    "operatorDidNotJoinInTimeLabel": "No agent available right now.",
    "waitingForOperatorToJoinLabel": "Waiting for an agent...",
    "waitingForOperatorSeconds": 60,
    "redirectToHumanSupportForm": true
  },
  "consent": {
    "newConversationRequirePolicyAccept": false,
    "humanSupportRequirePolicyAccept": false,
    "leadCollectionRequirePolicyAccept": true,
    "liveChatRequirePolicyAccept": false,
    "newConversationConsentDescription": "By starting a conversation you agree to our terms.",
    "privacyPolicyConsentCheckboxLabel": "I have read and accepted the privacy policy."
  },
  "whiteLabel": {
    "hideRoboAssistLogo": false,
    "whitelabelLogoLink": "https://acme.com",
    "assignToCustomDomain": false,
    "whitelabelLogoUrl": "https://api.chatlab.com/aichat/content/custom_logo_4287_2026060110.png"
  },
  "security": {
    "allowedDomains": "acme.com,support.acme.com",
    "spamFilterEnabled": true,
    "talkMessagesRateLimit": 30,
    "talkMessagesRateLimitDurationSeconds": 60,
    "talkMessagesRateLimitHitMessage": "Please slow down."
  },
  "advanced": {
    "model": "5",
    "temperature": 0.2,
    "chatContextSize": 32000,
    "botMessagesLimit": 2000,
    "internalLocale": "en_US",
    "productsViewEnabled": false
  },
  "meta": {
    "id": 4287,
    "createdAt": "2026-06-01T10:11:02Z",
    "updatedAt": "2026-06-01T12:45:08Z"
  }
}

GET /v1/usage

Read current subscription usage for the account that owns the Management key.

Response body (200)

{
  "subscriptionType": "your-plan-id",
  "messages": {"used": 4123, "limit": 11000, "remaining": 6877},
  "bots": {"used": 3, "limit": 5, "remaining": 2}
}
  • subscriptionType is a lower-cased identifier of the account's current plan (your-plan-id is an illustrative placeholder). Plans come from a dynamic catalog, so the exact set of identifiers can change over time as plans are renamed or added - treat this as an opaque string, not a fixed enum.
  • messages.used / limit / remaining are the current calendar-month message credits, resetting on the first day of the month, not the subscription renewal day. They are credit-weighted, not raw reply counts.
  • bots.used / limit / remaining count owned bots, including paused bots, against your account's total bot limit.

Rate limit headers

Responses that reach the rate-limit stage (i.e. auth and IP whitelist passed) include:

X-RateLimit-Limit: 10
X-RateLimit-Remaining: 7
X-RateLimit-Reset: 1715430000
  • X-RateLimit-Limit - the per-key cap actually applied to this call (10 by default, or your 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"
  }
}

Plan limits use 402 with code: "quota_exceeded". This covers the bot count cap on create and the advanced.botMessagesLimit sum check on create and update:

{
  "error": {
    "type": "quota_exceeded_error",
    "code": "quota_exceeded",
    "message": "The sum of per-bot message limits across your bots would exceed the monthly message credits of your subscription plan."
  }
}

Related

For conversation endpoints and SSE streaming, see Bot Talk API.