{"openapi":"3.1.0","info":{"title":"NexiAgent public API","version":"1.0.0","summary":"The website chat endpoints, the platform status feed and the signed webhooks NexiAgent delivers.","description":"NexiAgent (a NexiChat product) sells two AI assistants for small businesses: a call answering assistant and a 24/7 website chat.\n\nThis document describes everything a third party can call or receive. There is no management API: assistants are configured in the customer panel at https://my.nexiagent.com, or installed by the NexiAgent team.\n\n**Authentication.** The chat endpoints carry no secret. A site's public key (`pk_live_…`) identifies the subscription, and the request is accepted only when its `Origin` header is on that site's allowed-domains list. Webhooks are authenticated the other way round: NexiAgent signs each delivery with HMAC-SHA256 and the receiver verifies it.\n\n**Rate limits.** Every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset` (seconds until the window resets). A `429` adds `Retry-After`. Defaults: 30 chat messages per minute per IP, 60 per hour per conversation, 60 new conversations per 10 minutes per site, 60 `/api/chat/hello` loads per minute per IP, 120 status reads per minute per IP.\n\n**Errors.** Every error is JSON: `{ \"error\": \"<stable_code>\", \"message\": \"<sentence>\", \"docs\": \"<url>\" }`. Unknown paths under `/api` also answer in JSON.\n\n**Prices** shown on the site are without VAT; VAT (23%) is added at checkout.\n\n**Versioning.** This is version 1 of the API; every response says so in `X-API-Version`, and a request may pin it with the same header (a version that does not exist is refused with 400 `unsupported_version`). Paths are unversioned. Within a version, fields are only ever added: nothing is removed, renamed or retyped, and no new required input appears. A breaking change ships as a new version under new paths (`/api/v2/…`) while this version keeps answering. The policy is also machine-readable in `x-versioning-policy`.\n\n**Deprecation.** A deprecated operation announces itself in its own responses: `Deprecation` (RFC 9745) gives the date it was deprecated, `Sunset` (RFC 8594) the date it stops answering — never less than 180 days later — and the operation is marked `deprecated: true` here with its replacement in the description. The same notice appears on the developer page. Nothing is deprecated today. See `x-deprecation-policy`.\n\n**Discovery.** This document is served at `/openapi.json` and listed in `/.well-known/api-catalog`; `/llms.txt` explains the site to a language model, and every public page answers in Markdown to `Accept: text/markdown`. Webhook deliveries carry an `at` timestamp and an `id` in `data`, so a receiver can deduplicate retries.\n\n**Idempotency.** `POST /api/chat` is a conversational turn and is not idempotent: sending the same message twice asks the assistant twice. Webhook deliveries are retried on failure and may arrive more than once; treat `data.id` as the idempotency key.","termsOfService":"https://nexiagent.com/terms","contact":{"name":"NexiAgent","url":"https://nexiagent.com/contact","email":"geral@nexiagent.com"},"x-logo":{"url":"https://nexiagent.com/icon-512.png"}},"externalDocs":{"description":"Product documentation","url":"https://docs.nexiagent.com"},"x-versioning-policy":{"current":"1","scheme":"header","header":"X-API-Version","request":"optional; pins the version, unknown values refused with 400 unsupported_version","response":"always present; the version that answered","pathsVersioned":false,"compatibility":"additive-only: fields are added, never removed, renamed or retyped; no new required inputs","breakingChanges":"a new major version under new paths; the previous version keeps answering through its deprecation period","announcedAt":["https://nexiagent.com/developers","https://nexiagent.com/openapi.json"]},"x-deprecation-policy":{"headers":["Deprecation","Sunset"],"minimumNoticeDays":180,"markedInSpec":"deprecated: true on the operation, replacement named in its description","announcedAt":["https://nexiagent.com/developers","https://nexiagent.com/openapi.json"],"deprecatedOperations":[]},"servers":[{"url":"https://nexiagent.com","description":"Website and chat API"},{"url":"https://status.nexiagent.com","description":"Platform status"}],"tags":[{"name":"chat","description":"The 24/7 website chat, as called by the widget or by your own front end."},{"name":"status","description":"Public health of the platform."},{"name":"discovery","description":"Machine-readable descriptions of this site."}],"paths":{"/api/chat/hello":{"get":{"operationId":"getChatSiteHello","tags":["chat"],"summary":"Check that a chat site may appear on this page, and fetch its appearance","description":"The widget's first call. Answers whether the requesting origin is allowed, records the install (the panel shows 'seen on your website'), and returns the greeting, title, accent colour and the branding line. Called with the page's `Origin`; a server-side call without one is refused with `origin_not_allowed`.","parameters":[{"$ref":"#/components/parameters/ApiVersion"},{"name":"k","in":"query","required":true,"description":"The site's public key from the panel. Not a secret: it ships in the page's HTML.","schema":{"type":"string","pattern":"^pk_live_[A-Za-z0-9_-]+$","maxLength":80},"example":"pk_live_abc123"},{"name":"Origin","in":"header","required":true,"description":"The page's origin, set by the browser. Must be on the site's allowed-domains list.","schema":{"type":"string","format":"uri"},"example":"https://www.example.pt"}],"responses":{"200":{"description":"The origin is allowed; the widget may draw itself.","headers":{"X-API-Version":{"$ref":"#/components/headers/X-API-Version"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatHello"}}}},"400":{"description":"The key is missing, or X-API-Version names a version that does not exist.","headers":{"X-API-Version":{"$ref":"#/components/headers/X-API-Version"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid":{"value":{"error":"invalid","message":"The k query parameter (the site's public key) is missing.","docs":"https://nexiagent.com/developers"}},"unsupported_version":{"value":{"error":"unsupported_version","message":"The X-API-Version header names a version this API does not serve; the current version is 1.","docs":"https://nexiagent.com/developers"}}}}}},"403":{"description":"The page's origin is not allowed for this site. The response echoes the origin so the exact string can be approved in the panel.","headers":{"X-API-Version":{"$ref":"#/components/headers/X-API-Version"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"origin_not_allowed":{"value":{"error":"origin_not_allowed","message":"This page's origin is not on the site's allowed-domains list. Add it in the panel.","docs":"https://nexiagent.com/developers"}}}}}},"404":{"description":"No active site has this key (the same answer for a paused site, so keys cannot be enumerated).","headers":{"X-API-Version":{"$ref":"#/components/headers/X-API-Version"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"unknown":{"value":{"error":"unknown","message":"No active chat site has this key.","docs":"https://nexiagent.com/developers"}}}}}},"429":{"description":"Rate limited.","headers":{"X-API-Version":{"$ref":"#/components/headers/X-API-Version"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"value":{"error":"rate_limited","message":"Too many requests; wait for Retry-After seconds and try again.","docs":"https://nexiagent.com/developers"}}}}}}}}},"/api/chat":{"post":{"operationId":"sendChatMessage","tags":["chat"],"summary":"Send one visitor message to the site's assistant and stream the reply","description":"One conversational turn. The reply streams back as Server-Sent Events: `start` (with the `conversationId`), any number of `delta` (text fragments), then `done` — or `error`. A returning `visitorId` continues its conversation; a new one starts a conversation, which is the unit the plan is metered in. Requires the page's `Origin` to be on the site's allowed-domains list.","parameters":[{"$ref":"#/components/parameters/ApiVersion"},{"name":"Origin","in":"header","required":true,"description":"The page's origin, set by the browser.","schema":{"type":"string","format":"uri"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatMessageRequest"}}}},"responses":{"200":{"description":"The assistant's reply, streamed.","headers":{"X-API-Version":{"$ref":"#/components/headers/X-API-Version"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"text/event-stream":{"schema":{"$ref":"#/components/schemas/ChatStreamEvent"},"example":"event: start\ndata: {\"conversationId\":\"cv_7Kd2\"}\n\nevent: delta\ndata: {\"text\":\"Hoje estamos abertos \"}\n\nevent: delta\ndata: {\"text\":\"até às 19h.\"}\n\nevent: done\ndata: {\"conversationId\":\"cv_7Kd2\",\"handedOver\":false}\n\n"}}},"400":{"description":"The body is not JSON, a required field is missing, or X-API-Version names a version that does not exist.","headers":{"X-API-Version":{"$ref":"#/components/headers/X-API-Version"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_json":{"value":{"error":"invalid_json","message":"The request body is not valid JSON.","docs":"https://nexiagent.com/developers"}},"invalid_request":{"value":{"error":"invalid_request","message":"siteKey, visitorId and message are all required.","docs":"https://nexiagent.com/developers"}},"unsupported_version":{"value":{"error":"unsupported_version","message":"The X-API-Version header names a version this API does not serve; the current version is 1.","docs":"https://nexiagent.com/developers"}}}}}},"403":{"description":"The page's origin is not allowed for this site.","headers":{"X-API-Version":{"$ref":"#/components/headers/X-API-Version"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"origin_not_allowed":{"value":{"error":"origin_not_allowed","message":"This page's origin is not on the site's allowed-domains list. Add it in the panel.","docs":"https://nexiagent.com/developers"}}}}}},"404":{"description":"No active site has this key.","headers":{"X-API-Version":{"$ref":"#/components/headers/X-API-Version"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"unknown_site":{"value":{"error":"unknown_site","message":"No active chat site has this key.","docs":"https://nexiagent.com/developers"}}}}}},"413":{"description":"The message is longer than 2000 characters.","headers":{"X-API-Version":{"$ref":"#/components/headers/X-API-Version"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"message_too_long":{"value":{"error":"message_too_long","message":"A message is at most 2000 characters.","docs":"https://nexiagent.com/developers"}}}}}},"429":{"description":"Rate limited (per IP, per conversation, or new conversations per site).","headers":{"X-API-Version":{"$ref":"#/components/headers/X-API-Version"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"value":{"error":"rate_limited","message":"Too many requests; wait for Retry-After seconds and try again.","docs":"https://nexiagent.com/developers"}}}}}},"503":{"description":"The site's monthly allowance is spent, or the assistant is temporarily unavailable.","headers":{"X-API-Version":{"$ref":"#/components/headers/X-API-Version"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"limit_reached":{"value":{"error":"limit_reached","message":"This site's monthly conversation allowance is spent; it resumes when the period renews or the plan changes.","docs":"https://nexiagent.com/developers"}},"unavailable":{"value":{"error":"unavailable","message":"The assistant cannot answer right now. Try again shortly.","docs":"https://nexiagent.com/developers"}}}}}}}}},"/api/status.json":{"servers":[{"url":"https://status.nexiagent.com"}],"get":{"operationId":"getPlatformStatus","tags":["status"],"summary":"Current status of every platform component","description":"Public, unauthenticated. Answers `503` when the status backend itself cannot be reached, so a monitor can tell 'could not measure' from 'all fine'. Also reachable as `https://nexiagent.com/api/status.json`, which redirects here.","parameters":[{"$ref":"#/components/parameters/ApiVersion"}],"responses":{"200":{"description":"Status snapshot.","headers":{"X-API-Version":{"$ref":"#/components/headers/X-API-Version"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformStatus"}}}},"400":{"description":"The X-API-Version header names a version that does not exist.","headers":{"X-API-Version":{"$ref":"#/components/headers/X-API-Version"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"unsupported_version":{"value":{"error":"unsupported_version","message":"The X-API-Version header names a version this API does not serve; the current version is 1.","docs":"https://nexiagent.com/developers"}}}}}},"429":{"description":"Rate limited (120 reads per minute per IP).","headers":{"X-API-Version":{"$ref":"#/components/headers/X-API-Version"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"value":{"error":"rate_limited","message":"Too many requests; wait for Retry-After seconds and try again.","docs":"https://nexiagent.com/developers"}}}}}},"503":{"description":"The status backend is unreachable.","headers":{"X-API-Version":{"$ref":"#/components/headers/X-API-Version"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"webhooks":{"assistantEvent":{"post":{"operationId":"receiveAssistantEvent","summary":"An event captured by the customer's assistant, delivered to the customer's endpoint","description":"Available from the Pro plan. Configured in the panel under 'Send to your tools'. Each delivery carries `x-nexiagent-event`, `x-nexiagent-timestamp` (seconds since 1970, included in the signature) and `x-nexiagent-signature` (hex HMAC-SHA256 of `{timestamp}.{raw body}` with the site's signing key). Reject deliveries older than five minutes. Answer 2xx within 10 seconds; otherwise NexiAgent retries six times over about half a day (1 min, 5 min, 25 min, ~2 h, ~10 h). Only `https` and only public addresses are accepted as endpoints.","parameters":[{"name":"x-nexiagent-event","in":"header","required":true,"schema":{"$ref":"#/components/schemas/WebhookEventName"}},{"name":"x-nexiagent-timestamp","in":"header","required":true,"schema":{"type":"string","pattern":"^[0-9]+$"},"description":"Unix seconds; part of the signed string."},{"name":"x-nexiagent-signature","in":"header","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{64}$"},"description":"HMAC-SHA256(`{timestamp}.{body}`), hex."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDelivery"}}}},"responses":{"2XX":{"description":"Delivered. Anything else is retried."}}}}},"components":{"parameters":{"ApiVersion":{"name":"X-API-Version","in":"header","required":false,"description":"Pin the API version the request is written against. Optional: without it the current version (1) answers. A version that does not exist is refused with 400 `unsupported_version` rather than answered by another version.","schema":{"type":"string","enum":["1"]},"example":"1"}},"headers":{"X-API-Version":{"description":"The API version that answered; currently 1.","schema":{"type":"string","enum":["1"]}},"Deprecation":{"description":"Present only on a deprecated operation: when it was deprecated (RFC 9745; `@` followed by Unix seconds).","schema":{"type":"string","pattern":"^@[0-9]+$"}},"Sunset":{"description":"Present only on a deprecated operation: the HTTP-date after which it stops answering, at least 180 days after Deprecation (RFC 8594).","schema":{"type":"string"}},"RateLimit-Limit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Stable machine-readable code.","enum":["invalid","invalid_json","invalid_request","message_too_long","rate_limited","unknown","unknown_site","origin_not_allowed","limit_reached","unavailable","not_found","method_not_allowed","unsupported_version"]},"message":{"type":"string","description":"One readable sentence."},"docs":{"type":"string","format":"uri","description":"Where to read more."},"origin":{"type":["string","null"],"description":"On origin_not_allowed: the host that was refused, to approve in the panel."}}},"ChatHello":{"type":"object","required":["ok","appearance","brand"],"properties":{"ok":{"type":"boolean","const":true},"appearance":{"type":"object","description":"Set in the panel. Missing fields mean 'use the widget default'.","properties":{"title":{"type":"string"},"greeting":{"type":"string"},"accent":{"type":"string","description":"Hex colour, e.g. #000000."},"hostedEnabled":{"type":"boolean"}}},"brand":{"description":"Null on the Ultra plan (white label). The 'AI assistant' disclosure is not affected by this.","oneOf":[{"type":"null"},{"type":"object","required":["name","url"],"properties":{"name":{"type":"string"},"url":{"type":"string","format":"uri"}}}]}}},"ChatMessageRequest":{"type":"object","required":["siteKey","visitorId","message"],"properties":{"siteKey":{"type":"string","pattern":"^pk_live_[A-Za-z0-9_-]+$","maxLength":80,"description":"The site's public key."},"visitorId":{"type":"string","maxLength":64,"description":"A random string the client keeps per visitor; the same value continues the same conversation."},"message":{"type":"string","minLength":1,"maxLength":2000},"locale":{"type":"string","maxLength":10,"description":"BCP-47 tag of the page, e.g. pt-PT. Optional."}}},"ChatStreamEvent":{"type":"object","description":"One Server-Sent Events frame of the reply; the response body is a sequence of these. `event` is the SSE event name and `data` its JSON payload: `start` first, then any number of `delta` (append `text` in order), then `done` — or `error` at any point.","required":["event","data"],"properties":{"event":{"type":"string","enum":["start","delta","done","error"]},"data":{"oneOf":[{"title":"start","type":"object","required":["conversationId"],"properties":{"conversationId":{"type":"string"}}},{"title":"delta","type":"object","required":["text"],"properties":{"text":{"type":"string","description":"A fragment of the reply; append in order."}}},{"title":"done","type":"object","required":["conversationId","handedOver"],"properties":{"conversationId":{"type":"string"},"handedOver":{"type":"boolean","description":"True when the assistant passed the conversation to a person."}}},{"title":"error","type":"object","required":["error"],"properties":{"error":{"type":"string","description":"A stable code, as in Error.error."}}}]}}},"PlatformStatus":{"type":"object","required":["ok","status","components"],"properties":{"ok":{"type":"boolean"},"status":{"type":"string","enum":["up","degraded","down"]},"stale":{"type":"boolean","description":"True when the reading is cached rather than live."},"read_at":{"type":"string","format":"date-time"},"components":{"type":"array","items":{"type":"object","properties":{"component":{"type":"string"},"label":{"type":"string"},"external":{"type":"boolean"},"status":{"type":"string","enum":["up","degraded","down"]},"latency_ms":{"type":["number","null"]},"detail":{"type":["string","null"]},"checked_at":{"type":["string","null"],"format":"date-time"}}}},"incidents":{"type":"array","items":{"type":"object"}}}},"WebhookEventName":{"type":"string","enum":["booking.created","message.taken","lead.captured","screening.recorded","document.requested","handover"]},"WebhookDelivery":{"type":"object","required":["event","at","data"],"properties":{"event":{"$ref":"#/components/schemas/WebhookEventName"},"at":{"type":"string","format":"date-time"},"data":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["booking","message"]},"channel":{"type":"string","enum":["voice","chat"]},"contactName":{"type":["string","null"]},"contactPhone":{"type":["string","null"]},"contactEmail":{"type":["string","null"]},"scheduledFor":{"type":["string","null"],"format":"date-time"},"notes":{"type":["string","null"]},"ticketId":{"type":"string","description":"handover only."},"summary":{"type":"string","description":"handover only."},"conversationId":{"type":["string","null"],"description":"handover only."}}}},"example":{"event":"booking.created","at":"2026-08-07T10:32:11.204Z","data":{"id":"bk_7Kd2","kind":"booking","channel":"voice","contactName":"Ana Silva","contactPhone":"+351910000000","contactEmail":null,"scheduledFor":"2026-08-12T15:00:00.000Z","notes":"Primeira consulta."}}}}}}