{"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. A path\nthat does not exist under `/api`, `/mcp` or `/.well-known` answers with\nthat same JSON body even when the request sends no `Accept` header.\n\n**Rate limits.** Every response on a rate-limited path reports the\ncaller's standing in the IETF `RateLimit` header fields, in both the\ndiscrete form (`RateLimit-Limit`, `RateLimit-Remaining`,\n`RateLimit-Reset`) and the structured form (`RateLimit`,\n`RateLimit-Policy`). `RateLimit-Reset` is a delta in seconds. A `429`\nadds `Retry-After` with the same delta. Budgets: the demo API allows\n30 requests per 60 seconds per IP address; the MCP endpoint allows 60 requests per 60 seconds per IP address.\n\n**Versioning.** The version of the contract is `1.0.0` and is\nechoed on every response in `API-Version`. Breaking changes take a new\nURL path prefix; the previous prefix keeps answering for the notice\nperiod published at https://www.chatbotgen.com/developers/versioning. A surface that is on its way\nout carries `Deprecation` (RFC 9745), `Sunset` (RFC 8594) and a\n`Link: rel=\"successor-version\"` header for the whole notice period, so\nno integration can be removed from under a caller without warning.\n\n**Discovery.** Every response carries `Link` headers pointing at this\ndocument (`rel=\"service-desc\"`), the developer documentation\n(`rel=\"service-doc\"`) and the deprecation policy\n(`rel=\"deprecation-policy\"`). `GET /api` returns the same facts as JSON\nfor a client that would rather read one small document than this one.\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","x-api-index":"https://www.chatbotgen.com/api","x-deprecation-policy":"https://www.chatbotgen.com/developers/versioning","x-rate-limit-headers":["RateLimit-Limit","RateLimit-Remaining","RateLimit-Reset","RateLimit-Policy","RateLimit","Retry-After"]},"components":{"headers":{"ApiVersion":{"description":"Semantic version of the API contract that produced this response.","schema":{"type":"string"},"example":"1.0.0"},"Deprecation":{"description":"RFC 9745. Present only on a surface scheduled for removal: an `@` followed by the Unix timestamp at which it was deprecated. Read `Sunset` for when it stops answering.","schema":{"type":"string"},"example":"@1767225600"},"ETag":{"description":"Strong entity tag for this representation. Send it back in `If-None-Match` to revalidate.","schema":{"type":"string"},"example":"\"9f2c1d4a7b0e3f8c5a6d9e2b1c4f7a0d\""},"RateLimit":{"description":"Limit, remaining and reset combined into one structured field.","schema":{"type":"string"},"example":"limit=30, remaining=29, reset=42"},"RateLimitLimit":{"description":"Requests allowed in the current window.","schema":{"type":"integer","minimum":0},"example":30},"RateLimitPolicy":{"description":"The policy in the structured form of the IETF RateLimit header fields: a quoted policy name, `q` for the quota, `w` for the window in seconds.","schema":{"type":"string"},"example":"\"demo-api\"; q=30; w=60"},"RateLimitRemaining":{"description":"Requests still available in the current window.","schema":{"type":"integer","minimum":0},"example":29},"RateLimitReset":{"description":"Seconds until the window rolls over and `RateLimit-Remaining` returns to `RateLimit-Limit`. A delta, not a timestamp, so no clock agreement is needed.","schema":{"type":"integer","minimum":0},"example":42},"RetryAfter":{"description":"Seconds to wait before retrying. Sent on a 429 with the same value as `RateLimit-Reset`.","schema":{"type":"integer","minimum":0},"example":42},"SuccessorVersion":{"description":"Sent as `Link: <url>; rel=\"successor-version\"` alongside `Deprecation`: where to move the integration.","schema":{"type":"string"},"example":"<https://www.chatbotgen.com/api/v2/tools/sessions>; rel=\"successor-version\""},"Sunset":{"description":"RFC 8594. The HTTP-date after which a deprecated surface stops answering. Absent while a surface is supported.","schema":{"type":"string"},"example":"Sat, 01 Jan 2028 00:00:00 GMT"}},"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."}}},"ApiIndex":{"type":"object","description":"The document served at `/api`: what can be called, how fast, under which version, and what a failure looks like.","title":"ApiIndex","required":["name","version","documentation_url","openapi_url","versioning","rate_limits","errors","endpoints"],"properties":{"name":{"type":"string"},"version":{"type":"string","description":"Semantic version of the API contract."},"authentication":{"type":"object","required":["type","required"],"properties":{"type":{"const":"bearer","type":"string"},"description":{"type":"string"},"required":{"type":"boolean"},"instructions_url":{"type":"string","format":"uri"},"token_prefix":{"type":"string"}}},"description":{"type":"string"},"errors":{"type":"object","description":"The error contract, with a worked example of the body.","required":["media_type","schema_url","example"],"properties":{"description":{"type":"string"},"media_type":{"const":"application/json","type":"string"},"example":{"$ref":"#/components/schemas/Error"},"schema_url":{"type":"string","format":"uri"}}},"endpoints":{"type":"array","description":"Every callable endpoint, flattened from this document's `paths`.","items":{"$ref":"#/components/schemas/EndpointSummary"}},"mcp":{"type":"object","description":"How to reach the MCP server and which protocol versions it negotiates.","required":["url","transport","protocol_versions"],"properties":{"url":{"type":"string","format":"uri"},"transport":{"const":"streamable-http","type":"string"},"protocol_versions":{"type":"array","items":{"type":"string"}},"manifest_url":{"type":"string","format":"uri"},"server_card_url":{"type":"string","format":"uri"}}},"rate_limits":{"$ref":"#/components/schemas/RateLimitPolicies"},"documentation_url":{"type":"string","format":"uri"},"contact_email":{"type":"string","format":"email"},"openapi_url":{"type":"string","format":"uri"},"openapi_yaml_url":{"type":"string","format":"uri"},"status_url":{"type":"string","format":"uri"},"terms_of_service":{"type":"string","format":"uri"},"versioning":{"$ref":"#/components/schemas/VersioningPolicy"}}},"CreateDemoSessionRequest":{"type":"object","description":"Optional body for `createDemoSession`. Every field has a default, so an empty body and no body at all mean the same thing.","title":"CreateDemoSessionRequest","properties":{"locale":{"default":"en","type":"string","enum":["en","es"],"description":"Language of the demo chatbot's widget copy and of its answers. Anything other than `es` is treated as English."}}},"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."}}},"Deprecation":{"type":"object","description":"One API surface on its way out.","title":"Deprecation","required":["path_prefix","deprecated_at","sunset_at","successor"],"properties":{"successor":{"type":"string","format":"uri","description":"Where to move the integration."},"deprecated_at":{"type":"string","format":"date-time"},"path_prefix":{"type":"string","description":"Every path under this prefix is affected."},"sunset_at":{"type":"string","format":"date-time","description":"After this moment the surface stops answering."}}},"EndpointSummary":{"type":"object","description":"One callable endpoint, as listed by `/api`.","title":"EndpointSummary","required":["method","path","operation_id","summary"],"properties":{"path":{"type":"string","description":"Path template, e.g. `/api/tools/sessions/{session_id}`."},"method":{"type":"string","enum":["GET","POST","PUT","PATCH","DELETE"]},"summary":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"operation_id":{"type":"string","description":"The same `operationId` this document uses. Stable across releases."}}},"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."}}},"Health":{"type":"object","description":"Liveness of the application and of each component it needs. Returned when the request asks for `application/json`.","title":"Health","required":["status","database","service","version","time"],"properties":{"status":{"type":"string","enum":["ok","unavailable"],"description":"`ok` when every component below answers."},"version":{"type":"string","description":"Version of the API contract."},"time":{"type":"string","format":"date-time","description":"Server time when the check ran, in UTC."},"service":{"const":"chatbotgen","type":"string"},"database":{"type":"string","enum":["ok","unreachable"],"description":"Whether the PostgreSQL cluster answered a trivial query."},"documentation_url":{"type":"string","format":"uri"}}},"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"}}},"McpEndpointManifest":{"type":"object","description":"The SEP-1960 manifest served at /.well-known/mcp.","title":"McpEndpointManifest","required":["mcp_version","endpoints"],"properties":{"endpoints":{"type":"array","items":{"type":"object","required":["url","transport"],"properties":{"auth":{"type":"object","properties":{"type":{"type":"string"},"description":{"type":"string"},"required":{"type":"boolean"}}},"url":{"type":"string","format":"uri"},"transport":{"const":"streamable-http","type":"string"},"capabilities":{"type":"array","items":{"type":"string"}}}}},"mcp_version":{"type":"string","description":"The MCP release this manifest targets, not the negotiated wire version."}}},"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"}}}},"McpServerCard":{"type":"object","description":"The SEP-1649 server card served at /.well-known/mcp/server-card.json.","title":"McpServerCard","required":["version","protocolVersion","serverInfo","transport","capabilities"],"properties":{"version":{"type":"string","description":"Version of the card format."},"transport":{"type":"object","required":["type","url"],"properties":{"type":{"const":"streamable-http","type":"string"},"url":{"type":"string","format":"uri"}}},"capabilities":{"type":"object","properties":{"tools":{"type":"boolean"},"resources":{"type":"boolean"},"prompts":{"type":"boolean"}}},"protocolVersion":{"type":"string","description":"The MCP protocol version this server prefers."},"serverInfo":{"type":"object","required":["name","version"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"description":{"type":"string"},"homepage":{"type":"string","format":"uri"}}},"$schema":{"type":"string","format":"uri"}}},"OpenApiDocument":{"type":"object","description":"An OpenAPI 3.1 description document. This one describes itself.","title":"OpenApiDocument","required":["openapi","info","paths","components"],"properties":{"info":{"type":"object","description":"Title, version, contact, licence, and the prose contract."},"components":{"type":"object","description":"Reusable schemas, response headers and security schemes."},"paths":{"type":"object","description":"Callable endpoints, keyed by path then method."},"tags":{"type":"array","items":{"type":"object"}},"externalDocs":{"type":"object"},"openapi":{"const":"3.1.0","type":"string"},"servers":{"type":"array","items":{"type":"object"}},"webhooks":{"type":"object","description":"Requests this API receives rather than serves."}}},"RateLimitPolicies":{"type":"object","description":"The budgets in force and the headers that report them on every response.","title":"RateLimitPolicies","required":["headers","policies"],"properties":{"description":{"type":"string"},"headers":{"type":"array","items":{"type":"string"}},"policies":{"type":"array","items":{"type":"object","required":["name","scope","limit","window_seconds"],"properties":{"name":{"type":"string","description":"Matches the name in `RateLimit-Policy`."},"scope":{"type":"string","description":"Which paths the budget covers."},"limit":{"type":"integer","description":"Requests allowed per window."},"window_seconds":{"type":"integer"}}}}}},"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."}}},"VersioningPolicy":{"type":"object","description":"How this API is versioned and how removal is signalled. The prose version is at https://www.chatbotgen.com/developers/versioning.","title":"VersioningPolicy","required":["scheme","current","policy_url","deprecations"],"properties":{"major":{"type":"string","description":"Major version, which is what the path prefix encodes."},"scheme":{"const":"url-path","type":"string","description":"The version lives in the URL path; a breaking change takes a new prefix."},"current":{"type":"string","description":"Semantic version of the current contract."},"deprecations":{"type":"array","description":"Surfaces currently scheduled for removal. Empty means nothing is — which is a published fact, not a missing field.","items":{"$ref":"#/components/schemas/Deprecation"}},"policy_url":{"type":"string","format":"uri"},"deprecation_headers":{"type":"array","description":"The headers a deprecated surface carries for its whole notice period.","items":{"type":"string"}}}}},"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":{"get":{"description":"The third discovery convention: `mcp_version` plus an `endpoints` array, each with its transport, capabilities and authentication requirement.","parameters":[{"in":"header","name":"If-None-Match","description":"An `ETag` from a previous response. The server answers `304` with no body when the document has not changed since, which is how a scheduled re-fetch stays cheap.","required":false,"schema":{"type":"string"},"examples":{"etag":{"value":"\"9f2c1d...\"","summary":"A tag from a previous response"}}}],"summary":"The same MCP server in the SEP-1960 endpoints shape","tags":["Discovery"],"operationId":"getMcpEndpointManifest","responses":{"200":{"description":"The SEP-1960 manifest.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpEndpointManifest"}}},"headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"304":{"description":"The `If-None-Match` validator matched, so the document is unchanged and no body is sent.","headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"default":{"description":"Any status not listed above. The body is the same `Error` shape: branch on `code`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/.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\nThis is the most complete of the three MCP descriptors: the server card\nextended with the tool definitions and the authentication note.\n","parameters":[{"in":"header","name":"If-None-Match","description":"An `ETag` from a previous response. The server answers `304` with no body when the document has not changed since, which is how a scheduled re-fetch stays cheap.","required":false,"schema":{"type":"string"},"examples":{"etag":{"value":"\"9f2c1d...\"","summary":"A tag from a previous response"}}}],"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"}}},"headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"304":{"description":"The `If-None-Match` validator matched, so the document is unchanged and no body is sent.","headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"default":{"description":"Any status not listed above. The body is the same `Error` shape: branch on `code`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/.well-known/mcp/server-card.json":{"get":{"description":"MCP server discovery is not settled: two specification enhancement\nproposals are in flight and neither is merged. This path serves the\nSEP-1649 shape — `$schema`, `version`, `protocolVersion`, `serverInfo`,\n`transport`, `capabilities` — with no tool list.\n\nUse `/.well-known/mcp.json` instead if you want the tools too.\n","parameters":[{"in":"header","name":"If-None-Match","description":"An `ETag` from a previous response. The server answers `304` with no body when the document has not changed since, which is how a scheduled re-fetch stays cheap.","required":false,"schema":{"type":"string"},"examples":{"etag":{"value":"\"9f2c1d...\"","summary":"A tag from a previous response"}}}],"summary":"The same MCP server in the SEP-1649 server-card shape","tags":["Discovery"],"operationId":"getMcpServerCard","responses":{"200":{"description":"The SEP-1649 server card.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpServerCard"}}},"headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"304":{"description":"The `If-None-Match` validator matched, so the document is unchanged and no body is sent.","headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"default":{"description":"Any status not listed above. The body is the same `Error` shape: branch on `code`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api":{"get":{"description":"The API root. Returns the endpoint list, the rate-limit budgets and the\nheaders that report them, the versioning and deprecation policy, the\nerror contract with a worked example, and the URLs of the fuller\ndescriptions.\n\nSmall on purpose: a client that only needs to know what exists and how\nfast it may call should not have to parse this whole document first.\n","parameters":[{"in":"header","name":"If-None-Match","description":"An `ETag` from a previous response. The server answers `304` with no body when the document has not changed since, which is how a scheduled re-fetch stays cheap.","required":false,"schema":{"type":"string"},"examples":{"etag":{"value":"\"9f2c1d...\"","summary":"A tag from a previous response"}}}],"summary":"JSON index of the public API","tags":["Discovery"],"operationId":"getApiIndex","responses":{"200":{"description":"The API index.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiIndex"}}},"headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"304":{"description":"The `If-None-Match` validator matched, so the document is unchanged and no body is sent.","headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"default":{"description":"Any status not listed above. The body is the same `Error` shape: branch on `code`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/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\nThe request body is optional. Send `{\"locale\":\"es\"}` to get a demo whose\nwidget copy and answers are in Spanish; omit the body entirely for\nEnglish.\n\nRate limit: 30 requests per 60 seconds per IP address.\n","summary":"Create an anonymous demo session","tags":["Demo tools"],"operationId":"createDemoSession","requestBody":{"description":"Optional. Omit it for an English demo.","required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDemoSessionRequest"}}}},"responses":{"200":{"description":"The new demo session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DemoSessionResponse"}}},"headers":{"API-Version":{"$ref":"#/components/headers/ApiVersion"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"API-Version":{"$ref":"#/components/headers/ApiVersion"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}}},"503":{"description":"`global_cap` — the demo pool is full right now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any status not listed above. The body is the same `Error` shape: branch on `code`.","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"}}},"headers":{"API-Version":{"$ref":"#/components/headers/ApiVersion"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"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"}}},"headers":{"API-Version":{"$ref":"#/components/headers/ApiVersion"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}}},"default":{"description":"Any status not listed above. The body is the same `Error` shape: branch on `code`.","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"}}},"headers":{"API-Version":{"$ref":"#/components/headers/ApiVersion"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"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"}}},"headers":{"API-Version":{"$ref":"#/components/headers/ApiVersion"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}}},"503":{"description":"`global_cap` — the demo pool is full right now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any status not listed above. The body is the same `Error` shape: branch on `code`.","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"}}},"headers":{"API-Version":{"$ref":"#/components/headers/ApiVersion"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"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"}}},"headers":{"API-Version":{"$ref":"#/components/headers/ApiVersion"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}}},"503":{"description":"`global_cap` — the demo pool is full right now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any status not listed above. The body is the same `Error` shape: branch on `code`.","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"}}},"headers":{"API-Version":{"$ref":"#/components/headers/ApiVersion"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"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"}}},"headers":{"API-Version":{"$ref":"#/components/headers/ApiVersion"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}}},"default":{"description":"Any status not listed above. The body is the same `Error` shape: branch on `code`.","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"}}},"headers":{"API-Version":{"$ref":"#/components/headers/ApiVersion"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}}},"503":{"description":"`global_cap` — the demo pool is full right now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any status not listed above. The body is the same `Error` shape: branch on `code`.","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"}}},"headers":{"API-Version":{"$ref":"#/components/headers/ApiVersion"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"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"}}},"headers":{"API-Version":{"$ref":"#/components/headers/ApiVersion"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}}},"503":{"description":"`global_cap` — the demo pool is full right now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any status not listed above. The body is the same `Error` shape: branch on `code`.","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"}},{"in":"header","name":"If-None-Match","description":"An `ETag` from a previous response. The server answers `304` with no body when the document has not changed since, which is how a scheduled re-fetch stays cheap.","required":false,"schema":{"type":"string"},"examples":{"etag":{"value":"\"9f2c1d...\"","summary":"A tag from a previous response"}}}],"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"}}},"headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"304":{"description":"The `If-None-Match` validator matched, so the document is unchanged and no body is sent.","headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"404":{"description":"No documentation page has that category and slug."},"default":{"description":"Any status not listed above. The body is the same `Error` shape: branch on `code`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/llms-full.txt":{"get":{"description":"The whole product documentation set concatenated, for a client that\nwould rather make one request than crawl the docs tree.\n\nThis is the largest document the site serves, so it is the one where\n`If-None-Match` pays for itself: a scheduled re-fetch that has not\nchanged costs a 304 instead of the entire corpus.\n","parameters":[{"in":"header","name":"If-None-Match","description":"An `ETag` from a previous response. The server answers `304` with no body when the document has not changed since, which is how a scheduled re-fetch stays cheap.","required":false,"schema":{"type":"string"},"examples":{"etag":{"value":"\"9f2c1d...\"","summary":"A tag from a previous response"}}}],"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"}}},"headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"304":{"description":"The `If-None-Match` validator matched, so the document is unchanged and no body is sent.","headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"default":{"description":"Any status not listed above. The body is the same `Error` shape: branch on `code`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/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.","parameters":[{"in":"header","name":"If-None-Match","description":"An `ETag` from a previous response. The server answers `304` with no body when the document has not changed since, which is how a scheduled re-fetch stays cheap.","required":false,"schema":{"type":"string"},"examples":{"etag":{"value":"\"9f2c1d...\"","summary":"A tag from a previous response"}}}],"summary":"llmstxt.org index of the site","tags":["Discovery"],"operationId":"getLlmsTxt","responses":{"200":{"description":"The llms.txt index.","content":{"text/plain":{"schema":{"type":"string"}}},"headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"304":{"description":"The `If-None-Match` validator matched, so the document is unchanged and no body is sent.","headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"default":{"description":"Any status not listed above. The body is the same `Error` shape: branch on `code`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/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","parameters":[{"in":"header","name":"MCP-Protocol-Version","description":"The protocol version negotiated during `initialize`. The transport requires it on every subsequent request; omitting it is treated as the oldest supported version.","required":false,"schema":{"type":"string","enum":["2025-06-18","2025-03-26","2024-11-05"]}}],"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"}}}},"default":{"description":"Any status not listed above. The body is the same `Error` shape: branch on `code`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"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 60 seconds per IP address, with a tighter per-tool budget on\n`create_chatbot` because it triggers a live crawl. Every response\ncarries the `RateLimit-*` headers, so a client can pace itself without\never being refused.\n","parameters":[{"in":"header","name":"MCP-Protocol-Version","description":"The protocol version negotiated during `initialize`. The transport requires it on every subsequent request; omitting it is treated as the oldest supported version.","required":false,"schema":{"type":"string","enum":["2025-06-18","2025-03-26","2024-11-05"]}},{"in":"header","name":"Mcp-Session-Id","description":"Session identifier, if the client received one from `initialize`. This server is stateless between requests and ignores it, but accepts it so a spec-compliant client needs no special case.","required":false,"schema":{"type":"string"}}],"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"}}},"headers":{"API-Version":{"$ref":"#/components/headers/ApiVersion"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"202":{"description":"The message was a notification; there is no body to return.","headers":{"API-Version":{"$ref":"#/components/headers/ApiVersion"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"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"}}},"headers":{"API-Version":{"$ref":"#/components/headers/ApiVersion"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}}},"default":{"description":"Any status not listed above. The body is the same `Error` shape: branch on `code`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{},{"bearerAuth":[]}]}},"/openapi.json":{"get":{"description":"The OpenAPI 3.1 description of the ChatbotGen API, JSON-encoded. Built per request from the running app, so it cannot describe a version of the API that is no longer deployed.","parameters":[{"in":"header","name":"If-None-Match","description":"An `ETag` from a previous response. The server answers `304` with no body when the document has not changed since, which is how a scheduled re-fetch stays cheap.","required":false,"schema":{"type":"string"},"examples":{"etag":{"value":"\"9f2c1d...\"","summary":"A tag from a previous response"}}}],"summary":"This document","tags":["Discovery"],"operationId":"getOpenApiDocument","responses":{"200":{"description":"The OpenAPI document.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenApiDocument"}}},"headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"304":{"description":"The `If-None-Match` validator matched, so the document is unchanged and no body is sent.","headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"default":{"description":"Any status not listed above. The body is the same `Error` shape: branch on `code`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/openapi.yaml":{"get":{"description":"The same resource as `/openapi.json` in the encoding much API tooling still assumes. Two encodings of one document, not two documents.","parameters":[{"in":"header","name":"If-None-Match","description":"An `ETag` from a previous response. The server answers `304` with no body when the document has not changed since, which is how a scheduled re-fetch stays cheap.","required":false,"schema":{"type":"string"},"examples":{"etag":{"value":"\"9f2c1d...\"","summary":"A tag from a previous response"}}}],"summary":"This document, YAML-encoded","tags":["Discovery"],"operationId":"getOpenApiDocumentAsYaml","responses":{"200":{"description":"The OpenAPI document.","content":{"application/yaml":{"schema":{"type":"string"}}},"headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"304":{"description":"The `If-None-Match` validator matched, so the document is unchanged and no body is sent.","headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"default":{"description":"Any status not listed above. The body is the same `Error` shape: branch on `code`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sitemap.xml":{"get":{"description":"Standard sitemaps.org XML, including hreflang alternates for the bilingual pages.","parameters":[{"in":"header","name":"If-None-Match","description":"An `ETag` from a previous response. The server answers `304` with no body when the document has not changed since, which is how a scheduled re-fetch stays cheap.","required":false,"schema":{"type":"string"},"examples":{"etag":{"value":"\"9f2c1d...\"","summary":"A tag from a previous response"}}}],"summary":"XML sitemap of every indexable URL","tags":["Discovery"],"operationId":"getSitemap","responses":{"200":{"description":"The sitemap.","content":{"application/xml":{"schema":{"type":"string"}}},"headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"304":{"description":"The `If-None-Match` validator matched, so the document is unchanged and no body is sent.","headers":{"ETag":{"$ref":"#/components/headers/ETag"}}},"default":{"description":"Any status not listed above. The body is the same `Error` shape: branch on `code`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v2/health":{"get":{"description":"Returns `ok` when the application is running and its database answers.\nUsed by the deployment proxy; safe to poll.\n\nSend `Accept: application/json` for the machine-readable form, which\nnames the component that failed instead of leaving a caller to match on\na sentence. Anything else — including no `Accept` header at all — gets\nthe plain-text body the proxy healthcheck has always read.\n","parameters":[{"in":"header","name":"Accept","description":"Send `application/json` for the structured body. Anything else, including an absent header, gets `text/plain`.","required":false,"schema":{"default":"text/plain","type":"string","enum":["application/json","text/plain"]}}],"summary":"Liveness and database reachability","tags":["Operations"],"operationId":"getHealth","responses":{"200":{"description":"The service is up and its database answers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}},"text/plain":{"schema":{"const":"ok","type":"string"}}}},"503":{"description":"The application is up but its database is not reachable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}},"text/plain":{"schema":{"const":"db unreachable","type":"string"}}}},"default":{"description":"Any status not listed above. The body is the same `Error` shape: branch on `code`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"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."}}}}}}