{"info":{"version":"1.0.0","description":"ChatbotGen builds AI chatbots trained on your own content and deploys\nthem to a website widget, WhatsApp, and Telegram.\n\nTwo machine surfaces are public.\n\n**The MCP server** (`POST /mcp`) is the one most agents want. It speaks\nJSON-RPC 2.0 over the Model Context Protocol Streamable HTTP transport\nand exposes chatbot creation and training as tools. Anonymously it\nbuilds a demo chatbot from a website URL and hands back a chat link, an\nembed snippet, and a claim link a human opens to keep it. With an\naccount API key the same tools operate on that account's real chatbots.\nIts tool list is published at `/.well-known/mcp.json`.\n\n**The demo-tool API** (`/api/tools/*`) backs the free, no-signup tools\nat https://www.chatbotgen.com/tools. It needs no key: a session is created lazily and every\nlater call is authorized by the opaque `session_id` in the path.\n\nErrors are always JSON with a stable `code`, a human-readable `message`,\nand a `hint` describing the recovery. See the `Error` schema.\n\nPages on this site also answer `Accept: text/markdown` with a markdown\nrepresentation, and every documentation page answers on a `.md` URL.\n","title":"ChatbotGen API","summary":"The public HTTP API and MCP server for ChatbotGen.","contact":{"name":"ChatbotGen support","url":"https://www.chatbotgen.com/developers","email":"support@chatbotgen.com"},"license":{"name":"ChatbotGen Terms of Service","url":"https://www.chatbotgen.com/terms"},"termsOfService":"https://www.chatbotgen.com/terms"},"components":{"schemas":{"AddFileRequest":{"type":"object","required":["file_name","file_s3_key"],"properties":{"file_name":{"type":"string","description":"Display name for the file."},"file_size":{"type":"integer","description":"Size in bytes."},"file_content_type":{"type":"string","description":"MIME type of the uploaded file."},"file_s3_key":{"type":"string","description":"The `s3_key` returned by `createDemoSessionUpload`."}}},"AddTextRequest":{"type":"object","required":["text"],"properties":{"text":{"type":"string","description":"The content to train on. Counts against the demo's character cap.","maxLength":50000}}},"AddUrlRequest":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"Full public URL of the page to train on, scheme included. Must be reachable from the internet."}}},"CreateUploadRequest":{"type":"object","required":["filename","content_type"],"properties":{"size":{"maximum":10000000,"type":"integer","description":"Size in bytes. Maximum 10,000,000."},"filename":{"type":"string","description":"Name of the file, used in the storage key."},"content_type":{"type":"string","enum":["application/pdf","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/msword","text/plain","text/markdown"],"description":"MIME type of the file. Must be one of the accepted types."}}},"DemoCaps":{"type":"object","description":"Hard limits on a demo session. Exceeding one is a 422, never a partial write.","title":"DemoCaps","required":["max_messages","max_chars","max_urls","max_files"],"properties":{"max_messages":{"type":"integer","description":"Messages the demo will answer."},"max_urls":{"type":"integer","description":"Website URLs."},"max_chars":{"type":"integer","description":"Total training characters."},"max_files":{"type":"integer","description":"Uploaded files."}}},"DemoFile":{"type":"object","required":["id","status"],"properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","description":"Extraction status of this file."},"title":{"type":["string","null"]},"file_name":{"type":["string","null"]},"char_count":{"type":["integer","null"]}}},"DemoSession":{"type":"object","description":"The full live state of an anonymous demo chatbot.","title":"DemoSession","required":["session_id","slug","status","caps","sources"],"properties":{"status":{"type":"string","description":"Training status. `ready` means the chatbot can answer.","examples":["queued","training","ready"]},"sources":{"$ref":"#/components/schemas/DemoSources"},"session_id":{"type":"string","description":"Opaque id that authorizes every other call in this group."},"messages_used":{"type":"integer","description":"Demo messages answered so far."},"slug":{"type":"string","description":"Slug of the demo chatbot."},"chars_used":{"type":"integer","description":"Training characters consumed so far."},"needs_retrain":{"type":"boolean","description":"True when sources changed since the last training run."},"caps":{"$ref":"#/components/schemas/DemoCaps"}}},"DemoSessionResponse":{"type":"object","title":"DemoSessionResponse","required":["ok","demo"],"properties":{"ok":{"const":true,"type":"boolean"},"demo":{"$ref":"#/components/schemas/DemoSession"}}},"DemoSources":{"type":"object","description":"Everything attached to the demo, grouped by source type.","title":"DemoSources","required":["urls","files","texts"],"properties":{"files":{"type":"array","items":{"$ref":"#/components/schemas/DemoFile"}},"urls":{"type":"array","items":{"$ref":"#/components/schemas/DemoUrl"}},"texts":{"type":"array","items":{"$ref":"#/components/schemas/DemoText"}}}},"DemoText":{"type":"object","required":["id","status"],"properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string"},"char_count":{"type":["integer","null"]}}},"DemoUrl":{"type":"object","required":["id","url","status"],"properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","description":"Extraction status of this page."},"domain":{"type":["string","null"]},"url":{"type":"string","format":"uri"},"char_count":{"type":["integer","null"],"description":"Characters extracted."}}},"Error":{"type":"object","description":"Every error from every endpoint. `code` is the stable machine key; `message` says what happened; `hint` says what to do about it.","title":"Error","required":["ok","code","status","message","hint"],"properties":{"error":{"type":"string","description":"Deprecated alias of `code`, kept for existing clients."},"code":{"type":"string","description":"Stable machine-readable key. Branch on this, never on `message`.","examples":["expired","url_cap","char_cap","global_cap","not_found","rate_limited"]},"message":{"type":"string","description":"Human-readable description of the failure."},"ok":{"const":false,"type":"boolean","description":"Always false on an error."},"status":{"type":"integer","description":"The HTTP status of this response."},"hint":{"type":"string","description":"What to do next: retry, change an input, or stop."},"documentation_url":{"type":"string","format":"uri","description":"Where the failure is documented."}}},"JsonRpcRequest":{"type":"object","description":"A JSON-RPC 2.0 message, or an array of them for a batch. Omit `id` to send a notification.","title":"JsonRpcRequest","required":["jsonrpc","method"],"properties":{"id":{"type":["string","integer"],"description":"Correlation id. Omit for a notification, which is answered with 202."},"params":{"type":"object","description":"Method parameters."},"method":{"type":"string","description":"MCP method.","examples":["initialize","tools/list","tools/call"]},"jsonrpc":{"const":"2.0","type":"string"}}},"JsonRpcResponse":{"type":"object","description":"A JSON-RPC 2.0 response. Exactly one of `result` or `error` is present.","title":"JsonRpcResponse","required":["jsonrpc"],"properties":{"error":{"type":"object","description":"Present on failure.","required":["code","message"],"properties":{"code":{"type":"integer","description":"JSON-RPC error code."},"data":{"type":"object"},"message":{"type":"string"}}},"id":{"type":["string","integer","null"]},"result":{"type":"object","description":"Present on success."},"jsonrpc":{"const":"2.0","type":"string"}}},"McpManifest":{"type":"object","description":"The descriptor served at /.well-known/mcp.json.","title":"McpManifest","required":["name","version","transport","url","tools"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"authentication":{"type":"object"},"description":{"type":"string"},"tools":{"type":"array","description":"Tool definitions, each with its JSON Schema, as `tools/list` returns.","items":{"type":"object"}},"url":{"type":"string","format":"uri","description":"Where to POST JSON-RPC messages."},"transport":{"const":"streamable-http","type":"string"},"protocolVersions":{"type":"array","items":{"type":"string"}}}},"UploadTarget":{"type":"object","description":"A presigned S3 PUT target. Expires 10 minutes after it is issued.","title":"UploadTarget","required":["ok","upload_url","s3_key"],"properties":{"ok":{"const":true,"type":"boolean"},"s3_key":{"type":"string","description":"Quote this back to `addDemoSessionFile` once the PUT succeeds."},"upload_url":{"type":"string","format":"uri","description":"PUT the file bytes here with the declared Content-Type."}}}},"securitySchemes":{"bearerAuth":{"scheme":"bearer","type":"http","description":"A ChatbotGen account API key, generated at https://www.chatbotgen.com/app/settings/account. Only the MCP endpoint accepts one; the demo API is authorized by its session id instead.","bearerFormat":"cbg_..."}}},"paths":{"/.well-known/mcp.json":{"get":{"description":"Name, version, transport, endpoint, authentication, and the full\nanonymous tool list with each tool's JSON Schema. Lets a client decide\nwhether to connect before it opens a session.\n","summary":"Descriptor for the ChatbotGen MCP server","tags":["Discovery"],"operationId":"getMcpManifest","responses":{"200":{"description":"The MCP server descriptor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpManifest"}}}}}}},"/api/tools/sessions":{"post":{"description":"Provisions an ephemeral demo chatbot and returns its state, including the\n`session_id` every other call in this group needs. No account and no key.\n\nCall this once and reuse the `session_id`; there is a global cap on\nconcurrent demos, and `503 global_cap` means the free demo is saturated\nrather than that anything is broken — retry in a few minutes, or point\nthe person at https://www.chatbotgen.com/app/register.\n\nRate limit: 30 requests per minute per IP address.\n","summary":"Create an anonymous demo session","tags":["Demo tools"],"operationId":"createDemoSession","responses":{"200":{"description":"The new demo session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DemoSessionResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`global_cap` — the demo pool is full right now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/tools/sessions/{session_id}":{"get":{"description":"Returns sources, character and message usage against the caps, and the\ntraining status. Poll this while training runs: `status` moves to\n`ready` when the chatbot can answer.\n\nA demo that has expired answers `404 expired`; create a new session\nrather than retrying this one.\n","parameters":[{"in":"path","name":"session_id","description":"The opaque session id returned by `createDemoSession`.","required":true,"schema":{"type":"string"}}],"summary":"Read a demo session's live state","tags":["Demo tools"],"operationId":"getDemoSession","responses":{"200":{"description":"The demo session's current state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DemoSessionResponse"}}}},"404":{"description":"`expired` — no live demo has that session id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/tools/sessions/{session_id}/files":{"post":{"description":"Step two of the file flow. Registers a file already uploaded with the\npresigned URL from `createDemoSessionUpload`. Text extraction runs in the\nbackground; poll `getDemoSession` and watch the file's `status`.\n","parameters":[{"in":"path","name":"session_id","description":"The opaque session id returned by `createDemoSession`.","required":true,"schema":{"type":"string"}}],"summary":"Attach an uploaded file to a demo session","tags":["Demo tools"],"operationId":"addDemoSessionFile","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddFileRequest"}}}},"responses":{"200":{"description":"The demo session, including the new file.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DemoSessionResponse"}}}},"404":{"description":"`expired` — no live demo has that session id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"A cap was reached or the input was unusable — see `code` for which: `url_cap`, `file_cap`, `char_cap`, `empty`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`global_cap` — the demo pool is full right now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/tools/sessions/{session_id}/texts":{"post":{"description":"Attaches free text — a policy, opening hours, a product description — to\nthe demo. Counts against the same 50000-character budget\nas crawled pages and uploaded files.\n","parameters":[{"in":"path","name":"session_id","description":"The opaque session id returned by `createDemoSession`.","required":true,"schema":{"type":"string"}}],"summary":"Train a demo session on a block of text","tags":["Demo tools"],"operationId":"addDemoSessionText","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddTextRequest"}}}},"responses":{"200":{"description":"The demo session, including the new text source.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DemoSessionResponse"}}}},"404":{"description":"`expired` — no live demo has that session id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"A cap was reached or the input was unusable — see `code` for which: `url_cap`, `file_cap`, `char_cap`, `empty`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`global_cap` — the demo pool is full right now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/tools/sessions/{session_id}/training":{"post":{"description":"Chunks and embeds everything attached to the demo. Training is\nasynchronous: this returns immediately and `status` moves to `ready`\nwhen it finishes, so poll `getDemoSession`.\n\n`422 empty` means no source produced readable text — a URL that returned\nno body, or a PDF with no text layer. Add a different source rather than\nretrying.\n","parameters":[{"in":"path","name":"session_id","description":"The opaque session id returned by `createDemoSession`.","required":true,"schema":{"type":"string"}}],"summary":"Embed a demo session's sources so the chatbot can answer","tags":["Demo tools"],"operationId":"trainDemoSession","responses":{"200":{"description":"The demo session, now training.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DemoSessionResponse"}}}},"404":{"description":"`expired` — no live demo has that session id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`empty` — there is nothing trainable in this session yet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/tools/sessions/{session_id}/uploads":{"post":{"description":"Step one of the two-step file flow. Returns a short-lived (10 minute) S3\n`PUT` URL and the `s3_key` to quote afterwards. `PUT` the file bytes to\n`upload_url` with the same `Content-Type` you declared here, then call\n`addDemoSessionFile` with the returned `s3_key`.\n\nAccepted types: PDF, DOCX, DOC, plain text, Markdown. Maximum 10 MB, and\nat most 3 files per demo.\n","parameters":[{"in":"path","name":"session_id","description":"The opaque session id returned by `createDemoSession`.","required":true,"schema":{"type":"string"}}],"summary":"Get a presigned URL for a file upload","tags":["Demo tools"],"operationId":"createDemoSessionUpload","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUploadRequest"}}}},"responses":{"200":{"description":"The presigned upload target.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadTarget"}}}},"404":{"description":"`expired` — no live demo has that session id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`file_cap`, `file_type` or `file_size` — see `code` for which constraint failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`global_cap` — the demo pool is full right now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/tools/sessions/{session_id}/urls":{"post":{"description":"Fetches the page, extracts its text, and attaches it to the demo. The\ncontent is not embedded until `trainDemoSession` runs.\n\nA demo accepts at most 3 URLs and\n50000 characters in total; past either you get\n`422 url_cap` or `422 char_cap`. Both are permanent for this session —\nremove a source or start a new one.\n","parameters":[{"in":"path","name":"session_id","description":"The opaque session id returned by `createDemoSession`.","required":true,"schema":{"type":"string"}}],"summary":"Train a demo session on a web page","tags":["Demo tools"],"operationId":"addDemoSessionUrl","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddUrlRequest"}}}},"responses":{"200":{"description":"The demo session, including the new URL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DemoSessionResponse"}}}},"404":{"description":"`expired` — no live demo has that session id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"A cap was reached or the input was unusable — see `code` for which: `url_cap`, `file_cap`, `char_cap`, `empty`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`global_cap` — the demo pool is full right now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/docs/{category}/{slug}.md":{"get":{"description":"Append `.md` to any documentation URL to get its markdown source with the\nfrontmatter stripped and no HTML shell around it. Requires no `Accept`\nheader. The page list lives in `/llms.txt`.\n\nSpanish pages fall back to English when no translation exists.\n","parameters":[{"in":"path","name":"category","description":"Documentation category slug, e.g. `getting-started`.","required":true,"schema":{"type":"string"}},{"in":"path","name":"slug","description":"Page slug, e.g. `welcome`.","required":true,"schema":{"type":"string"}}],"summary":"Raw markdown source of one documentation page","tags":["Discovery"],"operationId":"getDocPageAsMarkdown","responses":{"200":{"description":"The markdown source of the page.","content":{"text/plain":{"schema":{"type":"string"}}}},"404":{"description":"No documentation page has that category and slug."}}}},"/llms-full.txt":{"get":{"description":"The whole product documentation set concatenated, for a client that would rather make one request than crawl the docs tree.","summary":"Every documentation page inlined as one file","tags":["Discovery"],"operationId":"getLlmsFullTxt","responses":{"200":{"description":"The full documentation corpus.","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/llms.txt":{"get":{"description":"A curated map of chatbotgen.com for language models: one line per page with a written description, and markdown URLs for the documentation.","summary":"llmstxt.org index of the site","tags":["Discovery"],"operationId":"getLlmsTxt","responses":{"200":{"description":"The llms.txt index.","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/mcp":{"get":{"description":"`GET /mcp` means \"open a server-initiated SSE stream\" in the Streamable\nHTTP transport. This server does not offer one, so it answers `405` with\n`Allow: POST` rather than a `404` that would read like a wrong URL.\n","summary":"Reports that this MCP endpoint is POST-only","tags":["MCP"],"operationId":"describeMcpTransport","responses":{"405":{"description":"Always. Use POST.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcResponse"}}}}}},"post":{"description":"The Model Context Protocol Streamable HTTP transport. Send `initialize`,\nthen `tools/list`, then `tools/call`. Supported protocol versions:\n2025-06-18, 2025-03-26, 2024-11-05.\n\nA request (a message with an `id`) is answered with a single JSON body —\nthis server never opens an SSE stream, so `application/json` is always\nthe response media type. A notification (no `id`) is answered with\n`202 Accepted` and an empty body, as the transport requires. A JSON-RPC\nbatch is a top-level array and is answered with an array.\n\nAnonymous callers see `create_chatbot`, `get_chatbot`,\n`add_knowledge_url`, `add_knowledge_text` and `add_qa`, operating on\nsandbox chatbots: 5 pages crawled,\n10 messages answered, 50000\ncharacters of training content, deleted after 8\ndays unless a human opens the returned `claim_url`.\n\nSend `Authorization: Bearer cbg_...` to operate on a real account's\nchatbots under its plan limits; `list_chatbots` is then also available.\nAn invalid key is rejected at the transport layer with `401` and a\n`WWW-Authenticate` header, not as a JSON-RPC error.\n\nRate limit: 60 requests per minute per IP address, with a tighter per-tool budget on\n`create_chatbot` because it triggers a live crawl.\n","summary":"Send a JSON-RPC 2.0 message to the ChatbotGen MCP server","tags":["MCP"],"operationId":"callMcpServer","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcRequest"},"examples":{"initialize":{"value":{"id":1,"params":{"capabilities":{},"protocolVersion":"2025-06-18","clientInfo":{"name":"example-client","version":"1.0.0"}},"method":"initialize","jsonrpc":"2.0"},"summary":"Handshake"},"createChatbot":{"value":{"id":2,"params":{"name":"create_chatbot","arguments":{"language":"en","website_url":"https://example.com"}},"method":"tools/call","jsonrpc":"2.0"},"summary":"Build a demo chatbot for a website"}}}}},"responses":{"200":{"description":"The JSON-RPC result or error for the request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcResponse"}}}},"202":{"description":"The message was a notification; there is no body to return."},"401":{"description":"The `Authorization` header carried a key we do not recognise.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcResponse"}}}},"429":{"description":"Rate limit exceeded. Wait for the window to roll over.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{},{"bearerAuth":[]}]}},"/openapi.json":{"get":{"description":"The OpenAPI 3.1 description of the ChatbotGen API. `/openapi.yaml` serves the same document YAML-encoded.","summary":"This document","tags":["Discovery"],"operationId":"getOpenApiDocument","responses":{"200":{"description":"The OpenAPI document.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/sitemap.xml":{"get":{"description":"Standard sitemaps.org XML, including hreflang alternates for the bilingual pages.","summary":"XML sitemap of every indexable URL","tags":["Discovery"],"operationId":"getSitemap","responses":{"200":{"description":"The sitemap.","content":{"application/xml":{"schema":{"type":"string"}}}}}}},"/v2/health":{"get":{"description":"Returns `ok` when the application is running and its database answers. Used by the deployment proxy; safe to poll.","summary":"Liveness and database reachability","tags":["Operations"],"operationId":"getHealth","responses":{"200":{"description":"`ok`.","content":{"text/plain":{"schema":{"type":"string"}}}},"503":{"description":"`db unreachable` — the app is up but its database is not.","content":{"text/plain":{"schema":{"type":"string"}}}}}}}},"tags":[{"name":"MCP","description":"Model Context Protocol server. Lets an AI assistant create and train chatbots directly.","externalDocs":{"description":"MCP details","url":"https://www.chatbotgen.com/ai-info#mcp"}},{"name":"Demo tools","description":"The free \"Chat With Your Content\" demo API. No account, no key; a session id authorizes each call."},{"name":"Discovery","description":"Machine-readable descriptions of this site: OpenAPI, the MCP manifest, llms.txt, the sitemap."},{"name":"Operations","description":"Service health."}],"security":[{}],"externalDocs":{"description":"ChatbotGen developer resources","url":"https://www.chatbotgen.com/developers"},"openapi":"3.1.0","servers":[{"description":"Production","url":"https://www.chatbotgen.com"}],"webhooks":{"stripe":{"post":{"description":"Subscription lifecycle and checkout completion, verified by HMAC signature. Documented so it is not mistaken for a callable endpoint.","summary":"Stripe billing events (received at /webhooks/stripe)","operationId":"receiveStripeWebhook","responses":{"200":{"description":"Event accepted."}}}},"telegram":{"post":{"description":"Inbound Telegram messages, authorized by the unguessable uuid in the path.","summary":"Telegram bot updates (received at /webhooks/telegram/{uuid})","operationId":"receiveTelegramWebhook","responses":{"200":{"description":"Update accepted."}}}},"whatsapp":{"post":{"description":"QR, pairing, connected, disconnected and inbound message events from the WhatsApp service, signature-verified in production.","summary":"WhatsApp session and message events (received at /webhooks/whatsapp/{chatbot_id})","operationId":"receiveWhatsappWebhook","responses":{"200":{"description":"Event accepted."}}}}}}