{"openapi":"3.0.3","info":{"title":"Octocom API","description":"\nThe Octocom API provides programmatic access to your customer support data and configuration.\n\n## Overview\n\nThis API allows you to retrieve and manage data from your Octocom account. The primary resource is **Conversations**, which contain the full history of customer interactions including messages, AI classifications, and agent notes.\n\n## Authentication\n\nAll API requests require authentication using an API key. Include your API key in the `X-API-Key` header with every request:\n\n```\nX-API-Key: your_api_key_here\n```\n\nYou can generate and manage your API keys from the **API Settings** page in your Octocom dashboard.\n\n## Rate Limiting\n\nAuthenticated requests are limited to **500 requests per minute per organization** (a fixed 60-second window shared by every endpoint and every API key of the organization). Exceeding it returns `429 Too Many Requests`; wait for the window to roll over and retry. Unauthenticated endpoints (browser sessions) are limited per IP instead.\n\n## Change detection and concurrency\n\nConfiguration resources (workflows, articles, bot rules, actions, condition providers, event handlers, python modules, recurring jobs, ...) carry an `updatedAt` timestamp on both list and detail responses, so a nightly export can diff on it without fetching every item.\n\nUpdate endpoints for versioned resources accept an optional `If-Match` header carrying the `updatedAt` you last read. If the resource has changed since, the request is rejected with `412 Precondition Failed` and the current `updatedAt`, so a push can never silently overwrite an edit made after your last pull.\n\nWrites made through the API are recorded in the resource's version history with actor type `apiKey`.\n\n## Errors\n\nThe API uses standard HTTP status codes:\n- `200` - Success\n- `400` - Invalid request\n- `401` - Invalid or missing API key\n- `404` - Resource not found\n- `409` - Conflict (for example a duplicate name)\n- `412` - `If-Match` precondition failed\n- `429` - Rate limit exceeded\n- `500` - Server error\n","version":"1.0.0"},"components":{"securitySchemes":{"apiKey":{"type":"apiKey","name":"X-API-Key","in":"header","description":"API key for authentication. Get yours from the API Settings page in your dashboard."}},"schemas":{}},"paths":{"/rest/v1/organization-store/{namespace}/entries":{"post":{"summary":"Create an organization store entry","tags":["Organization Store"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string","minLength":1,"maxLength":200},"value":{}},"required":["key","value"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"path","name":"namespace","required":true}],"responses":{"201":{"description":"Entry created","content":{"application/json":{"schema":{"description":"Entry created","type":"object","properties":{"namespace":{"type":"string"},"key":{"type":"string"},"value":{},"revision":{"type":"integer","minimum":1},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["namespace","key","value","revision","createdAt","updatedAt"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string"},"currentRevision":{"type":"integer","minimum":1}},"required":["error"]}}}},"409":{"description":"Key already exists","content":{"application/json":{"schema":{"description":"Key already exists","type":"object","properties":{"error":{"type":"string"},"currentRevision":{"type":"integer","minimum":1}},"required":["error"]}}}},"429":{"description":"Storage quota reached","content":{"application/json":{"schema":{"description":"Storage quota reached","type":"object","properties":{"error":{"type":"string"},"currentRevision":{"type":"integer","minimum":1}},"required":["error"]}}}}}},"get":{"summary":"List organization store entries","tags":["Organization Store"],"description":"Returns one bounded page. Pass nextCursor unchanged to retrieve the next page.","parameters":[{"schema":{"type":"string","maxLength":200},"in":"query","name":"keyPrefix","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","maxLength":1000},"in":"query","name":"cursor","required":false},{"schema":{"type":"string","enum":["createdAt","updatedAt"],"default":"createdAt"},"in":"query","name":"sortBy","required":false},{"schema":{"type":"string","enum":["asc","desc"],"default":"desc"},"in":"query","name":"direction","required":false},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"path","name":"namespace","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/rest/v1/organization-store/{namespace}/entries/{key}":{"get":{"summary":"Get an organization store entry","tags":["Organization Store"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"path","name":"namespace","required":true},{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"path","name":"key","required":true}],"responses":{"200":{"description":"Entry found","content":{"application/json":{"schema":{"description":"Entry found","type":"object","properties":{"namespace":{"type":"string"},"key":{"type":"string"},"value":{},"revision":{"type":"integer","minimum":1},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["namespace","key","value","revision","createdAt","updatedAt"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string"},"currentRevision":{"type":"integer","minimum":1}},"required":["error"]}}}},"404":{"description":"Entry not found","content":{"application/json":{"schema":{"description":"Entry not found","type":"object","properties":{"error":{"type":"string"},"currentRevision":{"type":"integer","minimum":1}},"required":["error"]}}}}}},"put":{"summary":"Create or update an organization store entry","tags":["Organization Store"],"description":"Omit ifRevision for an unconditional upsert. Provide the revision returned by a previous read for atomic compare-and-set.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"value":{},"ifRevision":{"type":"integer","exclusiveMinimum":true,"minimum":0}},"required":["value"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"path","name":"namespace","required":true},{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"path","name":"key","required":true}],"responses":{"200":{"description":"Entry stored","content":{"application/json":{"schema":{"description":"Entry stored","type":"object","properties":{"namespace":{"type":"string"},"key":{"type":"string"},"value":{},"revision":{"type":"integer","minimum":1},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["namespace","key","value","revision","createdAt","updatedAt"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string"},"currentRevision":{"type":"integer","minimum":1}},"required":["error"]}}}},"409":{"description":"Revision conflict","content":{"application/json":{"schema":{"description":"Revision conflict","type":"object","properties":{"error":{"type":"string"},"currentRevision":{"type":"integer","minimum":1}},"required":["error"]}}}},"429":{"description":"Storage quota reached","content":{"application/json":{"schema":{"description":"Storage quota reached","type":"object","properties":{"error":{"type":"string"},"currentRevision":{"type":"integer","minimum":1}},"required":["error"]}}}}}},"delete":{"summary":"Delete an organization store entry","tags":["Organization Store"],"description":"Provide ifRevision to delete only if no other execution changed the entry after it was read.","parameters":[{"schema":{"type":"integer","exclusiveMinimum":true,"minimum":0},"in":"query","name":"ifRevision","required":false},{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"path","name":"namespace","required":true},{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"path","name":"key","required":true}],"responses":{"200":{"description":"Entry deleted","content":{"application/json":{"schema":{"description":"Entry deleted","type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string"},"currentRevision":{"type":"integer","minimum":1}},"required":["error"]}}}},"404":{"description":"Entry not found","content":{"application/json":{"schema":{"description":"Entry not found","type":"object","properties":{"error":{"type":"string"},"currentRevision":{"type":"integer","minimum":1}},"required":["error"]}}}},"409":{"description":"Revision conflict","content":{"application/json":{"schema":{"description":"Revision conflict","type":"object","properties":{"error":{"type":"string"},"currentRevision":{"type":"integer","minimum":1}},"required":["error"]}}}}}}},"/rest/v1/organization-store/{namespace}/search":{"post":{"summary":"Search organization store entries","tags":["Organization Store"],"description":"Matches entries whose JSON value contains the supplied filter. Arrays and nested objects use PostgreSQL JSON containment semantics.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"keyPrefix":{"type":"string","maxLength":200},"limit":{"type":"integer","minimum":1,"maximum":500,"default":100},"cursor":{"type":"string","maxLength":1000},"sortBy":{"type":"string","enum":["createdAt","updatedAt"],"default":"createdAt"},"direction":{"type":"string","enum":["asc","desc"],"default":"desc"},"filter":{}},"required":["filter"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":100},"in":"path","name":"namespace","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/rest/v1/bot-config":{"get":{"summary":"Get bot configuration","tags":["Bot Config"],"description":"The business's bot configuration: the custom system-prompt sections (task, safety, instruction following, function calling, tone, email/chat writing style, output syntax, language & grammar), business description, auto-resolve settings and the system-prompt Python section.\n\nText fields are returned as their **effective** value — the override if one is set, otherwise the system default. `customizedFields` lists which ones are actually overridden.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"object","properties":{"businessSlug":{"type":"string"},"businessName":{"type":"string"},"isFlexible":{"type":"boolean"},"agenticMode":{"type":"boolean"},"customizedFields":{"type":"array","items":{"type":"string"},"description":"Which of the text fields carry a custom override. All text fields are returned as their EFFECTIVE value (the override, or the system default when none is set)."},"customTaskInstructions":{"type":["string","null"]},"customSafetyInstructions":{"type":["string","null"]},"customInstructionFollowingInstructions":{"type":["string","null"]},"customFunctionCallingInstructions":{"type":["string","null"]},"customTone":{"type":["string","null"]},"customEmailWritingStyle":{"type":["string","null"]},"customChatWritingStyle":{"type":["string","null"]},"customOutputSyntax":{"type":["string","null"]},"customLanguageAndGrammar":{"type":["string","null"]},"businessDescription":{"type":["string","null"]},"resolutionPrompt":{"type":["string","null"]},"enableEmojis":{"type":"boolean"},"autoResolveEnabled":{"type":"boolean"},"maxFollowups":{"type":"number"},"handoffOnMaxFollowups":{"type":"boolean"},"systemPromptSectionCode":{"type":["string","null"],"description":"Business-owned code appended to the system prompt each reply (defines build_prompt_sections(context))."},"systemPromptSectionLanguage":{"type":"string","enum":["python","javascript"],"description":"Language of the prompt-section code: \"python\". A legacy \"javascript\" section no longer runs."}},"required":["businessSlug","customizedFields"]}},"required":["data"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"patch":{"summary":"Update bot configuration","tags":["Bot Config"],"description":"Patch the bot configuration. Omitted fields are unchanged. For the text fields, send a string to override the default, or `null` to reset that field to the system default. Returns the effective configuration after the update.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","description":"Business identifier (required)"},"customTaskInstructions":{"type":"string","nullable":true,"description":"A string overrides the default; null resets to the default"},"customSafetyInstructions":{"type":"string","nullable":true,"description":"A string overrides the default; null resets to the default"},"customInstructionFollowingInstructions":{"type":"string","nullable":true,"description":"A string overrides the default; null resets to the default"},"customFunctionCallingInstructions":{"type":"string","nullable":true,"description":"A string overrides the default; null resets to the default"},"customTone":{"type":"string","nullable":true,"description":"A string overrides the default; null resets to the default"},"customEmailWritingStyle":{"type":"string","nullable":true,"description":"A string overrides the default; null resets to the default"},"customChatWritingStyle":{"type":"string","nullable":true,"description":"A string overrides the default; null resets to the default"},"customOutputSyntax":{"type":"string","nullable":true,"description":"A string overrides the default; null resets to the default"},"customLanguageAndGrammar":{"type":"string","nullable":true,"description":"A string overrides the default; null resets to the default"},"businessDescription":{"type":"string","nullable":true,"description":"A string overrides the default; null resets to the default"},"resolutionPrompt":{"type":"string","nullable":true,"description":"A string overrides the default; null resets to the default"},"enableEmojis":{"type":"boolean"},"autoResolveEnabled":{"type":"boolean"},"maxFollowups":{"type":"integer","minimum":0},"handoffOnMaxFollowups":{"type":"boolean"},"systemPromptSectionCode":{"type":"string","nullable":true,"description":"Code defining build_prompt_sections(context); null clears it"},"systemPromptSectionLanguage":{"type":"string","enum":["python"],"description":"Language of the prompt-section code; only \"python\""}},"required":["businessSlug"],"additionalProperties":false}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"description":"Updated","type":"object","properties":{"data":{"type":"object","properties":{"businessSlug":{"type":"string"},"businessName":{"type":"string"},"isFlexible":{"type":"boolean"},"agenticMode":{"type":"boolean"},"customizedFields":{"type":"array","items":{"type":"string"},"description":"Which of the text fields carry a custom override. All text fields are returned as their EFFECTIVE value (the override, or the system default when none is set)."},"customTaskInstructions":{"type":["string","null"]},"customSafetyInstructions":{"type":["string","null"]},"customInstructionFollowingInstructions":{"type":["string","null"]},"customFunctionCallingInstructions":{"type":["string","null"]},"customTone":{"type":["string","null"]},"customEmailWritingStyle":{"type":["string","null"]},"customChatWritingStyle":{"type":["string","null"]},"customOutputSyntax":{"type":["string","null"]},"customLanguageAndGrammar":{"type":["string","null"]},"businessDescription":{"type":["string","null"]},"resolutionPrompt":{"type":["string","null"]},"enableEmojis":{"type":"boolean"},"autoResolveEnabled":{"type":"boolean"},"maxFollowups":{"type":"number"},"handoffOnMaxFollowups":{"type":"boolean"},"systemPromptSectionCode":{"type":["string","null"],"description":"Business-owned code appended to the system prompt each reply (defines build_prompt_sections(context))."},"systemPromptSectionLanguage":{"type":"string","enum":["python","javascript"],"description":"Language of the prompt-section code: \"python\". A legacy \"javascript\" section no longer runs."}},"required":["businessSlug","customizedFields"]}},"required":["data"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/actions":{"get":{"summary":"List custom actions","tags":["Custom Actions"],"description":"List the organization's custom actions (Python and API), without code. Use `GET /rest/v1/actions/{id}` for the full definition.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header. Actions belong to the organization (not a business); `allowedBusinessIds` limits which businesses may use one.","parameters":[{"schema":{"type":"string","enum":["true","false"],"default":"false"},"in":"query","name":"includeInactive","required":false,"description":"Include inactive entries (default false)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["python","api"]},"name":{"type":"string"},"description":{"type":"string"},"isActive":{"type":"boolean"},"allowedBusinessIds":{"type":["array","null"],"items":{"type":"string","format":"uuid"}},"tags":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","type","name","isActive","createdAt","updatedAt"]}}},"required":["data"]}}}}}},"post":{"summary":"Create a custom action","tags":["Custom Actions"],"description":"Create a Python action (`type: \"python\"`: code defining `execute_action(context)` plus the argument names the bot supplies) or an API action (`type: \"api\"`: an HTTP call template; `$variable` placeholders in path/headers/query/body are filled by the bot).\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header. Actions belong to the organization (not a business); `allowedBusinessIds` limits which businesses may use one.","requestBody":{"required":true,"content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[a-zA-Z_][a-zA-Z0-9_]*$"},"description":{"type":"string","minLength":1,"maxLength":2000},"customerFacingLabel":{"type":"string","maxLength":80,"nullable":true,"default":null},"code":{"type":"string","minLength":1},"language":{"type":"string","enum":["python"],"default":"python"},"arguments":{"type":"array","items":{"type":"string"}},"isActive":{"type":"boolean","default":true},"allowedBusinessIds":{"type":"array","items":{"type":"string","format":"uuid"},"nullable":true,"default":null},"tags":{"type":"array","items":{"type":"string","minLength":1,"maxLength":25},"maxItems":8,"default":[]},"type":{"type":"string","enum":["python"]}},"required":["name","description","code","arguments","type"],"additionalProperties":false},{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[a-zA-Z_][a-zA-Z0-9_]*$"},"description":{"type":"string","minLength":1,"maxLength":2000},"customerFacingLabel":{"type":"string","maxLength":80,"nullable":true,"default":null},"httpMethod":{"type":"string","enum":["GET","POST","PUT","PATCH","DELETE"]},"baseUrl":{"type":"string","format":"uri"},"path":{"type":"string","minLength":1},"headers":{"type":"object","additionalProperties":{"type":"string"},"default":{}},"queryParams":{"type":"object","additionalProperties":{"type":"string"},"default":{}},"bodySchema":{"type":"object","additionalProperties":{},"default":{}},"timeout":{"type":"number","minimum":1000,"maximum":60000,"default":30000},"isActive":{"type":"boolean","default":true},"allowedBusinessIds":{"type":"array","items":{"type":"string","format":"uuid"},"nullable":true,"default":null},"tags":{"type":"array","items":{"type":"string","minLength":1,"maxLength":25},"maxItems":8,"default":[]},"type":{"type":"string","enum":["api"]}},"required":["name","description","httpMethod","baseUrl","path","type"],"additionalProperties":false}]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"description":"Created","type":"object","properties":{"data":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["python"]},"name":{"type":"string"},"description":{"type":"string"},"customerFacingLabel":{"type":["string","null"]},"code":{"type":"string","description":"Source code"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new code. Legacy rows may read \"javascript\"; those no longer run and must be converted to Python."},"arguments":{"type":"array","items":{"type":"string"}},"isActive":{"type":"boolean"},"allowedBusinessIds":{"type":["array","null"],"items":{"type":"string","format":"uuid"},"description":"null = every business in the organization"},"tags":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","type","name","description","code","arguments"]},{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["api"]},"name":{"type":"string"},"description":{"type":"string"},"customerFacingLabel":{"type":["string","null"]},"httpMethod":{"type":"string"},"baseUrl":{"type":"string"},"path":{"type":"string"},"headers":{"type":"object","additionalProperties":{"type":"string"}},"queryParams":{"type":"object","additionalProperties":{"type":"string"}},"bodySchema":{"type":"object"},"timeout":{"type":"number"},"isActive":{"type":"boolean"},"allowedBusinessIds":{"type":["array","null"],"items":{"type":"string","format":"uuid"}},"tags":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","type","name","description","httpMethod","baseUrl"]}]}},"required":["data"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/actions/{id}":{"get":{"summary":"Get a custom action","tags":["Custom Actions"],"description":"Full definition of one action. Python actions include `code` and `arguments`; API actions include the endpoint configuration.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header. Actions belong to the organization (not a business); `allowedBusinessIds` limits which businesses may use one.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["python"]},"name":{"type":"string"},"description":{"type":"string"},"customerFacingLabel":{"type":["string","null"]},"code":{"type":"string","description":"Source code"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new code. Legacy rows may read \"javascript\"; those no longer run and must be converted to Python."},"arguments":{"type":"array","items":{"type":"string"}},"isActive":{"type":"boolean"},"allowedBusinessIds":{"type":["array","null"],"items":{"type":"string","format":"uuid"},"description":"null = every business in the organization"},"tags":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","type","name","description","code","arguments"]},{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["api"]},"name":{"type":"string"},"description":{"type":"string"},"customerFacingLabel":{"type":["string","null"]},"httpMethod":{"type":"string"},"baseUrl":{"type":"string"},"path":{"type":"string"},"headers":{"type":"object","additionalProperties":{"type":"string"}},"queryParams":{"type":"object","additionalProperties":{"type":"string"}},"bodySchema":{"type":"object"},"timeout":{"type":"number"},"isActive":{"type":"boolean"},"allowedBusinessIds":{"type":["array","null"],"items":{"type":"string","format":"uuid"}},"tags":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","type","name","description","httpMethod","baseUrl"]}]}},"required":["data"]}}}},"404":{"description":"Action not found","content":{"application/json":{"schema":{"description":"Action not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"patch":{"summary":"Update a custom action","tags":["Custom Actions"],"description":"Patch an action in place; omitted fields are unchanged and a new version entry is written. The action's type is fixed — Python fields are accepted for Python actions and endpoint fields for API actions; fields that don't apply to the action's type are ignored.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header. Actions belong to the organization (not a business); `allowedBusinessIds` limits which businesses may use one.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[a-zA-Z_][a-zA-Z0-9_]*$"},"description":{"type":"string","minLength":1,"maxLength":2000},"customerFacingLabel":{"type":"string","maxLength":80,"nullable":true,"default":null},"code":{"type":"string","minLength":1},"language":{"type":"string","enum":["python"],"default":"python"},"arguments":{"type":"array","items":{"type":"string"}},"isActive":{"type":"boolean","default":true},"allowedBusinessIds":{"type":"array","items":{"type":"string","format":"uuid"},"nullable":true,"default":null},"tags":{"type":"array","items":{"type":"string","minLength":1,"maxLength":25},"maxItems":8,"default":[]},"httpMethod":{"type":"string","enum":["GET","POST","PUT","PATCH","DELETE"]},"baseUrl":{"type":"string","format":"uri"},"path":{"type":"string","minLength":1},"headers":{"type":"object","additionalProperties":{"type":"string"},"default":{}},"queryParams":{"type":"object","additionalProperties":{"type":"string"},"default":{}},"bodySchema":{"type":"object","additionalProperties":{},"default":{}},"timeout":{"type":"number","minimum":1000,"maximum":60000,"default":30000}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"description":"Updated","type":"object","properties":{"data":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["python"]},"name":{"type":"string"},"description":{"type":"string"},"customerFacingLabel":{"type":["string","null"]},"code":{"type":"string","description":"Source code"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new code. Legacy rows may read \"javascript\"; those no longer run and must be converted to Python."},"arguments":{"type":"array","items":{"type":"string"}},"isActive":{"type":"boolean"},"allowedBusinessIds":{"type":["array","null"],"items":{"type":"string","format":"uuid"},"description":"null = every business in the organization"},"tags":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","type","name","description","code","arguments"]},{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["api"]},"name":{"type":"string"},"description":{"type":"string"},"customerFacingLabel":{"type":["string","null"]},"httpMethod":{"type":"string"},"baseUrl":{"type":"string"},"path":{"type":"string"},"headers":{"type":"object","additionalProperties":{"type":"string"}},"queryParams":{"type":"object","additionalProperties":{"type":"string"}},"bodySchema":{"type":"object"},"timeout":{"type":"number"},"isActive":{"type":"boolean"},"allowedBusinessIds":{"type":["array","null"],"items":{"type":"string","format":"uuid"}},"tags":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","type","name","description","httpMethod","baseUrl"]}]}},"required":["data"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Action not found","content":{"application/json":{"schema":{"description":"Action not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/condition-providers":{"get":{"summary":"List condition providers","tags":["Condition Providers"],"description":"Condition providers are Python scripts that evaluate named boolean conditions, used by workflows to pick a variant. The list omits `code`; use the detail endpoint for it.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"arguments":{"type":"array","items":{"type":"string"}},"conditions":{"type":"array","items":{"type":"string"}},"code":{"type":"string","description":"Source code (detail only)"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new code. Legacy rows may read \"javascript\"; those no longer run and must be converted to Python."},"tags":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","name","arguments","conditions","createdAt","updatedAt"]}}},"required":["data"]}}}}}},"post":{"summary":"Create a condition provider","tags":["Condition Providers"],"description":"Code must define `evaluate_conditions(context)` returning `{ \"conditions\": { <name>: <bool>, ... } }` for every name in `conditions`.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"arguments":{"type":"array","items":{"type":"string"}},"conditions":{"type":"array","items":{"type":"string"},"minItems":1},"code":{"type":"string","minLength":1},"language":{"type":"string","enum":["python"],"default":"python"},"tags":{"type":"array","items":{"type":"string","minLength":1,"maxLength":25},"maxItems":8,"default":[]}},"required":["name","arguments","conditions","code"],"additionalProperties":false}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"description":"Created","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"arguments":{"type":"array","items":{"type":"string"}},"conditions":{"type":"array","items":{"type":"string"}},"code":{"type":"string","description":"Source code (detail only)"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new code. Legacy rows may read \"javascript\"; those no longer run and must be converted to Python."},"tags":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","name","arguments","conditions","createdAt","updatedAt"]}},"required":["data"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/condition-providers/{id}":{"get":{"summary":"Get a condition provider","tags":["Condition Providers"],"description":"One condition provider including its Python code.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"arguments":{"type":"array","items":{"type":"string"}},"conditions":{"type":"array","items":{"type":"string"}},"code":{"type":"string","description":"Source code (detail only)"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new code. Legacy rows may read \"javascript\"; those no longer run and must be converted to Python."},"tags":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","name","arguments","conditions","createdAt","updatedAt"]}},"required":["data"]}}}},"404":{"description":"Condition provider not found","content":{"application/json":{"schema":{"description":"Condition provider not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"patch":{"summary":"Update a condition provider","tags":["Condition Providers"],"description":"Patch in place; omitted fields are unchanged and a new version entry is written.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"arguments":{"type":"array","items":{"type":"string"}},"conditions":{"type":"array","items":{"type":"string"},"minItems":1},"code":{"type":"string","minLength":1},"language":{"type":"string","enum":["python"],"default":"python"},"tags":{"type":"array","items":{"type":"string","minLength":1,"maxLength":25},"maxItems":8,"default":[]}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"description":"Updated","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"arguments":{"type":"array","items":{"type":"string"}},"conditions":{"type":"array","items":{"type":"string"}},"code":{"type":"string","description":"Source code (detail only)"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new code. Legacy rows may read \"javascript\"; those no longer run and must be converted to Python."},"tags":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","name","arguments","conditions","createdAt","updatedAt"]}},"required":["data"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Condition provider not found","content":{"application/json":{"schema":{"description":"Condition provider not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/event-handlers":{"get":{"summary":"List event handlers","tags":["Event Handlers"],"description":"Event handlers are Python scripts that run when an event fires (conversation created/closed/handed off, messages, ratings, tags, campaign events...). The list omits `code`.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string","enum":["true","false"],"default":"false"},"in":"query","name":"includeInactive","required":false,"description":"Include inactive entries (default false)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"eventType":{"type":"string"},"code":{"type":"string","description":"Source code (detail only)"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new code. Legacy rows may read \"javascript\"; those no longer run and must be converted to Python."},"isActive":{"type":"boolean"},"allowedBusinessIds":{"type":["array","null"],"items":{"type":"string","format":"uuid"}},"tags":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","name","eventType","isActive","createdAt","updatedAt"]}}},"required":["data"]}}}}}},"post":{"summary":"Create an event handler","tags":["Event Handlers"],"description":"Code must define `handle_event(context)`. `allowedBusinessIds` null = runs for every business.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"eventType":{"type":"string","enum":["conversation_created","conversation_closed","conversation_handed_off","csat_sent","conversation_rated","bot_message_sent","customer_message_sent","agent_message_sent","campaign_recipient_unreachable","campaign_call_completed","campaign_pre_call","rate_limit_hit","bot_qa_result","tag_modified","comment_handled","automation_execution_failed"]},"code":{"type":"string","minLength":1},"language":{"type":"string","enum":["python"],"default":"python"},"isActive":{"type":"boolean","default":true},"allowedBusinessIds":{"type":"array","items":{"type":"string","format":"uuid"},"nullable":true,"default":null},"tags":{"type":"array","items":{"type":"string","minLength":1,"maxLength":25},"maxItems":8,"default":[]}},"required":["name","eventType","code"],"additionalProperties":false}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"description":"Created","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"eventType":{"type":"string"},"code":{"type":"string","description":"Source code (detail only)"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new code. Legacy rows may read \"javascript\"; those no longer run and must be converted to Python."},"isActive":{"type":"boolean"},"allowedBusinessIds":{"type":["array","null"],"items":{"type":"string","format":"uuid"}},"tags":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","name","eventType","isActive","createdAt","updatedAt"]}},"required":["data"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"409":{"description":"Conflicting pre-call handler","content":{"application/json":{"schema":{"description":"Conflicting pre-call handler","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/event-handlers/{id}":{"get":{"summary":"Get an event handler","tags":["Event Handlers"],"description":"One event handler including its Python code.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"eventType":{"type":"string"},"code":{"type":"string","description":"Source code (detail only)"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new code. Legacy rows may read \"javascript\"; those no longer run and must be converted to Python."},"isActive":{"type":"boolean"},"allowedBusinessIds":{"type":["array","null"],"items":{"type":"string","format":"uuid"}},"tags":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","name","eventType","isActive","createdAt","updatedAt"]}},"required":["data"]}}}},"404":{"description":"Event handler not found","content":{"application/json":{"schema":{"description":"Event handler not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"patch":{"summary":"Update an event handler","tags":["Event Handlers"],"description":"Patch in place; omitted fields are unchanged.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"eventType":{"type":"string","enum":["conversation_created","conversation_closed","conversation_handed_off","csat_sent","conversation_rated","bot_message_sent","customer_message_sent","agent_message_sent","campaign_recipient_unreachable","campaign_call_completed","campaign_pre_call","rate_limit_hit","bot_qa_result","tag_modified","comment_handled","automation_execution_failed"]},"code":{"type":"string","minLength":1},"language":{"type":"string","enum":["python"],"default":"python"},"isActive":{"type":"boolean","default":true},"allowedBusinessIds":{"type":"array","items":{"type":"string","format":"uuid"},"nullable":true,"default":null},"tags":{"type":"array","items":{"type":"string","minLength":1,"maxLength":25},"maxItems":8,"default":[]}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"description":"Updated","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"eventType":{"type":"string"},"code":{"type":"string","description":"Source code (detail only)"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new code. Legacy rows may read \"javascript\"; those no longer run and must be converted to Python."},"isActive":{"type":"boolean"},"allowedBusinessIds":{"type":["array","null"],"items":{"type":"string","format":"uuid"}},"tags":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","name","eventType","isActive","createdAt","updatedAt"]}},"required":["data"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Event handler not found","content":{"application/json":{"schema":{"description":"Event handler not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"409":{"description":"Conflicting pre-call handler","content":{"application/json":{"schema":{"description":"Conflicting pre-call handler","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/python-modules":{"get":{"summary":"List python modules","tags":["Python Modules"],"description":"Shared Python modules injected into every Python execution in the organization (actions, condition providers, event handlers, recurring jobs). The list omits `code`.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string","enum":["true","false"],"default":"true"},"in":"query","name":"includeInactive","required":false,"description":"Include inactive entries (default true)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"code":{"type":"string","description":"Source code (detail only)"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new code. Legacy rows may read \"javascript\"; those no longer run and must be converted to Python."},"isActive":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","name","isActive","createdAt","updatedAt"]}}},"required":["data"]}}}}}},"post":{"summary":"Create a python module","tags":["Python Modules"],"description":"A syntax error in an active module breaks every Python execution in the organization — test before activating.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[a-zA-Z_][a-zA-Z0-9_]*$"},"code":{"type":"string","minLength":1},"language":{"type":"string","enum":["python"],"default":"python"},"isActive":{"type":"boolean","default":true}},"required":["name","code"],"additionalProperties":false}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"description":"Created","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"code":{"type":"string","description":"Source code (detail only)"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new code. Legacy rows may read \"javascript\"; those no longer run and must be converted to Python."},"isActive":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","name","isActive","createdAt","updatedAt"]}},"required":["data"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/python-modules/{id}":{"get":{"summary":"Get a python module","tags":["Python Modules"],"description":"One module including its code.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"code":{"type":"string","description":"Source code (detail only)"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new code. Legacy rows may read \"javascript\"; those no longer run and must be converted to Python."},"isActive":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","name","isActive","createdAt","updatedAt"]}},"required":["data"]}}}},"404":{"description":"Python module not found","content":{"application/json":{"schema":{"description":"Python module not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"patch":{"summary":"Update a python module","tags":["Python Modules"],"description":"Patch in place; omitted fields are unchanged. Takes effect on the next Python execution.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[a-zA-Z_][a-zA-Z0-9_]*$"},"code":{"type":"string","minLength":1},"language":{"type":"string","enum":["python"],"default":"python"},"isActive":{"type":"boolean","default":true}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"description":"Updated","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"code":{"type":"string","description":"Source code (detail only)"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new code. Legacy rows may read \"javascript\"; those no longer run and must be converted to Python."},"isActive":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","name","isActive","createdAt","updatedAt"]}},"required":["data"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Python module not found","content":{"application/json":{"schema":{"description":"Python module not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/recurring-jobs":{"get":{"summary":"List recurring jobs","tags":["Recurring Jobs"],"description":"Python jobs that run on a cron schedule in a business's context. The list omits `code`.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string","enum":["true","false"],"default":"true"},"in":"query","name":"includeInactive","required":false,"description":"Include inactive entries (default true)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string"},"businessId":{"type":"string","format":"uuid"},"businessSlug":{"type":["string","null"]},"code":{"type":"string","description":"Source code (detail only)"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new code. Legacy rows may read \"javascript\"; those no longer run and must be converted to Python."},"schedule":{"type":"string","description":"5-field cron expression"},"timezone":{"type":"string"},"isActive":{"type":"boolean"},"nextRunAt":{"type":"string","format":"date-time"},"lastRunAt":{"type":["string","null"],"format":"date-time"},"consecutiveFailures":{"type":"number"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","name","businessId","schedule","timezone","isActive","createdAt","updatedAt"]}}},"required":["data"]}}}}}},"post":{"summary":"Create a recurring job","tags":["Recurring Jobs"],"description":"Code must define `run_job(context)`. `schedule` is a 5-field cron expression evaluated in `timezone`.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":2000,"default":""},"code":{"type":"string","minLength":1},"language":{"type":"string","enum":["python"],"default":"python"},"schedule":{"type":"string","minLength":1,"maxLength":200},"timezone":{"type":"string","minLength":1,"maxLength":100,"default":"UTC"},"isActive":{"type":"boolean","default":true},"businessSlug":{"type":"string","description":"The business the job runs in the context of"}},"required":["name","code","schedule","businessSlug"],"additionalProperties":false}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"description":"Created","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string"},"businessId":{"type":"string","format":"uuid"},"businessSlug":{"type":["string","null"]},"code":{"type":"string","description":"Source code (detail only)"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new code. Legacy rows may read \"javascript\"; those no longer run and must be converted to Python."},"schedule":{"type":"string","description":"5-field cron expression"},"timezone":{"type":"string"},"isActive":{"type":"boolean"},"nextRunAt":{"type":"string","format":"date-time"},"lastRunAt":{"type":["string","null"],"format":"date-time"},"consecutiveFailures":{"type":"number"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","name","businessId","schedule","timezone","isActive","createdAt","updatedAt"]}},"required":["data"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/recurring-jobs/{id}":{"get":{"summary":"Get a recurring job","tags":["Recurring Jobs"],"description":"One recurring job including its Python code.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string"},"businessId":{"type":"string","format":"uuid"},"businessSlug":{"type":["string","null"]},"code":{"type":"string","description":"Source code (detail only)"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new code. Legacy rows may read \"javascript\"; those no longer run and must be converted to Python."},"schedule":{"type":"string","description":"5-field cron expression"},"timezone":{"type":"string"},"isActive":{"type":"boolean"},"nextRunAt":{"type":"string","format":"date-time"},"lastRunAt":{"type":["string","null"],"format":"date-time"},"consecutiveFailures":{"type":"number"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","name","businessId","schedule","timezone","isActive","createdAt","updatedAt"]}},"required":["data"]}}}},"404":{"description":"Recurring job not found","content":{"application/json":{"schema":{"description":"Recurring job not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"patch":{"summary":"Update a recurring job","tags":["Recurring Jobs"],"description":"Patch in place; omitted fields are unchanged and a new version entry is written. Any update re-arms the next run and resets the consecutive-failure counter.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":2000,"default":""},"code":{"type":"string","minLength":1},"language":{"type":"string","enum":["python"],"default":"python"},"schedule":{"type":"string","minLength":1,"maxLength":200},"timezone":{"type":"string","minLength":1,"maxLength":100,"default":"UTC"},"isActive":{"type":"boolean","default":true},"businessSlug":{"type":"string","description":"The business the job runs in the context of"}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"description":"Updated","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string"},"businessId":{"type":"string","format":"uuid"},"businessSlug":{"type":["string","null"]},"code":{"type":"string","description":"Source code (detail only)"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new code. Legacy rows may read \"javascript\"; those no longer run and must be converted to Python."},"schedule":{"type":"string","description":"5-field cron expression"},"timezone":{"type":"string"},"isActive":{"type":"boolean"},"nextRunAt":{"type":"string","format":"date-time"},"lastRunAt":{"type":["string","null"],"format":"date-time"},"consecutiveFailures":{"type":"number"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["id","name","businessId","schedule","timezone","isActive","createdAt","updatedAt"]}},"required":["data"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Recurring job not found","content":{"application/json":{"schema":{"description":"Recurring job not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/product-sync/parser":{"get":{"summary":"Get the product-sync parser","tags":["Product Sync"],"description":"The business's AI-product-sync parser: Python defining `fetch_next_chunk(state)` and `map_one(raw, context)`, plus notes and when it last ran successfully.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"object","properties":{"businessSlug":{"type":"string"},"code":{"type":"string","description":"Python source"},"model":{"type":["string","null"],"description":"LLM that generated the parser, if any"},"notes":{"type":["string","null"]},"generatedAt":{"type":["string","null"],"format":"date-time"},"lastSuccessfulRunAt":{"type":["string","null"],"format":"date-time","description":"When a sync using this parser last completed successfully"},"provider":{"type":["string","null"],"description":"The business's current product provider"},"autoSyncEnabled":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["businessSlug","code","createdAt","updatedAt"]}},"required":["data"]}}}},"404":{"description":"Parser not found","content":{"application/json":{"schema":{"description":"Parser not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"put":{"summary":"Set the product-sync parser","tags":["Product Sync"],"description":"Replace the business's parser. If the business is not yet on the `aiGenerated` product provider this also switches it over, enables auto-sync and schedules the first sync; otherwise only the parser changes and the schedule is untouched.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","description":"Business identifier (required)"},"code":{"type":"string","minLength":1,"maxLength":200000,"description":"Python source defining fetch_next_chunk and map_one"},"notes":{"type":"string","nullable":true},"model":{"type":"string","nullable":true,"description":"Name of the LLM that generated the parser, if any"}},"required":["businessSlug","code"],"additionalProperties":false}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"description":"Saved","type":"object","properties":{"data":{"type":"object","properties":{"businessSlug":{"type":"string"},"code":{"type":"string","description":"Python source"},"model":{"type":["string","null"],"description":"LLM that generated the parser, if any"},"notes":{"type":["string","null"]},"generatedAt":{"type":["string","null"],"format":"date-time"},"lastSuccessfulRunAt":{"type":["string","null"],"format":"date-time","description":"When a sync using this parser last completed successfully"},"provider":{"type":["string","null"],"description":"The business's current product provider"},"autoSyncEnabled":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp; use for drift detection."}},"required":["businessSlug","code","createdAt","updatedAt"]}},"required":["data"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/web-chat-configs":{"get":{"summary":"List web-chat configurations","tags":["Web Chat"],"description":"Every web-chat widget configuration for a business, including deployments (hostname / path / query rules), greetings, first-message suggestions, attention grabbers, appearance, live-chat and behavior settings.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object"}}},"required":["data"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/web-chat-configs/{id}":{"get":{"summary":"Get a web-chat configuration","tags":["Web Chat"],"description":"One web-chat configuration by id.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"404":{"description":"Web-chat configuration not found","content":{"application/json":{"schema":{"description":"Web-chat configuration not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"patch":{"summary":"Update a web-chat configuration","tags":["Web Chat"],"description":"Patch a web-chat configuration. Only provided fields change; array fields (`deployments`, `greetings`, `firstMessageSuggestions`, `autoOpenPaths`...) replace the whole array, so read the config first before editing them. Images and interaction/virtual-try-on links are managed in the dashboard.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1},"topBarTitle":{"type":"string","nullable":true},"topBarBackgroundColor":{"type":"string","nullable":true},"topBarTextColor":{"type":"string","nullable":true},"topBarIconColor":{"type":"string","nullable":true},"enableTopBarBorder":{"type":"boolean"},"botMessageBackgroundColor":{"type":"string","nullable":true},"botMessageTextColor":{"type":"string","nullable":true},"customerMessageBackgroundColor":{"type":"string","nullable":true},"customerMessageTextColor":{"type":"string","nullable":true},"cardBackgroundColor":{"type":"string","nullable":true},"cardTextColor":{"type":"string","nullable":true},"cardAccentColor":{"type":"string","nullable":true},"cardAccentTextColor":{"type":"string","nullable":true},"disclaimerText":{"type":"string","nullable":true},"disclaimerButtonBackgroundColor":{"type":"string","nullable":true},"disclaimerButtonTextColor":{"type":"string","nullable":true},"enableDisclaimer":{"type":"boolean"},"termsAndConditionsYesButtonText":{"type":"string"},"termsAndConditionsNoButtonText":{"type":"string"},"positionBottom":{"type":"string","nullable":true},"positionRight":{"type":"string","nullable":true},"positionLeft":{"type":"string","nullable":true},"zIndex":{"type":"number"},"mobilePositionBottom":{"type":"string","nullable":true},"mobilePositionRight":{"type":"string","nullable":true},"mobilePositionLeft":{"type":"string","nullable":true},"mobileHeightPercentage":{"type":"number","nullable":true},"mobileWidthPercentage":{"type":"number","nullable":true},"launcherType":{"anyOf":[{"type":"string","enum":["icon"]},{"type":"string","enum":["icon_text"]},{"type":"string","enum":["image"]}]},"launcherText":{"type":"string","nullable":true},"launcherBackgroundColor":{"type":"string","nullable":true},"launcherTextColor":{"type":"string","nullable":true},"launcherIconColor":{"type":"string","nullable":true},"launcherSize":{"type":"string","nullable":true},"launcherIconSize":{"type":"string","nullable":true},"launcherMobileType":{"anyOf":[{"type":"string","enum":["copy_desktop"]},{"type":"string","enum":["icon"]},{"type":"string","enum":["icon_text"]},{"type":"string","enum":["image"]}]},"launcherMobileText":{"type":"string","nullable":true},"launcherMobileBackgroundColor":{"type":"string","nullable":true},"launcherMobileTextColor":{"type":"string","nullable":true},"launcherMobileIconColor":{"type":"string","nullable":true},"launcherMobileSize":{"type":"string","nullable":true},"launcherMobileIconSize":{"type":"string","nullable":true},"attentionGrabberName":{"type":"string"},"enableAttentionGrabberLabel":{"type":"boolean"},"enableAttentionGrabbers":{"type":"boolean"},"enableAttentionGrabberMobile":{"type":"boolean"},"enableAttentionGrabberIntroMessage":{"type":"boolean"},"altAttentionGrabberStyle":{"type":"boolean"},"attentionGrabberMessage":{"type":"string","nullable":true},"attentionGrabberSuggestionBubbleBackgroundColor":{"type":"string","nullable":true},"attentionGrabberSuggestionBubbleTextColor":{"type":"string","nullable":true},"attentionGrabberSuggestionBubbleHoverBackgroundColor":{"type":"string","nullable":true},"attentionGrabberSuggestionBubbleHoverTextColor":{"type":"string","nullable":true},"attentionGrabberSuggestions":{"type":"array","items":{"type":"object","properties":{"displayText":{"type":"string"},"message":{"type":"string"}},"required":["displayText","message"],"additionalProperties":false}},"attentionGrabberDelay":{"type":"number","nullable":true},"enableAiGeneratedAttentionGrabbers":{"type":"boolean"},"aiGeneratedAttentionGrabberPrompt":{"type":"string","nullable":true},"aiGeneratedAttentionGrabberPathRegex":{"type":"string","nullable":true},"enableManualAttentionGrabbers":{"type":"boolean"},"manualAttentionGrabberPathRegex":{"type":"string","nullable":true},"requestEmailBeforeChat":{"type":"boolean"},"requireTermsAndConditionsAgreement":{"type":"boolean"},"firstMessageSuggestions":{"type":"array","items":{"type":"object","properties":{"displayText":{"type":"string","minLength":1},"messageText":{"type":"string","minLength":1},"pathRegex":{"type":"string","nullable":true}},"required":["displayText","messageText"],"additionalProperties":false}},"greetings":{"type":"array","items":{"type":"object","properties":{"pathRegex":{"type":"string","nullable":true},"content":{"type":"string","minLength":1},"priority":{"type":"number","minimum":0}},"required":["content","priority"],"additionalProperties":false}},"deployments":{"type":"array","items":{"type":"object","properties":{"hostname":{"type":"string","minLength":1,"nullable":true},"pathRegex":{"type":"string","nullable":true},"queryConditions":{"type":"array","items":{"type":"object","properties":{"parameter":{"type":"string","minLength":1},"operator":{"type":"string","enum":["equals","matches","exists"]},"value":{"type":"string","nullable":true}},"required":["parameter","operator","value"],"additionalProperties":false},"nullable":true,"description":"Optional parsed query-parameter rules. Every condition must match. Use null or [] for domain/path-only matching."},"enableDeployment":{"type":"boolean"}},"required":["hostname","pathRegex","enableDeployment"],"additionalProperties":false}},"enableLiveChat":{"type":"boolean"},"liveChatResponseTime":{"type":"number","nullable":true},"liveChatTimeout":{"type":"number","nullable":true},"liveChatUseAgentInitialsAsIcon":{"type":"boolean"},"openChatAfter":{"type":"number","nullable":true},"autoOpenPaths":{"type":"array","items":{"type":"string"},"nullable":true},"supportHours":{"type":"object","properties":{"Monday":{"type":"array","items":{"type":"object","properties":{"open":{"type":"string","pattern":"^([01]\\d|2[0-3]):([0-5]\\d)(:([0-5]\\d))?$"},"close":{"type":"string","pattern":"^([01]\\d|2[0-3]):([0-5]\\d)(:([0-5]\\d))?$"}},"required":["open","close"],"additionalProperties":false}},"Tuesday":{"type":"array","items":{"type":"object","properties":{"open":{"type":"string","pattern":"^([01]\\d|2[0-3]):([0-5]\\d)(:([0-5]\\d))?$"},"close":{"type":"string","pattern":"^([01]\\d|2[0-3]):([0-5]\\d)(:([0-5]\\d))?$"}},"required":["open","close"],"additionalProperties":false}},"Wednesday":{"type":"array","items":{"type":"object","properties":{"open":{"type":"string","pattern":"^([01]\\d|2[0-3]):([0-5]\\d)(:([0-5]\\d))?$"},"close":{"type":"string","pattern":"^([01]\\d|2[0-3]):([0-5]\\d)(:([0-5]\\d))?$"}},"required":["open","close"],"additionalProperties":false}},"Thursday":{"type":"array","items":{"type":"object","properties":{"open":{"type":"string","pattern":"^([01]\\d|2[0-3]):([0-5]\\d)(:([0-5]\\d))?$"},"close":{"type":"string","pattern":"^([01]\\d|2[0-3]):([0-5]\\d)(:([0-5]\\d))?$"}},"required":["open","close"],"additionalProperties":false}},"Friday":{"type":"array","items":{"type":"object","properties":{"open":{"type":"string","pattern":"^([01]\\d|2[0-3]):([0-5]\\d)(:([0-5]\\d))?$"},"close":{"type":"string","pattern":"^([01]\\d|2[0-3]):([0-5]\\d)(:([0-5]\\d))?$"}},"required":["open","close"],"additionalProperties":false}},"Saturday":{"type":"array","items":{"type":"object","properties":{"open":{"type":"string","pattern":"^([01]\\d|2[0-3]):([0-5]\\d)(:([0-5]\\d))?$"},"close":{"type":"string","pattern":"^([01]\\d|2[0-3]):([0-5]\\d)(:([0-5]\\d))?$"}},"required":["open","close"],"additionalProperties":false}},"Sunday":{"type":"array","items":{"type":"object","properties":{"open":{"type":"string","pattern":"^([01]\\d|2[0-3]):([0-5]\\d)(:([0-5]\\d))?$"},"close":{"type":"string","pattern":"^([01]\\d|2[0-3]):([0-5]\\d)(:([0-5]\\d))?$"}},"required":["open","close"],"additionalProperties":false}},"timezone":{"type":"string"}},"required":["timezone"],"additionalProperties":false,"nullable":true},"exitIntentEnabled":{"type":"boolean"},"exitIntentMessage":{"type":"string","nullable":true},"messageInputPlaceholder":{"type":"string"},"languageOverride":{"type":"string","enum":["en","es","lt","no","ru"],"nullable":true},"logLevel":{"type":"string","enum":["debug","info","warn","error"],"nullable":true},"hideInitially":{"type":"boolean"},"hideAttentionGrabberIcon":{"type":"boolean"},"secondMessageSuggestions":{"type":"array","items":{"type":"object","properties":{"firstMessagesAllowed":{"type":"array","items":{"type":"string"}},"messageText":{"type":"string"}},"required":["messageText"],"additionalProperties":false},"nullable":true},"removeCompetitorWidgets":{"type":"boolean"},"enableResponseStreaming":{"type":"boolean"},"enableWidgetV2":{"type":"boolean"},"enableCustomJs":{"type":"boolean"},"customJs":{"type":"string","nullable":true}},"additionalProperties":false,"description":"Settings to change. Omitted fields stay unchanged; arrays replace the complete existing array. Read read_octocom_docs(['web-chat/configuration']) for the field reference and examples."}}},"description":"Settings to change. Omitted fields stay unchanged; arrays replace the complete existing array. Read read_octocom_docs(['web-chat/configuration']) for the field reference and examples."},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"description":"Updated","type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Web-chat configuration not found","content":{"application/json":{"schema":{"description":"Web-chat configuration not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/interaction-buttons":{"get":{"summary":"Get the interaction buttons configuration","tags":["Web Chat"],"description":"The business's AI suggestion buttons (\"interaction buttons\") configuration — one per business. `data` is null if the feature was never configured.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":["object","null"]}},"required":["data"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"patch":{"summary":"Create or update the interaction buttons configuration","tags":["Web Chat"],"description":"Partial update — only provided fields change. On first write `selectorDesktop` is required and the rest falls back to defaults (feature disabled, mirror desktop to mobile, AI-generated suggestions). The business must already have a web-chat configuration.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Desktop on/off switch for the feature. Default on first write: false."},"mobileEnabled":{"type":"boolean","description":"Mobile on/off switch. Default on first write: false. Independent of `enabled`."},"mirrorDesktopConfigToMobile":{"type":"boolean","description":"When true, all desktop fields are reused on mobile and the mobile-specific fields are ignored. Default on first write: true."},"selectorDesktop":{"type":"string","minLength":1,"description":"CSS selector for the desktop injection point. Comma-separated list allowed — the first match wins. Required on first write."},"selectorMobile":{"type":"string","nullable":true,"description":"CSS selector for the mobile injection point. Null/empty when mirroring desktop or when mobile is disabled."},"pathRegexDesktop":{"type":"string","nullable":true,"description":"Regex matched against the URL pathname; only inject on matching pages. Null/empty = all pages."},"pathRegexMobile":{"type":"string","nullable":true,"description":"Mobile-only pathname regex. Ignored when mirroring desktop."},"insertionPositionDesktop":{"type":"string","enum":["before","after","inside","replace"],"description":"How to inject relative to the selected element. Default on first write: 'before'."},"insertionPositionMobile":{"type":"string","enum":["before","after","inside","replace"],"nullable":true,"description":"Mobile-only insertion position. Ignored when mirroring desktop."},"generateButtonsFromPageContext":{"type":"boolean","description":"When true, button labels are generated per-page by an LLM using `suggestionPrompts`. When false, the labels in `staticButtonTextValues*` are used verbatim. Default on first write: true."},"staticButtonTextValuesDesktop":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Static desktop button labels (used when `generateButtonsFromPageContext` is false). Empty array / null clears."},"staticButtonTextValuesMobile":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Static mobile button labels. Ignored when mirroring desktop."},"suggestionPrompts":{"type":"array","items":{"type":"object","properties":{"urlPattern":{"type":"string","description":"URL pathname regex this prompt applies to. Empty string acts as a catch-all that matches every page."},"prompt":{"type":"string","minLength":1,"description":"System prompt sent to the suggestion-generator LLM."}},"required":["urlPattern","prompt"],"additionalProperties":false},"minItems":1,"description":"Ordered list of LLM prompts used by the AI suggestion generator. The first entry whose `urlPattern` matches the page wins; an entry with an empty `urlPattern` is a catch-all. Must contain at least one entry."},"includeAskSomethingElse":{"type":"boolean","description":"Whether to render an extra \"Ask something else\" button after the suggestions. Default on first write: true."},"showSparkleIcon":{"type":"boolean","description":"Prepend a sparkle (✨) glyph to AI-generated desktop button labels. Default on first write: true."},"showSparkleIconMobile":{"type":"boolean","description":"Mobile-specific sparkle toggle. Ignored when mirroring desktop."},"htmlIsCustomDesktop":{"type":"boolean","description":"When true, `htmlTemplateDesktop` is treated as merchant-authored markup and rendered as-is (with `data-octocom-*` markers). When false, the bundled visual-builder template is used. Default on first write: false."},"htmlIsCustomMobile":{"type":"boolean","description":"Mobile-specific custom-HTML toggle. Ignored when mirroring desktop."},"htmlTemplateDesktop":{"type":"string","nullable":true,"description":"Desktop HTML template. Must include an element with `data-octocom-button-template` (cloned per suggestion). Null/empty falls back to the bundled visual-builder template."},"htmlTemplateMobile":{"type":"string","nullable":true,"description":"Mobile HTML template. Ignored when mirroring desktop."},"minimumPriceUsd":{"type":"number","nullable":true,"description":"Optional minimum product price (USD) below which suggestions aren't generated. Null = no minimum."},"businessSlug":{"type":"string","description":"Business identifier (required)"}},"required":["businessSlug"],"additionalProperties":false}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"description":"Saved","type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/product-url-matching":{"get":{"summary":"Get the product URL matching config","tags":["Product Sync"],"description":"`pathBlacklistRegexes`: regexes applied in order (as string replaces) to strip decorations such as category segments from a storefront URL before it is matched against products' canonical URLs. Powers suggestion buttons, virtual try-on and recently-viewed lookups.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"object","properties":{"businessSlug":{"type":"string"},"pathBlacklistRegexes":{"type":"array","items":{"type":"string"}}}}},"required":["data"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"put":{"summary":"Set the product URL matching config","tags":["Product Sync"],"description":"Replaces `pathBlacklistRegexes` wholesale (pass `[]` to clear). Patterns are JS RegExp source strings without surrounding slashes; every pattern must compile.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","description":"Business identifier (required)"},"pathBlacklistRegexes":{"type":"array","items":{"type":"string"}}},"required":["businessSlug","pathBlacklistRegexes"],"additionalProperties":false}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"description":"Saved","type":"object"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/search/ui-config":{"get":{"summary":"Get the storefront search UI config","tags":["Storefront Search"],"description":"The business's storefront-search configuration: master toggle, per-injection-point HTML / selector / position (search bar, mobile search bar, suggestions, filters, results), results mode, facets, stock and confidence policies, redirect mode, allowed origins, and the catalogue-preparation state. `data` is null when the business has never configured search.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":["object","null"]}},"required":["data"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"patch":{"summary":"Update the storefront search UI config","tags":["Storefront Search"],"description":"Partial patch: omitted fields are untouched, an explicit `null` clears a field back to its default. Turning `enabled` on requires the catalogue preparation to be `ready` (run it from the dashboard first). `imageIndexEnabled`, the publishable key and the preparation fields are read-only here.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","description":"Business identifier (required)"},"enabled":{"type":"boolean"},"voiceEnabled":{"type":"boolean"},"voiceLanguages":{"type":"string","maxLength":60,"nullable":true},"searchBarHtml":{"type":"string","nullable":true},"searchSuggestionsHtml":{"type":"string","nullable":true},"searchFiltersHtml":{"type":"string","nullable":true},"searchResultsHtml":{"type":"string","nullable":true},"searchBarSelector":{"type":"string","nullable":true},"searchSuggestionsSelector":{"type":"string","nullable":true},"searchFiltersSelector":{"type":"string","nullable":true},"searchResultsSelector":{"type":"string","nullable":true},"searchBarPosition":{"type":"string","enum":["before","after","inside","replace"],"nullable":true},"searchSuggestionsPosition":{"type":"string","enum":["before","after","inside","replace"],"nullable":true},"searchFiltersPosition":{"type":"string","enum":["before","after","inside","replace"],"nullable":true},"searchResultsPosition":{"type":"string","enum":["before","after","inside","replace"],"nullable":true},"searchBarMobileHtml":{"type":"string","nullable":true},"searchBarMobileSelector":{"type":"string","nullable":true},"searchBarMobilePosition":{"type":"string","enum":["before","after","inside","replace"],"nullable":true},"resultsMode":{"type":"string","enum":["inline","page","overlay"]},"redirectMode":{"type":"string","enum":["navigate","banner"]},"resultsPageUrl":{"type":"string","maxLength":500,"nullable":true},"resultsPageQueryParam":{"type":"string","maxLength":40,"nullable":true},"searchOverlayHtml":{"type":"string","nullable":true},"searchFilterFacets":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","minLength":1,"maxLength":80},"label":{"type":"string","minLength":1,"maxLength":60},"limit":{"type":"integer","minimum":1,"maximum":50}},"required":["field","label"],"additionalProperties":false},"maxItems":12,"nullable":true},"hideOutOfStock":{"type":"boolean"},"hideLowConfidenceResults":{"type":"boolean"},"searchAllowedOrigins":{"type":"array","items":{"type":"string","minLength":1,"maxLength":253},"maxItems":100}},"required":["businessSlug"],"additionalProperties":false}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"description":"Saved","type":"object","properties":{"data":{"type":"object"}},"required":["data"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/search/merchandising-rules":{"get":{"summary":"List merchandising rules","tags":["Storefront Search"],"description":"Search merchandising rules: WHEN each fires (trigger), WHICH products it applies to (target) and WHAT it does (effect). Rules outside their date window are returned with `active: false`.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"enabled":{"type":"boolean"},"active":{"type":"boolean","description":"enabled AND inside its date window right now"},"priority":{"type":"number"},"trigger":{"type":"object"},"target":{"type":"object"},"effect":{"type":"object"},"startsAt":{"type":["string","null"],"format":"date-time"},"endsAt":{"type":["string","null"],"format":"date-time"},"timezone":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","enabled","trigger","target","effect"]}}},"required":["data"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"post":{"summary":"Create a merchandising rule","tags":["Storefront Search"],"description":"Trigger: `{type:'always'}` | `{type:'query', mode:'exact'|'contains', terms:[...]}` | `{type:'category', values:[...], source:'inferred'}`. Target: `{type:'brand', values:[...]}` | `{type:'category', values:[...]}` | `{type:'products', ids:[...]}`. Effect: `{type:'boost', factor:0.3}` (signed, -1..1). Only boost effects are applied today; pin/bury/hide are rejected. Takes effect on the next search.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","description":"Business identifier (required)"},"name":{"type":"string","minLength":1},"trigger":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["always"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["query"]},"mode":{"type":"string","enum":["exact","contains"],"default":"exact"},"terms":{"type":"array","items":{"type":"string","minLength":1},"minItems":1}},"required":["type","terms"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["category"]},"values":{"type":"array","items":{"type":"string","minLength":1},"minItems":1},"source":{"type":"string","enum":["filter","inferred"],"default":"filter"}},"required":["type","values"],"additionalProperties":false}]},"target":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["brand"]},"values":{"type":"array","items":{"type":"string","minLength":1},"minItems":1}},"required":["type","values"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["category"]},"values":{"type":"array","items":{"type":"string","minLength":1},"minItems":1}},"required":["type","values"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["products"]},"ids":{"type":"array","items":{"type":"string","minLength":1},"minItems":1}},"required":["type","ids"],"additionalProperties":false}]},"effect":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["boost"]},"factor":{"type":"number","minimum":-1,"maximum":1}},"required":["type","factor"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["pin"]},"position":{"type":"integer","minimum":1}},"required":["type","position"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["bury"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["hide"]}},"required":["type"],"additionalProperties":false}]},"enabled":{"type":"boolean","description":"Default true"},"priority":{"type":"integer","description":"Default 0"},"startsAt":{"type":"string","nullable":true,"description":"ISO timestamp. With an offset it is absolute; without one it is wall-clock time in `timezone`."},"endsAt":{"type":"string","nullable":true,"description":"ISO timestamp, same rules as startsAt"},"timezone":{"type":"string","nullable":true,"description":"IANA zone the window is written in, e.g. 'Europe/Athens'. Null = UTC."}},"required":["businessSlug","name","trigger","target","effect"],"additionalProperties":false}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"description":"Created","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"enabled":{"type":"boolean"},"active":{"type":"boolean","description":"enabled AND inside its date window right now"},"priority":{"type":"number"},"trigger":{"type":"object"},"target":{"type":"object"},"effect":{"type":"object"},"startsAt":{"type":["string","null"],"format":"date-time"},"endsAt":{"type":["string","null"],"format":"date-time"},"timezone":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","enabled","trigger","target","effect"]}},"required":["data"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/search/merchandising-rules/{id}":{"patch":{"summary":"Update a merchandising rule","tags":["Storefront Search"],"description":"Every field is optional; `trigger` / `target` / `effect` are replaced wholesale when given. Use `enabled: false` to pause a rule without losing it.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","description":"Business identifier (required)"},"name":{"type":"string","minLength":1},"trigger":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["always"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["query"]},"mode":{"type":"string","enum":["exact","contains"],"default":"exact"},"terms":{"type":"array","items":{"type":"string","minLength":1},"minItems":1}},"required":["type","terms"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["category"]},"values":{"type":"array","items":{"type":"string","minLength":1},"minItems":1},"source":{"type":"string","enum":["filter","inferred"],"default":"filter"}},"required":["type","values"],"additionalProperties":false}]},"target":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["brand"]},"values":{"type":"array","items":{"type":"string","minLength":1},"minItems":1}},"required":["type","values"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["category"]},"values":{"type":"array","items":{"type":"string","minLength":1},"minItems":1}},"required":["type","values"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["products"]},"ids":{"type":"array","items":{"type":"string","minLength":1},"minItems":1}},"required":["type","ids"],"additionalProperties":false}]},"effect":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["boost"]},"factor":{"type":"number","minimum":-1,"maximum":1}},"required":["type","factor"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["pin"]},"position":{"type":"integer","minimum":1}},"required":["type","position"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["bury"]}},"required":["type"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","enum":["hide"]}},"required":["type"],"additionalProperties":false}]},"enabled":{"type":"boolean"},"priority":{"type":"integer"},"startsAt":{"type":"string","nullable":true,"description":"ISO timestamp. With an offset it is absolute; without one it is wall-clock time in `timezone`."},"endsAt":{"type":"string","nullable":true,"description":"ISO timestamp, same rules as startsAt"},"timezone":{"type":"string","nullable":true,"description":"IANA zone the window is written in, e.g. 'Europe/Athens'. Null = UTC."}},"required":["businessSlug"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"description":"Updated","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"enabled":{"type":"boolean"},"active":{"type":"boolean","description":"enabled AND inside its date window right now"},"priority":{"type":"number"},"trigger":{"type":"object"},"target":{"type":"object"},"effect":{"type":"object"},"startsAt":{"type":["string","null"],"format":"date-time"},"endsAt":{"type":["string","null"],"format":"date-time"},"timezone":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","enabled","trigger","target","effect"]}},"required":["data"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Rule not found","content":{"application/json":{"schema":{"description":"Rule not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"delete":{"summary":"Delete a merchandising rule","tags":["Storefront Search"],"description":"Deletes the rule outright. To pause one instead, PATCH it with `enabled: false`.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"description":"Deleted","type":"object"}}}},"404":{"description":"Rule not found","content":{"application/json":{"schema":{"description":"Rule not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/search/synonyms":{"get":{"summary":"List search synonyms","tags":["Storefront Search"],"description":"Merchant-authored synonyms: the shopper words each mined catalogue value also answers to. `inLexicon` says whether the current lexicon still carries that value — an entry whose value is no longer mined is inert.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"synonyms":{"type":"array","items":{"type":"string"}},"inLexicon":{"type":"boolean"}},"required":["value","synonyms","inLexicon"]}}},"required":["data"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"put":{"summary":"Set the synonyms of a catalogue value","tags":["Storefront Search"],"description":"REPLACES the synonym list of one mined catalogue value (matched case- and accent-insensitively). Pass an empty array to clear it. Synonyms shorter than 3 characters are dropped. Effective on the next search.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","description":"Business identifier (required)"},"value":{"type":"string","minLength":1,"description":"The catalogue value, as mined into the lexicon"},"synonyms":{"type":"array","items":{"type":"string"},"description":"The complete list; empty clears"}},"required":["businessSlug","value","synonyms"],"additionalProperties":false}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"description":"Saved","type":"object"}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/search/redirects":{"get":{"summary":"List search redirects","tags":["Storefront Search"],"description":"Keyword-to-URL search redirects, plus the business's `redirectMode` ('navigate' short-circuits the search; 'banner' runs it and offers the link above the results — set via the search UI config).\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"redirectMode":{"type":"string","enum":["navigate","banner"]},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"trigger":{"type":"string"},"matchMode":{"type":"string","enum":["exact","contains"]},"url":{"type":"string"},"enabled":{"type":"boolean"},"startsAt":{"type":["string","null"],"format":"date-time"},"endsAt":{"type":["string","null"],"format":"date-time"},"timezone":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","trigger","matchMode","url","enabled"]}}},"required":["redirectMode","data"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"put":{"summary":"Create or replace a search redirect","tags":["Storefront Search"],"description":"Identity is (business, folded trigger, matchMode), so re-saving 'Black Friday' updates the 'black friday' row. `matchMode` 'exact' (default) fires only when the whole query matches; 'contains' fires when the trigger appears as a whole word. `startsAt`/`endsAt` make it a scheduled campaign. Effective on the next search.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","description":"Business identifier (required)"},"trigger":{"type":"string","minLength":1,"description":"The shopper query"},"url":{"type":"string","minLength":1,"description":"Where to send the shopper"},"matchMode":{"type":"string","enum":["exact","contains"],"description":"Default 'exact'"},"enabled":{"type":"boolean","description":"Default true"},"startsAt":{"type":"string","nullable":true,"description":"ISO timestamp. With an offset it is absolute; without one it is wall-clock time in `timezone`."},"endsAt":{"type":"string","nullable":true,"description":"ISO timestamp, same rules as startsAt"},"timezone":{"type":"string","nullable":true,"description":"IANA zone the window is written in, e.g. 'Europe/Athens'. Null = UTC."}},"required":["businessSlug","trigger","url"],"additionalProperties":false}}}},"responses":{"200":{"description":"Saved","content":{"application/json":{"schema":{"description":"Saved","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"trigger":{"type":"string"},"matchMode":{"type":"string","enum":["exact","contains"]},"url":{"type":"string"},"enabled":{"type":"boolean"},"startsAt":{"type":["string","null"],"format":"date-time"},"endsAt":{"type":["string","null"],"format":"date-time"},"timezone":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","trigger","matchMode","url","enabled"]}},"required":["data"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"delete":{"summary":"Delete a search redirect","tags":["Storefront Search"],"description":"Identified the same way it was saved: business + trigger + matchMode (trigger matched on its folded form).\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"},{"schema":{"type":"string","minLength":1},"in":"query","name":"trigger","required":true},{"schema":{"type":"string","enum":["exact","contains"],"default":"exact"},"in":"query","name":"matchMode","required":false}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"description":"Deleted","type":"object"}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/comment-workflows":{"get":{"summary":"List comment workflows","tags":["Comment Workflows"],"description":"Comment workflows control how the bot handles social media comments (Instagram, Facebook): when to follow, the instructions, and the action taken (reply, DM, hide, delete, hand off).\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"isLive","required":false,"description":"Filter by live status"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"whenToFollow":{"type":"string"},"botInstructions":{"type":"string"},"dmBotInstructions":{"type":["string","null"]},"connectedFacebookPageIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Connected Facebook pages this workflow is limited to; empty applies to every connected page"},"action":{"type":"string","enum":["respond_comment","delete","hide","respond_dm","respond_comment_and_dm","hide_and_dm","handoff"]},"isLive":{"type":"boolean"},"priority":{"type":"number"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","name","whenToFollow","botInstructions","action","isLive"]}}},"required":["data"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/comment-workflows/{id}":{"get":{"summary":"Get a comment workflow","tags":["Comment Workflows"],"description":"One comment workflow by id.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"whenToFollow":{"type":"string"},"botInstructions":{"type":"string"},"dmBotInstructions":{"type":["string","null"]},"connectedFacebookPageIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Connected Facebook pages this workflow is limited to; empty applies to every connected page"},"action":{"type":"string","enum":["respond_comment","delete","hide","respond_dm","respond_comment_and_dm","hide_and_dm","handoff"]},"isLive":{"type":"boolean"},"priority":{"type":"number"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","name","whenToFollow","botInstructions","action","isLive"]}},"required":["data"]}}}},"404":{"description":"Comment workflow not found","content":{"application/json":{"schema":{"description":"Comment workflow not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/tags":{"get":{"summary":"List tags","tags":["Tags"],"description":"All conversation tags in the organization. To add or remove a tag on a conversation use the Conversations tag endpoints.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"isFavorite":{"type":"boolean"}},"required":["id","title","isFavorite"]}}},"required":["data"]}}}}}}},"/rest/v1/views":{"get":{"summary":"List inbox views","tags":["Views"],"description":"Saved inbox views (conversation filters shown in the dashboard sidebar): organization-wide, team (`teamId`) and personal (`userId`) views.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"teamId":{"type":["string","null"],"format":"uuid","description":"Set when the view is restricted to a team"},"userId":{"type":["string","null"],"format":"uuid","description":"Set when the view is personal to a user"},"icon":{"type":["string","null"]},"pinnedByDefault":{"type":"boolean"},"defaultPinPosition":{"type":["number","null"]},"filters":{"type":"object","description":"The saved conversation filters defining the view"}},"required":["id","title","filters"]}}},"required":["data"]}}}}}}},"/rest/v1/documents":{"get":{"summary":"List knowledge base documents","tags":["Articles"],"description":"Uploaded documents (PDF, DOCX, ...) the bot draws on alongside articles, with a download URL for each.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"url":{"type":"string"},"mimeType":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","name","url","createdAt"]}}},"required":["data"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/secrets":{"get":{"summary":"List secret names","tags":["Secrets"],"description":"Names (and descriptions) of the organization's stored secrets, so exported Python can be checked against the `get_secret(\"NAME\")` calls it makes. Values are never returned — they are readable only from inside the Python sandbox.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"hint":{"type":"string","description":"Non-reversible fragment for recognition, never the value"},"lastUsedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}},"required":["name","description","hint"]}}},"required":["data"]}}}}}}},"/rest/v1/custom-http-endpoints":{"get":{"summary":"List custom HTTP endpoints","tags":["Custom HTTP Endpoints"],"description":"**Authentication:** Requires a valid API key in the `X-API-Key` header.\n\nManages endpoint configuration. Invoking the generated URL has no built-in authentication; its code handles access checks.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"query","name":"businessId","required":false}],"responses":{"200":{"description":"Endpoints (code omitted; use get for source)","content":{"application/json":{"schema":{"description":"Endpoints (code omitted; use get for source)","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"businessId":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string"},"code":{"type":"string"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new endpoints. Legacy endpoints may read \"javascript\"; those no longer run and must be converted to Python."},"methods":{"type":"array","items":{"type":"string","enum":["GET","POST"]}},"isActive":{"type":"boolean"},"url":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}}}}},"post":{"summary":"Create a custom HTTP endpoint","tags":["Custom HTTP Endpoints"],"description":"**Authentication:** Requires a valid API key in the `X-API-Key` header.\n\nManages endpoint configuration. Invoking the generated URL has no built-in authentication; its code handles access checks.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessId":{"type":"string","format":"uuid"},"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":2000,"default":""},"code":{"type":"string","minLength":1,"maxLength":100000,"description":"Python defining handle(context), returning JSON or http_response(body, status=..., headers=...)."},"language":{"type":"string","enum":["python"],"default":"python"},"methods":{"type":"array","items":{"type":"string","enum":["GET","POST"]},"minItems":1,"maxItems":2,"default":["POST"]},"isActive":{"type":"boolean","default":true}},"required":["businessId","name","code"],"additionalProperties":false}}}},"responses":{"201":{"description":"Endpoint configuration","content":{"application/json":{"schema":{"description":"Endpoint configuration","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"businessId":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string"},"code":{"type":"string"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new endpoints. Legacy endpoints may read \"javascript\"; those no longer run and must be converted to Python."},"methods":{"type":"array","items":{"type":"string","enum":["GET","POST"]}},"isActive":{"type":"boolean"},"url":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}}}}},"/rest/v1/custom-http-endpoints/{id}":{"get":{"summary":"Get a custom HTTP endpoint","tags":["Custom HTTP Endpoints"],"description":"**Authentication:** Requires a valid API key in the `X-API-Key` header.\n\nManages endpoint configuration. Invoking the generated URL has no built-in authentication; its code handles access checks.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Endpoint configuration","content":{"application/json":{"schema":{"description":"Endpoint configuration","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"businessId":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string"},"code":{"type":"string"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new endpoints. Legacy endpoints may read \"javascript\"; those no longer run and must be converted to Python."},"methods":{"type":"array","items":{"type":"string","enum":["GET","POST"]}},"isActive":{"type":"boolean"},"url":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}}}},"patch":{"summary":"Update a custom HTTP endpoint","tags":["Custom HTTP Endpoints"],"description":"**Authentication:** Requires a valid API key in the `X-API-Key` header.\n\nManages endpoint configuration. Invoking the generated URL has no built-in authentication; its code handles access checks.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessId":{"type":"string","format":"uuid"},"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":2000,"default":""},"code":{"type":"string","minLength":1,"maxLength":100000,"description":"Python defining handle(context), returning JSON or http_response(body, status=..., headers=...)."},"language":{"type":"string","enum":["python"],"default":"python"},"methods":{"type":"array","items":{"type":"string","enum":["GET","POST"]},"minItems":1,"maxItems":2,"default":["POST"]},"isActive":{"type":"boolean","default":true}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Endpoint configuration","content":{"application/json":{"schema":{"description":"Endpoint configuration","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"businessId":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string"},"code":{"type":"string"},"language":{"type":"string","enum":["python","javascript"],"description":"Always \"python\" for new endpoints. Legacy endpoints may read \"javascript\"; those no longer run and must be converted to Python."},"methods":{"type":"array","items":{"type":"string","enum":["GET","POST"]}},"isActive":{"type":"boolean"},"url":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}}}},"delete":{"summary":"Delete a custom HTTP endpoint","tags":["Custom HTTP Endpoints"],"description":"**Authentication:** Requires a valid API key in the `X-API-Key` header.\n\nManages endpoint configuration. Invoking the generated URL has no built-in authentication; its code handles access checks.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/rest/v1/conversations/{id}/snooze":{"post":{"summary":"Snooze a conversation","tags":["Conversations"],"description":"Snooze an open conversation until a future ISO 8601 timestamp with a timezone. Replaces an existing snooze deadline. Repeating the same request does not create another snooze period. Closed and imported conversations cannot be snoozed.\n\nRead status and snoozedUntil on conversation details or list results; filter the list with status=snoozed. Adding an internal note does not change snooze state.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"until":{"type":"string","format":"date-time","description":"Future wake-up timestamp, e.g. 2027-01-15T09:00:00Z"}},"required":["until"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true,"description":"Conversation UUID or public ID"}],"responses":{"200":{"description":"Snooze state updated (also succeeds when already in the requested state)","content":{"application/json":{"schema":{"description":"Snooze state updated (also succeeds when already in the requested state)","type":"object","properties":{"success":{"type":"boolean"},"snoozedUntil":{"type":["string","null"],"format":"date-time"}},"required":["success","snoozedUntil"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Conversation not found or not accessible","content":{"application/json":{"schema":{"description":"Conversation not found or not accessible","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"409":{"description":"Conversation is closed or imported","content":{"application/json":{"schema":{"description":"Conversation is closed or imported","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/conversations/{id}/unsnooze":{"post":{"summary":"Unsnooze a conversation","tags":["Conversations"],"description":"Clear a conversation's snooze and bring it back to agents' attention. Already-unsnoozed conversations succeed without generating another event. Does not reopen closed conversations. Imported conversations cannot be changed. No request body is required.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true,"description":"Conversation UUID or public ID"}],"responses":{"200":{"description":"Snooze state updated (also succeeds when already in the requested state)","content":{"application/json":{"schema":{"description":"Snooze state updated (also succeeds when already in the requested state)","type":"object","properties":{"success":{"type":"boolean"},"snoozedUntil":{"type":["string","null"],"format":"date-time"}},"required":["success","snoozedUntil"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Conversation not found or not accessible","content":{"application/json":{"schema":{"description":"Conversation not found or not accessible","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"409":{"description":"Conversation is closed or imported","content":{"application/json":{"schema":{"description":"Conversation is closed or imported","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/conversations/{id}/replies":{"post":{"summary":"Send an agent reply in an existing conversation","tags":["Conversations"],"description":"Send through the conversation's current channel using the inbox agent-message pipeline. Requires an existing handoff; returns 409 for bot-owned conversations. Does not remove handoff or trigger a bot reply. Closed conversations reopen and snoozed conversations unsnooze, as in the inbox. Imported and phone-call conversations are unsupported. API-channel conversations require allowAgentReplies and deliver via webhook.\n\nagentUserId must identify an active user in your organization (discover IDs with GET /rest/v1/users). The user's name is recorded on the message; email From names follow the email configuration. Email replies reuse the existing thread.\n\nfiles accepts public or signed {url, name} attachments with the same limits as inbound messages. close defaults to false; true closes after the channel accepts or queues delivery, not after confirmed customer receipt.\n\nidempotencyKey is scoped to your organization. Reuse it with identical reply data to retrieve the previous result without resending. Different data returns 409. Incomplete or uncertain sends return 409 and require review. A retry can finish a failed close; completed retries do not close a subsequently reopened conversation.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":5000},"agentUserId":{"type":"string","format":"uuid"},"idempotencyKey":{"type":"string","minLength":1,"maxLength":200},"files":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","format":"uri","maxLength":8192},"name":{"type":"string","minLength":1,"maxLength":255}},"required":["url","name"],"additionalProperties":false},"maxItems":10,"description":"Files to download and store as native attachments: {url, name}. Direct public or signed HTTP(S) URLs only; no auth headers or redirects. Up to 10 files/request, 20 MiB/file, 50 MiB total."},"close":{"type":"boolean","default":false}},"required":["text","agentUserId","idempotencyKey"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true,"description":"Conversation UUID or public ID"}],"responses":{"200":{"description":"Reply accepted or previously sent","content":{"application/json":{"schema":{"description":"Reply accepted or previously sent","type":"object","properties":{"success":{"type":"boolean"},"conversationId":{"type":"string"},"messageId":{"type":"string"},"duplicate":{"type":"boolean"}},"required":["success","conversationId","messageId","duplicate"]}}}},"400":{"description":"Invalid reply or attachments","content":{"application/json":{"schema":{"description":"Invalid reply or attachments","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Invalid API key","content":{"application/json":{"schema":{"description":"Invalid API key","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Conversation or sender not accessible","content":{"application/json":{"schema":{"description":"Conversation or sender not accessible","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"409":{"description":"Conversation state or idempotency conflict","content":{"application/json":{"schema":{"description":"Conversation state or idempotency conflict","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"429":{"description":"API rate limit exceeded","content":{"application/json":{"schema":{"description":"API rate limit exceeded","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"500":{"description":"Reply or close failed","content":{"application/json":{"schema":{"description":"Reply or close failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/conversations":{"get":{"summary":"List conversations","tags":["Conversations"],"description":"Retrieve a paginated list of conversations for your organization.\n\nReturns up to 200 conversations per page (default 50), sorted by creation date ascending by default.\nUse sortBy=updatedAt and updatedSince/updatedBefore for change tracking. Indexed candidate selection can lag by a few seconds; details are loaded from live data.\nUse the date range parameters to filter conversations by when they were created.\nUse status, handedOff, spam, assigned, and assigneeId parameters to filter by conversation state and ownership.\nUse customerEmail to find a customer's conversation history directly.\nUse businessSlug to limit the lookup to one business.\nUse metadataKey and metadataValue together to filter by a specific metadata key-value pair.\nUse tag to return only conversations carrying a given tag.\n\nImported conversations (historical archive records created via `POST /rest/v1/conversations/import`) are **excluded by default**. Use `imported=yes` to list only those, or `imported=all` for both.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string","format":"date-time"},"in":"query","name":"startDate","required":false,"description":"Filter conversations created on or after this date (ISO 8601 format, e.g., 2024-01-15T00:00:00Z)"},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"endDate","required":false,"description":"Filter conversations created on or before this date (ISO 8601 format, e.g., 2024-01-15T23:59:59Z)"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"in":"query","name":"limit","required":false},{"schema":{"type":"string","enum":["createdAt","updatedAt"],"default":"createdAt"},"in":"query","name":"sortBy","required":false},{"schema":{"type":"string","enum":["asc","desc"],"default":"asc"},"in":"query","name":"sortOrder","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"updatedSince","required":false,"description":"Inclusive minimum change tracking timestamp"},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"updatedBefore","required":false,"description":"Inclusive maximum change tracking timestamp"},{"schema":{"type":"integer","exclusiveMinimum":true,"minimum":0,"maximum":45035996273704.95,"default":1},"in":"query","name":"page","required":false,"description":"Page number for pagination (starts at 1)"},{"schema":{"type":"string","enum":["open","closed","snoozed"]},"in":"query","name":"status","required":false,"description":"Filter by conversation status (open = not closed and not snoozed, closed = has been closed, snoozed = currently snoozed)"},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"handedOff","required":false,"description":"Filter by handed-off state (true = only handed-off conversations, false = only bot-handled conversations)"},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"spam","required":false,"description":"Filter by spam classification (true = only spam conversations, false = exclude spam conversations). This uses the dashboard spam flag, not a tag named 'spam'."},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"assigned","required":false,"description":"Filter by assignment state (true = conversations with an assignee, false = unassigned conversations)."},{"schema":{"type":"string","format":"uuid"},"in":"query","name":"assigneeId","required":false,"description":"Filter by the assigned user's UUID."},{"schema":{"type":"string","maxLength":200},"in":"query","name":"metadataKey","required":false,"description":"Filter by metadata key. Must be used together with metadataValue. Returns only conversations that have this exact key-value metadata pair."},{"schema":{"type":"string","maxLength":50000},"in":"query","name":"metadataValue","required":false,"description":"Filter by metadata value. Must be used together with metadataKey. Returns only conversations that have this exact key-value metadata pair."},{"schema":{"type":"string","maxLength":100},"in":"query","name":"tag","required":false,"description":"Filter by tag. Returns only conversations carrying a tag with this exact title. Use this instead of paging every open conversation to find the ones you tagged."},{"schema":{"type":"string","format":"email"},"in":"query","name":"customerEmail","required":false,"description":"Filter by the associated customer's exact email address (case-insensitive)."},{"schema":{"type":"string","maxLength":200},"in":"query","name":"businessSlug","required":false,"description":"Filter by business slug."},{"schema":{"type":"string","enum":["yes","no","all"],"default":"no"},"in":"query","name":"imported","required":false,"description":"Filter by imported state. Imported conversations are historical archive records created via POST /rest/v1/conversations/import and are excluded by default. Use 'yes' for only imported, 'all' for both."}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"updatedAt":{"type":"string","format":"date-time","description":"Change tracking timestamp, not last customer activity"},"id":{"type":"string","format":"uuid","description":"Unique identifier","example":"550e8400-e29b-41d4-a716-446655440000"},"publicId":{"type":"string","description":"Public-facing ID for external references","example":"CONV-2024-001234"},"createdAt":{"type":"string","format":"date-time","description":"Creation timestamp","example":"2024-01-15T10:30:00Z"},"closedAt":{"type":["string","null"],"format":"date-time","description":"When closed (null if open)"},"status":{"type":"string","enum":["open","closed","snoozed"],"description":"Current conversation status"},"snoozedUntil":{"type":["string","null"],"format":"date-time","description":"Scheduled wake-up time, or null when not snoozed. Use status to filter the conversation list."},"handedOffAt":{"type":["string","null"],"format":"date-time","description":"When handed to human agent (null if bot-only)"},"subject":{"type":"string","description":"Subject or title"},"lastMessageCreatedAt":{"type":"string","format":"date-time","description":"Most recent message timestamp"},"sentiment":{"type":["string","null"],"description":"AI-analyzed sentiment (e.g., 'Strongly Positive', 'Neutral')"},"topics":{"type":["array","null"],"items":{"type":"string"},"description":"AI-classified topics"},"type":{"type":"string","enum":["customer","external"],"description":"'customer' for direct, 'external' for third-party"},"isOutbound":{"type":"boolean","description":"Whether initiated by the business"},"isImported":{"type":"boolean","description":"Whether this is a historical archive record imported from another system"},"businessSlug":{"type":"string","description":"Business identifier"},"initialChannel":{"type":["string","null"],"enum":["web","email","whatsapp","messenger","instagram","telegram","sms","phone-call","google-business-messages","google-reviews","trustpilot-reviews","facebook-reviews","facebookComment","facebookMention","instagramMention","instagramComment","amazon","ebay","aircall","brymec","echo","test-review","zoom-transcript","api",null],"description":"Channel where conversation started"},"latestChannel":{"type":["string","null"],"enum":["web","email","whatsapp","messenger","instagram","telegram","sms","phone-call","google-business-messages","google-reviews","trustpilot-reviews","facebook-reviews","facebookComment","facebookMention","instagramMention","instagramComment","amazon","ebay","aircall","brymec","echo","test-review","zoom-transcript","api",null],"description":"Channel of most recent message"},"customer":{"type":["object","null"],"properties":{"id":{"type":"string","format":"uuid"},"email":{"type":["string","null"],"format":"email"},"phone":{"type":["string","null"]},"name":{"type":["string","null"]}},"required":["id","email","phone","name"],"description":"Customer associated with the conversation"},"rating":{"type":["object","null"],"properties":{"score":{"type":"integer","minimum":1,"maximum":5},"comment":{"type":["string","null"]}},"required":["score","comment"],"description":"Submitted CSAT rating, or null when none exists"},"tags":{"type":"array","items":{"type":"string"},"description":"User-applied tags"}},"required":["id","publicId","createdAt","status","snoozedUntil","subject","lastMessageCreatedAt","type","isOutbound","isImported","businessSlug","customer","rating","tags"]},"description":"List of conversations"},"pagination":{"type":"object","properties":{"page":{"type":"number","description":"Current page number"},"limit":{"type":"number","description":"Maximum items per page"},"hasMore":{"type":"boolean","description":"Whether more pages exist"}},"required":["page","limit","hasMore"]}},"required":["data","pagination"]}}}},"401":{"description":"Authentication failed - invalid or missing API key","content":{"application/json":{"schema":{"description":"Authentication failed - invalid or missing API key","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"post":{"summary":"Create a conversation with its opening messages","tags":["Conversations"],"description":"Create a conversation and attach the messages it starts with. Use this for the first customer message in a thread; use `POST /rest/v1/conversations/{id}/messages` for every reply after that.\n\n**Returns immediately.** We store the messages and answer with the conversation id. The bot's reply is not on this response — it arrives on your webhook. When `allowAgentReplies` is enabled, later replies from human agents arrive through the webhook too.\n\n**Initial handoff state.** Set `handedOff` to `true` to create the conversation already handed off to a human, without an automatic bot reply. It defaults to `false`, preserving normal bot handling. This is independent of `allowAgentReplies`; enable both if agents should reply from the dashboard.\n\n**Human agent replies.** Set `allowAgentReplies` to `true` when agents should be able to reply to this conversation from the Octocom dashboard. It defaults to `false`. Agent replies produce the same `agent_message_sent` webhook event as other agent messages; your integration is responsible for delivering them to the customer.\n\n**Seeding context.** `messages` may include messages you already sent the customer from your own systems, marked `\"sender\": \"bot\"`. They are stored as context and never re-sent anywhere: only the final message triggers a bot turn, unless `handedOff` is `true`. This matters when the customer is replying to a transactional message — \"when will it arrive?\" is unanswerable without the order confirmation it responds to. Optional, but it noticeably improves answers, and it gives agents the full thread if the conversation is handed off.\n\nThe last entry in `messages` must be from the customer.\n\n**Idempotency.** Supply `externalId` — your own id for the message you are pushing. Replaying the same one returns the ids from the first call and creates nothing, so a retry after a timeout is safe.\n\n**Rate limits:** conversation creation is capped per organization per hour; contact us if your volume needs a higher ceiling. The shared REST limit of 500 requests/minute also applies.\n\n**Attachments:** Optional `files` arrays contain `{url, name}` entries. Text may be omitted for file-only messages. URLs must be directly fetchable without auth headers (signed URLs work); redirects are not followed. Maximum 10 files per request, 20 MiB per file, 50 MiB total. Files are downloaded and stored before messages are created.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","minLength":1,"description":"Business identifier (use /rest/v1/businesses to list)"},"customer":{"type":"object","properties":{"name":{"type":"string","description":"Customer name"},"phone":{"type":"string","description":"Customer phone number. The identity for messaging channels."},"email":{"type":"string","format":"email","nullable":true,"description":"Customer email, if you have one"}},"additionalProperties":false,"description":"Who the conversation is with"},"messages":{"type":"array","items":{"type":"object","properties":{"sender":{"type":"string","enum":["customer","bot"],"description":"`customer` for the customer's own words, `bot` to seed a message you already sent them."},"content":{"type":"string","maxLength":10000,"default":"","description":"Message text; may be omitted when files are provided"},"files":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","format":"uri","maxLength":8192},"name":{"type":"string","minLength":1,"maxLength":255}},"required":["url","name"],"additionalProperties":false},"maxItems":10,"description":"Files to download and store as native attachments: {url, name}. Direct public or signed HTTP(S) URLs only; no auth headers or redirects. Up to 10 files/request, 20 MiB/file, 50 MiB total."},"timestamp":{"type":"string","format":"date-time","description":"When the message was sent (ISO 8601)"}},"required":["sender"],"additionalProperties":false},"minItems":1,"maxItems":50,"description":"The thread so far. At least one; the last must be from the customer."},"externalId":{"type":"string","minLength":1,"maxLength":200,"description":"Your identifier for this message. Enables idempotent retries."},"metadata":{"type":"object","additionalProperties":{"type":"string","maxLength":50000},"description":"Key-value pairs stored on the conversation and shown to agents (max 200 pairs)"},"tags":{"type":"array","items":{"type":"string","minLength":1,"maxLength":100},"maxItems":20,"description":"Tags to apply to the conversation. Visible to agents, and available to webhook filters, so an integration can route its own traffic without inspecting the payload."},"handedOff":{"type":"boolean","default":false,"description":"Create the conversation already handed off to a human, without an automatic bot reply. Defaults to false. Does not enable dashboard agent replies; use allowAgentReplies for that."},"allowAgentReplies":{"type":"boolean","default":false,"description":"Allow human agents to reply to this conversation from the Octocom dashboard. Defaults to false. Agent replies are emitted through the agent_message_sent webhook event."}},"required":["businessSlug","messages"],"additionalProperties":false}}}},"responses":{"201":{"description":"Conversation created","content":{"application/json":{"schema":{"description":"Conversation created","type":"object","properties":{"data":{"type":"object","properties":{"conversationId":{"type":"string"},"publicId":{"type":"string"},"messageIds":{"type":"array","items":{"type":"string"}},"isHandedOff":{"type":"boolean"}}}},"required":["data"]}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"description":"Invalid request body","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed - invalid or missing API key","content":{"application/json":{"schema":{"description":"Authentication failed - invalid or missing API key","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"429":{"description":"Conversation creation rate limit exceeded","content":{"application/json":{"schema":{"description":"Conversation creation rate limit exceeded","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/conversations/bulk":{"post":{"summary":"Get conversation details in bulk","tags":["Conversations"],"description":"Retrieve up to 200 conversation UUIDs using batched database queries. Same detail fields and contractual creation-date cutoffs as single detail. Duplicate IDs are returned once, in request order. Missing, foreign and pre-cutoff IDs are returned in unavailableIds without distinguishing the reason. Shares the 500 requests/minute organization limit and additionally allows 2,000 requested IDs/minute per organization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":200}},"required":["ids"],"additionalProperties":false}}}},"responses":{"200":{"description":"Bulk details","content":{"application/json":{"schema":{"description":"Bulk details","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"updatedAt":{"type":"string","format":"date-time","description":"Change tracking timestamp, not last customer activity"},"id":{"type":"string","format":"uuid","description":"Unique identifier"},"publicId":{"type":"string","description":"Public-facing ID for external references"},"createdAt":{"type":"string","format":"date-time","description":"Creation timestamp"},"closedAt":{"type":["string","null"],"format":"date-time","description":"When closed"},"status":{"type":"string","enum":["open","closed","snoozed"],"description":"Current conversation status"},"snoozedUntil":{"type":["string","null"],"format":"date-time","description":"Scheduled wake-up time, or null when not snoozed. Use status to filter the conversation list."},"handedOffAt":{"type":["string","null"],"format":"date-time","description":"When handed to human agent"},"isHandedOff":{"type":"boolean","description":"Whether a human agent currently owns the conversation"},"language":{"type":"string","description":"Detected language (ISO 639-1 code)"},"subject":{"type":"string","description":"Subject or title"},"businessSlug":{"type":"string","description":"Business identifier"},"sentiment":{"type":["string","null"],"description":"AI-analyzed sentiment"},"sentimentReason":{"type":["string","null"],"description":"Explanation for sentiment classification"},"topics":{"type":["array","null"],"items":{"type":"string"},"description":"AI-classified topics"},"customer":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Customer unique identifier","example":"c1d2e3f4-5678-90ab-cdef-123456789abc"},"email":{"type":["string","null"],"format":"email","description":"Customer email address","example":"customer@example.com"},"phone":{"type":["string","null"],"description":"Customer phone number","example":"+1-555-123-4567"},"name":{"type":["string","null"],"description":"Customer display name","example":"John Smith"}},"required":["id"],"nullable":true,"description":"Customer associated with this conversation (null if no customer linked)"},"rating":{"type":["object","null"],"properties":{"score":{"type":"integer","minimum":1,"maximum":5,"description":"Submitted CSAT score"},"comment":{"type":["string","null"],"description":"Optional customer comment"}},"required":["score","comment"],"description":"Submitted CSAT rating, or null when none exists"},"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier"},"sender":{"type":"string","description":"Sender type: 'customer', 'bot', 'agent', or 'external'"},"text":{"type":"string","description":"Message content"},"channel":{"type":"string","enum":["web","email","whatsapp","messenger","instagram","telegram","sms","phone-call","google-business-messages","google-reviews","trustpilot-reviews","facebook-reviews","facebookComment","facebookMention","instagramMention","instagramComment","amazon","ebay","aircall","brymec","echo","test-review","zoom-transcript","api"],"description":"Channel the message was sent through"},"createdAt":{"type":"string","format":"date-time","description":"Message timestamp"},"isSent":{"type":"boolean","description":"Whether the message has been delivered. Scheduled bot messages remain false until they are sent."},"sendMessageAfter":{"type":["string","null"],"format":"date-time","description":"When a scheduled message becomes eligible to send, or null when it is not delayed."},"agentName":{"type":["string","null"],"description":"Agent name if sender is 'agent'"},"agentUsername":{"type":["string","null"],"description":"Agent alias as shown to the customer if sender is 'agent' (falls back to the agent user's display name)"},"agentEmail":{"type":["string","null"],"description":"Account email of the agent who sent the message, when the agent is linked to a user"},"files":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"File unique identifier"},"name":{"type":"string","description":"Original filename"},"contentType":{"type":"string","description":"MIME type of the file","example":"image/jpeg"},"url":{"type":"string","description":"Public URL to download the file"},"isSafe":{"type":"boolean","description":"Whether the file passed malware scanning"}},"required":["id","name","contentType","url","isSafe"]},"description":"Files attached to the message (empty if none)"}},"required":["id","sender","text","channel","createdAt","isSent","sendMessageAfter","files"]},"description":"Complete message history"},"dataCollectionResults":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Data field identifier"},"value":{"type":"string","description":"Collected value"},"createdAt":{"type":"string","format":"date-time","description":"Collection timestamp"}},"required":["key","value","createdAt"]},"description":"AI-extracted data from conversation"},"notes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier"},"text":{"type":"string","description":"Note content"},"createdAt":{"type":"string","format":"date-time","description":"Creation timestamp"},"userId":{"type":"string","format":"uuid","description":"ID of user who created the note"}},"required":["id","text","createdAt","userId"]},"description":"Internal notes added by agents"},"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier"},"event":{"type":"string","description":"Event type identifier"},"createdAt":{"type":"string","format":"date-time","description":"Event timestamp"},"metadata":{"description":"Event-specific metadata"}},"required":["id","event","createdAt"]},"description":"Conversation lifecycle events"},"botFunctionCalls":{"type":"array","items":{"type":"object","properties":{"toolName":{"type":"string","description":"Name of the function/tool called"},"toolArguments":{"type":"string","description":"JSON string of arguments passed"},"content":{"type":"string","description":"Result from the function call"},"createdAt":{"type":"string","format":"date-time","description":"Call timestamp"}},"required":["toolName","toolArguments","content","createdAt"]},"description":"Functions invoked by the bot"},"tags":{"type":"array","items":{"type":"string"},"description":"User-applied tags"},"workflows":{"type":"array","items":{"type":"string"},"description":"Deduplicated workflow names used across all bot responses"},"variants":{"type":"array","items":{"type":"string"},"description":"Deduplicated workflow variant names (format: 'WorkflowName / VariantName')"},"metadata":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Metadata key"},"value":{"type":"string","description":"Metadata value"},"createdAt":{"type":"string","format":"date-time","description":"When first created"},"updatedAt":{"type":"string","format":"date-time","description":"When last updated"}},"required":["key","value","createdAt","updatedAt"]},"description":"Key-value metadata pairs"}},"required":["id","publicId","createdAt","status","snoozedUntil","language","subject","businessSlug","rating","messages","dataCollectionResults","notes","events","botFunctionCalls","tags","workflows","variants","metadata"]}},"unavailableIds":{"type":"array","items":{"type":"string","format":"uuid"}}},"required":["data","unavailableIds"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"429":{"description":"Organization request or bulk detail budget exceeded","content":{"application/json":{"schema":{"description":"Organization request or bulk detail budget exceeded","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/conversations/{id}":{"get":{"summary":"Get conversation details","tags":["Conversations"],"description":"Retrieve complete details for a single conversation, including full message history,\nAI analysis, agent notes, and bot activity.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.\nThe conversation must belong to a business within your organization.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Conversation UUID"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"object","properties":{"updatedAt":{"type":"string","format":"date-time","description":"Change tracking timestamp, not last customer activity"},"id":{"type":"string","format":"uuid","description":"Unique identifier"},"publicId":{"type":"string","description":"Public-facing ID for external references"},"createdAt":{"type":"string","format":"date-time","description":"Creation timestamp"},"closedAt":{"type":["string","null"],"format":"date-time","description":"When closed"},"status":{"type":"string","enum":["open","closed","snoozed"],"description":"Current conversation status"},"snoozedUntil":{"type":["string","null"],"format":"date-time","description":"Scheduled wake-up time, or null when not snoozed. Use status to filter the conversation list."},"handedOffAt":{"type":["string","null"],"format":"date-time","description":"When handed to human agent"},"isHandedOff":{"type":"boolean","description":"Whether a human agent currently owns the conversation"},"language":{"type":"string","description":"Detected language (ISO 639-1 code)"},"subject":{"type":"string","description":"Subject or title"},"businessSlug":{"type":"string","description":"Business identifier"},"sentiment":{"type":["string","null"],"description":"AI-analyzed sentiment"},"sentimentReason":{"type":["string","null"],"description":"Explanation for sentiment classification"},"topics":{"type":["array","null"],"items":{"type":"string"},"description":"AI-classified topics"},"customer":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Customer unique identifier","example":"c1d2e3f4-5678-90ab-cdef-123456789abc"},"email":{"type":["string","null"],"format":"email","description":"Customer email address","example":"customer@example.com"},"phone":{"type":["string","null"],"description":"Customer phone number","example":"+1-555-123-4567"},"name":{"type":["string","null"],"description":"Customer display name","example":"John Smith"}},"required":["id"],"nullable":true,"description":"Customer associated with this conversation (null if no customer linked)"},"rating":{"type":["object","null"],"properties":{"score":{"type":"integer","minimum":1,"maximum":5,"description":"Submitted CSAT score"},"comment":{"type":["string","null"],"description":"Optional customer comment"}},"required":["score","comment"],"description":"Submitted CSAT rating, or null when none exists"},"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier"},"sender":{"type":"string","description":"Sender type: 'customer', 'bot', 'agent', or 'external'"},"text":{"type":"string","description":"Message content"},"channel":{"type":"string","enum":["web","email","whatsapp","messenger","instagram","telegram","sms","phone-call","google-business-messages","google-reviews","trustpilot-reviews","facebook-reviews","facebookComment","facebookMention","instagramMention","instagramComment","amazon","ebay","aircall","brymec","echo","test-review","zoom-transcript","api"],"description":"Channel the message was sent through"},"createdAt":{"type":"string","format":"date-time","description":"Message timestamp"},"isSent":{"type":"boolean","description":"Whether the message has been delivered. Scheduled bot messages remain false until they are sent."},"sendMessageAfter":{"type":["string","null"],"format":"date-time","description":"When a scheduled message becomes eligible to send, or null when it is not delayed."},"agentName":{"type":["string","null"],"description":"Agent name if sender is 'agent'"},"agentUsername":{"type":["string","null"],"description":"Agent alias as shown to the customer if sender is 'agent' (falls back to the agent user's display name)"},"agentEmail":{"type":["string","null"],"description":"Account email of the agent who sent the message, when the agent is linked to a user"},"files":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"File unique identifier"},"name":{"type":"string","description":"Original filename"},"contentType":{"type":"string","description":"MIME type of the file","example":"image/jpeg"},"url":{"type":"string","description":"Public URL to download the file"},"isSafe":{"type":"boolean","description":"Whether the file passed malware scanning"}},"required":["id","name","contentType","url","isSafe"]},"description":"Files attached to the message (empty if none)"}},"required":["id","sender","text","channel","createdAt","isSent","sendMessageAfter","files"]},"description":"Complete message history"},"dataCollectionResults":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Data field identifier"},"value":{"type":"string","description":"Collected value"},"createdAt":{"type":"string","format":"date-time","description":"Collection timestamp"}},"required":["key","value","createdAt"]},"description":"AI-extracted data from conversation"},"notes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier"},"text":{"type":"string","description":"Note content"},"createdAt":{"type":"string","format":"date-time","description":"Creation timestamp"},"userId":{"type":"string","format":"uuid","description":"ID of user who created the note"}},"required":["id","text","createdAt","userId"]},"description":"Internal notes added by agents"},"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier"},"event":{"type":"string","description":"Event type identifier"},"createdAt":{"type":"string","format":"date-time","description":"Event timestamp"},"metadata":{"description":"Event-specific metadata"}},"required":["id","event","createdAt"]},"description":"Conversation lifecycle events"},"botFunctionCalls":{"type":"array","items":{"type":"object","properties":{"toolName":{"type":"string","description":"Name of the function/tool called"},"toolArguments":{"type":"string","description":"JSON string of arguments passed"},"content":{"type":"string","description":"Result from the function call"},"createdAt":{"type":"string","format":"date-time","description":"Call timestamp"}},"required":["toolName","toolArguments","content","createdAt"]},"description":"Functions invoked by the bot"},"tags":{"type":"array","items":{"type":"string"},"description":"User-applied tags"},"workflows":{"type":"array","items":{"type":"string"},"description":"Deduplicated workflow names used across all bot responses"},"variants":{"type":"array","items":{"type":"string"},"description":"Deduplicated workflow variant names (format: 'WorkflowName / VariantName')"},"metadata":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Metadata key"},"value":{"type":"string","description":"Metadata value"},"createdAt":{"type":"string","format":"date-time","description":"When first created"},"updatedAt":{"type":"string","format":"date-time","description":"When last updated"}},"required":["key","value","createdAt","updatedAt"]},"description":"Key-value metadata pairs"}},"required":["id","publicId","createdAt","status","snoozedUntil","language","subject","businessSlug","rating","messages","dataCollectionResults","notes","events","botFunctionCalls","tags","workflows","variants","metadata"]}},"required":["data"]}}}},"401":{"description":"Authentication failed - invalid or missing API key","content":{"application/json":{"schema":{"description":"Authentication failed - invalid or missing API key","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Conversation not found or not accessible","content":{"application/json":{"schema":{"description":"Conversation not found or not accessible","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/customers":{"get":{"summary":"List customers","tags":["Customers"],"description":"Retrieve a paginated list of customers across all businesses in your organization.\n\nReturns up to 50 customers per page, sorted by creation date.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string","format":"date-time"},"in":"query","name":"startDate","required":false,"description":"Filter customers created on or after this date (ISO 8601 format, e.g., 2024-01-15T00:00:00Z)"},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"endDate","required":false,"description":"Filter customers created on or before this date (ISO 8601 format, e.g., 2024-01-15T23:59:59Z)"},{"schema":{"type":"integer","exclusiveMinimum":true,"minimum":0,"default":1},"in":"query","name":"page","required":false,"description":"Page number for pagination"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier","example":"c1d2e3f4-5678-90ab-cdef-123456789abc"},"email":{"type":["string","null"],"format":"email","description":"Customer email address","example":"customer@example.com"},"phone":{"type":["string","null"],"description":"Customer phone number","example":"+1-555-123-4567"},"name":{"type":["string","null"],"description":"Customer display name","example":"John Smith"},"businessSlug":{"type":"string","description":"Business identifier","example":"acme-corp"},"createdAt":{"type":["string","null"],"format":"date-time","description":"When the customer was first seen","example":"2024-01-10T08:00:00Z"},"conversationIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"IDs of conversations associated with this customer","example":["550e8400-e29b-41d4-a716-446655440000"]}},"required":["id","businessSlug","conversationIds"]},"description":"List of customers"},"pagination":{"type":"object","properties":{"page":{"type":"number","description":"Current page number"},"limit":{"type":"number","description":"Maximum items per page"},"hasMore":{"type":"boolean","description":"Whether more pages exist"}},"required":["page","limit","hasMore"]}},"required":["data","pagination"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/custom-data":{"get":{"summary":"List custom data classifications","tags":["Custom Data"],"description":"Retrieve a paginated list of AI-classified data from conversations.\n\nUse this endpoint to get aggregated classification results without fetching full conversations.\nSupports filtering by classification key and custom page sizes up to 200.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"},{"schema":{"type":"string"},"in":"query","name":"key","required":false,"description":"Filter by classification key"},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"startDate","required":false,"description":"Filter by classification date (ISO 8601 format, e.g., 2024-01-15T00:00:00Z)"},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"endDate","required":false,"description":"Filter by classification date (ISO 8601 format, e.g., 2024-01-15T23:59:59Z)"},{"schema":{"type":"integer","exclusiveMinimum":true,"minimum":0,"default":1},"in":"query","name":"page","required":false,"description":"Page number"},{"schema":{"type":"integer","exclusiveMinimum":true,"minimum":0,"maximum":200,"default":50},"in":"query","name":"limit","required":false,"description":"Items per page (max 200)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier","example":"d4e5f6a7-8901-2345-bcde-f67890123456"},"conversationId":{"type":"string","format":"uuid","description":"Associated conversation ID","example":"550e8400-e29b-41d4-a716-446655440000"},"key":{"type":"string","description":"Classification key (e.g., 'issue_type', 'sentiment')","example":"issue_type"},"value":{"type":"string","description":"Classified value for this key","example":"billing_inquiry"},"createdAt":{"type":"string","format":"date-time","description":"When the classification was made","example":"2024-01-15T10:35:00Z"}},"required":["id","conversationId","key","value","createdAt"]},"description":"List of classification results"},"pagination":{"type":"object","properties":{"page":{"type":"number","description":"Current page number"},"limit":{"type":"number","description":"Maximum items per page"},"hasMore":{"type":"boolean","description":"Whether more pages exist"}},"required":["page","limit","hasMore"]}},"required":["data","pagination"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/users":{"get":{"summary":"List organization users","tags":["Users"],"description":"Retrieve a paginated list of users in your organization.\n\nReturns active users only (excludes archived users).\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"integer","exclusiveMinimum":true,"minimum":0,"default":1},"in":"query","name":"page","required":false,"description":"Page number"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier","example":"u1234567-89ab-cdef-0123-456789abcdef"},"email":{"type":"string","format":"email","description":"User email","example":"agent@company.com"},"fullName":{"type":"string","description":"User's full name","example":"Jane Doe"},"isAgent":{"type":"boolean","description":"Whether user can handle conversations","example":true},"isManager":{"type":"boolean","description":"Whether user has manager privileges","example":false},"teamId":{"type":["string","null"],"format":"uuid","description":"Team the user belongs to","example":"t9876543-21fe-dcba-9876-543210fedcba"}},"required":["id","email","fullName","isAgent","isManager"]},"description":"List of users"},"pagination":{"type":"object","properties":{"page":{"type":"number","description":"Current page number"},"limit":{"type":"number","description":"Maximum items per page"},"hasMore":{"type":"boolean","description":"Whether more pages exist"}},"required":["page","limit","hasMore"]}},"required":["data","pagination"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/teams":{"get":{"summary":"List organization teams","tags":["Teams"],"description":"Retrieve a paginated list of teams in your organization.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"integer","exclusiveMinimum":true,"minimum":0,"default":1},"in":"query","name":"page","required":false,"description":"Page number"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier","example":"t9876543-21fe-dcba-9876-543210fedcba"},"name":{"type":"string","description":"Team name","example":"Support Team"},"createdAt":{"type":"string","format":"date-time","description":"Creation timestamp","example":"2024-01-01T00:00:00Z"},"updatedAt":{"type":"string","format":"date-time","description":"Last update timestamp","example":"2024-06-15T14:30:00Z"}},"required":["id","name","createdAt","updatedAt"]},"description":"List of teams"},"pagination":{"type":"object","properties":{"page":{"type":"number","description":"Current page number"},"limit":{"type":"number","description":"Maximum items per page"},"hasMore":{"type":"boolean","description":"Whether more pages exist"}},"required":["page","limit","hasMore"]}},"required":["data","pagination"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/macros":{"get":{"summary":"List macros","tags":["Macros"],"description":"Retrieve a paginated list of response macros in your organization.\n\nMacros are reusable response templates that agents can use to quickly respond to common inquiries.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"integer","exclusiveMinimum":true,"minimum":0,"default":1},"in":"query","name":"page","required":false,"description":"Page number"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier","example":"m1234567-89ab-cdef-0123-456789abcdef"},"title":{"type":"string","description":"Macro title","example":"Shipping Delay Response"},"content":{"type":["string","null"],"description":"Macro response template","example":"We apologize for the delay with your order. Our team is working to resolve this as quickly as possible."},"businessSlug":{"type":["string","null"],"description":"Business scope (null = organization-wide)","example":"acme-corp"}},"required":["id","title"]},"description":"List of macros"},"pagination":{"type":"object","properties":{"page":{"type":"number","description":"Current page number"},"limit":{"type":"number","description":"Maximum items per page"},"hasMore":{"type":"boolean","description":"Whether more pages exist"}},"required":["page","limit","hasMore"]}},"required":["data","pagination"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/macros/import":{"post":{"summary":"Bulk import macros","tags":["Macros"],"description":"Create or update many macros in a single request.\n\nIntended for migrating a macro library off another help desk. Folders, tags and\nassignees are referenced by **name/email rather than UUID** and are resolved (and\ncreated, for folders and tags) as part of the import, so the caller doesn't have\nto pre-create them or look their IDs up.\n\nEach macro is applied independently — a failure on one row does not abort the\nrest. The response reports a per-macro status so failures can be corrected and\nre-sent; re-sending is safe, since `onConflict` controls what happens to macros\nwhose title already exists.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"onConflict":{"type":"string","enum":["skip","update","error"],"default":"skip","description":"What to do when a macro with the same title already exists in the organization"},"createMissingTags":{"type":"boolean","default":true,"description":"Create tags referenced by setTag actions that don't exist yet"},"macros":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string","minLength":1,"description":"Macro title"},"content":{"type":"string","nullable":true,"description":"Macro body. Markdown, with Mustache variables"},"folder":{"type":"string","nullable":true,"description":"Folder name — created if it doesn't exist"},"businessSlug":{"type":"string","nullable":true,"description":"Restrict to one business (omit for organization-wide)"},"availableForEmail":{"type":"string","nullable":true,"description":"Restrict to a single user by email (omit to make it available to everyone)"},"actions":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["close","assignMember","setTag","snooze"]},"data":{"type":"string","nullable":true,"description":"Snooze duration, or a pre-resolved UUID for setTag/assignMember"},"tag":{"type":"string","description":"setTag: tag title (created if missing)"},"userEmail":{"type":"string","description":"assignMember: the assignee's email"}},"required":["type"],"additionalProperties":false}}},"required":["title"],"additionalProperties":false},"minItems":1,"maxItems":500,"description":"Macros to import (max 500 per request)"}},"required":["macros"],"additionalProperties":false}}}},"responses":{"200":{"description":"Import finished (check per-macro statuses)","content":{"application/json":{"schema":{"description":"Import finished (check per-macro statuses)","type":"object","properties":{"data":{"type":"object","properties":{"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"failed":{"type":"number"},"foldersCreated":{"type":"array","items":{"type":"string"}},"tagsCreated":{"type":"array","items":{"type":"string"}},"macros":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"id":{"type":["string","null"]},"status":{"type":"string","enum":["created","updated","skipped","failed"]},"error":{"type":"string"}},"required":["title","id","status"]}}},"required":["created","updated","skipped","failed","foldersCreated","tagsCreated","macros"]}},"required":["data"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/workflows":{"get":{"summary":"List workflows","tags":["Workflows"],"description":"Retrieve a paginated list of bot workflows for a specific business.\n\nWorkflows define automated conversation flows and bot behaviors.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"},{"schema":{"type":"integer","exclusiveMinimum":true,"minimum":0,"default":1},"in":"query","name":"page","required":false,"description":"Page number"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier","example":"w1234567-89ab-cdef-0123-456789abcdef"},"title":{"type":"string","description":"Workflow name","example":"Order Status Inquiry"},"slug":{"type":"string","description":"URL-friendly identifier","example":"order-status-inquiry"},"isLive":{"type":"boolean","description":"Whether workflow is active","example":true},"whenToFollow":{"type":"string","description":"Trigger condition for the workflow","example":"When a customer asks about their order status"},"priority":{"type":"number","description":"Execution priority (lower = higher priority)","example":10},"availableChannels":{"type":"array","items":{"type":"string"},"description":"Channels where this workflow applies","example":["web","email","whatsapp"]},"isEmbedded":{"type":"boolean","description":"Whether the workflow is embedded (must have exactly 1 variant)","example":false},"orderIdRequired":{"type":"boolean","description":"Whether the bot must identify an order before following","example":false},"subscriptionIdRequired":{"type":"boolean","description":"Whether the bot must identify a subscription before following","example":false},"conditionProviderId":{"type":["string","null"],"format":"uuid","description":"Condition provider used to choose between variants (null = none)"},"requiredConditions":{"type":"array","items":{"type":"string"},"description":"Workflow-level conditions that must hold","example":[]},"createdAt":{"type":"string","format":"date-time","description":"Creation timestamp","example":"2024-02-01T09:00:00Z"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp, including variant edits. Use for drift detection and as the `If-Match` value.","example":"2024-02-03T12:00:00Z"}},"required":["id","title","slug","isLive","isEmbedded","whenToFollow","priority","orderIdRequired","subscriptionIdRequired","conditionProviderId","requiredConditions","availableChannels","createdAt","updatedAt"]},"description":"List of workflows"},"pagination":{"type":"object","properties":{"page":{"type":"number","description":"Current page number"},"limit":{"type":"number","description":"Maximum items per page"},"hasMore":{"type":"boolean","description":"Whether more pages exist"}},"required":["page","limit","hasMore"]}},"required":["data","pagination"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"post":{"summary":"Create a workflow","tags":["Workflows"],"description":"Create a new bot workflow with one or more variants.\n\nWorkflows control how the bot responds to different situations. Each workflow needs at least one variant with bot instructions.\n\n**Embedded workflows** must have exactly one variant, no condition provider, and no required conditions. Incompatible configurations return HTTP 400.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","description":"Business identifier (required)"},"title":{"type":"string","minLength":1,"description":"Workflow title"},"whenToFollow":{"type":"string","minLength":1,"description":"Human-readable condition for when this workflow applies (e.g., 'Customer asks about returns')"},"isLive":{"type":"boolean","description":"Whether the workflow is active"},"variants":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string","minLength":1,"description":"Variant name"},"botInstructions":{"type":"string","minLength":1,"description":"Instructions for the bot to follow"},"actionNames":{"type":"array","items":{"type":"string"},"default":[],"description":"Available actions for this variant"},"requiredConditions":{"type":"array","items":{"type":"string"},"default":[],"description":"Conditions that must be true for this variant"}},"required":["title","botInstructions"],"additionalProperties":false},"minItems":1,"description":"At least one variant required"},"availableChannels":{"type":"array","items":{"type":"string"},"description":"Limit to specific channels (omit or empty array for all channels)"},"isEmbedded":{"type":"boolean","default":false,"description":"Embedded workflows require exactly one variant, no condition provider, and no required conditions."}},"required":["businessSlug","title","whenToFollow","isLive","variants"],"additionalProperties":false}}}},"responses":{"201":{"description":"Workflow created successfully","content":{"application/json":{"schema":{"description":"Workflow created successfully","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier","example":"w1234567-89ab-cdef-0123-456789abcdef"},"title":{"type":"string","description":"Workflow name","example":"Order Status Inquiry"},"slug":{"type":"string","description":"URL-friendly identifier","example":"order-status-inquiry"},"isLive":{"type":"boolean","description":"Whether workflow is active","example":true},"whenToFollow":{"type":"string","description":"Trigger condition for the workflow","example":"When a customer asks about their order status"},"priority":{"type":"number","description":"Execution priority (lower = higher priority)","example":10},"availableChannels":{"type":"array","items":{"type":"string"},"description":"Channels where this workflow applies","example":["web","email","whatsapp"]},"isEmbedded":{"type":"boolean","description":"Whether the workflow is embedded (must have exactly 1 variant)","example":false},"orderIdRequired":{"type":"boolean","description":"Whether the bot must identify an order before following","example":false},"subscriptionIdRequired":{"type":"boolean","description":"Whether the bot must identify a subscription before following","example":false},"conditionProviderId":{"type":["string","null"],"format":"uuid","description":"Condition provider used to choose between variants (null = none)"},"requiredConditions":{"type":"array","items":{"type":"string"},"description":"Workflow-level conditions that must hold","example":[]},"createdAt":{"type":"string","format":"date-time","description":"Creation timestamp","example":"2024-02-01T09:00:00Z"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp, including variant edits. Use for drift detection and as the `If-Match` value.","example":"2024-02-03T12:00:00Z"},"variants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique variant identifier","example":"v1234567-89ab-cdef-0123-456789abcdef"},"title":{"type":"string","description":"Variant name","example":"Default"},"position":{"type":"number","description":"Order position (0-based)","example":0},"botInstructions":{"type":"string","description":"Instructions for the bot to follow","example":"Help the customer track their order using the order ID."},"actionNames":{"type":"array","items":{"type":"string"},"description":"Available actions for this variant","example":["getOrderStatus"]},"requiredConditions":{"type":"array","items":{"type":"string"},"description":"Conditions that must be true for this variant","example":[]}},"required":["id","title","position","botInstructions","actionNames","requiredConditions"]},"description":"Workflow variants with bot instructions, in position order"}},"required":["id","title","slug","isLive","isEmbedded","whenToFollow","priority","orderIdRequired","subscriptionIdRequired","conditionProviderId","requiredConditions","availableChannels","createdAt","updatedAt","variants"]}},"required":["data"]}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"description":"Invalid request body","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/workflows/{id}":{"delete":{"summary":"Delete a workflow","tags":["Workflows"],"description":"Delete a workflow by ID. This will also delete all associated variants. A backup is saved before deletion.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.\nThe workflow must belong to a business within your organization.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Workflow UUID"}],"responses":{"200":{"description":"Workflow deleted successfully","content":{"application/json":{"schema":{"description":"Workflow deleted successfully","type":"object","properties":{"success":{"type":"boolean","description":"Whether the operation succeeded","example":true},"message":{"type":"string","description":"Success message","example":"Workflow deleted successfully"}},"required":["success","message"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Workflow not found or not accessible","content":{"application/json":{"schema":{"description":"Workflow not found or not accessible","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"get":{"summary":"Get a workflow","tags":["Workflows"],"description":"Retrieve one workflow with its variants (bot instructions, action names, required conditions) and all top-level settings. This is the full current state — no version entry is needed.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Workflow UUID"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier","example":"w1234567-89ab-cdef-0123-456789abcdef"},"title":{"type":"string","description":"Workflow name","example":"Order Status Inquiry"},"slug":{"type":"string","description":"URL-friendly identifier","example":"order-status-inquiry"},"isLive":{"type":"boolean","description":"Whether workflow is active","example":true},"whenToFollow":{"type":"string","description":"Trigger condition for the workflow","example":"When a customer asks about their order status"},"priority":{"type":"number","description":"Execution priority (lower = higher priority)","example":10},"availableChannels":{"type":"array","items":{"type":"string"},"description":"Channels where this workflow applies","example":["web","email","whatsapp"]},"isEmbedded":{"type":"boolean","description":"Whether the workflow is embedded (must have exactly 1 variant)","example":false},"orderIdRequired":{"type":"boolean","description":"Whether the bot must identify an order before following","example":false},"subscriptionIdRequired":{"type":"boolean","description":"Whether the bot must identify a subscription before following","example":false},"conditionProviderId":{"type":["string","null"],"format":"uuid","description":"Condition provider used to choose between variants (null = none)"},"requiredConditions":{"type":"array","items":{"type":"string"},"description":"Workflow-level conditions that must hold","example":[]},"createdAt":{"type":"string","format":"date-time","description":"Creation timestamp","example":"2024-02-01T09:00:00Z"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp, including variant edits. Use for drift detection and as the `If-Match` value.","example":"2024-02-03T12:00:00Z"},"variants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique variant identifier","example":"v1234567-89ab-cdef-0123-456789abcdef"},"title":{"type":"string","description":"Variant name","example":"Default"},"position":{"type":"number","description":"Order position (0-based)","example":0},"botInstructions":{"type":"string","description":"Instructions for the bot to follow","example":"Help the customer track their order using the order ID."},"actionNames":{"type":"array","items":{"type":"string"},"description":"Available actions for this variant","example":["getOrderStatus"]},"requiredConditions":{"type":"array","items":{"type":"string"},"description":"Conditions that must be true for this variant","example":[]}},"required":["id","title","position","botInstructions","actionNames","requiredConditions"]},"description":"Workflow variants with bot instructions, in position order"}},"required":["id","title","slug","isLive","isEmbedded","whenToFollow","priority","orderIdRequired","subscriptionIdRequired","conditionProviderId","requiredConditions","availableChannels","createdAt","updatedAt","variants"]}},"required":["data"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Workflow not found or not accessible","content":{"application/json":{"schema":{"description":"Workflow not found or not accessible","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"patch":{"summary":"Update a workflow","tags":["Workflows"],"description":"Patch a workflow in place. Omitted fields are unchanged. The workflow keeps its id, slug and version history, and one new version entry is written.\n\n**Variants:** pass `variants` to replace the variant list wholesale, in the order given. Include a variant's `id` to update it in place (recommended — ids are stable across pushes); omit the `id` to create a new variant; any existing variant not in the list is deleted. Variant ids must belong to this workflow. Embedded workflows must end up with exactly one variant, no condition provider, and no required conditions; incompatible configurations return HTTP 400 without applying the patch.\n\n**Optimistic concurrency (optional):** send the `updatedAt` you last read in an `If-Match` header. If the resource has changed since, the request is rejected with `412 Precondition Failed` and the current `updatedAt`, so a push can never clobber an edit made after your last pull.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","description":"Business identifier"},"title":{"type":"string","minLength":1},"whenToFollow":{"type":"string","minLength":1},"isLive":{"type":"boolean"},"isEmbedded":{"type":"boolean"},"priority":{"type":"integer"},"orderIdRequired":{"type":"boolean"},"subscriptionIdRequired":{"type":"boolean"},"conditionProviderId":{"type":"string","format":"uuid","nullable":true,"description":"Condition provider used to choose between variants. Required for more than one variant; null clears it."},"availableChannels":{"type":"array","items":{"type":"string"},"description":"Limit to specific channels (empty array = all)"},"variants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Existing variant id to update in place; omit to create"},"title":{"type":"string","minLength":1,"description":"Variant name"},"botInstructions":{"type":"string","minLength":1,"description":"Instructions for the bot to follow"},"actionNames":{"type":"array","items":{"type":"string"},"default":[],"description":"Available actions for this variant"},"requiredConditions":{"type":"array","items":{"type":"string"},"default":[],"description":"Conditions that must be true for this variant"}},"required":["title","botInstructions"],"additionalProperties":false},"minItems":1,"description":"Full replacement variant list, in position order"}},"required":["businessSlug"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Workflow UUID"},{"schema":{"type":"string"},"in":"header","name":"if-match","required":false,"description":"Optional. The `updatedAt` value last read for this resource. The update only applies if the resource still has that `updatedAt`; otherwise 412."}],"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"description":"Updated","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier","example":"w1234567-89ab-cdef-0123-456789abcdef"},"title":{"type":"string","description":"Workflow name","example":"Order Status Inquiry"},"slug":{"type":"string","description":"URL-friendly identifier","example":"order-status-inquiry"},"isLive":{"type":"boolean","description":"Whether workflow is active","example":true},"whenToFollow":{"type":"string","description":"Trigger condition for the workflow","example":"When a customer asks about their order status"},"priority":{"type":"number","description":"Execution priority (lower = higher priority)","example":10},"availableChannels":{"type":"array","items":{"type":"string"},"description":"Channels where this workflow applies","example":["web","email","whatsapp"]},"isEmbedded":{"type":"boolean","description":"Whether the workflow is embedded (must have exactly 1 variant)","example":false},"orderIdRequired":{"type":"boolean","description":"Whether the bot must identify an order before following","example":false},"subscriptionIdRequired":{"type":"boolean","description":"Whether the bot must identify a subscription before following","example":false},"conditionProviderId":{"type":["string","null"],"format":"uuid","description":"Condition provider used to choose between variants (null = none)"},"requiredConditions":{"type":"array","items":{"type":"string"},"description":"Workflow-level conditions that must hold","example":[]},"createdAt":{"type":"string","format":"date-time","description":"Creation timestamp","example":"2024-02-01T09:00:00Z"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp, including variant edits. Use for drift detection and as the `If-Match` value.","example":"2024-02-03T12:00:00Z"},"variants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique variant identifier","example":"v1234567-89ab-cdef-0123-456789abcdef"},"title":{"type":"string","description":"Variant name","example":"Default"},"position":{"type":"number","description":"Order position (0-based)","example":0},"botInstructions":{"type":"string","description":"Instructions for the bot to follow","example":"Help the customer track their order using the order ID."},"actionNames":{"type":"array","items":{"type":"string"},"description":"Available actions for this variant","example":["getOrderStatus"]},"requiredConditions":{"type":"array","items":{"type":"string"},"description":"Conditions that must be true for this variant","example":[]}},"required":["id","title","position","botInstructions","actionNames","requiredConditions"]},"description":"Workflow variants with bot instructions, in position order"}},"required":["id","title","slug","isLive","isEmbedded","whenToFollow","priority","orderIdRequired","subscriptionIdRequired","conditionProviderId","requiredConditions","availableChannels","createdAt","updatedAt","variants"]}},"required":["data"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"description":"Not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"412":{"description":"If-Match precondition failed","content":{"application/json":{"schema":{"description":"If-Match precondition failed","type":"object","properties":{"error":{"type":"string","description":"Error message"},"currentUpdatedAt":{"type":"string","format":"date-time","description":"The resource's current `updatedAt`. Re-read the resource, merge, and retry with this value in `If-Match`."}},"required":["error","currentUpdatedAt"]}}}}}}},"/rest/v1/workflows/{id}/versions":{"get":{"summary":"List workflow version history","tags":["Workflows"],"description":"Return the change history for a workflow (including variant changes), newest first.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier"},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Workflow UUID"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object"}}}}}}},"/rest/v1/workflows/{id}/restore":{"post":{"summary":"Restore a workflow to a previous version","tags":["Workflows"],"description":"Restore a workflow (and its variants) to the state captured in a given version. Works on soft-deleted workflows too.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","description":"Business identifier"},"versionId":{"type":"string","format":"uuid","description":"The version ID to restore to"}},"required":["businessSlug","versionId"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Workflow UUID"}],"responses":{"200":{"description":"Restored","content":{"application/json":{"schema":{"description":"Restored","type":"object"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"description":"Not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/workflows/deleted":{"get":{"summary":"List soft-deleted workflows","tags":["Workflows"],"description":"List workflows soft-deleted but still restorable.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object"}}}}}}},"/rest/v1/articles":{"get":{"summary":"List knowledge base articles","tags":["Articles"],"description":"Retrieve a paginated list of knowledge base articles for a specific business.\n\nArticles are used by the AI bot to answer customer questions.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"},{"schema":{"type":"integer","exclusiveMinimum":true,"minimum":0,"default":1},"in":"query","name":"page","required":false,"description":"Page number"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier","example":"a1234567-89ab-cdef-0123-456789abcdef"},"title":{"type":"string","description":"Article title","example":"How to Track Your Order"},"isPublic":{"type":"boolean","description":"Whether article is publicly visible","example":true},"isActive":{"type":"boolean","description":"Whether article is active","example":true},"language":{"type":["string","null"],"description":"Article language (ISO 639-1)","example":"en"},"categoryId":{"type":["string","null"],"format":"uuid","description":"Category the article belongs to","example":"cat12345-6789-abcd-ef01-234567890abc"},"channels":{"type":["array","null"],"items":{"type":"string"},"description":"Channels this article is used on (null = all channels)","example":null},"createdAt":{"type":"string","format":"date-time","description":"Creation timestamp","example":"2024-03-10T11:00:00Z"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp. Use for drift detection and as the `If-Match` value.","example":"2024-03-12T09:30:00Z"}},"required":["id","title","isPublic","isActive","createdAt","updatedAt"]},"description":"List of articles"},"pagination":{"type":"object","properties":{"page":{"type":"number","description":"Current page number"},"limit":{"type":"number","description":"Maximum items per page"},"hasMore":{"type":"boolean","description":"Whether more pages exist"}},"required":["page","limit","hasMore"]}},"required":["data","pagination"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"post":{"summary":"Create an article","tags":["Articles"],"description":"Create a new knowledge base article for a business.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","description":"Business identifier (required)"},"title":{"type":"string","minLength":1,"description":"Article title"},"content":{"type":"string","minLength":1,"description":"Article body content"},"isPublic":{"type":"boolean"},"isActive":{"type":"boolean"},"categoryId":{"type":"string","format":"uuid"}},"required":["businessSlug","title","content"],"additionalProperties":false}}}},"responses":{"201":{"description":"Article created","content":{"application/json":{"schema":{"description":"Article created","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier","example":"a1234567-89ab-cdef-0123-456789abcdef"},"title":{"type":"string","description":"Article title","example":"How to Track Your Order"},"isPublic":{"type":"boolean","description":"Whether article is publicly visible","example":true},"isActive":{"type":"boolean","description":"Whether article is active","example":true},"language":{"type":["string","null"],"description":"Article language (ISO 639-1)","example":"en"},"categoryId":{"type":["string","null"],"format":"uuid","description":"Category the article belongs to","example":"cat12345-6789-abcd-ef01-234567890abc"},"channels":{"type":["array","null"],"items":{"type":"string"},"description":"Channels this article is used on (null = all channels)","example":null},"createdAt":{"type":"string","format":"date-time","description":"Creation timestamp","example":"2024-03-10T11:00:00Z"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp. Use for drift detection and as the `If-Match` value.","example":"2024-03-12T09:30:00Z"}},"required":["id","title","isPublic","isActive","createdAt","updatedAt"]}},"required":["data"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/articles/{id}":{"get":{"summary":"Get an article","tags":["Articles"],"description":"Retrieve one knowledge base article including its full content. This is the current state — no version entry is needed.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Article UUID"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier","example":"a1234567-89ab-cdef-0123-456789abcdef"},"title":{"type":"string","description":"Article title","example":"How to Track Your Order"},"isPublic":{"type":"boolean","description":"Whether article is publicly visible","example":true},"isActive":{"type":"boolean","description":"Whether article is active","example":true},"language":{"type":["string","null"],"description":"Article language (ISO 639-1)","example":"en"},"categoryId":{"type":["string","null"],"format":"uuid","description":"Category the article belongs to","example":"cat12345-6789-abcd-ef01-234567890abc"},"channels":{"type":["array","null"],"items":{"type":"string"},"description":"Channels this article is used on (null = all channels)","example":null},"createdAt":{"type":"string","format":"date-time","description":"Creation timestamp","example":"2024-03-10T11:00:00Z"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp. Use for drift detection and as the `If-Match` value.","example":"2024-03-12T09:30:00Z"},"content":{"type":"string","description":"Full article body (markdown)","example":"You can track your order from the Orders page..."}},"required":["id","title","isPublic","isActive","createdAt","updatedAt","content"]}},"required":["data"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"description":"Not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"patch":{"summary":"Update an article","tags":["Articles"],"description":"Update an article. Omitted fields are unchanged. Creates a new version entry.\n\n**Optimistic concurrency (optional):** send the `updatedAt` you last read in an `If-Match` header. If the resource has changed since, the request is rejected with `412 Precondition Failed` and the current `updatedAt`, so a push can never clobber an edit made after your last pull.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","description":"Business identifier (required)"},"title":{"type":"string","minLength":1},"content":{"type":"string","minLength":1},"isPublic":{"type":"boolean"},"isActive":{"type":"boolean"},"categoryId":{"type":"string","format":"uuid"}},"required":["businessSlug"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Article UUID"},{"schema":{"type":"string"},"in":"header","name":"if-match","required":false,"description":"Optional. The `updatedAt` value last read for this resource. The update only applies if the resource still has that `updatedAt`; otherwise 412."}],"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"description":"Updated","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier","example":"a1234567-89ab-cdef-0123-456789abcdef"},"title":{"type":"string","description":"Article title","example":"How to Track Your Order"},"isPublic":{"type":"boolean","description":"Whether article is publicly visible","example":true},"isActive":{"type":"boolean","description":"Whether article is active","example":true},"language":{"type":["string","null"],"description":"Article language (ISO 639-1)","example":"en"},"categoryId":{"type":["string","null"],"format":"uuid","description":"Category the article belongs to","example":"cat12345-6789-abcd-ef01-234567890abc"},"channels":{"type":["array","null"],"items":{"type":"string"},"description":"Channels this article is used on (null = all channels)","example":null},"createdAt":{"type":"string","format":"date-time","description":"Creation timestamp","example":"2024-03-10T11:00:00Z"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp. Use for drift detection and as the `If-Match` value.","example":"2024-03-12T09:30:00Z"}},"required":["id","title","isPublic","isActive","createdAt","updatedAt"]}},"required":["data"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"description":"Not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"412":{"description":"If-Match precondition failed","content":{"application/json":{"schema":{"description":"If-Match precondition failed","type":"object","properties":{"error":{"type":"string","description":"Error message"},"currentUpdatedAt":{"type":"string","format":"date-time","description":"The resource's current `updatedAt`. Re-read the resource, merge, and retry with this value in `If-Match`."}},"required":["error","currentUpdatedAt"]}}}}}},"delete":{"summary":"Soft-delete an article","tags":["Articles"],"description":"Soft-delete an article. Recoverable via /articles/:id/restore.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Article UUID"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"description":"Deleted","type":"object"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"description":"Not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/articles/{id}/versions":{"get":{"summary":"List article version history","tags":["Articles"],"description":"Return the change history for an article, newest first.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Article UUID"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object"}}}}}}},"/rest/v1/articles/{id}/restore":{"post":{"summary":"Restore an article to a previous version","tags":["Articles"],"description":"Restore an article (including soft-deleted ones) to the state captured in a given version.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","description":"Business identifier (required)"},"versionId":{"type":"string","format":"uuid","description":"The version ID to restore to"}},"required":["businessSlug","versionId"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Article UUID"}],"responses":{"200":{"description":"Restored","content":{"application/json":{"schema":{"description":"Restored","type":"object"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"description":"Not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/articles/deleted":{"get":{"summary":"List soft-deleted articles","tags":["Articles"],"description":"List articles soft-deleted but still restorable.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object"}}}}}}},"/rest/v1/bot-rules":{"get":{"summary":"List bot rules","tags":["Bot Rules"],"description":"Retrieve a paginated list of custom bot rules for a specific business.\n\nBot rules define additional instructions and constraints for the AI bot's behavior.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"},{"schema":{"type":"integer","exclusiveMinimum":true,"minimum":0,"default":1},"in":"query","name":"page","required":false,"description":"Page number"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier","example":"r1234567-89ab-cdef-0123-456789abcdef"},"title":{"type":"string","description":"Rule title","example":"No Competitor Mentions"},"rule":{"type":"string","description":"Rule instruction text","example":"Never mention competitor products or services. Focus only on our offerings."},"channels":{"type":["array","null"],"items":{"type":"string"},"description":"Channels this rule applies to (null = all channels)","example":["web","email"]},"createdAt":{"type":"string","format":"date-time","description":"Creation timestamp","example":"2024-02-01T09:00:00Z"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp. Use for drift detection and as the `If-Match` value.","example":"2024-02-03T12:00:00Z"}},"required":["id","title","rule","createdAt","updatedAt"]},"description":"List of bot rules"},"pagination":{"type":"object","properties":{"page":{"type":"number","description":"Current page number"},"limit":{"type":"number","description":"Maximum items per page"},"hasMore":{"type":"boolean","description":"Whether more pages exist"}},"required":["page","limit","hasMore"]}},"required":["data","pagination"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"post":{"summary":"Create a bot rule","tags":["Bot Rules"],"description":"Create a new custom bot rule for a specific business.\n\nBot rules define additional instructions and constraints for the AI bot's behavior.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","description":"Business identifier (required)"},"title":{"type":"string","minLength":1,"description":"Rule title"},"rule":{"type":"string","minLength":1,"description":"Rule instruction text for the bot"},"channels":{"type":"array","items":{"type":"string","enum":["web","email","whatsapp","messenger","instagram","telegram","sms","phone-call","google-business-messages","google-reviews","trustpilot-reviews","facebook-reviews","facebookComment","facebookMention","instagramMention","instagramComment","amazon","ebay","aircall","brymec","echo","test-review","zoom-transcript","api"]},"description":"Channels this rule applies to (omit for all channels)"}},"required":["businessSlug","title","rule"],"additionalProperties":false}}}},"responses":{"201":{"description":"Bot rule created successfully","content":{"application/json":{"schema":{"description":"Bot rule created successfully","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier","example":"r1234567-89ab-cdef-0123-456789abcdef"},"title":{"type":"string","description":"Rule title","example":"No Competitor Mentions"},"rule":{"type":"string","description":"Rule instruction text","example":"Never mention competitor products or services. Focus only on our offerings."},"channels":{"type":["array","null"],"items":{"type":"string"},"description":"Channels this rule applies to (null = all channels)","example":["web","email"]},"createdAt":{"type":"string","format":"date-time","description":"Creation timestamp","example":"2024-02-01T09:00:00Z"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp. Use for drift detection and as the `If-Match` value.","example":"2024-02-03T12:00:00Z"}},"required":["id","title","rule","createdAt","updatedAt"]}},"required":["data"]}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"description":"Invalid request body","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/bot-rules/{id}":{"delete":{"summary":"Delete a bot rule","tags":["Bot Rules"],"description":"Delete a bot rule by ID.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.\nThe bot rule must belong to a business within your organization.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Bot rule UUID"}],"responses":{"200":{"description":"Bot rule deleted successfully","content":{"application/json":{"schema":{"description":"Bot rule deleted successfully","type":"object","properties":{"success":{"type":"boolean","description":"Whether the operation succeeded","example":true},"message":{"type":"string","description":"Success message","example":"Bot rule deleted successfully"}},"required":["success","message"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Bot rule not found or not accessible","content":{"application/json":{"schema":{"description":"Bot rule not found or not accessible","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"patch":{"summary":"Update a bot rule","tags":["Bot Rules"],"description":"Update a bot rule by ID. Omitted fields are left unchanged. Creates a new version entry.\n\n**Optimistic concurrency (optional):** send the `updatedAt` you last read in an `If-Match` header. If the resource has changed since, the request is rejected with `412 Precondition Failed` and the current `updatedAt`, so a push can never clobber an edit made after your last pull.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","description":"Business identifier (required)"},"title":{"type":"string","minLength":1,"description":"New rule title"},"rule":{"type":"string","minLength":1,"description":"New rule instruction text"},"channels":{"type":"array","items":{"type":"string","enum":["web","email","whatsapp","messenger","instagram","telegram","sms","phone-call","google-business-messages","google-reviews","trustpilot-reviews","facebook-reviews","facebookComment","facebookMention","instagramMention","instagramComment","amazon","ebay","aircall","brymec","echo","test-review","zoom-transcript","api"]},"nullable":true,"description":"Channels (omit to leave unchanged, null to apply to all)"}},"required":["businessSlug"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Bot rule UUID"},{"schema":{"type":"string"},"in":"header","name":"if-match","required":false,"description":"Optional. The `updatedAt` value last read for this resource. The update only applies if the resource still has that `updatedAt`; otherwise 412."}],"responses":{"200":{"description":"Bot rule updated successfully","content":{"application/json":{"schema":{"description":"Bot rule updated successfully","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier","example":"r1234567-89ab-cdef-0123-456789abcdef"},"title":{"type":"string","description":"Rule title","example":"No Competitor Mentions"},"rule":{"type":"string","description":"Rule instruction text","example":"Never mention competitor products or services. Focus only on our offerings."},"channels":{"type":["array","null"],"items":{"type":"string"},"description":"Channels this rule applies to (null = all channels)","example":["web","email"]},"createdAt":{"type":"string","format":"date-time","description":"Creation timestamp","example":"2024-02-01T09:00:00Z"},"updatedAt":{"type":"string","format":"date-time","description":"Last change timestamp. Use for drift detection and as the `If-Match` value.","example":"2024-02-03T12:00:00Z"}},"required":["id","title","rule","createdAt","updatedAt"]}},"required":["data"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Bot rule not found","content":{"application/json":{"schema":{"description":"Bot rule not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"412":{"description":"If-Match precondition failed","content":{"application/json":{"schema":{"description":"If-Match precondition failed","type":"object","properties":{"error":{"type":"string","description":"Error message"},"currentUpdatedAt":{"type":"string","format":"date-time","description":"The resource's current `updatedAt`. Re-read the resource, merge, and retry with this value in `If-Match`."}},"required":["error","currentUpdatedAt"]}}}}}}},"/rest/v1/bot-rules/{id}/versions":{"get":{"summary":"List bot rule versions","tags":["Bot Rules"],"description":"Return the change history for a bot rule, newest first. Each entry includes who made the change, when, and the rule contents at that point.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Bot rule UUID"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object"}}}},"404":{"description":"Bot rule not found","content":{"application/json":{"schema":{"description":"Bot rule not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/bot-rules/{id}/restore":{"post":{"summary":"Restore a bot rule to a previous version","tags":["Bot Rules"],"description":"Restore a bot rule (including soft-deleted ones) to the state captured in a given version. Creates a new 'restored' version entry.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","description":"Business identifier (required)"},"versionId":{"type":"string","format":"uuid","description":"The entityVersions ID to restore to"}},"required":["businessSlug","versionId"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Bot rule UUID"}],"responses":{"200":{"description":"Bot rule restored","content":{"application/json":{"schema":{"description":"Bot rule restored","type":"object"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"description":"Not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/bot-rules/deleted":{"get":{"summary":"List soft-deleted bot rules","tags":["Bot Rules"],"description":"List bot rules that have been soft-deleted but can still be restored via /bot-rules/:id/restore.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"query","name":"businessSlug","required":true,"description":"Business identifier (required)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object"}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/businesses":{"get":{"summary":"List businesses","tags":["Businesses"],"description":"Retrieve a list of all businesses in your organization.\n\nUse the returned `slug` values when calling business-scoped endpoints like workflows, articles, and bot rules.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"slug":{"type":"string","description":"Business slug (use this for business-scoped API calls)","example":"acme-corp"},"name":{"type":"string","description":"Business display name","example":"Acme Corporation"}},"required":["id","slug","name"]},"description":"List of businesses"}},"required":["data"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/sheets/rows":{"post":{"summary":"Add a row to a Google Sheet","tags":["Google Sheets"],"description":"Add a new row to a Google Sheets document.\n\n**Prerequisites:**\n1. Share your Google Sheet with the Octocom service account as an editor:\n   `octocom-google-sheets@octocom.iam.gserviceaccount.com`\n2. Get the spreadsheet ID from your Google Sheets URL (the part between /d/ and /edit)\n\n**How it works:**\n- The keys in your `data` object become column headers\n- The values become the row data\n- If headers don't exist, they will be created\n- Pass `sheetName` to append to a specific tab; omit it to use the first tab\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"spreadsheetId":{"type":"string","minLength":1,"description":"The Google Sheets spreadsheet ID (from the URL between /d/ and /edit)"},"sheetName":{"type":"string","minLength":1,"description":"Name of the tab to append to, exactly as it appears on the tab at the bottom of the spreadsheet. Defaults to the first tab."},"data":{"type":"object","additionalProperties":{"type":"string"},"description":"Key-value pairs where keys are column headers and values are cell data"}},"required":["spreadsheetId","data"],"additionalProperties":false}}}},"responses":{"200":{"description":"Row added successfully","content":{"application/json":{"schema":{"description":"Row added successfully","type":"object","properties":{"success":{"type":"boolean","description":"Whether the operation succeeded","example":true},"message":{"type":"string","description":"Success or error message","example":"Row added successfully"},"row":{"type":"object","properties":{"rowNumber":{"type":"number","example":42},"values":{"type":"object","additionalProperties":{"type":"string"}}},"required":["rowNumber","values"]}},"required":["success","message","row"]}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"description":"Invalid request body","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed - invalid or missing API key","content":{"application/json":{"schema":{"description":"Authentication failed - invalid or missing API key","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"403":{"description":"Service account doesn't have editor access to the spreadsheet","content":{"application/json":{"schema":{"description":"Service account doesn't have editor access to the spreadsheet","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Spreadsheet or tab not found","content":{"application/json":{"schema":{"description":"Spreadsheet or tab not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"500":{"description":"Google Sheets API error","content":{"application/json":{"schema":{"description":"Google Sheets API error","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"patch":{"summary":"Update one Google Sheet row","tags":["Google Sheets"],"description":"Patch named cells in exactly one row without rewriting untouched cells.\n\nSelect the row in one of two ways:\n- Pass `where` to find exactly one row by an exact, case-sensitive AND match. Zero matches returns 404 and multiple matches returns 409. Unique-match searches are bounded to 10,000 populated rows.\n- Pass `rowNumber` together with `expected` values from a previous list result. This is efficient for very large sheets and fails with 409 if the row moved or changed.\n\nExisting headers are required; an update never creates or renames columns. The expected/matched values are re-read immediately before the patch so sorting or concurrent edits fail closed.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header. The spreadsheet must be shared with the Octocom Google Sheets service account as an editor.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"spreadsheetId":{"type":"string","minLength":1},"sheetName":{"type":"string","minLength":1},"data":{"type":"object","additionalProperties":{"type":"string"}},"where":{"type":"object","additionalProperties":{"type":"string"}},"rowNumber":{"type":"integer","minimum":2},"expected":{"type":"object","additionalProperties":{"type":"string"}}},"required":["spreadsheetId","data"],"additionalProperties":false}}}},"responses":{"200":{"description":"Row updated successfully","content":{"application/json":{"schema":{"description":"Row updated successfully","type":"object","properties":{"success":{"type":"boolean"},"previousValues":{"type":"object","additionalProperties":{"type":"string"}},"row":{"type":"object","properties":{"rowNumber":{"type":"number","description":"One-based Google Sheets row number (the header is row 1)","example":42},"values":{"type":"object","additionalProperties":{"type":"string"},"description":"Column-header to cell-value mapping"}},"required":["rowNumber","values"]}},"required":["success","previousValues","row"]}}}},"400":{"description":"Invalid selector, column, or request body","content":{"application/json":{"schema":{"description":"Invalid selector, column, or request body","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed - invalid or missing API key","content":{"application/json":{"schema":{"description":"Authentication failed - invalid or missing API key","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"403":{"description":"Service account doesn't have editor access to the spreadsheet","content":{"application/json":{"schema":{"description":"Service account doesn't have editor access to the spreadsheet","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Spreadsheet, tab, or matching row not found","content":{"application/json":{"schema":{"description":"Spreadsheet, tab, or matching row not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"409":{"description":"Multiple matches, or the selected row changed","content":{"application/json":{"schema":{"description":"Multiple matches, or the selected row changed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"422":{"description":"The sheet is too large for a unique-match update; list first and update by row number","content":{"application/json":{"schema":{"description":"The sheet is too large for a unique-match update; list first and update by row number","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"500":{"description":"Google Sheets API error","content":{"application/json":{"schema":{"description":"Google Sheets API error","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/sheets/rows/search":{"post":{"summary":"List or find rows in a Google Sheet","tags":["Google Sheets"],"description":"Return a bounded, cursor-paginated page of rows from a Google Sheet.\n\nPass `where` for an exact, case-sensitive AND match across one or more columns. Google Sheets does not provide indexed structured row queries, so filtered requests scan at most 5,000 rows per call and return `nextCursor` when more rows may remain. Do not infer that no row exists until `nextCursor` is null.\n\nPass `includeCellDetails: true` to additionally return each row's `cells` map with displayed text, optional formulas, and hyperlink URLs. The existing `values` map and exact filtering are unchanged.\n\nThe cursor is opaque and can become stale if people insert, delete, or sort rows between pages. Each result includes its current Google Sheets row number.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header. The spreadsheet must be shared with the Octocom Google Sheets service account.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"spreadsheetId":{"type":"string","minLength":1},"sheetName":{"type":"string","minLength":1},"where":{"type":"object","additionalProperties":{"type":"string"}},"limit":{"type":"integer","minimum":1,"maximum":100,"default":50},"cursor":{"type":"string","minLength":1},"includeCellDetails":{"type":"boolean","default":false}},"required":["spreadsheetId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Rows returned successfully","content":{"application/json":{"schema":{"description":"Rows returned successfully","type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"rowNumber":{"type":"number","description":"One-based Google Sheets row number (the header is row 1)","example":42},"values":{"type":"object","additionalProperties":{"type":"string"},"description":"Column-header to cell-value mapping"},"cells":{"type":"object","description":"Present only when includeCellDetails is true","additionalProperties":{"type":"object","properties":{"displayText":{"type":"string"},"formula":{"type":"string"},"hyperlinks":{"type":"array","items":{"type":"string"}}},"required":["displayText","hyperlinks"]}}},"required":["rowNumber","values"]}},"nextCursor":{"type":["string","null"],"description":"Opaque cursor for the next scan page, or null when the sheet is exhausted"}},"required":["rows","nextCursor"]}}}},"400":{"description":"Invalid cursor, column, or request body","content":{"application/json":{"schema":{"description":"Invalid cursor, column, or request body","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed - invalid or missing API key","content":{"application/json":{"schema":{"description":"Authentication failed - invalid or missing API key","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"403":{"description":"Service account doesn't have access to the spreadsheet","content":{"application/json":{"schema":{"description":"Service account doesn't have access to the spreadsheet","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Spreadsheet or tab not found","content":{"application/json":{"schema":{"description":"Spreadsheet or tab not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"500":{"description":"Google Sheets API error","content":{"application/json":{"schema":{"description":"Google Sheets API error","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/conversations/human-decision-requests":{"post":{"summary":"Register a human-decision request on a conversation","tags":["Conversations"],"description":"Register a request for a human to make a decision, shown as a card with buttons in the conversation in the Octocom dashboard.\n\n**How it works:**\n- A request is unique per (conversation, key). Calling again with the same key returns the existing request instead of creating a duplicate.\n- An agent resolves it from the conversation view; the bot is then re-triggered and reads the outcome.\n- While a request is pending the conversation is not auto-closed on inactivity. If nobody resolves it within `timeoutHours` (default 48) the request expires - a terminal state, it can no longer be resolved - and the conversation is handed off to an agent so a human finds it.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header. The conversation must belong to a business within your organization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","minLength":1,"description":"The conversation UUID or publicId"},"key":{"type":"string","minLength":1,"maxLength":100,"description":"Stable identifier for this request within the conversation"},"summary":{"type":"string","minLength":1,"description":"Human-readable summary shown to the agent"},"decisions":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","minLength":1},"label":{"type":"string","minLength":1},"input":{"type":"object","properties":{"label":{"type":"string","minLength":1},"type":{"type":"string","enum":["text"],"default":"text"},"required":{"type":"boolean"}},"required":["label"],"additionalProperties":false}},"required":["key","label"],"additionalProperties":false},"minItems":1,"description":"Buttons the agent can click to resolve the request"},"info":{"type":"object","additionalProperties":{"type":"string"},"default":{},"description":"Read-only context fields shown to the agent"},"timeoutHours":{"type":"integer","minimum":1,"maximum":336,"description":"Hours a pending request may wait before the conversation is handed off to an agent (default 48)"}},"required":["conversationId","key","summary","decisions"],"additionalProperties":false}}}},"responses":{"200":{"description":"Request registered (or existing one returned)","content":{"application/json":{"schema":{"description":"Request registered (or existing one returned)","type":"object","properties":{"status":{"type":"string","enum":["pending","resolved","expired","none"],"description":"Current state of the request. \"expired\": no human resolved it within its timeout; it can no longer be resolved and the conversation was handed off to an agent."},"requestId":{"type":["string","null"],"format":"uuid"},"decision":{"type":["string","null"],"description":"Chosen decision key, once resolved"},"inputs":{"type":["object","null"],"description":"Typed inputs provided by the human, once resolved"}},"required":["status"]}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"description":"Invalid request body","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed - invalid or missing API key","content":{"application/json":{"schema":{"description":"Authentication failed - invalid or missing API key","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Conversation not found or not accessible","content":{"application/json":{"schema":{"description":"Conversation not found or not accessible","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"description":"Internal server error","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"get":{"summary":"Read the state of a human-decision request","tags":["Conversations"],"description":"Read the current state of a human-decision request by its key.\n\nReturns `{ \"status\": \"pending\" }` while awaiting a human, `{ \"status\": \"resolved\", \"decision\": ..., \"inputs\": ... }` once resolved, `{ \"status\": \"expired\" }` if no human resolved it within its timeout (terminal; the conversation was handed off), or `{ \"status\": \"none\" }` if no request with that key exists in this conversation.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string","minLength":1},"in":"query","name":"conversationId","required":true,"description":"The conversation UUID or publicId"},{"schema":{"type":"string","minLength":1},"in":"query","name":"key","required":true,"description":"The request key"}],"responses":{"200":{"description":"Current state of the request","content":{"application/json":{"schema":{"description":"Current state of the request","type":"object","properties":{"status":{"type":"string","enum":["pending","resolved","expired","none"],"description":"Current state of the request. \"expired\": no human resolved it within its timeout; it can no longer be resolved and the conversation was handed off to an agent."},"requestId":{"type":["string","null"],"format":"uuid"},"decision":{"type":["string","null"],"description":"Chosen decision key, once resolved"},"inputs":{"type":["object","null"],"description":"Typed inputs provided by the human, once resolved"}},"required":["status"]}}}},"401":{"description":"Authentication failed - invalid or missing API key","content":{"application/json":{"schema":{"description":"Authentication failed - invalid or missing API key","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Conversation not found or not accessible","content":{"application/json":{"schema":{"description":"Conversation not found or not accessible","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"description":"Internal server error","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/conversations/tags":{"post":{"summary":"Add a tag to a conversation","tags":["Conversations"],"description":"Add a tag to a conversation. If the tag doesn't exist in your organization, it will be created automatically.\n\n**How it works:**\n- Provide the conversation ID (UUID or publicId) and the tag text\n- If the tag already exists, it will be linked to the conversation\n- If the tag is already on the conversation, the operation succeeds without duplicating\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.\nThe conversation must belong to a business within your organization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","minLength":1,"description":"The conversation UUID or publicId"},"tag":{"type":"string","minLength":1,"maxLength":100,"description":"The tag text to add to the conversation"}},"required":["conversationId","tag"],"additionalProperties":false}}}},"responses":{"200":{"description":"Tag added successfully","content":{"application/json":{"schema":{"description":"Tag added successfully","type":"object","properties":{"success":{"type":"boolean","description":"Whether the operation succeeded","example":true},"message":{"type":"string","description":"Success or error message","example":"Tag added successfully"},"tagId":{"type":"string","format":"uuid","description":"ID of the created or existing tag","example":"t1234567-89ab-cdef-0123-456789abcdef"}},"required":["success","message","tagId"]}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"description":"Invalid request body","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed - invalid or missing API key","content":{"application/json":{"schema":{"description":"Authentication failed - invalid or missing API key","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Conversation not found or not accessible","content":{"application/json":{"schema":{"description":"Conversation not found or not accessible","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"description":"Internal server error","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"delete":{"summary":"Remove a tag from a conversation","tags":["Conversations"],"description":"Remove a tag from a conversation.\n\nOnly the link between the conversation and the tag is removed — the tag itself remains available to the organization and on any other conversation carrying it.\n\nReturns 404 if the conversation does not have this tag, so a removal that did nothing is not reported as a success.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","description":"Conversation UUID or public ID"},"tag":{"type":"string","minLength":1,"maxLength":100,"description":"The tag text to remove from the conversation"}},"required":["conversationId","tag"],"additionalProperties":false}}}},"responses":{"200":{"description":"Tag removed successfully","content":{"application/json":{"schema":{"description":"Tag removed successfully","type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}},"required":["success","message"]}}}},"401":{"description":"Authentication failed - invalid or missing API key","content":{"application/json":{"schema":{"description":"Authentication failed - invalid or missing API key","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Conversation not found, not accessible, or does not have this tag","content":{"application/json":{"schema":{"description":"Conversation not found, not accessible, or does not have this tag","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/conversations/events":{"post":{"summary":"Create a conversation event","tags":["Conversations"],"description":"Create a custom event for a conversation.\n\n**How it works:**\n- Provide the conversation ID (UUID or publicId) and the event text\n- The event is stored as a raw string event in the conversation timeline\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.\nThe conversation must belong to a business within your organization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","minLength":1,"description":"The conversation UUID or publicId"},"event":{"type":"string","minLength":1,"maxLength":1000,"description":"The event text to record"}},"required":["conversationId","event"],"additionalProperties":false}}}},"responses":{"200":{"description":"Event created successfully","content":{"application/json":{"schema":{"description":"Event created successfully","type":"object","properties":{"success":{"type":"boolean","description":"Whether the operation succeeded","example":true},"message":{"type":"string","description":"Success or error message","example":"Event created successfully"},"eventId":{"type":"string","format":"uuid","description":"ID of the created event","example":"e1234567-89ab-cdef-0123-456789abcdef"}},"required":["success","message","eventId"]}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"description":"Invalid request body","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed - invalid or missing API key","content":{"application/json":{"schema":{"description":"Authentication failed - invalid or missing API key","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Conversation not found or not accessible","content":{"application/json":{"schema":{"description":"Conversation not found or not accessible","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"description":"Internal server error","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/conversations/outbound-email":{"post":{"summary":"Send an outbound email and create a conversation","tags":["Conversations"],"description":"Send an outbound email from a business in your organization to an arbitrary recipient. By default the conversation is handed off. Set `handedOff: false` to let the bot handle customer replies in the same conversation.\n\n**How it works:**\n- Provide the `businessSlug` of the business that should send the email — it must have an email integration installed\n- Provide the recipient address, subject and plain-text body\n- Optionally attach a list of tags to the created conversation\n- The conversation is created with `isOutbound: true` and closed after the initial send (or queueing for asynchronous email providers). Set `closeAfterSend: false` to keep it open.\n- Closing is independent of handoff: `handedOff: false` lets customer replies reopen the conversation for the bot.\n- Zendesk creates a public ticket comment authored by the configured bot agent. Delivery uses your Zendesk requester-notification triggers.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header. The business must belong to your organization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","minLength":1,"description":"Slug of the business to send the email from"},"to":{"type":"string","format":"email","description":"Recipient email address"},"subject":{"type":"string","minLength":1,"maxLength":998,"description":"Email subject line"},"text":{"type":"string","minLength":1,"description":"Plain-text body of the email"},"handedOff":{"type":"boolean","default":true,"description":"Set false to keep the conversation under bot control and automatically answer customer replies"},"closeAfterSend":{"type":"boolean","default":true,"description":"Close after the initial send (or queueing for asynchronous providers). Independent of handedOff; set false to keep open"},"customerName":{"type":"string","description":"Optional recipient name"},"tags":{"type":"array","items":{"type":"string","minLength":1,"maxLength":100},"description":"Tags for the Octocom conversation and supported helpdesk ticket (including Zendesk)"}},"required":["businessSlug","to","subject","text"],"additionalProperties":false}}}},"responses":{"200":{"description":"Outbound email queued successfully","content":{"application/json":{"schema":{"description":"Outbound email queued successfully","type":"object","properties":{"success":{"type":"boolean","description":"Whether the operation succeeded","example":true},"conversationId":{"type":"string","format":"uuid","description":"ID of the created conversation","example":"550e8400-e29b-41d4-a716-446655440000"},"publicId":{"type":"string","description":"Public-facing ID of the created conversation","example":"CONV-2024-001234"}},"required":["success","conversationId","publicId"]}}}},"400":{"description":"Invalid request body, or business has no email integration configured","content":{"application/json":{"schema":{"description":"Invalid request body, or business has no email integration configured","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed - invalid or missing API key","content":{"application/json":{"schema":{"description":"Authentication failed - invalid or missing API key","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found or not accessible","content":{"application/json":{"schema":{"description":"Business not found or not accessible","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"description":"Internal server error","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/conversations/import":{"post":{"summary":"Import historical conversations","tags":["Conversations"],"description":"Import conversations that happened somewhere else, as a permanent record in Octocom.\n\n**What this is for:**\n- Migrating ticket history when moving to Octocom from another helpdesk\n- Recording support interactions handled in an external system that you want searchable alongside your Octocom conversations\n\n**What imported conversations are:**\nImported conversations are *archive records*, not live tickets. They are:\n- Never answered by the bot, and never routed into an agent queue\n- Read-only in the dashboard — they cannot be replied to, reassigned, tagged or reopened\n- Hidden from conversation lists by default (filter with `imported=yes` on `GET /rest/v1/conversations` to see them)\n- Excluded from bot quality scoring and from automation metrics, so migrated history will not distort your reporting\n\nIf you need a thread your agents can actually continue working, resolve it in your old system and let the customer's next reply start a normal Octocom conversation.\n\n**Timestamps:**\nEvery timestamp you supply is stored as given — conversation `createdAt`, `closedAt`, and each message's `createdAt`. Messages are ordered by their timestamps, not by array position. If you omit `closedAt` on a closed conversation, the last message's timestamp is used.\n\n**Agent attribution:**\nSet `agentEmail` on a message to attribute it to one of your Octocom users. The address must belong to a user in your organization; if it does not, the message is still imported but without attribution and a warning is returned for that item.\n\n**Idempotency:**\nSupply `externalId` — your own identifier for the record in the source system. Importing the same `externalId` into the same business again does not create a duplicate; the item comes back with status `duplicate` and the id of the conversation that already exists. This makes it safe to retry a failed or partially-applied batch.\n\n**Batching and partial success:**\nUp to 50 conversations per request. Each item is applied independently, so one bad record does not roll back the rest — the response returns a per-item result and the endpoint still answers `200`. Always inspect `results` rather than relying on the status code alone.\n\n**Rate limits:** 60 import requests per minute per organization, in addition to the shared REST API limit.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","minLength":1,"description":"Business these conversations belong to (use /rest/v1/businesses to list)"},"conversations":{"type":"array","items":{"type":"object","properties":{"externalId":{"type":"string","minLength":1,"maxLength":200,"description":"Your identifier for this record in the source system. Enables idempotent retries."},"createdAt":{"type":"string","format":"date-time","description":"When the conversation started (ISO 8601)"},"closedAt":{"type":"string","format":"date-time","description":"When the conversation was closed. Defaults to the last message's timestamp."},"closed":{"type":"boolean","default":true,"description":"Whether the conversation is closed"},"handedOff":{"type":"boolean","default":true,"description":"Whether the conversation was handled by a human agent"},"subject":{"type":"string","maxLength":1000,"description":"Subject or title"},"language":{"type":"string","minLength":2,"maxLength":10,"description":"Language code. Defaults to the business's default language."},"channel":{"type":"string","enum":["web","email","whatsapp","messenger","instagram","telegram","sms","phone-call","google-business-messages","google-reviews","trustpilot-reviews","facebook-reviews","facebookComment","facebookMention","instagramMention","instagramComment","amazon","ebay","aircall","brymec","echo","test-review","zoom-transcript","api"],"default":"email","description":"Channel the conversation happened on. Used as the default for messages that do not specify one."},"customer":{"type":"object","properties":{"email":{"type":"string","format":"email"},"name":{"type":"string","maxLength":500},"phone":{"type":"string","maxLength":100}},"additionalProperties":false,"description":"Customer this conversation belongs to"},"assigneeEmail":{"type":"string","format":"email","description":"Email of the Octocom user who handled it. Ignored with a warning if no such user exists in your organization."},"tags":{"type":"array","items":{"type":"string","minLength":1,"maxLength":100},"maxItems":50,"description":"Tags to apply. Tags that do not exist yet are created."},"metadata":{"type":"object","additionalProperties":{"type":"string","maxLength":50000},"description":"Arbitrary key-value pairs to attach, e.g. a link back to the source ticket"},"messages":{"type":"array","items":{"type":"object","properties":{"sender":{"type":"string","enum":["customer","agent","bot"],"description":"Who sent this message"},"text":{"type":"string","minLength":1,"maxLength":100000,"description":"Message body as plain text or markdown"},"createdAt":{"type":"string","format":"date-time","description":"When the message was sent (ISO 8601)"},"channel":{"type":"string","enum":["web","email","whatsapp","messenger","instagram","telegram","sms","phone-call","google-business-messages","google-reviews","trustpilot-reviews","facebook-reviews","facebookComment","facebookMention","instagramMention","instagramComment","amazon","ebay","aircall","brymec","echo","test-review","zoom-transcript","api"],"description":"Defaults to the conversation's channel if omitted"},"agentEmail":{"type":"string","format":"email","description":"For agent messages, the Octocom user who sent it"}},"required":["sender","text","createdAt"],"additionalProperties":false},"minItems":1,"maxItems":500,"description":"Messages in the conversation, at least one"}},"required":["createdAt","messages"],"additionalProperties":false},"minItems":1,"maxItems":50,"description":"Conversations to import (max 50 per request)"}},"required":["businessSlug","conversations"],"additionalProperties":false}}}},"responses":{"200":{"description":"Batch processed. Inspect per-item results — some items may have failed.","content":{"application/json":{"schema":{"description":"Batch processed. Inspect per-item results — some items may have failed.","type":"object","properties":{"data":{"type":"object","properties":{"imported":{"type":"number","description":"Count created"},"duplicates":{"type":"number","description":"Count skipped as already imported"},"failed":{"type":"number","description":"Count that errored"},"results":{"type":"array","items":{"type":"object","properties":{"index":{"type":"number","description":"Position of this item in the submitted array"},"externalId":{"type":["string","null"],"description":"The externalId you supplied, if any"},"status":{"type":"string","enum":["imported","duplicate","error"],"description":"'imported' if created, 'duplicate' if this externalId was already imported into this business, 'error' if it failed"},"conversationId":{"type":"string","format":"uuid","description":"Set for 'imported' and 'duplicate'"},"publicId":{"type":"string","description":"Set for 'imported' and 'duplicate'"},"warnings":{"type":"array","items":{"type":"string"},"description":"Non-fatal issues, e.g. an agent email that is not a user in your organization"},"error":{"type":"string","description":"Failure reason, set only for 'error'"}},"required":["index","status"]}}},"required":["imported","duplicates","failed","results"]}},"required":["data"]}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"description":"Invalid request body","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed - invalid or missing API key","content":{"application/json":{"schema":{"description":"Authentication failed - invalid or missing API key","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found or not accessible","content":{"application/json":{"schema":{"description":"Business not found or not accessible","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"description":"Rate limit exceeded","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/conversations/{id}/metadata":{"post":{"summary":"Set a metadata key-value pair for a conversation","tags":["Conversations"],"description":"Store a metadata key-value pair scoped to a specific conversation.\n\n**Limitations:**\n- Key: maximum 200 characters\n- Value: maximum 50,000 characters\n- Maximum 200 pairs per conversation\n\nIf the key already exists, the value is updated.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string","minLength":1,"maxLength":200,"description":"Metadata key (max 200 chars)"},"value":{"type":"string","maxLength":50000,"description":"Value to store (max 50,000 chars)"}},"required":["key","value"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Conversation UUID"}],"responses":{"200":{"description":"Value stored successfully","content":{"application/json":{"schema":{"description":"Value stored successfully","type":"object","properties":{"success":{"type":"boolean","description":"Whether the operation succeeded"},"message":{"type":"string","description":"Success or error message"},"key":{"type":"string","description":"The metadata key"},"value":{"type":"string","description":"The metadata value"}},"required":["success","message"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Conversation not found","content":{"application/json":{"schema":{"description":"Conversation not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"429":{"description":"Pair limit exceeded","content":{"application/json":{"schema":{"description":"Pair limit exceeded","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"get":{"summary":"Get all metadata key-value pairs for a conversation","tags":["Conversations"],"description":"Retrieve all metadata key-value pairs for a specific conversation.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Conversation UUID"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Metadata key","maxLength":200},"value":{"type":"string","description":"Metadata value","maxLength":50000},"createdAt":{"type":"string","format":"date-time","description":"When first created"},"updatedAt":{"type":"string","format":"date-time","description":"When last updated"}},"required":["key","value","createdAt","updatedAt"]}}}}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/conversations/{id}/metadata/{key}":{"get":{"summary":"Get a specific metadata value for a conversation","tags":["Conversations"],"description":"Retrieve a specific metadata value by key for a conversation.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Conversation UUID"},{"schema":{"type":"string","minLength":1},"in":"path","name":"key","required":true,"description":"Metadata key"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Key not found","content":{"application/json":{"schema":{"description":"Key not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"delete":{"summary":"Delete a metadata key from a conversation","tags":["Conversations"],"description":"Delete a specific metadata key-value pair from a conversation.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Conversation UUID"},{"schema":{"type":"string","minLength":1},"in":"path","name":"key","required":true,"description":"Metadata key to delete"}],"responses":{"200":{"description":"Key deleted successfully","content":{"application/json":{"schema":{"description":"Key deleted successfully","type":"object","properties":{"success":{"type":"boolean","description":"Whether the operation succeeded"},"message":{"type":"string","description":"Success or error message"},"key":{"type":"string","description":"The metadata key"},"value":{"type":"string","description":"The metadata value"}},"required":["success","message"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Key not found","content":{"application/json":{"schema":{"description":"Key not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/conversations/{id}/notes":{"post":{"summary":"Add an internal note to a conversation","tags":["Conversations"],"description":"Add an internal note to a conversation.\n\nNotes are visible to your agents in the Octocom dashboard and are never shown to the customer.\n\nNotes created through this endpoint are attributed to `api` rather than to a user, and appear in the conversation timeline labelled as such.\n\nThe `id` path parameter accepts either the conversation UUID or its public ID.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":50000,"description":"Note body (max 50,000 chars)"},"authorType":{"type":"string","enum":["api","automation"],"default":"api","description":"How the note is labelled in the dashboard. Defaults to 'api'."}},"required":["text"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true,"description":"Conversation UUID or public ID"}],"responses":{"200":{"description":"Note created successfully","content":{"application/json":{"schema":{"description":"Note created successfully","type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}},"required":["success","message"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Conversation not found","content":{"application/json":{"schema":{"description":"Conversation not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}},"get":{"summary":"List internal notes on a conversation","tags":["Conversations"],"description":"Retrieve every internal note on a conversation, newest first.\n\nThe `id` path parameter accepts either the conversation UUID or its public ID.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true,"description":"Conversation UUID or public ID"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"description":"Successful response","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Note ID"},"text":{"type":"string","description":"Note body"},"createdAt":{"type":"string","format":"date-time","description":"When the note was created"},"authorType":{"type":"string","enum":["user","api","automation"],"description":"Who wrote the note: an agent in the dashboard (user), this API (api), or Python running in Octocom (automation)"},"authorName":{"type":"string","description":"Display name of the author. The agent's full name for user notes, otherwise a label derived from authorType."}},"required":["id","text","createdAt","authorType","authorName"]}}},"required":["data"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Conversation not found","content":{"application/json":{"schema":{"description":"Conversation not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/conversations/{id}/collected-data":{"post":{"summary":"Set a collected data result for a conversation","tags":["Conversations"],"description":"Store a collected data result (key-value) for a conversation — the same store the AI data collection feature writes to. Results appear in the conversation sidebar, Metrics → Custom Data charts, inbox \"Collected Data\" filters, and campaign analytics (on campaign calls, the `success` key with value `true`/`false` drives the campaign success-rate KPI).\n\n**Limitations:**\n- Key: maximum 200 characters\n- Value: maximum 2,000 characters\n- Maximum 50 keys per conversation\n\nIf the key already exists, its value is replaced (one value per key).\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header. The conversation must belong to a business within your organization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string","minLength":1,"maxLength":200,"description":"Result key (max 200 chars)"},"value":{"type":"string","minLength":1,"maxLength":2000,"description":"Value to store (max 2,000 chars)"}},"required":["key","value"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Conversation UUID"}],"responses":{"200":{"description":"Value stored successfully","content":{"application/json":{"schema":{"description":"Value stored successfully","type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"key":{"type":"string"},"value":{"type":"string"}},"required":["success","message"]}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"description":"Invalid request","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Conversation not found","content":{"application/json":{"schema":{"description":"Conversation not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"429":{"description":"Key limit exceeded","content":{"application/json":{"schema":{"description":"Key limit exceeded","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/customers/{id}":{"patch":{"summary":"Update a customer and optionally set metadata","tags":["Customers"],"description":"Update a customer's standard fields (name, phone, email) and/or upsert metadata key-value pairs in a single call.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"},"metadata":{"type":"object","additionalProperties":{"type":"string"}}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Customer UUID"}],"responses":{"200":{"description":"Default Response"}}}},"/rest/v1/customers/{id}/metadata":{"post":{"summary":"Set a metadata key-value pair for a customer","tags":["Customers"],"description":"Store or update a metadata key-value pair scoped to a specific customer.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string","minLength":1,"maxLength":200,"description":"Metadata key"},"value":{"type":"string","minLength":1,"maxLength":50000,"description":"Metadata value"}},"required":["key","value"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true,"description":"Customer UUID"}],"responses":{"200":{"description":"Default Response"}}}},"/rest/v1/conversations/{id}/messages":{"post":{"summary":"Append a customer message to a conversation","tags":["Conversations"],"description":"Add a customer message to an existing conversation created through `POST /rest/v1/conversations`. Use this for every reply after the first.\n\n**Returns 202.** The message is stored and the bot has been triggered; the reply arrives on your webhook. `isHandedOff` tells you whether a human currently owns the conversation. When the conversation was created with `allowAgentReplies` enabled, agent replies arrive on the same webhook as bot replies.\n\n**Idempotency.** Supply `externalId` — your own id for this message. Replaying it returns the original message id and stores nothing.\n\n**Attachments:** Optional `files` arrays contain `{url, name}` entries. Text may be omitted for file-only messages. URLs must be directly fetchable without auth headers (signed URLs work); redirects are not followed. Maximum 10 files per request, 20 MiB per file, 50 MiB total. Files are downloaded and stored before messages are created.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header. The conversation must have been created via the API by your organization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","maxLength":10000,"default":"","description":"Message text; may be omitted when files are provided"},"files":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","format":"uri","maxLength":8192},"name":{"type":"string","minLength":1,"maxLength":255}},"required":["url","name"],"additionalProperties":false},"maxItems":10,"description":"Files to download and store as native attachments: {url, name}. Direct public or signed HTTP(S) URLs only; no auth headers or redirects. Up to 10 files/request, 20 MiB/file, 50 MiB total."},"externalId":{"type":"string","minLength":1,"maxLength":200,"description":"Your identifier for this message. Enables idempotent retries."},"timestamp":{"type":"string","format":"date-time","description":"When the customer sent it (ISO 8601)"}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true,"description":"Conversation ID (UUID or publicId)"}],"responses":{"202":{"description":"Message accepted","content":{"application/json":{"schema":{"description":"Message accepted","type":"object","properties":{"data":{"type":"object","properties":{"messageId":{"type":"string"},"conversationId":{"type":"string"},"isHandedOff":{"type":"boolean"}}}},"required":["data"]}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"description":"Invalid request body","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed - invalid or missing API key","content":{"application/json":{"schema":{"description":"Authentication failed - invalid or missing API key","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"403":{"description":"Conversation not created via API by your organization","content":{"application/json":{"schema":{"description":"Conversation not created via API by your organization","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Conversation not found","content":{"application/json":{"schema":{"description":"Conversation not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"409":{"description":"The externalId is still being processed; retry shortly","content":{"application/json":{"schema":{"description":"The externalId is still being processed; retry shortly","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"429":{"description":"Message limit exceeded for this conversation","content":{"application/json":{"schema":{"description":"Message limit exceeded for this conversation","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/bot/conversations":{"post":{"summary":"Start a bot conversation","tags":["Bot Conversations"],"description":"Start a new conversation with the Octocom bot.\n\n**How it works:**\n- Send input text (simulating customer input) and receive the bot's response\n- The bot processes your input using the business's knowledge base, workflows, and configuration\n- You cannot create agent messages or bypass the bot - all responses come from the bot\n\n**Async mode:**\n- Set `async: true` to return immediately without waiting for the bot response\n- Poll `GET /rest/v1/conversations/:id` to retrieve the conversation with the bot's response\n- Useful for avoiding timeouts with complex queries or high latency\n\n**Metadata:**\n- Optionally attach key-value metadata pairs at creation time\n- Same limits as the metadata endpoint: keys max 200 chars, values max 50,000 chars, max 200 pairs per conversation\n\n**Rate limits:**\n- Maximum 100 conversations per hour per organization\n- Maximum 50 messages per conversation\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","minLength":1,"description":"Business identifier (use /rest/v1/businesses to list)"},"message":{"type":"string","minLength":1,"maxLength":5000,"description":"The input to send to the bot (max 5000 characters)"},"async":{"type":"boolean","default":false,"description":"When true, returns immediately without waiting for bot response. Poll GET /rest/v1/conversations/:id to retrieve the bot's response."},"customer":{"type":"object","properties":{"email":{"type":"string","format":"email","description":"Customer email to associate with the conversation"},"name":{"type":"string","description":"Customer name"},"phone":{"type":"string","description":"Customer phone number"}},"additionalProperties":false,"description":"Optional customer information to link to the conversation"},"metadata":{"type":"object","additionalProperties":{"type":"string","maxLength":50000},"description":"Optional key-value metadata pairs to attach to the conversation (max 200 pairs, keys max 200 chars, values max 50,000 chars)"}},"required":["businessSlug","message"],"additionalProperties":false}}}},"responses":{"200":{"description":"Conversation started successfully","content":{"application/json":{"schema":{"description":"Conversation started successfully","type":"object","properties":{"data":{"type":"object","properties":{"conversationId":{"type":"string","format":"uuid","description":"Unique conversation identifier"},"publicId":{"type":"string","description":"Human-readable conversation ID","example":"CONV-2024-001234"},"customerMessageId":{"type":"string","format":"uuid","description":"ID of the customer message that was created"},"botResponse":{"type":"string","description":"The bot's response (only present in synchronous mode when bot responds)"}},"required":["conversationId","publicId","customerMessageId"]}},"required":["data"]}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"description":"Invalid request body","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed - invalid or missing API key","content":{"application/json":{"schema":{"description":"Authentication failed - invalid or missing API key","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"408":{"description":"Bot response timeout (30 seconds, only in synchronous mode)","content":{"application/json":{"schema":{"description":"Bot response timeout (30 seconds, only in synchronous mode)","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded (100 conversations per hour)","content":{"application/json":{"schema":{"description":"Rate limit exceeded (100 conversations per hour)","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/bot/conversations/{id}/messages":{"post":{"summary":"Send a message to the bot","tags":["Bot Conversations"],"description":"Send a follow-up message in an existing bot conversation and receive the bot's response.\n\n**How it works:**\n- Send input text and receive the bot's response\n- The bot maintains conversation context from previous messages\n- You cannot create agent messages or bypass the bot - all responses come from the bot\n\n**Async mode:**\n- Set `async: true` to return immediately without waiting for the bot response\n- Poll `GET /rest/v1/conversations/:id` to retrieve the conversation with the bot's response\n- Useful for avoiding timeouts with complex queries or high latency\n\n**Rate limits:**\n- Maximum 50 messages per conversation\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header.\nThe conversation must have been created via this API by your organization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","minLength":1,"maxLength":5000,"description":"The input to send to the bot (max 5000 characters)"},"async":{"type":"boolean","default":false,"description":"When true, returns immediately without waiting for bot response. Poll GET /rest/v1/conversations/:id to retrieve the bot's response."}},"required":["message"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true,"description":"Conversation ID (UUID or publicId)"}],"responses":{"200":{"description":"Message sent successfully","content":{"application/json":{"schema":{"description":"Message sent successfully","type":"object","properties":{"data":{"type":"object","properties":{"customerMessageId":{"type":"string","format":"uuid","description":"ID of the customer message that was created"},"botResponse":{"type":"string","description":"The bot's response (only present in synchronous mode when bot responds)"}},"required":["customerMessageId"]}},"required":["data"]}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"description":"Invalid request body","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed - invalid or missing API key","content":{"application/json":{"schema":{"description":"Authentication failed - invalid or missing API key","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"403":{"description":"Conversation not created via API by your organization","content":{"application/json":{"schema":{"description":"Conversation not created via API by your organization","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Conversation not found","content":{"application/json":{"schema":{"description":"Conversation not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"408":{"description":"Bot response timeout (30 seconds, only in synchronous mode)","content":{"application/json":{"schema":{"description":"Bot response timeout (30 seconds, only in synchronous mode)","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"429":{"description":"Message limit exceeded (50 messages per conversation)","content":{"application/json":{"schema":{"description":"Message limit exceeded (50 messages per conversation)","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/campaigns/{campaignId}/recipients":{"post":{"summary":"Append recipients to a continuous phone campaign","tags":["Campaigns"],"description":"Append recipients to a continuous outbound phone campaign's calling queue. Built for automation (e.g. a recurring job that finds customers to call and feeds them in).\n\n**How it works:**\n- The campaign must be `continuous` (one-shot campaigns have a fixed list) and not completed/canceled\n- Phone numbers must be E.164 (`+15551234567`); common separators are stripped\n- Duplicates are skipped and counted in `skippedDuplicates` — both repeats within the request and numbers already in the campaign's ACTIVE queue (pending or being called); a number whose earlier pass already finished CAN be appended again\n- `dedupeKey` (optional, e.g. `\"order:123\"`) dedupes harder: a key already seen in this campaign — including on finished recipients — is skipped forever. Use it when feeding from an external source you re-poll, so each unit of work is called at most once; vary the key to intentionally re-contact\n- `context` is free text shown to the calling bot verbatim; `metadata` is an arbitrary JSON object surfaced only to custom python actions and event handlers, never to the bot\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header. The campaign must belong to your organization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"recipients":{"type":"array","items":{"type":"object","properties":{"phone":{"type":"string","minLength":1},"name":{"type":"string","maxLength":500,"nullable":true},"context":{"type":"string","maxLength":2000,"nullable":true},"dedupeKey":{"type":"string","minLength":1,"maxLength":200,"nullable":true},"metadata":{"type":"object","additionalProperties":{}}},"required":["phone"],"additionalProperties":false},"minItems":1,"maxItems":10000,"description":"Recipients to append (at most 10000 per request)"}},"required":["recipients"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"campaignId","required":true,"description":"Campaign ID"}],"responses":{"200":{"description":"Recipients appended","content":{"application/json":{"schema":{"description":"Recipients appended","type":"object","properties":{"added":{"type":"number","description":"How many recipients were added to the queue","example":42},"skippedDuplicates":{"type":"number","description":"How many recipients were skipped as duplicates — the phone number or dedupeKey repeats earlier in this request, the phone is already in the campaign's active queue (pending or being called), or the dedupeKey was already used in this campaign at any point","example":3}},"required":["added","skippedDuplicates"]}}}},"400":{"description":"Campaign is not continuous, or is completed/canceled, or the request body is invalid","content":{"application/json":{"schema":{"description":"Campaign is not continuous, or is completed/canceled, or the request body is invalid","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed - invalid or missing API key","content":{"application/json":{"schema":{"description":"Authentication failed - invalid or missing API key","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Campaign not found or not accessible","content":{"application/json":{"schema":{"description":"Campaign not found or not accessible","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"description":"Internal server error","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/browser-sessions/{browserSessionId}/purchases":{"post":{"summary":"Record a purchase for a browser session","tags":["Browser Sessions"],"description":"Record a purchase associated with a browser session.\n\nThis endpoint allows you to track purchases made by users during their browser session.\nThis endpoint is called from websites and does not require authentication, only rate limiting.\n\n**Rate Limits:** Maximum 100 requests per minute per IP address.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"lineItems":{"anyOf":[{"type":"array","items":{}},{"type":"object","additionalProperties":{}}],"description":"Purchase line items (will be stored as JSON)"},"totalPrice":{"type":"number","exclusiveMinimum":true,"minimum":0,"description":"Total price of the purchase"},"currencyCode":{"type":"string","minLength":1,"description":"Currency code (e.g., 'USD', 'EUR')"},"provider":{"type":"string","minLength":1,"default":"api","description":"Provider identifier (e.g., 'shopify', 'api')"},"orderId":{"type":"string","minLength":1,"description":"Optional merchant-defined order identifier for reconciliation (any string, e.g. '#1042' or 'order_abc123')."}},"required":["lineItems","totalPrice","currencyCode"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"browserSessionId","required":true,"description":"Browser session UUID"}],"responses":{"201":{"description":"Purchase recorded successfully","content":{"application/json":{"schema":{"description":"Purchase recorded successfully","type":"object","properties":{"success":{"type":"boolean","description":"Whether the operation succeeded","example":true},"message":{"type":"string","description":"Success message","example":"Purchase recorded successfully"},"id":{"type":"string","format":"uuid","description":"ID of the created purchase record"}},"required":["success","message","id"]}}}},"400":{"description":"Invalid request body","content":{"application/json":{"schema":{"description":"Invalid request body","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Browser session not found","content":{"application/json":{"schema":{"description":"Browser session not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"description":"Rate limit exceeded","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/products/search":{"post":{"summary":"Search products","tags":["Products"],"description":"Full storefront product search for a business in your organization.\n\nRuns the complete hybrid ranking pipeline and returns the raw product objects (exactly as ingested from your store feed) in reranked relevance order — highest-scoring first. Use this for a submitted search query; use `/rest/v1/products/search/suggest` for search-as-you-type.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header. The business must belong to your organization.\n\n**Rate limit:** 60 requests per minute per organization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","minLength":1,"description":"Business identifier (required)"},"query":{"type":"string","minLength":1,"maxLength":200,"description":"Search query"},"topK":{"type":"integer","minimum":1,"maximum":300,"description":"Maximum number of results (default 20)"},"filters":{"type":"object","properties":{"inStock":{"type":"boolean"},"onSale":{"type":"boolean"},"minPrice":{"type":"number"},"maxPrice":{"type":"number"}},"additionalProperties":false}},"required":["businessSlug","query"],"additionalProperties":false}}}},"responses":{"200":{"description":"Search results","content":{"application/json":{"schema":{"description":"Search results","type":"object","properties":{"data":{"type":"object","properties":{"results":{"type":"array","description":"Ranked results in reranked relevance order (highest score first).","items":{"type":"object","properties":{"id":{"type":"string","description":"Product ID"},"score":{"type":"number","description":"Rerank relevance score"},"product":{"type":["object","null"],"additionalProperties":true,"description":"The untouched feed product (TurboPuffer rawProduct). Null when the raw JSON is missing/unparseable."}},"required":["id","score","product"]}},"relaxed":{"type":"boolean","description":"Whether filters were relaxed to return results"},"confidence":{"type":"string","enum":["confident","partial","uncertain"],"description":"\"partial\" when the shopper named a specific product line or model (\"ferro geo\", \"eldorado home\") and no result contains it - the catalogue does not carry that product and these are its nearest neighbours; show them under a \"no exact match, similar products\" heading. \"uncertain\" when nothing about this search could be verified at all (no understood intent, no keyword matches, no corroborated signal on the top result) - show an honest empty/low-confidence state instead of presenting results as solid matches"},"redirect":{"type":"object","description":"A merchant keyword-to-URL redirect that matched this query. The search still ran and `results` is populated as usual - an API consumer has no page to navigate, and returning an empty result set because a redirect matched would silently break an existing integration. Surface the link alongside the results if you want the behaviour a storefront gets.","properties":{"trigger":{"type":"string","description":"The merchant's own spelling of the keyword that matched"},"url":{"type":"string","description":"Where the merchant wants this query sent"},"matchMode":{"type":"string","enum":["exact","contains"],"description":"Whether the rule matched the whole query or a phrase inside it"},"action":{"type":"string","enum":["banner"],"description":"Always `banner` on this API, whatever the business configured - `navigate` exists only on the storefront surface, which has a page to move"}},"required":["trigger","url","matchMode","action"]}},"required":["results","relaxed","confidence"]}},"required":["data"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"description":"Rate limit exceeded","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/products/search/suggest":{"post":{"summary":"Search-as-you-type suggestions","tags":["Products"],"description":"Instant search panel for a business in your organization — query completions, a handful of top-matching products, and category/brand facets.\n\nDesigned to be called per keystroke (debounced) as the shopper types. It is much cheaper than full search (a keyword probe, no LLM).\n\nWhen the typed text contains a SKU or part number, products matching it exactly are returned first — the same known-item behaviour as full search.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header. The business must belong to your organization.\n\n**Rate limit:** 600 requests per minute per organization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","minLength":1,"description":"Business identifier (required)"},"query":{"type":"string","minLength":1,"maxLength":200,"description":"Partial search query"},"limit":{"type":"integer","minimum":1,"maximum":10,"description":"Maximum number of query suggestions (default 8)"},"filters":{"type":"object","properties":{"inStock":{"type":"boolean"},"onSale":{"type":"boolean"},"minPrice":{"type":"number"},"maxPrice":{"type":"number"}},"additionalProperties":false}},"required":["businessSlug","query"],"additionalProperties":false}}}},"responses":{"200":{"description":"Instant search panel","content":{"application/json":{"schema":{"description":"Instant search panel","type":"object","properties":{"data":{"type":"object","properties":{"suggestions":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string"},"role":{"type":"string","enum":["category","brand","attribute"]}},"required":["text","role"]}},"products":{"type":"array","description":"Top probe-hit products in probe-relevance order.","items":{"type":"object","properties":{"id":{"type":"string","description":"Product ID"},"product":{"type":["object","null"],"additionalProperties":true,"description":"The untouched feed product (TurboPuffer rawProduct). Null when the raw JSON is missing/unparseable."}},"required":["id","product"]}},"categories":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string"},"url":{"type":"string"},"count":{"type":"number"}},"required":["text","url","count"]}},"brands":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string"},"url":{"type":"string"},"count":{"type":"number"}},"required":["text","url","count"]}},"totalCount":{"type":["number","null"],"description":"Estimated total keyword matches for the query, or null"}},"required":["suggestions","products","categories","brands","totalCount"]}},"required":["data"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"description":"Rate limit exceeded","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/products/search/panel":{"post":{"summary":"Search products with panel data","tags":["Products"],"description":"Full storefront product search returning the same payload as `/rest/v1/products/search/suggest` — but ranked by the complete pipeline rather than the search-as-you-type probe.\n\nUse this for a **submitted** query when you render the surrounding search furniture yourself: it answers with the ranked products *and* the category/brand facets, the total match count, and refinement suggestions, in one request. Use `/suggest` for the per-keystroke dropdown, and `/products/search` when you only need the ranked products.\n\nCompared to `/suggest`: same fields, better ranking (hybrid retrieval, LLM query understanding, rerank, relaxation), and correspondingly slower and more expensive — this is a full search, not a keyword probe. `products` additionally carries a `score`, and the response adds `relaxed`.\n\nFacet chips are only returned for values your feed gives a navigable URL for; `categories` and `brands` come back empty when it carries none.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header. The business must belong to your organization.\n\n**Rate limit:** 60 requests per minute per organization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","minLength":1,"description":"Business identifier (required)"},"query":{"type":"string","minLength":1,"maxLength":200,"description":"Search query"},"topK":{"type":"integer","minimum":1,"maximum":300,"description":"Maximum number of results (default 20)"},"filters":{"type":"object","properties":{"inStock":{"type":"boolean"},"onSale":{"type":"boolean"},"minPrice":{"type":"number"},"maxPrice":{"type":"number"}},"additionalProperties":false}},"required":["businessSlug","query"],"additionalProperties":false}}}},"responses":{"200":{"description":"Search results with panel data","content":{"application/json":{"schema":{"description":"Search results with panel data","type":"object","properties":{"data":{"type":"object","properties":{"products":{"type":"array","description":"Ranked results in reranked relevance order (highest score first).","items":{"type":"object","properties":{"id":{"type":"string","description":"Product ID"},"score":{"type":"number","description":"Rerank relevance score"},"product":{"type":["object","null"],"additionalProperties":true,"description":"The untouched feed product (TurboPuffer rawProduct). Null when the raw JSON is missing/unparseable."}},"required":["id","score","product"]}},"suggestions":{"type":"array","description":"Ways to narrow this search, as full query strings to submit. Refinements only — there is no partially-typed word to complete.","items":{"type":"object","properties":{"text":{"type":"string"},"role":{"type":"string","enum":["category","brand","attribute"]}},"required":["text","role"]}},"categories":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string"},"url":{"type":"string"},"count":{"type":"number"}},"required":["text","url","count"]}},"brands":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string"},"url":{"type":"string"},"count":{"type":"number"}},"required":["text","url","count"]}},"totalCount":{"type":["number","null"],"description":"Estimated total keyword matches for the query, or null. Not the length of `products`, which is capped by `topK`."},"relaxed":{"type":"boolean","description":"Whether filters were relaxed to return results"},"confidence":{"type":"string","enum":["confident","partial","uncertain"],"description":"\"partial\" when the shopper named a specific product line or model (\"ferro geo\", \"eldorado home\") and no result contains it - the catalogue does not carry that product and these are its nearest neighbours; show them under a \"no exact match, similar products\" heading. \"uncertain\" when nothing about this search could be verified at all (no understood intent, no keyword matches, no corroborated signal on the top result) - show an honest empty/low-confidence state instead of presenting results as solid matches"},"redirect":{"type":"object","description":"A merchant keyword-to-URL redirect that matched this query. The search still ran; render the link alongside the panel rather than navigating.","properties":{"trigger":{"type":"string"},"url":{"type":"string"},"matchMode":{"type":"string","enum":["exact","contains"]},"action":{"type":"string","enum":["banner"]}},"required":["trigger","url","matchMode","action"]}},"required":["products","suggestions","categories","brands","totalCount","relaxed","confidence"]}},"required":["data"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"description":"Rate limit exceeded","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/products/search/voice":{"post":{"summary":"Search products by voice","tags":["Products"],"description":"Transcribe a recorded spoken query and search for it, in one request.\n\nSend one short recording (a single spoken utterance — this is not a streaming endpoint). It is transcribed, and the transcript is fed to the same pipeline as `/rest/v1/products/search`, so `results` has the identical shape. Recognition is biased with your catalogue's own mined vocabulary, so brand and product names survive.\n\n**Audio** goes in the `audio` field, base64-encoded (a `data:` URL is also accepted). Supported types: audio/webm, audio/ogg, audio/mp4, audio/mpeg, audio/wav, audio/x-wav, audio/aac, audio/flac. Maximum 1 MB decoded — roughly 15 seconds of speech, which is far more than a search query needs.\n\n**Language** is detected between a small set of candidates rather than freely, which is materially more accurate on a three-word utterance. Omit `language` to use the business's configured voice languages (or its declared language plus English); pass a single code to state it outright, or a comma-separated list to detect between them.\n\n**No speech** is a normal outcome, not an error: an unusable recording answers `200` with `noSpeech: true` and no results. Say \"didn't catch that\" rather than \"no products match\".\n\nUnlike the storefront widget's mic, this endpoint does not require the dashboard's **Voice search** switch — that toggle governs what is exposed to shoppers on your storefront, while this surface is your own authenticated backend.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header. The business must belong to your organization.\n\n**Rate limit:** 30 requests per minute per organization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","minLength":1,"description":"Business identifier (required)"},"audio":{"type":"string","minLength":1,"description":"Base64-encoded audio, or a base64 `data:` URL"},"mimeType":{"type":"string","description":"Audio content type, e.g. \"audio/webm\" (default) or \"audio/wav\". Ignored when `audio` is a data URL that declares its own."},"mode":{"type":"string","enum":["search","transcribe"],"description":"`search` (default) returns the transcript and its results together. `transcribe` returns only the transcript — for a dictate-and-review flow where the query is submitted separately."},"language":{"type":"string","minLength":2,"maxLength":60,"description":"One ISO 639-1 code (\"el\"), or a comma-separated set to detect between (\"lt,en,ru\"). Defaults to the business's configured voice languages."},"topK":{"type":"integer","minimum":1,"maximum":300,"description":"Maximum number of results (default 20)"}},"required":["businessSlug","audio"],"additionalProperties":false}}}},"responses":{"200":{"description":"Transcript and search results","content":{"application/json":{"schema":{"description":"Transcript and search results","type":"object","properties":{"data":{"type":"object","properties":{"transcript":{"type":"string","description":"What was heard. Empty string when `noSpeech` is true."},"noSpeech":{"type":"boolean","description":"True when the recording carried nothing recognizable — distinct from a query that matched no products."},"language":{"type":"string","description":"The language actually heard (the detector's pick when detection ran)."},"results":{"type":"array","description":"Ranked results in reranked relevance order (highest score first). Always empty in `transcribe` mode, or when `noSpeech` is true.","items":{"type":"object","properties":{"id":{"type":"string","description":"Product ID"},"score":{"type":"number","description":"Rerank relevance score"},"product":{"type":["object","null"],"additionalProperties":true,"description":"The untouched feed product (TurboPuffer rawProduct). Null when the raw JSON is missing/unparseable."}},"required":["id","score","product"]}},"relaxed":{"type":"boolean","description":"Whether the query was relaxed to return results"},"confidence":{"type":"string","enum":["confident","partial","uncertain"],"description":"\"partial\" when the shopper named a specific product line or model (\"ferro geo\", \"eldorado home\") and no result contains it - the catalogue does not carry that product and these are its nearest neighbours; show them under a \"no exact match, similar products\" heading. \"uncertain\" when nothing about this search could be verified at all (no understood intent, no keyword matches, no corroborated signal on the top result) - show an honest empty/low-confidence state instead of presenting results as solid matches"}},"required":["transcript","noSpeech","language","results","relaxed","confidence"]}},"required":["data"]}}}},"400":{"description":"Malformed, empty, oversized, or unsupported upload","content":{"application/json":{"schema":{"description":"Malformed, empty, oversized, or unsupported upload","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"description":"Rate limit exceeded","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/products/search/image":{"post":{"summary":"Search products by image","tags":["Products"],"description":"Find the products in your catalogue that match a photo.\n\nThe image is read by a vision model and its understanding drives the same ranking pipeline as `/rest/v1/products/search`, so `results` has the identical shape. Pass `query` alongside the photo to refine it (\"in blue\", \"cheaper\") rather than replace it.\n\n**The image** goes in the `image` field, base64-encoded (a `data:` URL is also accepted). Supported types: image/jpeg, image/png, image/webp, image/gif. Maximum 6 MB decoded. Downscale large photos to around 1024px on the long edge first — it costs nothing in quality and is most of the latency on a slow connection.\n\n**`saw`** is what the model read off the photo. Show it to the shopper (\"Showing results for: office chair\") so a misread image is legible as a misread rather than looking like an empty catalogue.\n\nThis endpoint spends a vision call per request and cannot start retrieving until the photo is understood, so it is the slowest of the search modes.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header. The business must belong to your organization.\n\n**Rate limit:** 30 requests per minute per organization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","minLength":1,"description":"Business identifier (required)"},"image":{"type":"string","minLength":1,"description":"Base64-encoded image, or a base64 `data:` URL"},"mimeType":{"type":"string","description":"Image content type, e.g. \"image/jpeg\" (default) or \"image/png\". Ignored when `image` is a data URL that declares its own."},"query":{"type":"string","maxLength":200,"description":"Optional text refining the photo, e.g. \"in blue\". Refines the photo's understanding; it does not replace it."},"topK":{"type":"integer","minimum":1,"maximum":300,"description":"Maximum number of results (default 20)"}},"required":["businessSlug","image"],"additionalProperties":false}}}},"responses":{"200":{"description":"Search results for the photo","content":{"application/json":{"schema":{"description":"Search results for the photo","type":"object","properties":{"data":{"type":"object","properties":{"results":{"type":"array","description":"Ranked results in reranked relevance order (highest score first).","items":{"type":"object","properties":{"id":{"type":"string","description":"Product ID"},"score":{"type":"number","description":"Rerank relevance score"},"product":{"type":["object","null"],"additionalProperties":true,"description":"The untouched feed product (TurboPuffer rawProduct). Null when the raw JSON is missing/unparseable."}},"required":["id","score","product"]}},"relaxed":{"type":"boolean","description":"Whether the query was relaxed to return results"},"confidence":{"type":"string","enum":["confident","partial","uncertain"],"description":"\"partial\" when the shopper named a specific product line or model (\"ferro geo\", \"eldorado home\") and no result contains it - the catalogue does not carry that product and these are its nearest neighbours; show them under a \"no exact match, similar products\" heading. \"uncertain\" when nothing about this search could be verified at all (no understood intent, no keyword matches, no corroborated signal on the top result) - show an honest empty/low-confidence state instead of presenting results as solid matches"},"saw":{"type":"array","items":{"type":"string"},"description":"What the model read off the photo, e.g. [\"office chair\"]. Empty when it could not identify an item."}},"required":["results","relaxed","confidence","saw"]}},"required":["data"]}}}},"400":{"description":"Malformed, empty, oversized, or unsupported upload","content":{"application/json":{"schema":{"description":"Malformed, empty, oversized, or unsupported upload","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"description":"Rate limit exceeded","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}},"/rest/v1/products/search/similar":{"post":{"summary":"Find similar products","tags":["Products"],"description":"Products similar to a given one — a \"you may also like\" row for a product page.\n\nRanked by catalogue-wide semantic similarity to the source product, then reranked using the product's own indexed type and category, which is what keeps a phone's cases out of its neighbours. `results` has the same shape as `/rest/v1/products/search`, minus the source product itself.\n\nUse the product's id **as it appears in your feed** — the same `id` returned by the other search endpoints.\n\nCosts no model or embedding call, so this is the cheapest of the search endpoints and the safest to call on every product-page view.\n\n**Authentication:** Requires a valid API key in the `X-API-Key` header. The business must belong to your organization.\n\n**Rate limit:** 300 requests per minute per organization.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"businessSlug":{"type":"string","minLength":1,"description":"Business identifier (required)"},"productId":{"type":"string","minLength":1,"description":"Product ID to find neighbours of"},"topK":{"type":"integer","minimum":1,"maximum":50,"description":"Maximum number of results (default 12)"}},"required":["businessSlug","productId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Similar products","content":{"application/json":{"schema":{"description":"Similar products","type":"object","properties":{"data":{"type":"object","properties":{"results":{"type":"array","description":"Neighbours in reranked similarity order (most similar first). Never includes the source product.","items":{"type":"object","properties":{"id":{"type":"string","description":"Product ID"},"score":{"type":"number","description":"Rerank relevance score"},"product":{"type":["object","null"],"additionalProperties":true,"description":"The untouched feed product (TurboPuffer rawProduct). Null when the raw JSON is missing/unparseable."}},"required":["id","score","product"]}},"source":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"description":"The product the neighbours were found for. Null when `productId` isn't in the catalogue index — distinct from a product that simply has no neighbours, and worth surfacing differently."}},"required":["results","source"]}},"required":["data"]}}}},"401":{"description":"Authentication failed","content":{"application/json":{"schema":{"description":"Authentication failed","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"404":{"description":"Business not found","content":{"application/json":{"schema":{"description":"Business not found","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"description":"Rate limit exceeded","type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"]}}}}}}}},"servers":[{"url":"https://api.octocom.ai","description":"Production"}],"tags":[{"name":"Businesses","description":"List businesses in your organization. Use business slugs when calling business-scoped endpoints."},{"name":"Conversations","description":"Access customer conversation data including messages, AI analysis, and agent notes."},{"name":"Customers","description":"Retrieve customer contact information and profiles."},{"name":"Custom Data","description":"Access AI-classified data extracted from conversations for analytics and reporting."},{"name":"Users","description":"List team members and agents in your organization."},{"name":"Teams","description":"View team structure and organization."},{"name":"Macros","description":"Access response templates used by agents."},{"name":"Workflows","description":"Manage bot automation workflows that handle customer inquiries."},{"name":"Articles","description":"Access knowledge base articles used by the AI to answer questions."},{"name":"Bot Rules","description":"Manage custom instructions that guide AI bot behavior."},{"name":"Bot Config","description":"The bot's system-prompt configuration for a business: custom task, safety, instruction-following, function-calling, tone, writing style, output syntax and language sections, plus auto-resolve settings."},{"name":"Custom Actions","description":"Python and API actions the bot can invoke during a conversation."},{"name":"Condition Providers","description":"Python scripts that evaluate named conditions, used by workflows to choose a variant."},{"name":"Event Handlers","description":"Python scripts that run automatically when conversation, message, rating, tag or campaign events fire."},{"name":"Python Modules","description":"Shared Python code injected into every Python execution in the organization."},{"name":"Recurring Jobs","description":"Python jobs that run on a cron schedule in a business's context."},{"name":"Product Sync","description":"The AI product-sync parser and URL-to-product matching configuration."},{"name":"Web Chat","description":"Web-chat widget configurations (deployments, greetings, suggestions, appearance) and the AI suggestion buttons."},{"name":"Storefront Search","description":"Storefront search configuration: UI injection config, merchandising rules, synonyms and query redirects."},{"name":"Comment Workflows","description":"How the bot handles social media comments (Instagram, Facebook)."},{"name":"Tags","description":"Conversation tags defined in the organization."},{"name":"Views","description":"Saved inbox views (conversation filters)."},{"name":"Secrets","description":"Names of stored secrets referenced from Python. Values are never exposed."},{"name":"Products","description":"Search a business's product catalogue: typed queries, search-as-you-type suggestions, voice, photos, and similar-product lookups. All return the raw products as ingested from your store feed."},{"name":"Google Sheets","description":"Append, list, find, and conditionally update rows in any tab of a Google Sheet. Share the sheet with octocom-google-sheets@octocom.iam.gserviceaccount.com."},{"name":"Browser Sessions","description":"Record purchases for browser sessions (e.g. custom sale attribution). No authentication; rate limited by IP."}]}