{"openapi":"3.1.0","info":{"title":"Nexvio Public API","version":"1.0.0","description":"Team-scoped REST API for Nexvio. Authenticate with a team API key via\n`Authorization: Bearer nex_<key>`. The active team is inferred from the key;\ndo not pass team identifiers in auth headers.\n"},"servers":[{"url":"https://app.nexvio.ai/api/public/v1","description":"Current environment (resolved from request)"}],"tags":[{"name":"Platform","description":"Health and team context"},{"name":"Agents","description":"Agent CRUD"},{"name":"Conversations","description":"Conversations, messages, and export"},{"name":"Contacts","description":"Contacts and custom attributes"},{"name":"Integrations","description":"Connected integrations"},{"name":"Chat","description":"Playground-style agent chat"},{"name":"Knowledge","description":"Knowledge base items"},{"name":"Assets","description":"Agent branding assets"},{"name":"Versions","description":"Agent version management — draft branches, publishing, and config patching."},{"name":"Forms","description":"Form definitions and submission management"},{"name":"Webhooks","description":"Webhook subscription management and delivery logs"}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API Key","description":"Team API key with the `nex_` prefix, issued from Team Settings ->\nDeveloper API. This is currently the only supported authentication\nmethod for the public REST API — there is no OAuth2 flow wired up\nfor `/api/public/v1/*` yet (tracked as a follow-up; OAuth2 is used\ntoday only by the separate MCP tool-calling surface).\n\nKeys carry a `scopes` array. The scopes referenced by operations in\nthis document are:\n\n- `team:read` - read team-scoped resources (agents, contacts, forms,\n  webhooks, knowledge, integrations, conversations).\n- `team:write` - create, update, or delete team-scoped resources.\n  Also required by `POST /agents/{agentId}/chat`, which reads like a\n  query but is classified as a write because it has side effects\n  (it creates conversation/message records).\n\nTwo further scopes, `mcp:read` and `mcp:write`, exist for the\nseparate MCP tool-calling surface and are not used by any operation\nin this document. New team API keys are granted all four scopes\n(`team:read`, `team:write`, `mcp:read`, `mcp:write`) by default at\ncreation time, and keys migrated from the legacy API-key system are\nbackfilled with all four scopes as well.\n"}},"parameters":{"agentIdParam":{"name":"agentId","in":"path","required":true,"schema":{"type":"string"},"description":"The agent ID."},"versionIdParam":{"name":"versionId","in":"path","required":true,"schema":{"type":"string"},"description":"A version ID or well-known alias. Accepted aliases: `main` (currently published version), `published` (synonym for `main`).\n"},"FormId":{"name":"formId","in":"path","required":true,"schema":{"type":"string"},"description":"The unique ID of the form."},"SubmissionId":{"name":"submissionId","in":"path","required":true,"schema":{"type":"string"},"description":"The unique ID of the submission."},"WebhookId":{"name":"webhookId","in":"path","required":true,"schema":{"type":"string"},"description":"Unique ID of the webhook subscription."}},"headers":{"RateLimitLimit":{"description":"Maximum requests allowed per rolling 60-second window for the team.","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix timestamp (seconds) when the current rate-limit window resets.","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying. Only present on 429 responses.","schema":{"type":"integer"}},"WWWAuthenticate":{"description":"Present on 401 responses. Value is `Bearer realm=\"nexvio-public-api\", error=\"invalid_request\"` when the Authorization header is missing or malformed, or `Bearer realm=\"nexvio-public-api\", error=\"invalid_token\"` when the supplied API key is invalid or expired.\n","schema":{"type":"string"}}},"responses":{"Unauthorized":{"description":"Missing or invalid API key.","headers":{"WWW-Authenticate":{"$ref":"#/components/headers/WWWAuthenticate"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"API key lacks the required scope (`team:write`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ValidationError":{"description":"Request body failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"The requested resource was not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"AgentNotFound":{"description":"Agent not found or does not belong to the authenticated team.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"VersionNotFound":{"description":"Agent version not found or does not belong to the authenticated team.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Conflict — e.g. alias already exists, concurrent publish/discard race.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"Request body exceeds the 64 KiB size limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"FormNotFound":{"description":"Form not found or does not belong to the authenticated team.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"SubmissionNotFound":{"description":"Submission not found or does not belong to the authenticated team.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"HealthResponse":{"type":"object","required":["status","version"],"properties":{"status":{"type":"string","example":"ok"},"version":{"type":"string","example":"1"}}},"TeamContext":{"type":"object","required":["teamId","tenantId","scopes"],"properties":{"teamId":{"type":"string"},"tenantId":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthorized","forbidden","not_found","validation_error","rate_limit_exceeded","internal_error","agent_not_found","conversation_not_found","knowledge_not_found","message_not_found","contact_duplicate","webhook_not_found","form_not_found","submission_not_found","version_not_found"]},"message":{"type":"string"},"details":{"type":"object","additionalProperties":true}}}}},"AgentVersionStatus":{"type":"string","enum":["draft","published","archived"],"description":"Lifecycle status of an agent version."},"AgentVersionSummary":{"type":"object","description":"A single agent version row as returned by `listAgentVersions`. Sensitive fields are redacted: `identitySnapshot` is omitted entirely; `configSnapshot.aiModelKey` is omitted from the config object.\n","required":["id","agentId","teamId","name","status","createdAt","updatedAt"],"properties":{"id":{"type":"string","description":"Unique version ID."},"agentId":{"type":"string"},"teamId":{"type":"string"},"name":{"type":"string","description":"Human-readable name (e.g. `\"main\"` or `\"staging\"`)."},"alias":{"type":"string","nullable":true,"description":"URL-safe slug used by widget-frame environment selection."},"parentVersionId":{"type":"string","nullable":true},"status":{"$ref":"#/components/schemas/AgentVersionStatus"},"configSnapshot":{"type":"object","nullable":true,"description":"Config snapshot with `aiModelKey` redacted.","additionalProperties":true},"createdByActorId":{"type":"string","nullable":true},"updatedByActorId":{"type":"string","nullable":true},"publishedByActorId":{"type":"string","nullable":true},"createdAt":{"type":"integer","format":"int64","description":"Unix timestamp (seconds)."},"updatedAt":{"type":"integer","format":"int64"},"publishedAt":{"type":"integer","format":"int64","nullable":true},"archivedAt":{"type":"integer","format":"int64","nullable":true}}},"AgentVersionBundle":{"type":"object","description":"Full version bundle including config snapshot, actions, instructions, guardrails, help-page, identity, and connections.\n","required":["version","config"],"properties":{"version":{"$ref":"#/components/schemas/AgentVersionSummary"},"config":{"type":"object","additionalProperties":true,"description":"Effective config for this version."},"actions":{"type":"array","items":{"type":"object","additionalProperties":true}},"instructions":{"type":"object","properties":{"categories":{"type":"array","items":{"type":"object","additionalProperties":true}},"rules":{"type":"array","items":{"type":"object","additionalProperties":true}}}},"guardrails":{"type":"array","items":{"type":"object","additionalProperties":true}},"helpPage":{"type":"object","nullable":true,"additionalProperties":true},"identity":{"type":"object","nullable":true,"additionalProperties":true,"description":"Identity verification config (may be null)."},"connections":{"type":"array","items":{"type":"object","additionalProperties":true}},"contentGroupId":{"type":"string","nullable":true}}},"CreateAgentBranchBody":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":80,"description":"Human-readable name for the new draft environment. Cannot be `\"main\"` (reserved for the published environment).\n","example":"staging"},"alias":{"type":"string","maxLength":80,"nullable":true,"description":"URL-safe slug used by widget-frame to select this environment. Derived from `name` when omitted. Cannot be `\"main\"`.\n","example":"staging"},"fromVersionId":{"type":"string","nullable":true,"description":"ID of the version to fork from. Defaults to the currently published version when omitted.\n"}}},"VersionConfigPatch":{"type":"object","description":"A key/value patch of agent config fields. Both camelCase and snake_case variants are accepted for every key (e.g. `system_prompt` or `systemPrompt`).\n**Patchable fields** (camelCase canonical name → snake_case alias):\n| Field | snake_case alias | Notes | |-------|-----------------|-------| | `name` | — | Version display name | | `displayName` | `display_name` | Agent display name | | `titleBar` | `title_bar` | Widget title bar label | | `agentLabel` | `agent_label` | Label for the agent side of the chat | | `visitorLabel` | `visitor_label` | Label for the visitor side | | `msgGreeting` | `msg_greeting` | Default greeting message | | `initialMessages` | `initial_messages` | Array of initial bot messages | | `conversationStarters` | `conversation_starters` | Suggested starter prompts | | `keepStartersVisible` | `keep_starters_visible` | Keep starters after first message | | `popupType` | `popup_type` | Widget popup display mode | | `popupDelay` | `popup_delay` | Delay before popup appears (ms) | | `aiModel` | `ai_model` | AI model variant (e.g. `\"auto\"`, `\"gpt-4o\"`) | | `aiAgentId` | `ai_agent_id` | External AI agent ID override | | `systemPrompt` | `system_prompt` | Agent system prompt | | `supportEmail` | `support_email` | Escalation support email | | `showDataSources` | `show_data_sources` | Show knowledge source citations | | `enableAudio` | `enable_audio` | Enable voice input/output | | `enableTextStreaming` | `enable_text_streaming` | Stream tokens as they arrive | | `sessionIdleTimeoutSeconds` | `session_idle_timeout_seconds` | Inactivity timeout | | `appearance` | — | Widget appearance object | | `skillsEnabled` | `skills_enabled` | Enable/disable agent skills |\n**Always denied** (stripped server-side; sending only denied fields returns 400): `aiModelKey`, `identitySnapshot`, `domains`, `debugMode`, `mcpServerEnabled`, `mcpUiEnabled`, `agentSkillPolicyHeader`.\n","additionalProperties":true,"example":{"system_prompt":"You are a helpful support agent.","msg_greeting":"Hi! How can I help you today?","display_name":"Support Bot"}},"DiscardAgentVersionResult":{"type":"object","required":["id","status"],"properties":{"id":{"type":"string","description":"ID of the discarded version."},"status":{"type":"string","enum":["archived"],"description":"Always `\"archived\"` after a successful discard."}}},"FormField":{"type":"object","required":["id","type","label"],"description":"A single field definition within a form.","properties":{"id":{"type":"string","description":"Unique field identifier (slug-style, e.g. `\"email\"`)."},"type":{"type":"string","enum":["text","email","phone","number","textarea","dropdown","multiple_choice","checkboxes","date","rating","file"],"description":"Input type that drives widget rendering. `dropdown`, `multiple_choice`, and `checkboxes` require an `options` array. `rating` renders a star/numeric scale. `file` uploads an attachment.\n"},"label":{"type":"string","description":"User-visible label shown above the input."},"placeholder":{"type":"string","description":"Placeholder hint text shown inside the input when empty."},"required":{"type":"boolean","default":false,"description":"When `true` the form cannot be submitted without a value for this field."},"enabled":{"type":"boolean","default":true,"description":"When `false` the field is hidden from the rendered form."},"options":{"type":"array","description":"Selectable choices for `dropdown`, `multiple_choice`, and `checkboxes` fields. Ignored for other field types.\n","items":{"type":"object","required":["id","label","value"],"properties":{"id":{"type":"string","description":"Stable option identifier."},"label":{"type":"string","description":"Display label shown to the respondent."},"value":{"type":"string","description":"Machine-readable value stored in the submission."}}}},"validation":{"type":"object","description":"Optional input validation rules evaluated before submission.","properties":{"minLength":{"type":"integer","description":"Minimum character count (applies to `text`, `textarea`)."},"maxLength":{"type":"integer","description":"Maximum character count (applies to `text`, `textarea`)."},"min":{"type":"number","description":"Minimum numeric value (applies to `number`, `rating`)."},"max":{"type":"number","description":"Maximum numeric value (applies to `number`, `rating`)."},"pattern":{"type":"string","description":"ECMAScript-compatible regex that the value must match."}}},"conditionalDisplay":{"type":"object","description":"Show this field only when another field satisfies a condition. When omitted the field is always visible.\n","required":["fieldId","operator"],"properties":{"fieldId":{"type":"string","description":"ID of the controlling field."},"operator":{"type":"string","enum":["equals","not_equals","contains","not_empty"],"description":"Comparison operator applied to the controlling field's value."},"value":{"type":"string","description":"Value to compare against (not required for `not_empty`)."}}},"contactFieldMapping":{"type":"string","description":"Maps this field's value to a contact attribute when `settings.integrations.createContact` is enabled (e.g. `\"email\"`, `\"name\"`).\n"}}},"FormSettings":{"type":"object","description":"Display, behaviour, and integration settings for a form.","properties":{"heading":{"type":"string","description":"Primary heading shown at the top of the rendered form."},"subheading":{"type":"string","description":"Secondary text shown below the heading."},"successMessage":{"type":"string","description":"Message displayed to the respondent after a successful submission."},"dismissMessage":{"type":"string","description":"Tooltip or label on the dismiss control (when `allowDismiss` is `true`)."},"submitButtonText":{"type":"string","description":"Label of the submit button (default `\"Submit\"`)."},"allowDismiss":{"type":"boolean","description":"When `true` a dismiss / close button is rendered on the form widget."},"styling":{"type":"object","description":"Visual overrides for the form widget.","properties":{"primaryColor":{"type":"string","description":"Hex colour used for the submit button and accents (e.g. `\"#2563EB\"`)."},"backgroundColor":{"type":"string","description":"Hex background colour of the form container."},"borderRadius":{"type":"string","description":"CSS border-radius applied to the form container (e.g. `\"8px\"`)."},"fontFamily":{"type":"string","description":"CSS font-family override for form text."}}},"notifications":{"type":"object","description":"Email notification settings triggered on each submission.","properties":{"emailOnSubmission":{"type":"boolean","description":"When `true`, an email is sent to `notifyEmails` on each submission."},"notifyEmails":{"type":"array","description":"List of email addresses that receive submission notifications.","items":{"type":"string","format":"email"}}}},"integrations":{"type":"object","description":"Downstream integrations triggered automatically on submission.","properties":{"createContact":{"type":"boolean","description":"When `true`, a contact is upserted from submission field values that have a `contactFieldMapping` set.\n"},"createTicketOnCondition":{"type":"object","description":"Optionally create a support ticket when a condition is met.","required":["enabled"],"properties":{"enabled":{"type":"boolean","description":"Whether the ticket-creation side-effect is active."},"condition":{"type":"string","description":"Free-text rule evaluated against submission data to decide whether a ticket should be created.\n"}}}}}}},"Form":{"type":"object","required":["id","teamId","name","formType","fields","isEnabled","isDeleted","createdAt","updatedAt"],"properties":{"id":{"type":"string"},"teamId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"formType":{"type":"string","enum":["lead","contact","survey","feedback","custom"]},"settings":{"$ref":"#/components/schemas/FormSettings","nullable":true},"fields":{"type":"array","items":{"$ref":"#/components/schemas/FormField"}},"analytics":{"type":"object","nullable":true,"additionalProperties":true},"isEnabled":{"type":"boolean"},"isDeleted":{"type":"boolean"},"deletedAt":{"type":"integer","nullable":true},"createdByActorId":{"type":"string","nullable":true},"updatedByActorId":{"type":"string","nullable":true},"deletedByActorId":{"type":"string","nullable":true},"createdAt":{"type":"integer"},"updatedAt":{"type":"integer"},"lastSubmissionAt":{"type":"integer","nullable":true},"submissionCount":{"type":"integer"}}},"FormStats":{"type":"object","required":["totalSubmissions","submissionsToday","submissionsThisWeek","submissionsThisMonth","conversionRate"],"properties":{"totalSubmissions":{"type":"integer"},"submissionsToday":{"type":"integer"},"submissionsThisWeek":{"type":"integer"},"submissionsThisMonth":{"type":"integer"},"conversionRate":{"type":"number","format":"float"},"lastSubmissionAt":{"type":"integer","nullable":true}}},"FormTemplate":{"type":"object","required":["id","name","category","fields","isSystem","createdAt","updatedAt"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"category":{"type":"string"},"settings":{"$ref":"#/components/schemas/FormSettings","nullable":true},"fields":{"type":"array","items":{"$ref":"#/components/schemas/FormField"}},"isSystem":{"type":"boolean"},"teamId":{"type":"string","nullable":true},"createdAt":{"type":"integer"},"updatedAt":{"type":"integer"}}},"Submission":{"type":"object","required":["id","teamId","formId","submissionData","status","isDeleted","createdAt","updatedAt"],"properties":{"id":{"type":"string"},"teamId":{"type":"string"},"formId":{"type":"string"},"agentId":{"type":"string","nullable":true},"conversationId":{"type":"string","nullable":true},"contactId":{"type":"string","nullable":true},"submissionData":{"type":"object","additionalProperties":true},"metadata":{"type":"object","nullable":true,"additionalProperties":true},"status":{"type":"string","enum":["new","viewed","processed","converted","archived"]},"isDeleted":{"type":"boolean"},"deletedAt":{"type":"integer","nullable":true},"createdByActorId":{"type":"string","nullable":true},"updatedByActorId":{"type":"string","nullable":true},"deletedByActorId":{"type":"string","nullable":true},"createdAt":{"type":"integer"},"updatedAt":{"type":"integer"}}},"CreateFormRequest":{"type":"object","required":["name","fields"],"properties":{"name":{"type":"string","minLength":1},"description":{"type":"string"},"formType":{"type":"string","enum":["lead","contact","survey","feedback","custom"],"default":"lead"},"settings":{"$ref":"#/components/schemas/FormSettings"},"fields":{"type":"array","items":{"$ref":"#/components/schemas/FormField"},"minItems":1},"isEnabled":{"type":"boolean","default":true}}},"UpdateFormRequest":{"type":"object","minProperties":1,"properties":{"name":{"type":"string","minLength":1},"description":{"type":"string","nullable":true},"formType":{"type":"string","enum":["lead","contact","survey","feedback","custom"]},"settings":{"$ref":"#/components/schemas/FormSettings"},"fields":{"type":"array","items":{"$ref":"#/components/schemas/FormField"}},"isEnabled":{"type":"boolean"}}},"SubmissionMetadata":{"type":"object","properties":{"ipAddress":{"type":"string"},"userAgent":{"type":"string"},"referrer":{"type":"string"},"pageUrl":{"type":"string"},"source":{"type":"string"},"utmParams":{"type":"object","properties":{"source":{"type":"string"},"medium":{"type":"string"},"campaign":{"type":"string"},"term":{"type":"string"},"content":{"type":"string"}}}}},"CreateSubmissionRequest":{"type":"object","required":["submissionData"],"properties":{"agentId":{"type":"string"},"conversationId":{"type":"string"},"contactId":{"type":"string"},"submissionData":{"type":"object","additionalProperties":true,"description":"Key-value map of form field responses. String values are capped at 10 000 characters per field.\n"},"metadata":{"$ref":"#/components/schemas/SubmissionMetadata"}}},"UpdateSubmissionRequest":{"type":"object","minProperties":1,"properties":{"status":{"type":"string","enum":["new","viewed","processed","converted","archived"]},"contactId":{"type":"string","nullable":true}}},"FormResponse":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Form"}}},"FormListResponse":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Form"}}}},"FormWithOptionalStatsResponse":{"type":"object","required":["data"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/Form"},{"type":"object","properties":{"stats":{"$ref":"#/components/schemas/FormStats"}}}]}}},"FormTemplateListResponse":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FormTemplate"}}}},"SubmissionResponse":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Submission"}}},"SubmissionListResponse":{"type":"object","required":["data","meta"],"description":"Paginated list of submissions. `data` is an array of Submission objects; `meta` carries the pagination envelope.\n","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Submission"}},"meta":{"type":"object","required":["total","limit","offset"],"description":"Pagination metadata.","properties":{"total":{"type":"integer","description":"Total number of submissions matching the query (before pagination)."},"limit":{"type":"integer","description":"Maximum number of submissions returned in this page."},"offset":{"type":"integer","description":"Zero-based index of the first result in this page."}}}}},"DeletedMessageResponse":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["message"],"properties":{"message":{"type":"string","example":"Form deleted"}}}}},"WebhookEventPayload":{"type":"object","description":"The JSON body delivered to your webhook endpoint for every event. Nexvio also sends the following custom headers with every delivery attempt:\n| Header | Description | |--------|-------------| | `x-nexvio-event` | The `WebhookEventType` that triggered this delivery (e.g. `contacts.created`). | | `x-nexvio-delivery` | Unique delivery attempt ID (e.g. `del_01hx…`). | | `x-nexvio-signature-256` | `sha256=<HMAC-SHA256 hex>` of the raw request body, signed with your webhook signing secret. Verify this before processing the payload. | | `x-nexvio-timestamp` | Unix timestamp (seconds) when the event was emitted. |\n","required":["event","teamId","timestamp","data"],"properties":{"event":{"$ref":"#/components/schemas/WebhookEventType","description":"The event type that triggered this delivery."},"teamId":{"type":"string","description":"ID of the team that owns the subscription.","example":"team_01hx..."},"timestamp":{"type":"integer","description":"Unix timestamp (seconds) when the event was emitted.","example":1719878400},"data":{"type":"object","description":"Event-specific payload. Shape varies by event type.","additionalProperties":true,"example":{"id":"con_01hx...","email":"alice@example.com","name":"Alice"}}}},"WebhookEventType":{"type":"string","enum":["contacts.created","contacts.updated","tickets.created","tickets.updated","actions.collect-lead.executed","actions.create-ticket.executed","actions.schedule-meeting.executed","actions.send-email.executed"],"description":"A supported webhook event type."},"WebhookSubscription":{"type":"object","required":["id","teamId","url","events","hasSecret","isEnabled","isDeleted","createdAt","updatedAt"],"properties":{"id":{"type":"string","description":"Unique subscription ID.","example":"wh_01hx..."},"teamId":{"type":"string","description":"ID of the team that owns this subscription."},"url":{"type":"string","format":"uri","description":"Target HTTPS URL that receives webhook payloads.","example":"https://example.com/webhooks/nexvio"},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"},"description":"List of event types this subscription receives."},"description":{"type":"string","nullable":true,"description":"Optional human-readable label for this subscription."},"hasSecret":{"type":"boolean","description":"Whether a signing secret is configured. The secret itself is never returned here."},"secretLastRotatedAt":{"type":"integer","description":"Unix timestamp (seconds) when the secret was last rotated.","example":1719878400},"isEnabled":{"type":"boolean","description":"Whether the subscription is active and will receive events."},"isDeleted":{"type":"boolean","description":"Whether the subscription has been soft-deleted."},"createdBy":{"type":"string","nullable":true,"description":"Actor who created the subscription. For API-key-originated creates this is the `keyId` (e.g. `key_xxxx`); for dashboard creates this is the user ID.\n"},"createdAt":{"type":"integer","description":"Unix timestamp (seconds) of creation.","example":1719878400},"updatedAt":{"type":"integer","description":"Unix timestamp (seconds) of last update.","example":1719878400}}},"WebhookCreateBody":{"type":"object","required":["url","events"],"properties":{"url":{"type":"string","format":"uri","description":"Target URL that will receive webhook payloads. Must use `https://`. Must not target private, loopback, or cloud-metadata addresses (SSRF guard).\n","example":"https://example.com/webhooks/nexvio"},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"},"minItems":1,"description":"One or more event types to subscribe to."},"description":{"type":"string","maxLength":500,"description":"Optional human-readable label for this subscription."},"isEnabled":{"type":"boolean","default":true,"description":"Whether the subscription starts in an active state."}}},"WebhookUpdateBody":{"type":"object","description":"At least one field must be provided.","minProperties":1,"properties":{"url":{"type":"string","format":"uri","description":"Updated target URL. Must use `https://` and must not target private, loopback, or cloud-metadata addresses (SSRF guard).\n"},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"},"minItems":1},"description":{"type":"string","nullable":true,"maxLength":500},"isEnabled":{"type":"boolean"}}},"WebhookTestBody":{"type":"object","properties":{"eventType":{"$ref":"#/components/schemas/WebhookEventType","description":"Override which event type is used for the test. When omitted, the first subscribed event type is used.\n"}}},"WebhookDeliveryAttempt":{"type":"object","required":["id","teamId","subscriptionId","eventId","eventType","attempt","status","createdAt","updatedAt"],"properties":{"id":{"type":"string","example":"del_01hx..."},"teamId":{"type":"string"},"subscriptionId":{"type":"string"},"eventId":{"type":"string","description":"ID of the event this attempt belongs to."},"eventType":{"$ref":"#/components/schemas/WebhookEventType"},"attempt":{"type":"integer","description":"Attempt number (1-indexed).","example":1},"status":{"type":"string","enum":["pending","success","failed"]},"httpStatus":{"type":"integer","nullable":true,"description":"HTTP status code returned by the target URL.","example":200},"latencyMs":{"type":"integer","nullable":true,"description":"Round-trip latency in milliseconds.","example":143},"nextRetryAt":{"type":"integer","nullable":true,"description":"Unix timestamp (seconds) of the next retry, or null if no retry is scheduled."},"deliveredAt":{"type":"integer","nullable":true,"description":"Unix timestamp (seconds) when the delivery succeeded."},"errorMessage":{"type":"string","nullable":true,"description":"Error message if the attempt failed."},"responseBody":{"type":"string","nullable":true,"description":"First 1 KB of the response body from the target URL."},"requestHeaders":{"type":"object","nullable":true,"additionalProperties":{"type":"string"},"description":"Request headers sent with the delivery attempt."},"createdAt":{"type":"integer","example":1719878400},"updatedAt":{"type":"integer","example":1719878400}}},"WebsiteCrawlInitiated":{"type":"object","description":"Returned by POST /knowledge when type=website. No knowledge/content record exists yet at response time — this represents an asynchronously-initiated crawl, not a content record. See packages/data-core/src/content/website-operations.ts (addWebsiteDataSource) for the source of truth.\n","properties":{"data":{"type":"object","properties":{"integration_folder_id":{"type":"string","description":"ID of the integration folder the crawl will populate."},"scope_entry_id":{"type":"string","nullable":true,"description":"Agent-scope entry ID, present when the source is scoped to an agent."},"structure_info":{"type":"object","properties":{"root_integration_folder":{"type":"string"},"agent_scope":{"type":"string","nullable":true}}},"message":{"type":"string"}}}}}}},"paths":{"/health":{"get":{"operationId":"getHealth","summary":"Health check","tags":["Platform"],"security":[],"responses":{"200":{"description":"API is available","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/HealthResponse"}}}}}}}}},"/team":{"get":{"operationId":"getTeamContext","summary":"Resolve team from API key","description":"Returns the team bound to the current API key. Useful to verify authentication.","tags":["Platform"],"x-phase":0,"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Team context","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TeamContext"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/agents":{"get":{"operationId":"listAgents","summary":"List agents","tags":["Agents"],"x-phase":1,"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Agent list (Phase 1)"},"401":{"description":"Unauthorized"}}},"post":{"operationId":"createAgent","summary":"Create agent","tags":["Agents"],"x-phase":1,"security":[{"BearerAuth":["team:write"]}],"responses":{"201":{"description":"Agent created (Phase 1)"}}}},"/agents/{agentId}":{"get":{"operationId":"getAgent","summary":"Get agent","tags":["Agents"],"x-phase":1,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Agent details (Phase 1)"}}},"patch":{"operationId":"updateAgent","summary":"Update agent","tags":["Agents"],"x-phase":1,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Agent updated (Phase 1)"}}},"delete":{"operationId":"deleteAgent","summary":"Delete agent","tags":["Agents"],"x-phase":1,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Agent deleted","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"deleted":{"type":"boolean"}}}}}}}}}}},"/agents/{agentId}/versions":{"get":{"operationId":"listAgentVersions","summary":"List agent versions","description":"Returns all versions (draft, published, archived) for an agent together with the id of the currently published version. `identitySnapshot` and `configSnapshot.aiModelKey` are redacted from every item in the list. Mirrors `GET /api/agents/[agentId]/versions`.\n","tags":["Versions"],"parameters":[{"$ref":"#/components/parameters/agentIdParam"}],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Version list and published pointer","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["versions","publishedVersionId"],"properties":{"versions":{"type":"array","items":{"$ref":"#/components/schemas/AgentVersionSummary"}},"publishedVersionId":{"type":"string","description":"ID of the currently published version."}}}}},"example":{"data":{"publishedVersionId":"av_01hx_main","versions":[{"id":"av_01hx_main","agentId":"agent_01hx","teamId":"team_01hx","name":"main","alias":null,"status":"published","createdAt":1719878000,"updatedAt":1719878400},{"id":"av_01hx_staging","agentId":"agent_01hx","teamId":"team_01hx","name":"staging","alias":"staging","status":"draft","createdAt":1719878400,"updatedAt":1719878410}]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/AgentNotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createAgentBranch","summary":"Create a draft branch (environment)","description":"Creates a new draft environment forked from an existing version (defaults to the currently published version when `fromVersionId` is omitted). The `name` field becomes the human-readable environment label; `alias` is a URL-safe slug used by widget-frame to select the environment. Both `name` and `alias` cannot be `\"main\"` (reserved for published). Requires `team:write` scope. Mirrors `POST /api/agents/[agentId]/versions`.\n","tags":["Versions"],"parameters":[{"$ref":"#/components/parameters/agentIdParam"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAgentBranchBody"},"example":{"name":"staging","alias":"staging","fromVersionId":"av_01hx_main"}}}},"security":[{"BearerAuth":["team:write"]}],"responses":{"201":{"description":"Newly created draft version bundle","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AgentVersionBundle"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/AgentNotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/agents/{agentId}/versions/{versionId}":{"get":{"operationId":"getAgentVersionBundle","summary":"Get a single version bundle","description":"Fetches a version by its id or well-known alias (`main` / `published`). Returns the full bundle including config snapshot, actions, instructions, guardrails, help-page config, connections, and identity info. Mirrors `GET /api/agents/[agentId]/versions/[versionId]`.\n","tags":["Versions"],"parameters":[{"$ref":"#/components/parameters/agentIdParam"},{"$ref":"#/components/parameters/versionIdParam"}],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Version bundle","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AgentVersionBundle"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/VersionNotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"updateAgentVersionConfig","summary":"Update config fields for a version ref","description":"Patches config fields for a version ref. When `versionId` is `main` or `published` this updates the live published config and immediately materializes it into the live agent tables. For draft ids it patches only the draft snapshot.\n**Denied fields (stripped server-side, never applied)**: `aiModelKey`, `ai_model_key`, `identitySnapshot`, `identity_snapshot`, `domains`, `debug_mode`, `debugMode`, `mcpServerEnabled`, `mcp_server_enabled`, `mcpUiEnabled`, `mcp_ui_enabled`, `agentSkillPolicyHeader`, `agent_skill_policy_header`. Sending a body that contains only denied fields returns 400.\nAll other keys are mapped through CONFIG_FIELD_MAP (supports both camelCase and snake_case variants). Requires `team:write` scope. Mirrors `PATCH /api/agents/[agentId]/versions/[versionId]`.\n","tags":["Versions"],"parameters":[{"$ref":"#/components/parameters/agentIdParam"},{"$ref":"#/components/parameters/versionIdParam"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VersionConfigPatch"}}}},"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Updated version bundle","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AgentVersionBundle"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/VersionNotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/agents/{agentId}/versions/{versionId}/publish":{"post":{"operationId":"publishAgentVersion","summary":"Publish a draft version","description":"Atomically promotes a draft version to published status, demotes the previously published version to archived, then materializes the live agent tables. Returns a 409 if a concurrent publish races ahead. Requires `team:write` scope. Mirrors `POST /api/agents/[agentId]/versions/[versionId]/publish`.\n","tags":["Versions"],"parameters":[{"$ref":"#/components/parameters/agentIdParam"},{"$ref":"#/components/parameters/versionIdParam"}],"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Published version bundle","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AgentVersionBundle"}}}}}},"400":{"description":"Version is not a draft and cannot be published, or all supplied patch fields were restricted.\n","$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/VersionNotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/agents/{agentId}/versions/{versionId}/discard":{"post":{"operationId":"discardAgentVersion","summary":"Discard (archive) a draft version","description":"Archives a draft environment. Returns `{ id, status: \"archived\" }`. Returns 400 if the version is not a draft; returns 409 on a concurrent discard race. Requires `team:write` scope. Mirrors `POST /api/agents/[agentId]/versions/[versionId]/discard`.\n","tags":["Versions"],"parameters":[{"$ref":"#/components/parameters/agentIdParam"},{"$ref":"#/components/parameters/versionIdParam"}],"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Discard result","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/DiscardAgentVersionResult"}}},"example":{"data":{"id":"av_01hx_staging","status":"archived"}}}}},"400":{"description":"Version is not a draft and cannot be discarded.","$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/VersionNotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/agents/{agentId}/conversations":{"get":{"operationId":"listAgentConversations","summary":"List conversations for an agent","tags":["Conversations"],"x-phase":1,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Conversation list (Phase 1)"}}}},"/agents/{agentId}/conversations/{conversationId}":{"get":{"operationId":"getConversation","summary":"Get conversation","tags":["Conversations"],"x-phase":1,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}},{"name":"conversationId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Conversation details (Phase 1)"}}}},"/agents/{agentId}/conversations/{conversationId}/messages":{"get":{"operationId":"listConversationMessages","summary":"List conversation messages","tags":["Conversations"],"x-phase":1,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}},{"name":"conversationId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Message list (Phase 1)"}}}},"/agents/{agentId}/conversations/{conversationId}/messages/{messageId}":{"get":{"operationId":"getConversationMessage","summary":"Get a single message","tags":["Conversations"],"x-phase":2,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}},{"name":"conversationId","in":"path","required":true,"schema":{"type":"string"}},{"name":"messageId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Message details"},"404":{"description":"Message not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/agents/{agentId}/contacts":{"get":{"operationId":"listContacts","summary":"List contacts","tags":["Contacts"],"x-phase":1,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Contact list (Phase 1)"}}},"post":{"operationId":"createContact","summary":"Create contact","tags":["Contacts"],"x-phase":1,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:write"]}],"responses":{"201":{"description":"Contact created (Phase 1)"},"409":{"description":"Contact already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/agents/{agentId}/contacts/{contactId}":{"get":{"operationId":"getContact","summary":"Get contact","tags":["Contacts"],"x-phase":1,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}},{"name":"contactId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Contact details (Phase 1)"}}},"patch":{"operationId":"updateContact","summary":"Update contact","tags":["Contacts"],"x-phase":1,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}},{"name":"contactId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Contact updated (Phase 1)"}}},"delete":{"operationId":"deleteContact","summary":"Delete contact","tags":["Contacts"],"x-phase":1,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}},{"name":"contactId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Contact deleted","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}}}}}},"409":{"description":"Contact already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/agents/{agentId}/contact-attributes":{"get":{"operationId":"listContactAttributes","summary":"List contact custom attributes","tags":["Contacts"],"x-phase":1,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Attribute schema (Phase 1)"}}},"post":{"operationId":"createContactAttribute","summary":"Create contact custom attribute","tags":["Contacts"],"x-phase":1,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:write"]}],"responses":{"201":{"description":"Attribute created (Phase 1)"}}}},"/integrations":{"get":{"operationId":"listIntegrations","summary":"List integrations","tags":["Integrations"],"x-phase":1,"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Integration list (Phase 1)"}}}},"/integrations/{integrationId}":{"delete":{"operationId":"deleteIntegration","summary":"Delete integration","tags":["Integrations"],"x-phase":1,"parameters":[{"name":"integrationId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Integration removed","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}}}}}}}}},"/agents/{agentId}/chat":{"post":{"operationId":"chatWithAgent","summary":"Send a message and receive an AI response","description":"Uses the agent's configured model, system prompt, tools, and knowledge (playground flow).","tags":["Chat"],"x-phase":2,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["messages"],"properties":{"messages":{"type":"array","items":{"type":"object","required":["role","content"],"properties":{"role":{"type":"string","enum":["user","assistant"]},"content":{"type":"string"}}}},"conversation_id":{"type":"string"}}}}}},"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Assistant response"},"401":{"description":"Unauthorized"}}}},"/agents/{agentId}/conversations/{conversationId}/messages/{messageId}/feedback":{"patch":{"operationId":"updateMessageFeedback","summary":"Set message feedback","tags":["Conversations"],"x-phase":2,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}},{"name":"conversationId","in":"path","required":true,"schema":{"type":"string"}},{"name":"messageId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["rating"],"properties":{"rating":{"type":"string","enum":["up","down"],"description":"Thumbs up or down rating for the message"},"comment":{"type":"string","description":"Optional free-text comment"}}}}}},"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Feedback saved"}}}},"/agents/{agentId}/users/{userId}/conversations":{"get":{"operationId":"listUserConversations","summary":"List conversations for an external user","tags":["Conversations"],"x-phase":2,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}},{"name":"userId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Conversation list"}}}},"/agents/{agentId}/conversations/export":{"get":{"operationId":"exportConversations","summary":"Export conversations for an agent","tags":["Conversations"],"x-phase":2,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}},{"name":"include_messages","in":"query","schema":{"type":"boolean"}},{"name":"limit","in":"query","schema":{"type":"integer","default":50,"minimum":1,"maximum":500},"description":"Maximum number of conversations to return (capped at 500)"},{"name":"starting_after","in":"query","schema":{"type":"string"},"description":"Cursor for pagination (conversation ID to start after)"},{"name":"from","in":"query","schema":{"type":"string","format":"date-time"},"description":"Filter conversations created on or after this ISO 8601 timestamp"},{"name":"to","in":"query","schema":{"type":"string","format":"date-time"},"description":"Filter conversations created on or before this ISO 8601 timestamp"}],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Export payload"}}}},"/agents/{agentId}/contact-attributes/{name}":{"put":{"operationId":"updateContactAttribute","summary":"Update contact custom attribute by name","tags":["Contacts"],"x-phase":2,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Attribute updated"}}}},"/knowledge":{"get":{"operationId":"listKnowledge","summary":"List knowledge items","tags":["Knowledge"],"x-phase":2,"parameters":[{"name":"agent_id","in":"query","schema":{"type":"string"}},{"name":"directory_id","in":"query","schema":{"type":"string"}}],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Knowledge list"}}},"post":{"operationId":"createKnowledge","summary":"Add knowledge (website or QA)","tags":["Knowledge"],"x-phase":2,"security":[{"BearerAuth":["team:write"]}],"responses":{"201":{"description":"For `type: \"qa\"`, the created Q&A knowledge item is returned as\na full content record.\n\nFor `type: \"website\"`, no content record is created\nsynchronously: adding a website source only enqueues an\nasynchronous crawl. Content items for the crawled pages are\ncreated out of band once the crawl completes. The response\ninstead carries crawl-initiation metadata (target integration\nfolder, agent scope) — see `WebsiteCrawlInitiated`. Poll\n`GET /knowledge?agent_id=...` to see crawled pages once\ningestion finishes.\n","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/WebsiteCrawlInitiated"},{"type":"object","description":"Q&A knowledge item (type=qa)","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"type":{"type":"string","enum":["qa"]},"content":{"type":"string"},"created_at":{"type":"string"},"updated_at":{"type":"string"}}}}}]}}}}}}},"/knowledge/{contentId}":{"get":{"operationId":"getKnowledge","summary":"Get knowledge item","tags":["Knowledge"],"x-phase":2,"parameters":[{"name":"contentId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Knowledge item"}}},"delete":{"operationId":"deleteKnowledge","summary":"Delete knowledge item","tags":["Knowledge"],"x-phase":2,"parameters":[{"name":"contentId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Knowledge deleted"}}}},"/agents/{agentId}/assets/icon":{"post":{"operationId":"uploadAgentIcon","summary":"Upload agent icon","tags":["Assets"],"x-phase":3,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary"}}}}}},"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Icon uploaded"},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteAgentIcon","summary":"Delete agent icon","tags":["Assets"],"x-phase":3,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Icon removed"}}}},"/agents/{agentId}/assets/profile-picture":{"post":{"operationId":"uploadAgentProfilePicture","summary":"Upload agent profile picture","tags":["Assets"],"x-phase":3,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary"}}}}}},"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Profile picture uploaded"}}},"delete":{"operationId":"deleteAgentProfilePicture","summary":"Delete agent profile picture","tags":["Assets"],"x-phase":3,"parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Profile picture removed"}}}},"/forms":{"get":{"operationId":"listForms","summary":"List forms","description":"Returns all forms belonging to the authenticated team. Supports optional filtering by formType, isEnabled, and a free-text search.\n","tags":["Forms"],"parameters":[{"name":"formType","in":"query","required":false,"schema":{"type":"string","enum":["lead","contact","survey","feedback","custom"]},"description":"Filter by form type."},{"name":"isEnabled","in":"query","required":false,"schema":{"type":"string","enum":["true","false"]},"description":"Filter by enabled status. Pass \"true\" or \"false\"."},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Free-text search against form name."}],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"List of forms.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormListResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createForm","summary":"Create a form","description":"Creates a new form for the authenticated team. Requires the **team:write** scope. The actor is recorded as a system-type TeamActor keyed on the API key ID.\n","tags":["Forms"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFormRequest"}}}},"security":[{"BearerAuth":["team:write"]}],"responses":{"201":{"description":"Form created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormResponse"}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/forms/templates":{"get":{"operationId":"listFormTemplates","summary":"List form templates","description":"Returns form templates owned by the authenticated team only. System-wide (platform-provided) templates are not returned by this public API route. Registered as a static path before /forms/{formId} so that Next.js resolves the literal segment \"templates\" ahead of the dynamic catch-all.\n","tags":["Forms"],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"List of form templates.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormTemplateListResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/forms/{formId}":{"get":{"operationId":"getForm","summary":"Get a form","description":"Fetches a single form by ID. Pass **?includeStats=true** to append submission statistics to the response.\n","tags":["Forms"],"parameters":[{"$ref":"#/components/parameters/FormId"},{"name":"includeStats","in":"query","required":false,"schema":{"type":"string","enum":["true","false"]},"description":"Include submission statistics in the response."}],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Form object (optionally with stats).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormWithOptionalStatsResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/FormNotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"put":{"operationId":"updateForm","summary":"Update a form","description":"Updates an existing form. At least one field must be provided. Requires the **team:write** scope.\n","tags":["Forms"],"parameters":[{"$ref":"#/components/parameters/FormId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFormRequest"}}}},"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Updated form.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormResponse"}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/FormNotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/forms/{formId}/submissions":{"get":{"operationId":"listFormSubmissions","summary":"List form submissions","description":"Returns a paginated list of submissions for a form. Supports filtering by status, agentId, contactId, startDate, endDate, limit, and offset.\n","tags":["Forms"],"parameters":[{"$ref":"#/components/parameters/FormId"},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["new","viewed","processed","converted","archived"]}},{"name":"agentId","in":"query","required":false,"schema":{"type":"string"}},{"name":"contactId","in":"query","required":false,"schema":{"type":"string"}},{"name":"startDate","in":"query","required":false,"schema":{"type":"integer"},"description":"Unix timestamp (seconds); filter submissions created on or after this time."},{"name":"endDate","in":"query","required":false,"schema":{"type":"integer"},"description":"Unix timestamp (seconds); filter submissions created on or before this time."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Paginated submission list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionListResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/FormNotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createFormSubmission","summary":"Create a form submission","description":"Creates a new submission for the specified form. If form.settings.integrations.createContact is true the service will automatically upsert a contact from the submission data. A **forms/submission.created** workflow trigger is dispatched as a fire-and-forget side effect and will never block the 201 response. Requires the **team:write** scope.\n","tags":["Forms"],"parameters":[{"$ref":"#/components/parameters/FormId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSubmissionRequest"},"example":{"submissionData":{"email":"alice@example.com","name":"Alice","message":"I'd like to learn more about your enterprise plan."},"metadata":{"source":"widget","pageUrl":"https://example.com/pricing"}}}}},"security":[{"BearerAuth":["team:write"]}],"responses":{"201":{"description":"Submission created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionResponse"}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/FormNotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/forms/{formId}/submissions/{submissionId}":{"get":{"operationId":"getFormSubmission","summary":"Get a form submission","description":"Fetches a single submission by ID. The service enforces teamId ownership and an additional route-level guard verifies that submission.formId matches the path parameter to prevent cross-form data disclosure.\n","tags":["Forms"],"parameters":[{"$ref":"#/components/parameters/FormId"},{"$ref":"#/components/parameters/SubmissionId"}],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Submission object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/SubmissionNotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"put":{"operationId":"updateFormSubmission","summary":"Update a form submission","description":"Updates submission status or contactId. At least one field is required. Requires the **team:write** scope.\n","tags":["Forms"],"parameters":[{"$ref":"#/components/parameters/FormId"},{"$ref":"#/components/parameters/SubmissionId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSubmissionRequest"}}}},"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Updated submission.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionResponse"}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/SubmissionNotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/webhooks":{"get":{"operationId":"listWebhooks","summary":"List webhook subscriptions","description":"List all non-deleted webhook subscriptions for the authenticated team. The `signingSecret` is never included in list responses.\n","tags":["Webhooks"],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"List of webhook subscriptions","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookSubscription"}}}},"example":{"data":[{"id":"wh_01hx...","teamId":"team_01hx...","url":"https://example.com/webhooks/nexvio","events":["contacts.created","tickets.created"],"description":"Production webhook","hasSecret":true,"isEnabled":true,"isDeleted":false,"createdAt":1719878400,"updatedAt":1719878400}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createWebhook","summary":"Create webhook subscription","description":"Create a new webhook subscription. The `signingSecret` is returned **only once** in this response and on `rotate-secret`. Store it securely — it cannot be retrieved again.\n**SSRF protection**: The `url` field must use `https://` and must not target private, loopback, or cloud-metadata addresses.\n","tags":["Webhooks"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreateBody"},"example":{"url":"https://example.com/webhooks/nexvio","events":["contacts.created","tickets.created"],"description":"Production webhook","isEnabled":true}}}},"security":[{"BearerAuth":["team:write"]}],"responses":{"201":{"description":"Webhook subscription created","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["subscription","signingSecret"],"properties":{"subscription":{"$ref":"#/components/schemas/WebhookSubscription"},"signingSecret":{"type":"string","description":"Plaintext 64-hex-character HMAC-SHA256 signing secret. Returned **only on create and rotate** — store it securely.\n","example":"a3f8...c1d2"}}}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/webhooks/{webhookId}":{"parameters":[{"$ref":"#/components/parameters/WebhookId"}],"get":{"operationId":"getWebhook","summary":"Get webhook subscription","description":"Fetch a single webhook subscription by ID. `signingSecret` is never included in this response.\n","tags":["Webhooks"],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"Webhook subscription","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WebhookSubscription"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"updateWebhook","summary":"Update webhook subscription","description":"Partially update a webhook subscription. Only provided fields are changed. At least one field must be present in the body.\n**SSRF protection**: If updating `url`, it must use `https://` and must not target private, loopback, or cloud-metadata addresses.\n","tags":["Webhooks"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookUpdateBody"}}}},"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Updated webhook subscription","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/WebhookSubscription"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteWebhook","summary":"Delete webhook subscription","description":"Soft-delete a webhook subscription (`isDeleted=true`, `isEnabled=false`). The subscription will no longer receive events and will not appear in list responses.\n","tags":["Webhooks"],"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Webhook subscription deleted","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean","example":true}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/webhooks/{webhookId}/rotate-secret":{"parameters":[{"$ref":"#/components/parameters/WebhookId"}],"post":{"operationId":"rotateWebhookSecret","summary":"Rotate webhook signing secret","description":"Generate a new HMAC signing secret for the webhook subscription. The new `signingSecret` is returned **only in this response** — store it securely. The old secret is immediately invalidated.\n","tags":["Webhooks"],"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"New signing secret","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["signingSecret","rotatedAt"],"properties":{"signingSecret":{"type":"string","description":"New plaintext 64-hex-character HMAC-SHA256 signing secret. Returned **only on create and rotate** — store it securely.\n","example":"b9e1...d4a7"},"rotatedAt":{"type":"integer","description":"Unix timestamp (seconds) when the secret was rotated.","example":1719878400}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/webhooks/{webhookId}/deliveries":{"parameters":[{"$ref":"#/components/parameters/WebhookId"}],"get":{"operationId":"listWebhookDeliveries","summary":"List webhook delivery attempts","description":"List delivery attempt logs for a webhook subscription, ordered newest-first. Use `limit` to control how many records are returned (1–200, default 50).\n","tags":["Webhooks"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"Maximum number of delivery attempts to return (clamped to 200)."}],"security":[{"BearerAuth":["team:read"]}],"responses":{"200":{"description":"List of delivery attempts","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDeliveryAttempt"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/webhooks/{webhookId}/test":{"parameters":[{"$ref":"#/components/parameters/WebhookId"}],"post":{"operationId":"testWebhook","summary":"Send test webhook event","description":"Send a synthetic test event to the webhook's target URL. Triggers a real outbound HTTP delivery attempt, so this requires `team:write` scope.\nThe `eventType` in the request body overrides which event type is used; if omitted, the first subscribed event type is used.\n","tags":["Webhooks"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTestBody"}}}},"security":[{"BearerAuth":["team:write"]}],"responses":{"200":{"description":"Test event queued (or skipped — check `queued` flag)","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["subscriptionId","eventType"],"properties":{"queued":{"type":"boolean","description":"`true` when the test event was successfully enqueued for delivery. `false` when delivery was skipped (see `reason` for details).\n","example":true},"reason":{"type":"string","description":"Present only when `queued` is `false`. Indicates why the delivery was skipped.\n","enum":["missing_admin_portal_url","admin_portal_error","no_matching_subscriptions"]},"eventId":{"type":"string","description":"Event ID assigned by the delivery pipeline (present when queued=true).","example":"evt_01hx..."},"subscriptionId":{"type":"string","example":"wh_01hx..."},"eventType":{"$ref":"#/components/schemas/WebhookEventType"}}}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}