openapi: 3.1.0 info: title: Protodesk API version: 0.1.0 description: > The Protodesk Platform API: sync your customers in, attach your own business objects (orders, bookings, listings) to conversations, post and read messages across channels, and receive every workspace event via signed webhooks or a resumable SSE stream. Authenticate every request with a workspace-scoped API key: `Authorization: Bearer pk_...`. Operations that support retry protection document an `Idempotency-Key` header. Reuse it only with an identical request. List pagination is described on each endpoint. Task-oriented guides: [docs.protodesk.io](https://docs.protodesk.io). servers: - url: https://api.protodesk.io/v1 description: Production - url: http://localhost:8080/v1 description: Local development tags: - name: Knowledge description: Manage Evelin website imports, authored content, indexing and source eligibility. - name: Evelin description: Human-owned AI connections using the existing Evelin runtime. Generation never sends a customer message. - name: Customers description: People or companies contacting a workspace across channels. - name: Conversations description: Customer conversations attached to channels and external business objects. - name: Messages description: Customer, agent, AI, system, and internal messages inside conversations. - name: Attachments description: Scoped file transfer for messages. - name: Channel identities description: The same customer across WhatsApp, email, chat, and more — linked identities per channel. - name: Statuses & priorities description: The workspace's conversation statuses and priority levels. - name: Assignments description: Who a conversation has been assigned to, over time. Assign via POST /conversations/{conversationId}/assign. - name: Webhooks description: Webhook subscriptions for receiving workspace events. - name: Realtime description: Server-sent event stream of workspace events. - name: API Keys description: Create, list, and revoke workspace API keys. - name: Account description: The workspace and scopes behind your API key. - name: System description: Service metadata and health endpoints. paths: "/knowledge/websites": "get": "tags": ["Knowledge"] "summary": "List Knowledge website connections" "operationId": "list_knowledge_websites" "x-mcp-tool": "list_knowledge_websites" "x-required-scopes": ["knowledge:read"] "security": [{"ApiKeyAuth": []}] "description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication." "x-mcp-description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication." "parameters": [{"name": "limit", "in": "query", "schema": {"type": "integer", "minimum": 1, "maximum": 100}, "description": "Page size, default 50. Results sort by ID ascending."}, {"name": "after", "in": "query", "schema": {"type": "string", "maxLength": 128}, "description": "Use nextCursor from the preceding response until hasMore is false."}] "responses": "200": "description": "Current resource, list, queued operation, or retry receipt. A queued job is not proof of completed indexing." "content": "application/json": "schema": "type": "object" "additionalProperties": true "403": "description": "Required Knowledge scopes or current owner/admin connection are missing." "409": "description": "Revision, URL schedule, or idempotency conflict." "422": "description": "Invalid input, unsupported document, or extraction limit." "post": "tags": ["Knowledge"] "summary": "Import a website URL, sitemap, or llms export" "operationId": "create_knowledge_website" "x-mcp-tool": "create_knowledge_website" "x-required-scopes": ["knowledge:read", "knowledge:manage"] "security": [{"ApiKeyAuth": []}] "description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. All mutations require Idempotency-Key. Retry an uncertain result with the identical body and key. Existing-resource edits require its current revision; stale revisions return 409. Returns jobId when indexing is queued. Poll get_knowledge_indexing_job and inspect resource status before claiming readiness. Jobs and schedules stop when the originating credential is revoked, expires, loses scope, or its owner loses admin membership. kind=url imports exactly one page; sitemap follows listed pages; llms reads a linked text export. maxPages defaults to 200. Public URLs only; SSRF and robots protections remain enabled." "x-mcp-description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. All mutations require Idempotency-Key. Retry an uncertain result with the identical body and key. Existing-resource edits require its current revision; stale revisions return 409. Returns jobId when indexing is queued. Poll get_knowledge_indexing_job and inspect resource status before claiming readiness. Jobs and schedules stop when the originating credential is revoked, expires, loses scope, or its owner loses admin membership. kind=url imports exactly one page; sitemap follows listed pages; llms reads a linked text export. maxPages defaults to 200. Public URLs only; SSRF and robots protections remain enabled." "parameters": [{"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 8, "maxLength": 128}}] "requestBody": "required": true "content": "application/json": "schema": "type": "object" "additionalProperties": false "properties": "title": "type": "string" "minLength": 1 "maxLength": 200 "url": "type": "string" "format": "uri" "maxLength": 2048 "kind": "type": "string" "enum": ["url", "sitemap", "llms"] "maxPages": "type": "integer" "minimum": 1 "maximum": 200 "include": "type": "array" "maxItems": 20 "items": "type": "string" "maxLength": 256 "exclude": "type": "array" "maxItems": 20 "items": "type": "string" "maxLength": 256 "refreshSchedule": "type": "string" "enum": ["manual", "daily", "weekly"] "example": "title": "Documentation" "url": "https://example.com/sitemap.xml" "kind": "sitemap" "refreshSchedule": "weekly" "required": ["title", "url", "kind"] "responses": "200": "description": "Current resource, list, queued operation, or retry receipt. A queued job is not proof of completed indexing." "content": "application/json": "schema": "type": "object" "additionalProperties": true "403": "description": "Required Knowledge scopes or current owner/admin connection are missing." "409": "description": "Revision, URL schedule, or idempotency conflict." "422": "description": "Invalid input, unsupported document, or extraction limit." "/knowledge/websites/{id}": "get": "tags": ["Knowledge"] "summary": "Get a Knowledge website and its indexing diagnostics" "operationId": "get_knowledge_website" "x-mcp-tool": "get_knowledge_website" "x-required-scopes": ["knowledge:read"] "security": [{"ApiKeyAuth": []}] "description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication." "x-mcp-description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication." "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "minLength": 1, "maxLength": 128}}] "responses": "200": "description": "Current resource, list, queued operation, or retry receipt. A queued job is not proof of completed indexing." "content": "application/json": "schema": "type": "object" "additionalProperties": true "403": "description": "Required Knowledge scopes or current owner/admin connection are missing." "409": "description": "Revision, URL schedule, or idempotency conflict." "422": "description": "Invalid input, unsupported document, or extraction limit." "delete": "tags": ["Knowledge"] "summary": "Remove a Knowledge website and stop refreshing it" "operationId": "delete_knowledge_website" "x-mcp-tool": "delete_knowledge_website" "x-required-scopes": ["knowledge:read", "knowledge:manage"] "security": [{"ApiKeyAuth": []}] "description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. All mutations require Idempotency-Key. Retry an uncertain result with the identical body and key. Existing-resource edits require its current revision; stale revisions return 409. Removes the connection and schedule immediately. Pages supplied by another active connection remain eligible. Saved index records remain for inspection; late jobs cannot restore the deleted connection." "x-mcp-description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. All mutations require Idempotency-Key. Retry an uncertain result with the identical body and key. Existing-resource edits require its current revision; stale revisions return 409. Removes the connection and schedule immediately. Pages supplied by another active connection remain eligible. Saved index records remain for inspection; late jobs cannot restore the deleted connection." "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "minLength": 1, "maxLength": 128}}, {"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 8, "maxLength": 128}}] "requestBody": "required": true "content": "application/json": "schema": "type": "object" "additionalProperties": false "properties": "expectedRevision": "type": "integer" "minimum": 1 "example": "expectedRevision": 1 "required": ["expectedRevision"] "responses": "200": "description": "Current resource, list, queued operation, or retry receipt. A queued job is not proof of completed indexing." "content": "application/json": "schema": "type": "object" "additionalProperties": true "403": "description": "Required Knowledge scopes or current owner/admin connection are missing." "409": "description": "Revision, URL schedule, or idempotency conflict." "422": "description": "Invalid input, unsupported document, or extraction limit." "/knowledge/websites/{id}/refresh": "post": "tags": ["Knowledge"] "summary": "Refresh a Knowledge website" "operationId": "refresh_knowledge_website" "x-mcp-tool": "refresh_knowledge_website" "x-required-scopes": ["knowledge:read", "knowledge:manage"] "security": [{"ApiKeyAuth": []}] "description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. All mutations require Idempotency-Key. Retry an uncertain result with the identical body and key. Existing-resource edits require its current revision; stale revisions return 409. Returns jobId when indexing is queued. Poll get_knowledge_indexing_job and inspect resource status before claiming readiness. Jobs and schedules stop when the originating credential is revoked, expires, loses scope, or its owner loses admin membership." "x-mcp-description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. All mutations require Idempotency-Key. Retry an uncertain result with the identical body and key. Existing-resource edits require its current revision; stale revisions return 409. Returns jobId when indexing is queued. Poll get_knowledge_indexing_job and inspect resource status before claiming readiness. Jobs and schedules stop when the originating credential is revoked, expires, loses scope, or its owner loses admin membership." "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "minLength": 1, "maxLength": 128}}, {"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 8, "maxLength": 128}}] "requestBody": "required": true "content": "application/json": "schema": "type": "object" "additionalProperties": false "properties": "expectedRevision": "type": "integer" "minimum": 1 "example": "expectedRevision": 1 "required": ["expectedRevision"] "responses": "200": "description": "Current resource, list, queued operation, or retry receipt. A queued job is not proof of completed indexing." "content": "application/json": "schema": "type": "object" "additionalProperties": true "403": "description": "Required Knowledge scopes or current owner/admin connection are missing." "409": "description": "Revision, URL schedule, or idempotency conflict." "422": "description": "Invalid input, unsupported document, or extraction limit." "/knowledge/websites/{id}/schedule": "put": "tags": ["Knowledge"] "summary": "Set a Knowledge website refresh schedule" "operationId": "set_knowledge_refresh_schedule" "x-mcp-tool": "set_knowledge_refresh_schedule" "x-required-scopes": ["knowledge:read", "knowledge:manage"] "security": [{"ApiKeyAuth": []}] "description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. All mutations require Idempotency-Key. Retry an uncertain result with the identical body and key. Existing-resource edits require its current revision; stale revisions return 409. Manual stops future automatic refreshes. Daily and weekly run after 24 and 168 hours. This does not fetch the website immediately." "x-mcp-description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. All mutations require Idempotency-Key. Retry an uncertain result with the identical body and key. Existing-resource edits require its current revision; stale revisions return 409. Manual stops future automatic refreshes. Daily and weekly run after 24 and 168 hours. This does not fetch the website immediately." "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "minLength": 1, "maxLength": 128}}, {"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 8, "maxLength": 128}}] "requestBody": "required": true "content": "application/json": "schema": "type": "object" "additionalProperties": false "properties": "expectedRevision": "type": "integer" "minimum": 1 "refreshSchedule": "type": "string" "enum": ["manual", "daily", "weekly"] "example": "expectedRevision": 1 "refreshSchedule": "weekly" "required": ["expectedRevision", "refreshSchedule"] "responses": "200": "description": "Current resource, list, queued operation, or retry receipt. A queued job is not proof of completed indexing." "content": "application/json": "schema": "type": "object" "additionalProperties": true "403": "description": "Required Knowledge scopes or current owner/admin connection are missing." "409": "description": "Revision, URL schedule, or idempotency conflict." "422": "description": "Invalid input, unsupported document, or extraction limit." "/knowledge/articles": "get": "tags": ["Knowledge"] "summary": "List Knowledge articles" "operationId": "list_knowledge_articles" "x-mcp-tool": "list_knowledge_articles" "x-required-scopes": ["knowledge:read"] "security": [{"ApiKeyAuth": []}] "description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication." "x-mcp-description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication." "parameters": [{"name": "limit", "in": "query", "schema": {"type": "integer", "minimum": 1, "maximum": 100}, "description": "Page size, default 50. Results sort by ID ascending."}, {"name": "after", "in": "query", "schema": {"type": "string", "maxLength": 128}, "description": "Use nextCursor from the preceding response until hasMore is false."}] "responses": "200": "description": "Current resource, list, queued operation, or retry receipt. A queued job is not proof of completed indexing." "content": "application/json": "schema": "type": "object" "additionalProperties": true "403": "description": "Required Knowledge scopes or current owner/admin connection are missing." "409": "description": "Revision, URL schedule, or idempotency conflict." "422": "description": "Invalid input, unsupported document, or extraction limit." "post": "tags": ["Knowledge"] "summary": "Create an Evelin Knowledge article" "operationId": "create_knowledge_article" "x-mcp-tool": "create_knowledge_article" "x-required-scopes": ["knowledge:read", "knowledge:manage"] "security": [{"ApiKeyAuth": []}] "description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. All mutations require Idempotency-Key. Retry an uncertain result with the identical body and key. Existing-resource edits require its current revision; stale revisions return 409. Returns jobId when indexing is queued. Poll get_knowledge_indexing_job and inspect resource status before claiming readiness. Jobs and schedules stop when the originating credential is revoked, expires, loses scope, or its owner loses admin membership." "x-mcp-description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. All mutations require Idempotency-Key. Retry an uncertain result with the identical body and key. Existing-resource edits require its current revision; stale revisions return 409. Returns jobId when indexing is queued. Poll get_knowledge_indexing_job and inspect resource status before claiming readiness. Jobs and schedules stop when the originating credential is revoked, expires, loses scope, or its owner loses admin membership." "parameters": [{"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 8, "maxLength": 128}}] "requestBody": "required": true "content": "application/json": "schema": "type": "object" "additionalProperties": false "properties": "title": "type": "string" "minLength": 1 "maxLength": 200 "content": "type": "string" "minLength": 1 "maxLength": 262144 "description": "Saved plain text or Markdown, at most 256 KiB UTF-8. MCP request envelopes are limited to 128 KiB." "status": "type": "string" "enum": ["draft", "published"] "example": "title": "Return policy" "content": "Unopened items can be returned within thirty days." "status": "published" "required": ["title", "content"] "responses": "200": "description": "Current resource, list, queued operation, or retry receipt. A queued job is not proof of completed indexing." "content": "application/json": "schema": "type": "object" "additionalProperties": true "403": "description": "Required Knowledge scopes or current owner/admin connection are missing." "409": "description": "Revision, URL schedule, or idempotency conflict." "422": "description": "Invalid input, unsupported document, or extraction limit." "/knowledge/articles/{id}": "get": "tags": ["Knowledge"] "summary": "Read a Knowledge article and its indexing status" "operationId": "get_knowledge_article" "x-mcp-tool": "get_knowledge_article" "x-required-scopes": ["knowledge:read"] "security": [{"ApiKeyAuth": []}] "description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication." "x-mcp-description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication." "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "minLength": 1, "maxLength": 128}}] "responses": "200": "description": "Current resource, list, queued operation, or retry receipt. A queued job is not proof of completed indexing." "content": "application/json": "schema": "type": "object" "additionalProperties": true "403": "description": "Required Knowledge scopes or current owner/admin connection are missing." "409": "description": "Revision, URL schedule, or idempotency conflict." "422": "description": "Invalid input, unsupported document, or extraction limit." "put": "tags": ["Knowledge"] "summary": "Replace an authored Knowledge article" "operationId": "update_knowledge_article" "x-mcp-tool": "update_knowledge_article" "x-required-scopes": ["knowledge:read", "knowledge:manage"] "security": [{"ApiKeyAuth": []}] "description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. All mutations require Idempotency-Key. Retry an uncertain result with the identical body and key. Existing-resource edits require its current revision; stale revisions return 409. Returns jobId when indexing is queued. Poll get_knowledge_indexing_job and inspect resource status before claiming readiness. Jobs and schedules stop when the originating credential is revoked, expires, loses scope, or its owner loses admin membership. For standalone authored articles only. Website-backed articles must be changed at the source and refreshed. Use Help Center operations for Help Center content." "x-mcp-description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. All mutations require Idempotency-Key. Retry an uncertain result with the identical body and key. Existing-resource edits require its current revision; stale revisions return 409. Returns jobId when indexing is queued. Poll get_knowledge_indexing_job and inspect resource status before claiming readiness. Jobs and schedules stop when the originating credential is revoked, expires, loses scope, or its owner loses admin membership. For standalone authored articles only. Website-backed articles must be changed at the source and refreshed. Use Help Center operations for Help Center content." "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "minLength": 1, "maxLength": 128}}, {"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 8, "maxLength": 128}}] "requestBody": "required": true "content": "application/json": "schema": "type": "object" "additionalProperties": false "properties": "expectedRevision": "type": "integer" "minimum": 1 "title": "type": "string" "minLength": 1 "maxLength": 200 "content": "type": "string" "minLength": 1 "maxLength": 262144 "description": "Saved plain text or Markdown, at most 256 KiB UTF-8. MCP request envelopes are limited to 128 KiB." "status": "type": "string" "enum": ["draft", "published"] "example": "expectedRevision": 1 "title": "Return policy" "content": "Unopened items can be returned within fourteen days." "status": "published" "required": ["expectedRevision", "title", "content"] "responses": "200": "description": "Current resource, list, queued operation, or retry receipt. A queued job is not proof of completed indexing." "content": "application/json": "schema": "type": "object" "additionalProperties": true "403": "description": "Required Knowledge scopes or current owner/admin connection are missing." "409": "description": "Revision, URL schedule, or idempotency conflict." "422": "description": "Invalid input, unsupported document, or extraction limit." "delete": "tags": ["Knowledge"] "summary": "Delete an authored Knowledge article" "operationId": "delete_knowledge_article" "x-mcp-tool": "delete_knowledge_article" "x-required-scopes": ["knowledge:read", "knowledge:manage"] "security": [{"ApiKeyAuth": []}] "description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. All mutations require Idempotency-Key. Retry an uncertain result with the identical body and key. Existing-resource edits require its current revision; stale revisions return 409. For standalone authored articles only. Website-backed articles must be changed at the source and refreshed. Use Help Center operations for Help Center content." "x-mcp-description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. All mutations require Idempotency-Key. Retry an uncertain result with the identical body and key. Existing-resource edits require its current revision; stale revisions return 409. For standalone authored articles only. Website-backed articles must be changed at the source and refreshed. Use Help Center operations for Help Center content." "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "minLength": 1, "maxLength": 128}}, {"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 8, "maxLength": 128}}] "requestBody": "required": true "content": "application/json": "schema": "type": "object" "additionalProperties": false "properties": "expectedRevision": "type": "integer" "minimum": 1 "example": "expectedRevision": 1 "required": ["expectedRevision"] "responses": "200": "description": "Current resource, list, queued operation, or retry receipt. A queued job is not proof of completed indexing." "content": "application/json": "schema": "type": "object" "additionalProperties": true "403": "description": "Required Knowledge scopes or current owner/admin connection are missing." "409": "description": "Revision, URL schedule, or idempotency conflict." "422": "description": "Invalid input, unsupported document, or extraction limit." "/knowledge/articles/{id}/reindex": "post": "tags": ["Knowledge"] "summary": "Reindex a saved Knowledge article" "operationId": "reindex_knowledge_article" "x-mcp-tool": "reindex_knowledge_article" "x-required-scopes": ["knowledge:read", "knowledge:manage"] "security": [{"ApiKeyAuth": []}] "description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. All mutations require Idempotency-Key. Retry an uncertain result with the identical body and key. Existing-resource edits require its current revision; stale revisions return 409. Returns jobId when indexing is queued. Poll get_knowledge_indexing_job and inspect resource status before claiming readiness. Jobs and schedules stop when the originating credential is revoked, expires, loses scope, or its owner loses admin membership. For standalone authored articles only. Website-backed articles must be changed at the source and refreshed. Use Help Center operations for Help Center content." "x-mcp-description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. All mutations require Idempotency-Key. Retry an uncertain result with the identical body and key. Existing-resource edits require its current revision; stale revisions return 409. Returns jobId when indexing is queued. Poll get_knowledge_indexing_job and inspect resource status before claiming readiness. Jobs and schedules stop when the originating credential is revoked, expires, loses scope, or its owner loses admin membership. For standalone authored articles only. Website-backed articles must be changed at the source and refreshed. Use Help Center operations for Help Center content." "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "minLength": 1, "maxLength": 128}}, {"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 8, "maxLength": 128}}] "requestBody": "required": true "content": "application/json": "schema": "type": "object" "additionalProperties": false "properties": "expectedRevision": "type": "integer" "minimum": 1 "example": "expectedRevision": 1 "required": ["expectedRevision"] "responses": "200": "description": "Current resource, list, queued operation, or retry receipt. A queued job is not proof of completed indexing." "content": "application/json": "schema": "type": "object" "additionalProperties": true "403": "description": "Required Knowledge scopes or current owner/admin connection are missing." "409": "description": "Revision, URL schedule, or idempotency conflict." "422": "description": "Invalid input, unsupported document, or extraction limit." "/knowledge/documents": "post": "tags": ["Knowledge"] "summary": "Import a small document into Evelin Knowledge" "operationId": "import_knowledge_document" "x-mcp-tool": "import_knowledge_document" "x-required-scopes": ["knowledge:read", "knowledge:manage"] "security": [{"ApiKeyAuth": []}] "description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. All mutations require Idempotency-Key. Retry an uncertain result with the identical body and key. Existing-resource edits require its current revision; stale revisions return 409. Returns jobId when indexing is queued. Poll get_knowledge_indexing_job and inspect resource status before claiming readiness. Jobs and schedules stop when the originating credential is revoked, expires, loses scope, or its owner loses admin membership. Accepts text, Markdown, HTML, JSON, text-based PDF and DOCX. Scanned PDFs need OCR first. REST limit: 1 MiB decoded file, 256 KiB extracted UTF-8 text, 2 MiB request. MCP limit: 128 KiB total request; use files under about 80 KiB. Larger files use the dashboard upload. Returns a saved article; original file bytes are not retained." "x-mcp-description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. All mutations require Idempotency-Key. Retry an uncertain result with the identical body and key. Existing-resource edits require its current revision; stale revisions return 409. Returns jobId when indexing is queued. Poll get_knowledge_indexing_job and inspect resource status before claiming readiness. Jobs and schedules stop when the originating credential is revoked, expires, loses scope, or its owner loses admin membership. Accepts text, Markdown, HTML, JSON, text-based PDF and DOCX. Scanned PDFs need OCR first. REST limit: 1 MiB decoded file, 256 KiB extracted UTF-8 text, 2 MiB request. MCP limit: 128 KiB total request; use files under about 80 KiB. Larger files use the dashboard upload. Returns a saved article; original file bytes are not retained." "parameters": [{"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 8, "maxLength": 128}}] "requestBody": "required": true "content": "application/json": "schema": "type": "object" "additionalProperties": false "properties": "title": "type": "string" "minLength": 1 "maxLength": 200 "filename": "type": "string" "minLength": 1 "maxLength": 200 "contentType": "type": "string" "contentBase64": "type": "string" "minLength": 1 "maxLength": 1398104 "description": "Standard base64 file bytes, at most 1 MiB decoded over REST. For MCP keep the entire tool request under 128 KiB (about 80 KiB decoded file). Read actual file bytes with a file-capable client; never fabricate them." "example": "filename": "policy.txt" "contentType": "text/plain" "contentBase64": "UmV0dXJucyBhcmUgYWNjZXB0ZWQgd2l0aGluIHRoaXJ0eSBkYXlzLg==" "required": ["filename", "contentBase64"] "responses": "200": "description": "Current resource, list, queued operation, or retry receipt. A queued job is not proof of completed indexing." "content": "application/json": "schema": "type": "object" "additionalProperties": true "403": "description": "Required Knowledge scopes or current owner/admin connection are missing." "409": "description": "Revision, URL schedule, or idempotency conflict." "422": "description": "Invalid input, unsupported document, or extraction limit." "/knowledge/sources": "get": "tags": ["Knowledge"] "summary": "List indexed Knowledge sources and retrieval availability" "operationId": "list_knowledge_sources" "x-mcp-tool": "list_knowledge_sources" "x-required-scopes": ["knowledge:read"] "security": [{"ApiKeyAuth": []}] "description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication." "x-mcp-description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication." "parameters": [{"name": "limit", "in": "query", "schema": {"type": "integer", "minimum": 1, "maximum": 100}, "description": "Page size, default 50. Results sort by ID ascending."}, {"name": "after", "in": "query", "schema": {"type": "string", "maxLength": 128}, "description": "Use nextCursor from the preceding response until hasMore is false."}] "responses": "200": "description": "Current resource, list, queued operation, or retry receipt. A queued job is not proof of completed indexing." "content": "application/json": "schema": "type": "object" "additionalProperties": true "403": "description": "Required Knowledge scopes or current owner/admin connection are missing." "409": "description": "Revision, URL schedule, or idempotency conflict." "422": "description": "Invalid input, unsupported document, or extraction limit." "/knowledge/sources/{id}/content": "get": "tags": ["Knowledge"] "summary": "Preview retained Knowledge source text" "operationId": "get_knowledge_source_content" "x-mcp-tool": "get_knowledge_source_content" "x-required-scopes": ["knowledge:read"] "security": [{"ApiKeyAuth": []}] "description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. Returns at most 16,000 characters, including retained retired-source content. This is a preview, not an export." "x-mcp-description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. Returns at most 16,000 characters, including retained retired-source content. This is a preview, not an export." "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "minLength": 1, "maxLength": 128}}] "responses": "200": "description": "Current resource, list, queued operation, or retry receipt. A queued job is not proof of completed indexing." "content": "application/json": "schema": "type": "object" "additionalProperties": true "403": "description": "Required Knowledge scopes or current owner/admin connection are missing." "409": "description": "Revision, URL schedule, or idempotency conflict." "422": "description": "Invalid input, unsupported document, or extraction limit." "/knowledge/sources/{id}/retire": "post": "tags": ["Knowledge"] "summary": "Exclude an indexed source from Evelin answers" "operationId": "retire_knowledge_source" "x-mcp-tool": "retire_knowledge_source" "x-required-scopes": ["knowledge:read", "knowledge:manage"] "security": [{"ApiKeyAuth": []}] "description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. All mutations require Idempotency-Key. Retry an uncertain result with the identical body and key. Existing-resource edits require its current revision; stale revisions return 409. Retains content for inspection but excludes it from future retrieval. Refresh does not reactivate this record. This does not stop its website schedule; remove the website connection to stop future imports." "x-mcp-description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication. All mutations require Idempotency-Key. Retry an uncertain result with the identical body and key. Existing-resource edits require its current revision; stale revisions return 409. Retains content for inspection but excludes it from future retrieval. Refresh does not reactivate this record. This does not stop its website schedule; remove the website connection to stop future imports." "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "minLength": 1, "maxLength": 128}}, {"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 8, "maxLength": 128}}] "requestBody": "required": true "content": "application/json": "schema": "type": "object" "additionalProperties": false "properties": {} "example": {} "responses": "200": "description": "Current resource, list, queued operation, or retry receipt. A queued job is not proof of completed indexing." "content": "application/json": "schema": "type": "object" "additionalProperties": true "403": "description": "Required Knowledge scopes or current owner/admin connection are missing." "409": "description": "Revision, URL schedule, or idempotency conflict." "422": "description": "Invalid input, unsupported document, or extraction limit." "/knowledge/jobs/{id}": "get": "tags": ["Knowledge"] "summary": "Check a Knowledge indexing job" "operationId": "get_knowledge_indexing_job" "x-mcp-tool": "get_knowledge_indexing_job" "x-required-scopes": ["knowledge:read"] "security": [{"ApiKeyAuth": []}] "description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication." "x-mcp-description": "Requires a connection owned by a current workspace owner or admin. Knowledge is shared workspace content, separate from customer-facing Help Center publication." "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "minLength": 1, "maxLength": 128}}] "responses": "200": "description": "Current resource, list, queued operation, or retry receipt. A queued job is not proof of completed indexing." "content": "application/json": "schema": "type": "object" "additionalProperties": true "403": "description": "Required Knowledge scopes or current owner/admin connection are missing." "409": "description": "Revision, URL schedule, or idempotency conflict." "422": "description": "Invalid input, unsupported document, or extraction limit." /attachments/uploads: post: tags: [Attachments] summary: Start an attachment upload operationId: createAttachmentUpload x-mcp-tool: create_attachment_upload x-required-scopes: ["attachments:write"] security: [{ ApiKeyAuth: [] }] description: > Creates a connection-owned upload with a ten-minute lifetime. Compute the file's lowercase SHA-256 first, then PUT its raw bytes to the returned URL with the returned Content-Type and Content-Length headers. Do not forward your API key or OAuth token to that URL. Call complete_attachment_upload afterward. Requires a client capable of reading the local file and making an HTTP PUT; MCP does not transfer bytes inside tool arguments. Supports JPEG, PNG, WebP, GIF, MP3, Ogg, WAV, M4A, WebM, MP4, MOV, PDF, plain text and CSV up to 16 MiB. Office files and archives are not supported by this upload flow. The same idempotency key and file metadata reuse the upload without extending its expiry; changed metadata returns 409. A completed retry returns its ready attachment. Uploading never sends a message. Expired uploads require a new idempotency key. parameters: - { name: Idempotency-Key, in: header, required: true, schema: { type: string, minLength: 8, maxLength: 128 } } requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [filename, contentType, sizeBytes, sha256] properties: filename: { type: string, minLength: 1, maxLength: 255 } contentType: type: string enum: [image/jpeg, image/png, image/webp, image/gif, audio/mpeg, audio/ogg, audio/wav, audio/mp4, audio/webm, video/mp4, video/webm, video/quicktime, application/pdf, text/plain, text/csv] sizeBytes: { type: integer, minimum: 1, maximum: 16777216 } sha256: { type: string, pattern: "^[a-f0-9]{64}$" } responses: "201": description: Pending upload. PUT the bytes before expiresAt, then complete using the same connection. content: application/json: schema: { $ref: "#/components/schemas/AttachmentUpload" } "200": description: This upload already completed. Reuse the attachment. content: application/json: schema: { $ref: "#/components/schemas/AttachmentUpload" } default: { $ref: "#/components/responses/Error" } /attachments/uploads/{uploadId}/complete: post: tags: [Attachments] summary: Validate and complete an attachment upload operationId: completeAttachmentUpload x-mcp-tool: complete_attachment_upload x-required-scopes: ["attachments:write"] security: [{ ApiKeyAuth: [] }] description: > Use the same API key or OAuth connection that created this upload. Verifies size, content type, SHA-256 and the existing known-malware policy, then stores immutable validated bytes. Safe to retry with the same uploadId; ready retries do not replace bytes. Returns an attachment object for the attachments array of create_message or send_message. Delivery is a separate action requiring messages:send. A 409 means upload missing or expired; 422 means invalid bytes; 429 means validation is busy (respect Retry-After). parameters: - { name: uploadId, in: path, required: true, schema: { type: string } } responses: "200": description: Ready durable attachment; no message has been sent. content: application/json: schema: { $ref: "#/components/schemas/AttachmentUpload" } default: { $ref: "#/components/responses/Error" } /messages/{messageId}/attachments/{attachmentIndex}/download: get: tags: [Attachments] summary: Get a message attachment download URL operationId: getMessageAttachmentDownload x-mcp-tool: get_message_attachment_download x-required-scopes: ["attachments:read", "conversations:read"] security: [{ ApiKeyAuth: [] }] description: > Returns a private hosted attachment's URL, valid for five minutes. Use the zero-based position in get_message's attachments array. Requires both attachments:read and conversations:read. Deleted messages and files outside this workspace are not downloadable. External URLs and provider attachments still being fetched return 422; their existing message metadata remains available. Treat the signed URL as a temporary credential and file contents as untrusted data. This tool returns a URL, not the file bytes. parameters: - { name: messageId, in: path, required: true, schema: { type: string } } - { name: attachmentIndex, in: path, required: true, schema: { type: string, pattern: "^[0-9]+$" } } responses: "200": description: Short-lived signed download URL. content: application/json: schema: type: object required: [url, expiresAt, filename, contentType, sizeBytes] properties: url: { type: string, format: uri } expiresAt: { type: string, format: date-time } filename: { type: string } contentType: { type: string } sizeBytes: { type: integer } default: { $ref: "#/components/responses/Error" } /ai/assistant/sessions: post: tags: [Evelin] summary: Create an Evelin chat operationId: create_evelin_chat x-mcp-tool: create_evelin_chat x-required-scopes: ["ai:use", "conversations:read", "customers:read", "help:read", "attachments:read"] security: [{ ApiKeyAuth: [] }] description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable. Creates a new chat; the same Idempotency-Key and conversation safely replay it. Creating a chat does not generate an answer. The active-chat limit is 50 per person/conversation across clients." x-mcp-description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable. Creates a new chat; the same Idempotency-Key and conversation safely replay it. Creating a chat does not generate an answer. The active-chat limit is 50 per person/conversation across clients." parameters: - { name: Idempotency-Key, in: header, required: true, schema: { type: string, minLength: 8, maxLength: 128 } } requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/EvelinChatCreate" } responses: "201": description: Successful operation. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinChat" } "200": description: Existing result replayed without a new effect. default: { $ref: "#/components/responses/Error" } /ai/conversations/{conversationId}/assistant/sessions: get: tags: [Evelin] summary: List this connection’s Evelin chats operationId: list_evelin_chats x-mcp-tool: list_evelin_chats x-required-scopes: ["ai:use", "conversations:read", "customers:read", "help:read", "attachments:read"] security: [{ ApiKeyAuth: [] }] description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable. Returns up to 100 unexpired chats for the conversation, newest activity first, including archived chats." x-mcp-description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable. Returns up to 100 unexpired chats for the conversation, newest activity first, including archived chats." parameters: - { name: conversationId, in: path, required: true, schema: { type: string, minLength: 1 } } responses: "200": description: Successful operation. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinChatList" } default: { $ref: "#/components/responses/Error" } /ai/assistant/sessions/{sessionId}: get: tags: [Evelin] summary: Get an Evelin chat operationId: get_evelin_chat x-mcp-tool: get_evelin_chat x-required-scopes: ["ai:use", "conversations:read", "customers:read", "help:read", "attachments:read"] security: [{ ApiKeyAuth: [] }] description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable." x-mcp-description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable." parameters: - { name: sessionId, in: path, required: true, schema: { type: string, minLength: 1 } } responses: "200": description: Successful operation. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinChat" } default: { $ref: "#/components/responses/Error" } patch: tags: [Evelin] summary: Rename an Evelin chat operationId: rename_evelin_chat x-mcp-tool: rename_evelin_chat x-required-scopes: ["ai:use", "conversations:read", "customers:read", "help:read", "attachments:read"] security: [{ ApiKeyAuth: [] }] description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable." x-mcp-description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable." parameters: - { name: sessionId, in: path, required: true, schema: { type: string, minLength: 1 } } requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/EvelinChatRename" } responses: "200": description: Successful operation. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinChat" } default: { $ref: "#/components/responses/Error" } /ai/assistant/sessions/{sessionId}/archive: post: tags: [Evelin] summary: Archive an Evelin chat operationId: archive_evelin_chat x-mcp-tool: archive_evelin_chat x-required-scopes: ["ai:use", "conversations:read", "customers:read", "help:read", "attachments:read"] security: [{ ApiKeyAuth: [] }] description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable. Archived chats remain readable until retention expires; new work and pending worker reads/answer commits stop. Use cancel_evelin_chat_turn to cancel a pending run before archiving. Active chats use a 30-day sliding retention window." x-mcp-description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable. Archived chats remain readable until retention expires; new work and pending worker reads/answer commits stop. Use cancel_evelin_chat_turn to cancel a pending run before archiving. Active chats use a 30-day sliding retention window." parameters: - { name: sessionId, in: path, required: true, schema: { type: string, minLength: 1 } } responses: "200": description: Successful operation. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinChat" } default: { $ref: "#/components/responses/Error" } /ai/assistant/sessions/{sessionId}/turns: post: tags: [Evelin] summary: Ask Evelin in a chat operationId: ask_evelin x-mcp-tool: ask_evelin x-required-scopes: ["ai:use", "conversations:read", "customers:read", "help:read", "attachments:read"] security: [{ ApiKeyAuth: [] }] description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable. Starts asynchronous work using the existing AI credit policy. Poll get_evelin_chat_turn with the returned sessionId and id. Reuse the same idempotency key for uncertain retries. A failed turn can be retried using retryOfTurnId and a new key only while it is the latest turn. All changes need separate action confirmation; no messages are sent." x-mcp-description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable. Starts asynchronous work using the existing AI credit policy. Poll get_evelin_chat_turn with the returned sessionId and id. Reuse the same idempotency key for uncertain retries. A failed turn can be retried using retryOfTurnId and a new key only while it is the latest turn. All changes need separate action confirmation; no messages are sent." parameters: - { name: sessionId, in: path, required: true, schema: { type: string, minLength: 1 } } - { name: Idempotency-Key, in: header, required: true, schema: { type: string, minLength: 8, maxLength: 128 } } requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/EvelinChatQuestion" } responses: "202": description: Successful operation. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinChatTurn" } default: { $ref: "#/components/responses/Error" } get: tags: [Evelin] summary: List Evelin chat turns operationId: list_evelin_chat_turns x-mcp-tool: list_evelin_chat_turns x-required-scopes: ["ai:use", "conversations:read", "customers:read", "help:read", "attachments:read"] security: [{ ApiKeyAuth: [] }] description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable. Returns retained turns in chronological order. Use get_evelin_chat_turn to poll one pending answer." x-mcp-description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable. Returns retained turns in chronological order. Use get_evelin_chat_turn to poll one pending answer." parameters: - { name: sessionId, in: path, required: true, schema: { type: string, minLength: 1 } } responses: "200": description: Successful operation. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinChatTurnList" } default: { $ref: "#/components/responses/Error" } /ai/assistant/sessions/{sessionId}/turns/{turnId}: get: tags: [Evelin] summary: Get an Evelin chat turn operationId: get_evelin_chat_turn x-mcp-tool: get_evelin_chat_turn x-required-scopes: ["ai:use", "conversations:read", "customers:read", "help:read", "attachments:read"] security: [{ ApiKeyAuth: [] }] description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable." x-mcp-description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable." parameters: - { name: sessionId, in: path, required: true, schema: { type: string, minLength: 1 } } - { name: turnId, in: path, required: true, schema: { type: string, minLength: 1 } } responses: "200": description: Successful operation. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinChatTurn" } default: { $ref: "#/components/responses/Error" } /ai/assistant/sessions/{sessionId}/turns/{turnId}/cancel: post: tags: [Evelin] summary: Cancel an Evelin chat turn operationId: cancel_evelin_chat_turn x-mcp-tool: cancel_evelin_chat_turn x-required-scopes: ["ai:use", "conversations:read", "customers:read", "help:read", "attachments:read"] security: [{ ApiKeyAuth: [] }] description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable. Cancels a pending turn. Identical cancellation retries are safe; completed turns return 409. Already consumed work may still be charged under the existing credit policy." x-mcp-description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable. Cancels a pending turn. Identical cancellation retries are safe; completed turns return 409. Already consumed work may still be charged under the existing credit policy." parameters: - { name: sessionId, in: path, required: true, schema: { type: string, minLength: 1 } } - { name: turnId, in: path, required: true, schema: { type: string, minLength: 1 } } responses: "200": description: Successful operation. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinChatTurn" } default: { $ref: "#/components/responses/Error" } /ai/assistant/sessions/{sessionId}/turns/{turnId}/actions/{proposalId}/confirm: post: tags: [Evelin] summary: Confirm a proposed Evelin action operationId: confirm_evelin_action x-mcp-tool: confirm_evelin_action x-required-scopes: ["ai:use", "conversations:read", "customers:read", "help:read", "attachments:read"] security: [{ ApiKeyAuth: [] }] description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable. Confirm only the exact proposal reviewed and requested by the user. Conversation properties and personal reminders require conversations:write. Help Center drafts require help:articles:write; publication additionally requires help:articles:publish. Scopes must remain in the current grant and session ceiling. Stale proposals return 409. Identical retries return the original receipt without repeating the action. This can publish customer-visible Help Center content but never sends a customer message." x-mcp-description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable. Confirm only the exact proposal reviewed and requested by the user. Conversation properties and personal reminders require conversations:write. Help Center drafts require help:articles:write; publication additionally requires help:articles:publish. Scopes must remain in the current grant and session ceiling. Stale proposals return 409. Identical retries return the original receipt without repeating the action. This can publish customer-visible Help Center content but never sends a customer message." parameters: - { name: sessionId, in: path, required: true, schema: { type: string, minLength: 1 } } - { name: turnId, in: path, required: true, schema: { type: string, minLength: 1 } } - { name: proposalId, in: path, required: true, schema: { type: string, minLength: 1 } } - { name: Idempotency-Key, in: header, required: true, schema: { type: string, minLength: 8, maxLength: 128 } } responses: "201": description: Successful operation. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinActionReceipt" } "200": description: Existing result replayed without a new effect. default: { $ref: "#/components/responses/Error" } /ai/assistant/sessions/{sessionId}/turns/{turnId}/actions/undo: post: tags: [Evelin] summary: Undo eligible Evelin actions operationId: undo_evelin_actions x-mcp-tool: undo_evelin_actions x-required-scopes: ["ai:use", "conversations:read", "customers:read", "help:read", "attachments:read"] security: [{ ApiKeyAuth: [] }] description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable. Requires conversations:write. Only status and priority receipts with undo.status=available can be undone, once, with current revision checks. Repeated requests return the durable result." x-mcp-description: "Only this API key or OAuth grant can access this chat. Requires current owner/admin membership. Chats retain a ceiling of the permissions granted at creation; added permissions require a new chat. Private app chats and other clients are isolated. External integrations such as Linear are unavailable. Requires conversations:write. Only status and priority receipts with undo.status=available can be undone, once, with current revision checks. Repeated requests return the durable result." parameters: - { name: sessionId, in: path, required: true, schema: { type: string, minLength: 1 } } - { name: turnId, in: path, required: true, schema: { type: string, minLength: 1 } } requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/EvelinActionUndo" } responses: "200": description: Successful operation. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinActionReceiptList" } default: { $ref: "#/components/responses/Error" } /ai/feedback/{kind}/{targetId}: parameters: - name: kind in: path required: true description: Use reply_draft for suggested replies and follow-ups; artifact for rewrites, translations, and draft reviews. schema: { type: string, enum: [reply_draft, artifact] } - name: targetId in: path required: true description: The artifactId from a completed result created by this exact API key or OAuth connection. schema: { type: string } get: tags: [Evelin] summary: Read feedback on an Evelin result operationId: get_evelin_feedback x-mcp-tool: get_evelin_feedback x-required-scopes: ["ai:use", "conversations:read"] security: [{ ApiKeyAuth: [] }] x-mcp-description: "Only completed results created by this connection. Read feedback first and use its revision and targetVersion for writes. Personal by default; workspace visibility shares the answer and correction with workspace reviewers. Does not generate an answer or send a message." description: | Requires a connection owned by a current workspace owner/admin with ai:use and conversations:read. Feedback belongs to that human and is also visible in the app. Only the exact connection that created the result can access it through this API; other connections and private app results are excluded. Read the current revision and targetVersion before saving or withdrawing. Identical lost-response retries are safe with the same body; stale changes return 409. No Idempotency-Key is needed. Personal visibility is the default. Explicit workspace visibility shares the answer and correction for workspace review. Withdrawal retains revision history and removes the active feedback. This records feedback using the existing feedback system; it does not guarantee a future answer, publish guidance, or start a new Evelin generation run. responses: "200": description: Current feedback state, including revision after withdrawal. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinFeedbackState" } default: { $ref: "#/components/responses/Error" } put: tags: [Evelin] summary: Save feedback on an Evelin result operationId: save_evelin_feedback x-mcp-tool: save_evelin_feedback x-required-scopes: ["ai:use", "conversations:read"] security: [{ ApiKeyAuth: [] }] x-mcp-description: "Only completed results created by this connection. Read feedback first and use its revision and targetVersion for writes. Personal by default; workspace visibility shares the answer and correction with workspace reviewers. Does not generate an answer or send a message." description: | Requires a connection owned by a current workspace owner/admin with ai:use and conversations:read. Feedback belongs to that human and is also visible in the app. Only the exact connection that created the result can access it through this API; other connections and private app results are excluded. Read the current revision and targetVersion before saving or withdrawing. Identical lost-response retries are safe with the same body; stale changes return 409. No Idempotency-Key is needed. Personal visibility is the default. Explicit workspace visibility shares the answer and correction for workspace review. Withdrawal retains revision history and removes the active feedback. This records feedback using the existing feedback system; it does not guarantee a future answer, publish guidance, or start a new Evelin generation run. requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/EvelinFeedbackRequest" } example: expectedRevision: 0 targetVersion: "0000000000000000000000000000000000000000000000000000000000000000" signal: helpful reason: useful visibility: personal responses: "200": description: Current feedback state, including revision after withdrawal. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinFeedbackState" } default: { $ref: "#/components/responses/Error" } delete: tags: [Evelin] summary: Withdraw feedback on an Evelin result operationId: withdraw_evelin_feedback x-mcp-tool: withdraw_evelin_feedback x-required-scopes: ["ai:use", "conversations:read"] security: [{ ApiKeyAuth: [] }] x-mcp-description: "Only completed results created by this connection. Read feedback first and use its revision and targetVersion for writes. Personal by default; workspace visibility shares the answer and correction with workspace reviewers. Does not generate an answer or send a message." description: | Requires a connection owned by a current workspace owner/admin with ai:use and conversations:read. Feedback belongs to that human and is also visible in the app. Only the exact connection that created the result can access it through this API; other connections and private app results are excluded. Read the current revision and targetVersion before saving or withdrawing. Identical lost-response retries are safe with the same body; stale changes return 409. No Idempotency-Key is needed. Personal visibility is the default. Explicit workspace visibility shares the answer and correction for workspace review. Withdrawal retains revision history and removes the active feedback. This records feedback using the existing feedback system; it does not guarantee a future answer, publish guidance, or start a new Evelin generation run. requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/EvelinFeedbackWithdrawal" } example: expectedRevision: 1 targetVersion: "0000000000000000000000000000000000000000000000000000000000000000" responses: "200": description: Current feedback state, including revision after withdrawal. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinFeedbackState" } default: { $ref: "#/components/responses/Error" } /ai/conversations/{conversationId}/actions/reply-suggest: post: tags: [Evelin] summary: Suggest a reply with Evelin operationId: suggest_evelin_reply x-mcp-tool: suggest_evelin_reply x-required-scopes: ["ai:use", "conversations:read"] security: [{ ApiKeyAuth: [] }] description: "Requires ai:use and conversations:read on a connection owned by a current workspace owner/admin. API keys created before human ownership was recorded must be replaced in Settings. OAuth works through MCP; REST uses API keys. Creates a durable asynchronous run, never sends a message. Poll get_evelin_run, then retrieve the appropriate result. Uses the same workspace knowledge, runtime availability, and credit policy as the app. Identical retries with the same key reuse the run; changed inputs conflict. Private results remain bound to this exact connection. " parameters: - { name: conversationId, in: path, required: true, schema: { type: string } } - { name: Idempotency-Key, in: header, required: true, schema: { type: string, minLength: 8, maxLength: 128 } } requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/EvelinReplyRequest" } responses: "202": description: Run accepted or replayed. No message was sent. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinCreatedRun" } default: { $ref: "#/components/responses/Error" } /ai/conversations/{conversationId}/actions/follow-up-draft: post: tags: [Evelin] summary: Draft a follow-up with Evelin operationId: draft_evelin_follow_up x-mcp-tool: draft_evelin_follow_up x-required-scopes: ["ai:use", "conversations:read"] security: [{ ApiKeyAuth: [] }] description: "Requires ai:use and conversations:read on a connection owned by a current workspace owner/admin. API keys created before human ownership was recorded must be replaced in Settings. OAuth works through MCP; REST uses API keys. Creates a durable asynchronous run, never sends a message. Poll get_evelin_run, then retrieve the appropriate result. Uses the same workspace knowledge, runtime availability, and credit policy as the app. Identical retries with the same key reuse the run; changed inputs conflict. Private results remain bound to this exact connection. " parameters: - { name: conversationId, in: path, required: true, schema: { type: string } } - { name: Idempotency-Key, in: header, required: true, schema: { type: string, minLength: 8, maxLength: 128 } } requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/EvelinEmptyRequest" } responses: "202": description: Run accepted or replayed. No message was sent. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinCreatedRun" } default: { $ref: "#/components/responses/Error" } /ai/conversations/{conversationId}/actions/context-regenerate: post: tags: [Evelin] summary: Regenerate Ticket Context operationId: regenerate_ticket_context x-mcp-tool: regenerate_ticket_context x-required-scopes: ["ai:use", "conversations:read"] security: [{ ApiKeyAuth: [] }] description: "Requires ai:use and conversations:read on a connection owned by a current workspace owner/admin. API keys created before human ownership was recorded must be replaced in Settings. OAuth works through MCP; REST uses API keys. Creates a durable asynchronous run, never sends a message. Poll get_evelin_run, then retrieve the appropriate result. Uses the same workspace knowledge, runtime availability, and credit policy as the app. Identical retries with the same key reuse the run; changed inputs conflict. Private results remain bound to this exact connection. " parameters: - { name: conversationId, in: path, required: true, schema: { type: string } } - { name: Idempotency-Key, in: header, required: true, schema: { type: string, minLength: 8, maxLength: 128 } } requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/EvelinEmptyRequest" } responses: "202": description: Run accepted or replayed. No message was sent. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinCreatedRun" } default: { $ref: "#/components/responses/Error" } /ai/conversations/{conversationId}/actions/draft-grounding-review: post: tags: [Evelin] summary: Check a draft against workspace knowledge operationId: review_evelin_draft x-mcp-tool: review_evelin_draft x-required-scopes: ["ai:use", "conversations:read"] security: [{ ApiKeyAuth: [] }] description: "Requires ai:use and conversations:read on a connection owned by a current workspace owner/admin. API keys created before human ownership was recorded must be replaced in Settings. OAuth works through MCP; REST uses API keys. Creates a durable asynchronous run, never sends a message. Poll get_evelin_run, then retrieve the appropriate result. Uses the same workspace knowledge, runtime availability, and credit policy as the app. Identical retries with the same key reuse the run; changed inputs conflict. Private results remain bound to this exact connection. " parameters: - { name: conversationId, in: path, required: true, schema: { type: string } } - { name: Idempotency-Key, in: header, required: true, schema: { type: string, minLength: 8, maxLength: 128 } } requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/EvelinGroundingRequest" } responses: "202": description: Run accepted or replayed. No message was sent. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinCreatedRun" } default: { $ref: "#/components/responses/Error" } /ai/conversations/{conversationId}/actions/rewrite: post: tags: [Evelin] summary: Improve, polish, or translate a draft with Evelin operationId: rewrite_with_evelin x-mcp-tool: rewrite_with_evelin x-required-scopes: ["ai:use", "conversations:read"] security: [{ ApiKeyAuth: [] }] description: "Requires ai:use and conversations:read on a connection owned by a current workspace owner/admin. API keys created before human ownership was recorded must be replaced in Settings. OAuth works through MCP; REST uses API keys. Creates a durable asynchronous run, never sends a message. Poll get_evelin_run, then retrieve the appropriate result. Uses the same workspace knowledge, runtime availability, and credit policy as the app. Identical retries with the same key reuse the run; changed inputs conflict. Private results remain bound to this exact connection. For improve, omit tone, preset and targetLanguage. For polish, choose at most one of tone or preset. For translate, provide targetLanguage and omit tone and preset." parameters: - { name: conversationId, in: path, required: true, schema: { type: string } } - { name: Idempotency-Key, in: header, required: true, schema: { type: string, minLength: 8, maxLength: 128 } } requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/EvelinRewriteRequest" } responses: "202": description: Run accepted or replayed. No message was sent. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinCreatedRun" } default: { $ref: "#/components/responses/Error" } /ai/drafts/{draftArtifactId}/actions/review-translate: post: tags: [Evelin] summary: Translate this connection’s suggested reply for review operationId: translate_evelin_reply_for_review x-mcp-tool: translate_evelin_reply_for_review x-required-scopes: ["ai:use", "conversations:read"] security: [{ ApiKeyAuth: [] }] description: "Requires ai:use and conversations:read on a connection owned by a current workspace owner/admin. API keys created before human ownership was recorded must be replaced in Settings. OAuth works through MCP; REST uses API keys. Creates a durable asynchronous run, never sends a message. Poll get_evelin_run, then retrieve the appropriate result. Uses the same workspace knowledge, runtime availability, and credit policy as the app. Identical retries with the same key reuse the run; changed inputs conflict. Private results remain bound to this exact connection. " parameters: - { name: draftArtifactId, in: path, required: true, schema: { type: string } } - { name: Idempotency-Key, in: header, required: true, schema: { type: string, minLength: 8, maxLength: 128 } } requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/EvelinTranslationRequest" } responses: "202": description: Run accepted or replayed. No message was sent. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinCreatedRun" } default: { $ref: "#/components/responses/Error" } /ai/messages/{messageId}/translate: post: tags: [Evelin] summary: Translate an existing message with Evelin operationId: translate_message_with_evelin x-mcp-tool: translate_message_with_evelin x-required-scopes: ["ai:use", "conversations:read"] security: [{ ApiKeyAuth: [] }] description: "Requires ai:use and conversations:read on a connection owned by a current workspace owner/admin. API keys created before human ownership was recorded must be replaced in Settings. OAuth works through MCP; REST uses API keys. Creates a durable asynchronous run, never sends a message. Poll get_evelin_run, then retrieve the appropriate result. Uses the same workspace knowledge, runtime availability, and credit policy as the app. Identical retries with the same key reuse the run; changed inputs conflict. Private results remain bound to this exact connection. Translates the current message body without editing it. If the message changes before retry, the same key conflicts." parameters: - { name: messageId, in: path, required: true, schema: { type: string } } - { name: Idempotency-Key, in: header, required: true, schema: { type: string, minLength: 8, maxLength: 128 } } requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/EvelinTranslationRequest" } responses: "202": description: Run accepted or replayed. No message was sent. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinCreatedRun" } default: { $ref: "#/components/responses/Error" } /ai/runs/{runId}: get: tags: [Evelin] summary: Read this connection’s Evelin run status operationId: get_evelin_run x-mcp-tool: get_evelin_run x-required-scopes: ["ai:use", "conversations:read"] security: [{ ApiKeyAuth: [] }] description: "Requires ai:use and conversations:read on a connection owned by a current workspace owner/admin. API keys created before human ownership was recorded must be replaced in Settings. OAuth works through MCP; REST uses API keys. Run results are private to the originating connection; another client or a new OAuth grant cannot read them. " parameters: - { name: runId, in: path, required: true, schema: { type: string } } responses: "200": description: Authorized result. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinRunStatus" } default: { $ref: "#/components/responses/Error" } /ai/runs/{runId}/results/{kind}: get: tags: [Evelin] summary: Read this connection’s Evelin result operationId: get_evelin_result x-mcp-tool: get_evelin_result x-required-scopes: ["ai:use", "conversations:read"] security: [{ ApiKeyAuth: [] }] description: "Requires ai:use and conversations:read on a connection owned by a current workspace owner/admin. API keys created before human ownership was recorded must be replaced in Settings. OAuth works through MCP; REST uses API keys. Run results are private to the originating connection; another client or a new OAuth grant cannot read them. A missing, redacted, expired, or unfinished result returns 404. Reply suggestion can finish without a draft; inspect reply-guard." parameters: - { name: runId, in: path, required: true, schema: { type: string } } - { name: kind, in: path, required: true, schema: { type: string, enum: [reply-draft, follow-up-draft, rewrite, draft-grounding-review, draft-review-translation, reply-guard] } } responses: "200": description: Authorized result. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinResult" } default: { $ref: "#/components/responses/Error" } /ai/conversations/{conversationId}/context: get: tags: [Evelin] summary: Read the active Ticket Context operationId: get_ticket_context x-mcp-tool: get_ticket_context x-required-scopes: ["ai:use", "conversations:read"] security: [{ ApiKeyAuth: [] }] description: "Requires ai:use and conversations:read on a connection owned by a current workspace owner/admin. API keys created before human ownership was recorded must be replaced in Settings. OAuth works through MCP; REST uses API keys. Ticket Context is shared conversation context. The result may be null until context generation finishes. " parameters: - { name: conversationId, in: path, required: true, schema: { type: string } } responses: "200": description: Authorized result. content: application/json: schema: type: object required: [data] properties: data: { $ref: "#/components/schemas/EvelinContext" } default: { $ref: "#/components/responses/Error" } /authoring/collections: post: tags: ["Help authoring"] summary: "Create a Help Center collection" description: "Creates a collection in the chosen locale. A null parentId places it at the root. Requires a current workspace owner/admin and an explicitly delegated connection. Maximum request 64 KiB. Use a unique Idempotency-Key (8–128 printable characters); retry the identical request with the same key. Changed payloads conflict. Revoked or expired grants cannot replay." operationId: "create_authoring_collection" x-mcp-tool: "create_authoring_collection" x-required-scopes: ["help:read", "help:manage", "help:articles:publish"] security: [{"ApiKeyAuth": []}] parameters: [{"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 8, "maxLength": 128, "pattern": "^[!-~]{8,128}$"}}] requestBody: required: true content: application/json: schema: type: "object" additionalProperties: false properties: parentId: type: ["string", "null"] name: type: "string" slug: type: "string" description: type: "string" position: type: "integer" minimum: 0 locale: type: "string" required: ["name", "slug", "locale"] responses: "200": description: "Authorized result; identical write retries replay the original result." content: application/json: schema: { $ref: "#/components/schemas/HelpCollection" } default: $ref: "#/components/responses/Error" get: tags: ["Help authoring"] summary: "List Help Center collections" description: "Returns the collection tree, translations, and revisions. Requires a current workspace owner/admin and an explicitly delegated connection." operationId: "list_authoring_collections" x-mcp-tool: "list_authoring_collections" x-required-scopes: ["help:read"] security: [{"ApiKeyAuth": []}] responses: "200": description: "Authorized result; identical write retries replay the original result." content: application/json: schema: type: "object" properties: data: type: "array" items: { $ref: "#/components/schemas/HelpCollection" } default: $ref: "#/components/responses/Error" /authoring/collections/{id}: delete: tags: ["Help authoring"] summary: "Delete a Help Center collection" description: "Permanently deletes the selected collection. Read its current revision first. Articles and child collections are moved to the root; articles are not deleted. Only delete on an explicit user request. Requires a current workspace owner/admin and an explicitly delegated connection. Maximum request 64 KiB. Use a unique Idempotency-Key (8–128 printable characters); retry the identical request with the same key. Changed payloads conflict. Revoked or expired grants cannot replay." operationId: "delete_authoring_collection" x-mcp-description: "Permanently deletes this collection; children and articles move to the root. Read the current revision first." x-mcp-tool: "delete_authoring_collection" x-required-scopes: ["help:read", "help:manage", "help:articles:publish"] security: [{"ApiKeyAuth": []}] parameters: [{"name": "id", "in": "path", "required": true, "schema": {"type": "string"}}, {"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 8, "maxLength": 128, "pattern": "^[!-~]{8,128}$"}}] requestBody: required: true content: application/json: schema: type: "object" additionalProperties: false properties: expectedRevision: type: "integer" minimum: 1 required: ["expectedRevision"] responses: "200": description: "Authorized result; identical write retries replay the original result." content: application/json: schema: type: "object" additionalProperties: false properties: id: type: "string" deleted: type: "boolean" required: ["id", "deleted"] default: $ref: "#/components/responses/Error" put: tags: ["Help authoring"] summary: "Update a Help Center collection" description: "Read the collection first and supply its expectedRevision. This replaces editable fields; omitted or null parentId preserves the parent; an empty string moves it to the root. Changes may affect the live Help Center. Requires a current workspace owner/admin and an explicitly delegated connection. Maximum request 64 KiB. Use a unique Idempotency-Key (8–128 printable characters); retry the identical request with the same key. Changed payloads conflict. Revoked or expired grants cannot replay." operationId: "update_authoring_collection" x-mcp-tool: "update_authoring_collection" x-required-scopes: ["help:read", "help:manage", "help:articles:publish"] security: [{"ApiKeyAuth": []}] parameters: [{"name": "id", "in": "path", "required": true, "schema": {"type": "string"}}, {"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 8, "maxLength": 128, "pattern": "^[!-~]{8,128}$"}}] requestBody: required: true content: application/json: schema: type: "object" additionalProperties: false properties: parentId: type: ["string", "null"] name: type: "string" slug: type: "string" description: type: "string" position: type: "integer" minimum: 0 locale: type: "string" expectedRevision: type: "integer" minimum: 1 required: ["name", "slug", "locale", "expectedRevision"] responses: "200": description: "Authorized result; identical write retries replay the original result." content: application/json: schema: { $ref: "#/components/schemas/HelpCollection" } default: $ref: "#/components/responses/Error" /authoring/collections/order: put: tags: ["Help authoring"] summary: "Reorder Help Center collections" description: "Supply all sibling collection IDs in the desired order. Omit parentId for the root. Rejects incomplete lists or IDs outside this parent/workspace. Ordering is last-write-wins. Requires a current workspace owner/admin and an explicitly delegated connection. Maximum request 64 KiB. Use a unique Idempotency-Key (8–128 printable characters); retry the identical request with the same key. Changed payloads conflict. Revoked or expired grants cannot replay." operationId: "reorder_authoring_collections" x-mcp-tool: "reorder_authoring_collections" x-required-scopes: ["help:read", "help:manage", "help:articles:publish"] security: [{"ApiKeyAuth": []}] parameters: [{"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 8, "maxLength": 128, "pattern": "^[!-~]{8,128}$"}}] requestBody: required: true content: application/json: schema: type: "object" additionalProperties: false properties: parentId: type: "string" collectionIds: type: "array" items: type: "string" required: ["collectionIds"] responses: "200": description: "Authorized result; identical write retries replay the original result." content: application/json: schema: type: "object" additionalProperties: false properties: success: type: "boolean" required: ["success"] default: $ref: "#/components/responses/Error" /authoring/collections/{id}/articles/order: put: tags: ["Help authoring"] summary: "Reorder articles in a Help Center collection" description: "Supply every article ID in the selected collection and locale, in the desired order. Rejects incomplete lists. Ordering is last-write-wins. Requires a current workspace owner/admin and an explicitly delegated connection. Maximum request 64 KiB. Use a unique Idempotency-Key (8–128 printable characters); retry the identical request with the same key. Changed payloads conflict. Revoked or expired grants cannot replay." operationId: "reorder_authoring_articles" x-mcp-tool: "reorder_authoring_articles" x-required-scopes: ["help:read", "help:manage", "help:articles:publish"] security: [{"ApiKeyAuth": []}] parameters: [{"name": "id", "in": "path", "required": true, "schema": {"type": "string"}}, {"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 8, "maxLength": 128, "pattern": "^[!-~]{8,128}$"}}] requestBody: required: true content: application/json: schema: type: "object" additionalProperties: false properties: locale: type: "string" articleIds: type: "array" items: type: "string" required: ["locale", "articleIds"] responses: "200": description: "Authorized result; identical write retries replay the original result." content: application/json: schema: type: "object" additionalProperties: false properties: success: type: "boolean" required: ["success"] default: $ref: "#/components/responses/Error" /authoring/settings: put: tags: ["Help authoring"] summary: "Update Help Center settings" description: "Read settings first and submit the current expectedRevision. Replaces editable settings; cannot remove a locale used by an article. Publishing requires a published article in the default locale. Custom domains and public slugs are managed in the dashboard. Requires a current workspace owner/admin and an explicitly delegated connection. Maximum request 64 KiB. Use a unique Idempotency-Key (8–128 printable characters); retry the identical request with the same key. Changed payloads conflict. Revoked or expired grants cannot replay." operationId: "update_authoring_settings" x-mcp-tool: "update_authoring_settings" x-required-scopes: ["help:read", "help:manage", "help:articles:publish"] security: [{"ApiKeyAuth": []}] parameters: [{"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 8, "maxLength": 128, "pattern": "^[!-~]{8,128}$"}}] requestBody: required: true content: application/json: schema: type: "object" additionalProperties: false properties: expectedRevision: type: "integer" minimum: 1 status: type: "string" enum: ["draft", "published"] name: type: "string" description: type: "string" defaultLocale: type: "string" enabledLocales: type: "array" items: type: "string" translations: type: "array" items: type: "object" additionalProperties: false properties: locale: type: "string" name: type: "string" description: type: "string" required: ["locale", "name"] required: ["expectedRevision", "status", "name", "defaultLocale", "enabledLocales"] responses: "200": description: "Authorized result; identical write retries replay the original result." content: application/json: schema: type: "object" properties: status: type: "string" enum: ["draft", "published"] name: type: "string" description: type: "string" defaultLocale: type: "string" enabledLocales: type: "array" items: type: "string" translations: type: "array" items: type: "object" additionalProperties: false properties: locale: type: "string" name: type: "string" description: type: "string" required: ["locale", "name"] publicSlug: type: "string" revision: type: "integer" createdAt: type: "string" updatedAt: type: "string" default: $ref: "#/components/responses/Error" get: tags: ["Help authoring"] summary: "Read Help Center settings" description: "Returns publication status, name, description, locales, translations, and revision. Requires a current workspace owner/admin and an explicitly delegated connection." operationId: "get_authoring_settings" x-mcp-tool: "get_authoring_settings" x-required-scopes: ["help:read"] security: [{"ApiKeyAuth": []}] responses: "200": description: "Authorized result; identical write retries replay the original result." content: application/json: schema: type: "object" properties: status: type: "string" enum: ["draft", "published"] name: type: "string" description: type: "string" defaultLocale: type: "string" enabledLocales: type: "array" items: type: "string" translations: type: "array" items: type: "object" additionalProperties: false properties: locale: type: "string" name: type: "string" description: type: "string" required: ["locale", "name"] publicSlug: type: "string" revision: type: "integer" createdAt: type: "string" updatedAt: type: "string" default: $ref: "#/components/responses/Error" /authoring/proposals: get: tags: ["Help authoring"] summary: "List pending Help Center proposals" description: "Lists pending proposals across this workspace, without full bodies. Use nextAfter with after to fetch the next page; defaults to 20, maximum 100. Read proposal details before making a decision. Requires a current workspace owner/admin and an explicitly delegated connection." operationId: "list_authoring_review_queue" x-mcp-tool: "list_authoring_review_queue" x-required-scopes: ["help:read", "help:manage"] security: [{"ApiKeyAuth": []}] parameters: [{"name": "limit", "in": "query", "schema": {"type": "integer", "minimum": 1, "maximum": 100, "default": 20}}, {"name": "after", "in": "query", "schema": {"type": "string", "maxLength": 128}}] responses: "200": description: "Authorized result; identical write retries replay the original result." content: application/json: schema: type: "object" properties: data: type: "array" items: $ref: "#/components/schemas/AuthoringProposal" hasMore: type: "boolean" nextAfter: type: ["string", "null"] default: $ref: "#/components/responses/Error" /authoring/proposals/{id}/review: get: tags: ["Help authoring"] summary: "Read a Help Center proposal for review" description: "Reads a proposal from any connection in this workspace, including its immutable content. Requires help:manage; ordinary proposal reads remain restricted to the originating connection. Requires a current workspace owner/admin and an explicitly delegated connection." operationId: "get_authoring_review" x-mcp-tool: "get_authoring_review" x-required-scopes: ["help:read", "help:manage"] security: [{"ApiKeyAuth": []}] parameters: [{"name": "id", "in": "path", "required": true, "schema": {"type": "string"}}] responses: "200": description: "Authorized result; identical write retries replay the original result." content: application/json: schema: $ref: "#/components/schemas/AuthoringProposal" default: $ref: "#/components/responses/Error" /authoring/proposals/{id}/decision: post: tags: ["Help authoring"] summary: "Approve or dismiss a Help Center proposal" description: "Read the full proposal and act only on the user's explicit review decision. Approved proposals default to draft; approvalOutcome=publish makes the change public. Dismissal must omit content, settings, and approvalOutcome. Revision conflicts require a fresh review; do not silently overwrite newer article changes. Requires a current workspace owner/admin and an explicitly delegated connection. Maximum request 64 KiB. Use a unique Idempotency-Key (8–128 printable characters); retry the identical request with the same key. Changed payloads conflict. Revoked or expired grants cannot replay." operationId: "decide_authoring_proposal" x-mcp-description: "Read get_authoring_review first. Apply only the explicit user decision. Approval defaults to draft; approvalOutcome=publish makes content public." x-mcp-tool: "decide_authoring_proposal" x-required-scopes: ["help:read", "help:manage", "help:articles:publish"] security: [{"ApiKeyAuth": []}] parameters: [{"name": "id", "in": "path", "required": true, "schema": {"type": "string"}}, {"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 8, "maxLength": 128, "pattern": "^[!-~]{8,128}$"}}] requestBody: required: true content: application/json: schema: type: "object" additionalProperties: false properties: decision: type: "string" enum: ["approved", "dismissed"] approvalOutcome: type: "string" enum: ["draft", "publish"] content: $ref: "#/components/schemas/AuthoringContent" settings: type: "object" additionalProperties: false properties: slug: type: "string" collectionId: type: "string" locale: type: "string" tags: type: "array" items: type: "string" featured: type: "boolean" required: ["decision"] responses: "200": description: "Authorized result; identical write retries replay the original result." content: application/json: schema: $ref: "#/components/schemas/AuthoringProposal" default: $ref: "#/components/responses/Error" /authoring/import-candidates: get: tags: ["Help authoring"] summary: "Find Knowledge articles for Help Center import" description: "Searches eligible Knowledge articles in this workspace only. Returns at most limit matches (default 20, maximum 100); narrow q to find other articles. Does not publish or change source articles. Requires a current workspace owner/admin and an explicitly delegated connection." operationId: "list_authoring_import_candidates" x-mcp-tool: "list_authoring_import_candidates" x-required-scopes: ["help:read", "help:manage"] security: [{"ApiKeyAuth": []}] parameters: [{"name": "limit", "in": "query", "schema": {"type": "integer", "minimum": 1, "maximum": 100, "default": 20}}, {"name": "q", "in": "query", "schema": {"type": "string", "maxLength": 200}}] responses: "200": description: "Authorized result; identical write retries replay the original result." content: application/json: schema: type: "object" properties: data: type: "array" items: type: "object" properties: id: type: "string" title: type: "string" excerpt: type: "string" locale: type: "string" tags: type: "array" items: type: "string" updatedAt: type: "string" imported: type: "boolean" default: $ref: "#/components/responses/Error" /authoring/imports: post: tags: ["Help authoring"] summary: "Import Knowledge articles into the Help Center" description: "Imports selected workspace Knowledge articles. Choose skip or overwrite explicitly. Inspect per-item results: rejected/skipped items are reported rather than silently imported. Overwrite can change existing published content. Source Knowledge articles remain unchanged. Requires a current workspace owner/admin and an explicitly delegated connection. Maximum request 64 KiB. Use a unique Idempotency-Key (8–128 printable characters); retry the identical request with the same key. Changed payloads conflict. Revoked or expired grants cannot replay." operationId: "import_authoring_articles" x-mcp-description: "Imports workspace Knowledge articles. Overwrite may change live content. Inspect per-item rejected and skipped results." x-mcp-tool: "import_authoring_articles" x-required-scopes: ["help:read", "help:manage", "help:articles:publish"] security: [{"ApiKeyAuth": []}] parameters: [{"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 8, "maxLength": 128, "pattern": "^[!-~]{8,128}$"}}] requestBody: required: true content: application/json: schema: type: "object" additionalProperties: false properties: knowledgeArticleIds: type: "array" items: type: "string" minItems: 1 maxItems: 100 collectionId: type: "string" conflictPolicy: type: "string" enum: ["skip", "overwrite"] required: ["knowledgeArticleIds", "conflictPolicy"] responses: "200": description: "Authorized result; identical write retries replay the original result." content: application/json: schema: type: "object" properties: imported: type: "integer" overwritten: type: "integer" skipped: type: "integer" rejected: type: "integer" items: type: "array" items: type: "object" properties: knowledgeArticleId: type: "string" helpArticleId: type: "string" status: type: "string" reason: type: "string" default: $ref: "#/components/responses/Error" /authoring/context: get: tags: [Help authoring] summary: Identify an explicitly delegated Help Center connection description: Requires help:read on an unexpired human-owned authoring connection. Generic keys do not gain access. Every request rechecks the creator's current owner/admin membership. Environment labels do not create a sandbox; workspaceId is the actual target. operationId: getAuthoringContext x-mcp-tool: get_authoring_context x-required-scopes: ["help:read"] security: [{ ApiKeyAuth: [] }] responses: "200": description: Workspace, connection, granted scopes, capabilities, and bounds. canPublish and canCreateArticles reflect the connection’s granted article permissions. content: application/json: schema: type: object properties: workspaceId: { type: string } connectionId: { type: string } scopes: { type: array, items: { type: string } } capabilities: { type: array, items: { type: string } } canPublish: { type: boolean } canCreateArticles: { type: boolean } canManage: { type: boolean } maxPageSize: { type: integer } maxRequestBytes: { type: integer } maxArticleSetItems: { type: integer, maximum: 20 } defaultLocale: { type: string } enabledLocales: { type: array, items: { type: string } } collections: type: array description: Eligible collections whose IDs can be assigned to generated articles. items: type: object required: [id, slug, name] properties: id: { type: string } parentId: { type: [string, "null"] } slug: { type: string } name: { type: string } default: { $ref: "#/components/responses/Error" } /authoring/article-sets: post: tags: [Help authoring] summary: Propose a set of new Help Center articles description: Requires help:read and help:drafts:write. Atomically stores 1–20 immutable candidates, not canonical articles. Human approval adds unpublished drafts. Maximum serialized request 64 KiB. Retry only identical payload and Idempotency-Key. Duplicate locale/slug candidates or canonical URLs conflict; nothing is silently renamed. No repository access or publishing. operationId: createAuthoringArticleSet x-mcp-tool: create_authoring_article_set x-required-scopes: ["help:read", "help:drafts:write"] security: [{ ApiKeyAuth: [] }] parameters: - name: Idempotency-Key in: header required: true schema: { type: string, minLength: 8, maxLength: 128, pattern: '^[!-~]{8,128}$' } requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [name, articles] properties: name: { type: string, minLength: 1, maxLength: 120 } articles: type: array minItems: 1 maxItems: 20 items: { $ref: "#/components/schemas/AuthoringNewArticle" } responses: "200": description: Set stored or identical submission replayed. No article published. content: application/json: schema: { $ref: "#/components/schemas/AuthoringArticleSet" } default: { $ref: "#/components/responses/Error" } /authoring/article-sets/{setId}: get: tags: [Help authoring] summary: Read this connection's generated article set operationId: getAuthoringArticleSet x-mcp-tool: get_authoring_article_set x-required-scopes: ["help:read"] security: [{ ApiKeyAuth: [] }] parameters: - { name: setId, in: path, required: true, schema: { type: string } } responses: "200": description: Immutable candidates and their human decisions, originating connection only. content: application/json: schema: { $ref: "#/components/schemas/AuthoringArticleSet" } default: { $ref: "#/components/responses/Error" } /authoring/articles: post: tags: [Help authoring] summary: Create a Help Center draft article directly operationId: create_authoring_article x-mcp-tool: create_authoring_article x-required-scopes: ["help:read", "help:articles:write"] security: [{ ApiKeyAuth: [] }] description: Creates a canonical unpublished draft without a proposal or human review. Publishing is a separate operation. Explicit duplicate slugs conflict. Requires an Idempotency-Key. Identical retries from the same connection return the original result; changed payloads conflict. Revoked or expired grants cannot replay. parameters: - { name: Idempotency-Key, in: header, required: true, schema: { type: string, minLength: 8, maxLength: 128 } } requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [title, slug, locale, bodyMarkdown] properties: title: { type: string, minLength: 1, maxLength: 200 } slug: { type: string, minLength: 1, maxLength: 160 } locale: { type: string, minLength: 2 } excerpt: { type: string } bodyMarkdown: { type: string } collectionId: { type: string } tags: { type: array, items: { type: string } } featured: { type: boolean } responses: "200": description: Saved article, including its current revision at the time of the operation. content: application/json: schema: { $ref: "#/components/schemas/AuthoringArticle" } default: { $ref: "#/components/responses/Error" } get: tags: [Help authoring] summary: List draft and published Help Center article summaries description: Requires help:read. Orders by stable article ID; pass nextCursor as after. Searches titles and excerpts, never returns bodies in list results. Pagination is not a historical snapshot; concurrent additions may require a fresh listing. No private Knowledge or ticket data is included. Workspace comes only from the authenticated connection. operationId: listAuthoringArticles x-mcp-tool: list_authoring_articles x-required-scopes: ["help:read"] security: [{ ApiKeyAuth: [] }] parameters: - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 25 } } - { name: after, in: query, schema: { type: string, maxLength: 128 } } - { name: q, in: query, schema: { type: string } } responses: "200": description: Article summaries and nullable next cursor. content: application/json: schema: type: object properties: data: { type: array, items: { $ref: "#/components/schemas/AuthoringArticle" } } nextCursor: { type: [string, "null"] } default: { $ref: "#/components/responses/Error" } /authoring/articles/{id}: delete: tags: ["Help authoring"] summary: "Delete a Help Center article" description: "Permanently deletes the article, including published content. Read its revision first. Only delete on an explicit user request; use unpublish when content should be retained. Requires a current workspace owner/admin and an explicitly delegated connection. Maximum request 64 KiB. Use a unique Idempotency-Key (8–128 printable characters); retry the identical request with the same key. Changed payloads conflict. Revoked or expired grants cannot replay." operationId: "delete_authoring_article" x-mcp-description: "Permanently deletes the article. Use unpublish to retain content. Read its current revision first." x-mcp-tool: "delete_authoring_article" x-required-scopes: ["help:read", "help:manage", "help:articles:publish"] security: [{"ApiKeyAuth": []}] parameters: [{"name": "id", "in": "path", "required": true, "schema": {"type": "string"}}, {"name": "Idempotency-Key", "in": "header", "required": true, "schema": {"type": "string", "minLength": 8, "maxLength": 128, "pattern": "^[!-~]{8,128}$"}}] requestBody: required: true content: application/json: schema: type: "object" additionalProperties: false properties: expectedRevision: type: "integer" minimum: 1 required: ["expectedRevision"] responses: "200": description: "Authorized result; identical write retries replay the original result." content: application/json: schema: type: "object" additionalProperties: false properties: id: type: "string" deleted: type: "boolean" required: ["id", "deleted"] default: $ref: "#/components/responses/Error" parameters: - { name: id, in: path, required: true, schema: { type: string } } put: tags: [Help authoring] summary: Update a Help Center article directly operationId: update_authoring_article x-mcp-tool: update_authoring_article x-required-scopes: ["help:read", "help:articles:write"] security: [{ ApiKeyAuth: [] }] description: Replaces editable article fields. Read the article first and include the returned expectedRevision. Keeps its publication status. Editing an already published article also requires help:articles:publish and changes customer-visible content immediately. Omitted optional fields are cleared. Requires an Idempotency-Key. Identical retries from the same connection return the original result; changed payloads conflict. Revoked or expired grants cannot replay. parameters: - { name: Idempotency-Key, in: header, required: true, schema: { type: string, minLength: 8, maxLength: 128 } } requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [expectedRevision, title, slug, locale, bodyMarkdown] properties: title: { type: string, minLength: 1, maxLength: 200 } slug: { type: string, minLength: 1, maxLength: 160 } locale: { type: string, minLength: 2 } excerpt: { type: string } bodyMarkdown: { type: string } collectionId: { type: string } tags: { type: array, items: { type: string } } featured: { type: boolean } expectedRevision: { type: integer, format: int64, minimum: 1 } responses: "200": description: Saved article, including its current revision at the time of the operation. content: application/json: schema: { $ref: "#/components/schemas/AuthoringArticle" } default: { $ref: "#/components/responses/Error" } get: tags: [Help authoring] summary: Read one authorized Help Center article and revision operationId: getAuthoringArticle x-mcp-tool: get_authoring_article x-required-scopes: ["help:read"] security: [{ ApiKeyAuth: [] }] description: Requires help:read. Includes Markdown, never private Knowledge import metadata or ticket data. Response is not cacheable. responses: "200": description: Article including bodyMarkdown. content: application/json: schema: { $ref: "#/components/schemas/AuthoringArticle" } default: { $ref: "#/components/responses/Error" } /authoring/articles/{id}/publish: parameters: - { name: id, in: path, required: true, schema: { type: string } } post: tags: [Help authoring] summary: Publish a Help Center article operationId: publish_authoring_article x-mcp-tool: publish_authoring_article x-required-scopes: ["help:read", "help:articles:publish"] security: [{ ApiKeyAuth: [] }] description: Changes publication status immediately without a review proposal. Requires the latest expectedRevision. Public visibility also depends on the Help Center and collection being published. Requires an Idempotency-Key. Identical retries from the same connection return the original result; changed payloads conflict. Revoked or expired grants cannot replay. parameters: - { name: Idempotency-Key, in: header, required: true, schema: { type: string, minLength: 8, maxLength: 128 } } requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [expectedRevision] properties: expectedRevision: { type: integer, format: int64, minimum: 1 } responses: "200": description: Saved article, including its current revision at the time of the operation. content: application/json: schema: { $ref: "#/components/schemas/AuthoringArticle" } default: { $ref: "#/components/responses/Error" } /authoring/articles/{id}/unpublish: parameters: - { name: id, in: path, required: true, schema: { type: string } } post: tags: [Help authoring] summary: Unpublish a Help Center article operationId: unpublish_authoring_article x-mcp-tool: unpublish_authoring_article x-required-scopes: ["help:read", "help:articles:publish"] security: [{ ApiKeyAuth: [] }] description: Changes publication status immediately without a review proposal. Requires the latest expectedRevision. Public visibility also depends on the Help Center and collection being published. Requires an Idempotency-Key. Identical retries from the same connection return the original result; changed payloads conflict. Revoked or expired grants cannot replay. parameters: - { name: Idempotency-Key, in: header, required: true, schema: { type: string, minLength: 8, maxLength: 128 } } requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [expectedRevision] properties: expectedRevision: { type: integer, format: int64, minimum: 1 } responses: "200": description: Saved article, including its current revision at the time of the operation. content: application/json: schema: { $ref: "#/components/schemas/AuthoringArticle" } default: { $ref: "#/components/responses/Error" } /authoring/articles/{id}/proposals: parameters: - { name: id, in: path, required: true, schema: { type: string } } post: tags: [Help authoring] summary: Propose immutable content changes without publishing operationId: createAuthoringProposal x-mcp-tool: create_authoring_proposal x-required-scopes: ["help:read", "help:drafts:write"] security: [{ ApiKeyAuth: [] }] description: Requires help:read and help:drafts:write. Existing article only. Expected revision must match. Reusing the same key and payload for the same connection returns its existing proposal; another payload conflicts. Keys are retained with proposals. Replays recheck connection expiry, revocation, and creator membership. Never directly edits or publishes an article. Request body is capped by the public API write limit (64 KiB by default). The authoring connection and human creator are recorded server-side. parameters: - { name: Idempotency-Key, in: header, required: true, schema: { type: string, minLength: 8, maxLength: 128 } } requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [expectedRevision, content] properties: expectedRevision: { type: integer, format: int64, minimum: 1 } content: { $ref: "#/components/schemas/AuthoringContent" } responses: "200": description: Saved proposal or authorized retry, with a credential-free relative review URL. No article was published. content: application/json: schema: { $ref: "#/components/schemas/AuthoringProposal" } default: { $ref: "#/components/responses/Error" } /authoring/proposals/{id}: parameters: - { name: id, in: path, required: true, schema: { type: string } } get: tags: [Help authoring] summary: Read a proposal originating from this connection operationId: getAuthoringProposal x-mcp-tool: get_authoring_proposal x-required-scopes: ["help:read"] security: [{ ApiKeyAuth: [] }] description: Requires help:read. Other connections' proposals are not available through this endpoint, even when they belong to the same human. This endpoint reads proposals only; publication uses the separately scoped article publishing endpoint. responses: "200": description: Proposal content, current decision status and review URL. publicationChanged describes this request, not historical human decisions. content: application/json: schema: { $ref: "#/components/schemas/AuthoringProposal" } default: { $ref: "#/components/responses/Error" } /: get: tags: - System summary: API index operationId: getApiIndex responses: "200": description: API metadata. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: type: object required: - name - version - status properties: name: type: string example: Protodesk API version: type: string example: v1 status: type: string example: ready default: $ref: "#/components/responses/Error" /healthz: get: tags: - System summary: Health check operationId: getHealth servers: - url: https://api.protodesk.io - url: http://localhost:8080 responses: "200": description: Service health. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: type: object required: - service - status - version properties: service: type: string example: protodesk-backend status: type: string example: ok version: type: string example: dev default: $ref: "#/components/responses/Error" /me: get: tags: - Account summary: Get the current workspace and key scopes operationId: getMe x-mcp-tool: workspace_context x-required-scopes: [] security: - ApiKeyAuth: [] responses: "200": description: Authenticated API key context. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/Me" default: $ref: "#/components/responses/Error" /realtime/events/poll: get: tags: [Realtime] summary: Check workspace event notifications description: > Bounded, immediate JSON polling for REST and MCP. Requires realtime:read. The first call without a cursor returns no events and a starting nextCursor. Pass that cursor on subsequent calls, keeping the same filters, to read up to 100 notifications in chronological order. Save nextCursor after processing a page; retry the same input cursor to replay that page. Notifications contain resource identifiers and event types only, never message bodies, outbox payloads or private AI results. Fetch current resources using their separate read scopes. On hasMore, drain the next page; otherwise wait at least pollAfterSeconds before polling again. Cursors are workspace-specific and differ from SSE event IDs. Invalid or unavailable cursors return 400; restart and reconcile resources rather than silently assuming no changes. This is a change-hint feed, not an exactly-once delivery log: reconcile authoritative resource state, and use webhooks for durable background processing. MCP calls do not create a subscription or schedule future checks. Resource IDs and event types are visible across the workspace to holders of this scope. operationId: pollWorkspaceEvents x-mcp-tool: list_workspace_events x-required-scopes: ["realtime:read"] security: - ApiKeyAuth: [] parameters: - name: cursor in: query description: Opaque nextCursor from the preceding poll. Omit to establish a new checkpoint. schema: {type: string, maxLength: 512} - name: limit in: query schema: {type: integer, minimum: 1, maximum: 100, default: 25} - name: conversationId in: query description: Only notifications associated with this conversation. schema: {type: string, maxLength: 128} - name: eventTypes in: query description: Comma-separated supported event types. Keep filters unchanged while resuming. schema: {type: string, maxLength: 1024} example: message.created,conversation.resolved responses: "200": description: A bounded notification page or an initial checkpoint. content: application/json: schema: type: object required: [data, nextCursor, hasMore, pollAfterSeconds] properties: data: type: array maxItems: 100 items: type: object required: [id, type, resourceType, resourceId, createdAt] properties: id: {type: string} type: {type: string} resourceType: {type: string} resourceId: {type: string} conversationId: {type: string} createdAt: {type: string, format: date-time} nextCursor: {type: string} hasMore: {type: boolean} pollAfterSeconds: {type: integer, enum: [3]} default: $ref: "#/components/responses/Error" /realtime/events: get: tags: - Realtime summary: Stream workspace events (SSE) description: > A Server-Sent Events stream of the workspace event outbox — the same events and payloads delivered to webhooks. Each message carries the event id in the SSE `id:` field; reconnect with `Last-Event-ID` (or the `cursor` query param) to replay events missed while disconnected. Without a cursor, only events created after the connection opens are sent. A `: heartbeat` comment is sent during idle periods, and the server closes long-lived connections (~15m) with a `: reconnect` comment so clients resume with a fresh cursor. operationId: streamEvents security: - ApiKeyAuth: [] parameters: - name: cursor in: query required: false description: Event id to resume after. Overridden by the Last-Event-ID header if both are absent here. schema: type: string - name: conversationId in: query required: false description: Only stream events for this conversation. schema: type: string - name: eventTypes in: query required: false description: Comma-separated event types to include (defaults to all). schema: type: string example: message.created,conversation.updated - name: Last-Event-ID in: header required: false description: Standard SSE reconnect header; the last event id the client received. schema: type: string responses: "200": description: An event stream. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: text/event-stream: schema: type: string example: | id: evt_x6q5vl75f5d53m7oet2k4r5w6a event: message.created data: {"id":"evt_...","type":"message.created","apiVersion":"2026-07-09","createdAt":"2026-07-09T12:00:00Z","data":{}} default: $ref: "#/components/responses/Error" /keys: get: tags: - API Keys summary: List API keys description: Lists the workspace's API keys. Secrets are never returned. operationId: listApiKeys security: - ApiKeyAuth: [] responses: "200": description: API keys. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ListApiKeysResponse" default: $ref: "#/components/responses/Error" post: tags: - API Keys summary: Create an API key description: > Mints a new API key. The raw secret is returned once in the response and is never retrievable afterward. Requires the keys:manage scope. operationId: createApiKey security: - ApiKeyAuth: [] parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateApiKeyRequest" responses: "201": description: Created API key with its raw secret (shown once). headers: X-Request-Id: $ref: "#/components/headers/RequestId" Idempotency-Replayed: $ref: "#/components/headers/IdempotencyReplayed" content: application/json: schema: $ref: "#/components/schemas/CreatedApiKey" default: $ref: "#/components/responses/Error" /keys/{keyId}: delete: tags: - API Keys summary: Revoke an API key description: > Revokes an API key immediately. Subsequent requests using it are rejected with 401. Revoking is idempotent. operationId: revokeApiKey security: - ApiKeyAuth: [] parameters: - name: keyId in: path required: true schema: type: string responses: "200": description: The revoked API key. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ApiKey" default: $ref: "#/components/responses/Error" /customers: get: tags: - Customers summary: List customers operationId: listCustomers x-mcp-tool: list_customers x-required-scopes: ["customers:read"] security: - ApiKeyAuth: [] parameters: - name: externalId in: query schema: type: string description: Filter by your external customer id. - name: search in: query schema: type: string description: Case-insensitive substring match over name, email, and company. - name: identityChannel in: query schema: type: string description: > With identityIdentifier, resolve the customer owning a channel identity ("who is +62812… on WhatsApp?"). Both must be provided together; the result is a 0-or-1 item list. example: whatsapp - name: identityIdentifier in: query schema: type: string description: The identity value to look up (e.g. a phone number). example: "+628123456789" - name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 25 - $ref: "#/components/parameters/Cursor" responses: "200": description: Customers. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ListCustomersResponse" default: $ref: "#/components/responses/Error" post: tags: - Customers summary: Create a customer operationId: createCustomer x-mcp-tool: create_customer x-required-scopes: ["customers:write"] security: - ApiKeyAuth: [] parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateCustomerRequest" responses: "201": description: Created customer. headers: X-Request-Id: $ref: "#/components/headers/RequestId" Idempotency-Replayed: $ref: "#/components/headers/IdempotencyReplayed" content: application/json: schema: $ref: "#/components/schemas/Customer" default: $ref: "#/components/responses/Error" put: tags: - Customers summary: Upsert a customer by externalId description: > Creates a customer when no customer with the given externalId exists (201), or patches the existing one (200). Only the provided fields are written on update. externalId is the natural key and is required. operationId: upsertCustomer x-mcp-tool: upsert_customer x-required-scopes: ["customers:write"] security: - ApiKeyAuth: [] parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpsertCustomerRequest" responses: "200": description: Existing customer patched. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/Customer" "201": description: New customer created. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/Customer" default: $ref: "#/components/responses/Error" /customers/{customerId}: get: tags: - Customers summary: Get a customer operationId: getCustomer x-mcp-tool: get_customer x-required-scopes: ["customers:read"] security: - ApiKeyAuth: [] parameters: - name: customerId in: path required: true schema: type: string example: cus_x6q5vl75f5d53m7oet2k4r5w6a responses: "200": description: Customer. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/Customer" default: $ref: "#/components/responses/Error" delete: tags: - Customers summary: Delete a customer description: > Soft-deletes the customer: it disappears from lists but is retained so conversation history still resolves. Idempotent. operationId: deleteCustomer x-mcp-tool: delete_customer x-required-scopes: ["customers:write"] security: - ApiKeyAuth: [] parameters: - name: customerId in: path required: true schema: type: string example: cus_x6q5vl75f5d53m7oet2k4r5w6a responses: "204": description: Customer deleted. default: $ref: "#/components/responses/Error" patch: tags: - Customers summary: Update a customer operationId: updateCustomer x-mcp-tool: update_customer x-required-scopes: ["customers:write"] security: - ApiKeyAuth: [] parameters: - name: customerId in: path required: true schema: type: string example: cus_x6q5vl75f5d53m7oet2k4r5w6a requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateCustomerRequest" responses: "200": description: Updated customer. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/Customer" default: $ref: "#/components/responses/Error" /customers/{customerId}/identities: parameters: - name: customerId in: path required: true schema: type: string get: tags: - Channel identities summary: List a customer's channel identities operationId: listIdentities x-mcp-tool: list_identities x-required-scopes: ["customers:read"] security: - ApiKeyAuth: [] responses: "200": description: Channel identities. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ListIdentitiesResponse" default: $ref: "#/components/responses/Error" post: tags: - Channel identities summary: Link a channel identity to a customer description: > API-created identities default verified=false. Passing verified=true asserts your system authenticated the identity (recorded with verifiedAt) and allows it to auto-merge. A (channel, identifier) already claimed by another customer returns 409 with the owning customerId. operationId: createIdentity x-mcp-tool: create_identity x-required-scopes: ["customers:write"] security: - ApiKeyAuth: [] requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateIdentityRequest" responses: "201": description: Created channel identity. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ChannelIdentity" "409": description: The identity is already claimed by another customer. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/IdentityConflict" default: $ref: "#/components/responses/Error" /customers/{customerId}/identities/{identityId}: delete: tags: - Channel identities summary: Remove a channel identity operationId: deleteIdentity x-mcp-tool: delete_identity x-required-scopes: ["customers:write"] security: - ApiKeyAuth: [] parameters: - name: customerId in: path required: true schema: type: string - name: identityId in: path required: true schema: type: string responses: "204": description: Identity removed. default: $ref: "#/components/responses/Error" /conversations: get: tags: - Conversations summary: List conversations operationId: listConversations x-mcp-tool: list_conversations x-required-scopes: ["conversations:read"] security: - ApiKeyAuth: [] parameters: - name: customerId in: query schema: type: string description: Filter by Protodesk customer id. - name: status in: query schema: $ref: "#/components/schemas/ConversationStatus" - name: channel in: query schema: type: string example: whatsapp - name: externalObjectType in: query schema: type: string description: Filter by the attached external object's type. example: villa_listing - name: externalObjectId in: query schema: type: string description: Filter by the attached external object's id. example: listing_123 - name: updatedAfter in: query description: > Return only conversations updated strictly after this RFC3339 timestamp. Useful for polling changes since a last sync. schema: type: string format: date-time example: 2026-07-09T12:00:00Z - name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 25 - $ref: "#/components/parameters/Cursor" responses: "200": description: Conversations ordered by most recently updated. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ListConversationsResponse" default: $ref: "#/components/responses/Error" post: tags: - Conversations summary: Create a conversation operationId: createConversation x-mcp-tool: create_conversation x-required-scopes: ["conversations:write"] security: - ApiKeyAuth: [] parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateConversationRequest" responses: "201": description: Created conversation. headers: X-Request-Id: $ref: "#/components/headers/RequestId" Idempotency-Replayed: $ref: "#/components/headers/IdempotencyReplayed" content: application/json: schema: $ref: "#/components/schemas/Conversation" default: $ref: "#/components/responses/Error" /conversations/{conversationId}: get: tags: - Conversations summary: Get a conversation operationId: getConversation x-mcp-tool: get_conversation x-required-scopes: ["conversations:read"] security: - ApiKeyAuth: [] parameters: - name: conversationId in: path required: true schema: type: string example: conv_x6q5vl75f5d53m7oet2k4r5w6a responses: "200": description: Conversation. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/Conversation" default: $ref: "#/components/responses/Error" patch: tags: - Conversations summary: Update a conversation operationId: updateConversation x-mcp-tool: update_conversation x-required-scopes: ["conversations:write"] security: - ApiKeyAuth: [] parameters: - name: conversationId in: path required: true schema: type: string example: conv_x6q5vl75f5d53m7oet2k4r5w6a requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateConversationRequest" responses: "200": description: Updated conversation. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/Conversation" default: $ref: "#/components/responses/Error" /conversations/{conversationId}/assign: post: tags: - Conversations summary: Assign a conversation operationId: assignConversation x-mcp-tool: assign_conversation x-required-scopes: ["conversations:write"] security: - ApiKeyAuth: [] parameters: - name: conversationId in: path required: true schema: type: string example: conv_x6q5vl75f5d53m7oet2k4r5w6a - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/AssignConversationRequest" responses: "200": description: Assigned conversation. headers: X-Request-Id: $ref: "#/components/headers/RequestId" Idempotency-Replayed: $ref: "#/components/headers/IdempotencyReplayed" content: application/json: schema: $ref: "#/components/schemas/Conversation" default: $ref: "#/components/responses/Error" /conversations/{conversationId}/resolve: post: tags: - Conversations summary: Resolve a conversation operationId: resolveConversation x-mcp-tool: resolve_conversation x-required-scopes: ["conversations:write"] security: - ApiKeyAuth: [] parameters: - name: conversationId in: path required: true schema: type: string example: conv_x6q5vl75f5d53m7oet2k4r5w6a - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/ResolveConversationRequest" responses: "200": description: Resolved conversation. headers: X-Request-Id: $ref: "#/components/headers/RequestId" Idempotency-Replayed: $ref: "#/components/headers/IdempotencyReplayed" content: application/json: schema: $ref: "#/components/schemas/Conversation" default: $ref: "#/components/responses/Error" /teams: get: tags: [Workspace directory] summary: List workspace teams description: "List workspace teams. Use key when assigning a conversation; memberIds and aiAgentIds contain external assignee IDs." operationId: listTeams x-mcp-tool: list_teams x-required-scopes: ["conversations:read"] security: [{ApiKeyAuth: []}] parameters: - {in: query, name: q, description: Case-insensitive name or team-key search., schema: {type: string, maxLength: 200}} - {in: query, name: limit, schema: {type: integer, minimum: 1, maximum: 100, default: 20}} - {in: query, name: cursor, description: Opaque nextCursor from the same directory and search., schema: {type: string, maxLength: 2048}} responses: "200": description: Workspace-scoped directory records. content: application/json: schema: type: object required: [data, hasMore, nextCursor] properties: data: type: array items: type: object properties: id: {type: string} key: {type: string} name: {type: string} description: {type: string} isFallback: {type: boolean} memberIds: {type: array, items: {type: string}} aiAgentIds: {type: array, items: {type: string}} hasMore: {type: boolean} nextCursor: {type: [string, "null"]} default: $ref: "#/components/responses/Error" /members: get: tags: [Workspace directory] summary: List workspace members description: "List active workspace members. Use assigneeId and type when assigning a conversation; id identifies the directory record. Email addresses are not returned." operationId: listMembers x-mcp-tool: list_members x-required-scopes: ["conversations:read"] security: [{ApiKeyAuth: []}] parameters: - {in: query, name: q, description: Case-insensitive name or team-key search., schema: {type: string, maxLength: 200}} - {in: query, name: limit, schema: {type: integer, minimum: 1, maximum: 100, default: 20}} - {in: query, name: cursor, description: Opaque nextCursor from the same directory and search., schema: {type: string, maxLength: 2048}} responses: "200": description: Workspace-scoped directory records. content: application/json: schema: type: object required: [data, hasMore, nextCursor] properties: data: type: array items: type: object properties: id: {type: string} assigneeId: {type: string} type: {type: string} name: {type: string} role: {type: string} hasMore: {type: boolean} nextCursor: {type: [string, "null"]} default: $ref: "#/components/responses/Error" /labels: get: tags: [Workspace directory] summary: List workspace labels description: "List workspace labels." operationId: listLabels x-mcp-tool: list_labels x-required-scopes: ["conversations:read"] security: [{ApiKeyAuth: []}] responses: "200": description: Workspace-scoped directory records. content: application/json: schema: type: object required: [data] properties: data: type: array items: type: object properties: id: {type: string} key: {type: string} label: {type: string} color: {type: string} icon: {type: string} description: {type: string} position: {type: integer} usageCount: {type: integer} default: $ref: "#/components/responses/Error" /categories: get: tags: [Workspace directory] summary: List workspace categories description: "List workspace categories." operationId: listCategories x-mcp-tool: list_categories x-required-scopes: ["conversations:read"] security: [{ApiKeyAuth: []}] responses: "200": description: Workspace-scoped directory records. content: application/json: schema: type: object required: [data] properties: data: type: array items: type: object properties: id: {type: string} key: {type: string} label: {type: string} color: {type: string} icon: {type: string} description: {type: string} position: {type: integer} isDefault: {type: boolean} usageCount: {type: integer} default: $ref: "#/components/responses/Error" /statuses: get: tags: - Statuses & priorities summary: List workspace statuses operationId: listStatuses x-mcp-tool: list_statuses x-required-scopes: ["conversations:read"] security: - ApiKeyAuth: [] responses: "200": description: Workspace-defined conversation statuses ordered by position. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ListStatusesResponse" default: $ref: "#/components/responses/Error" /priorities: get: tags: - Statuses & priorities summary: List workspace priorities operationId: listPriorities x-mcp-tool: list_priorities x-required-scopes: ["conversations:read"] security: - ApiKeyAuth: [] responses: "200": description: Workspace-defined priority levels ordered by position. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ListPrioritiesResponse" default: $ref: "#/components/responses/Error" /conversations/{conversationId}/assignments: parameters: - name: conversationId in: path required: true schema: type: string get: tags: - Assignments summary: List a conversation's assignments operationId: listAssignments x-mcp-tool: list_assignments x-required-scopes: ["conversations:read"] security: - ApiKeyAuth: [] responses: "200": description: Assignments (all states), newest first. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ListAssignmentsResponse" default: $ref: "#/components/responses/Error" /conversations/{conversationId}/messages: get: tags: - Messages summary: List messages operationId: listMessages x-mcp-tool: list_messages x-required-scopes: ["conversations:read"] security: - ApiKeyAuth: [] parameters: - name: conversationId in: path required: true schema: type: string example: conv_x6q5vl75f5d53m7oet2k4r5w6a - name: visibility in: query description: > Filter by the internal flag. Use public for a customer-facing view that excludes internal notes. schema: type: string enum: - all - public - internal default: all - name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 25 - $ref: "#/components/parameters/Cursor" responses: "200": description: Messages in chronological order. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ListMessagesResponse" default: $ref: "#/components/responses/Error" post: tags: - Messages summary: Record a message description: Records a message without outbound delivery. Use the send endpoint to send a reply to a customer. operationId: createMessage x-mcp-tool: create_message x-required-scopes: ["messages:write"] security: - ApiKeyAuth: [] parameters: - name: conversationId in: path required: true schema: type: string example: conv_x6q5vl75f5d53m7oet2k4r5w6a - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateMessageRequest" responses: "201": description: Created message. headers: X-Request-Id: $ref: "#/components/headers/RequestId" Idempotency-Replayed: $ref: "#/components/headers/IdempotencyReplayed" content: application/json: schema: $ref: "#/components/schemas/Message" default: $ref: "#/components/responses/Error" /conversations/{conversationId}/send: post: tags: [Messages] summary: Send a reply to a customer description: > Sends as an agent on the conversation's channel. Email and WhatsApp delivery is queued atomically with the message; queued does not mean delivered. Chat replies appear in the visitor stream. Internal notes and test-thread replies stay in Protodesk. Requires messages:send; messages:write alone never authorizes delivery. Reuse the same Idempotency-Key and request body for uncertain retries. operationId: sendMessage x-mcp-tool: send_message x-required-scopes: ["messages:send"] security: - ApiKeyAuth: [] parameters: - name: conversationId in: path required: true schema: {type: string} - name: Idempotency-Key in: header required: true schema: {type: string, minLength: 8, maxLength: 255} requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/SendMessageRequest" responses: "201": description: Reply recorded; inspect queued and message.metadata.deliveryStatus for delivery state. headers: Idempotency-Replayed: $ref: "#/components/headers/IdempotencyReplayed" content: application/json: schema: $ref: "#/components/schemas/SendMessageResponse" default: $ref: "#/components/responses/Error" /messages/{messageId}: get: tags: [Messages] summary: Get a message and its delivery state description: Delivery state is in metadata.deliveryStatus; sent means accepted by the provider, while delivered requires a provider receipt. operationId: getMessage x-mcp-tool: get_message x-required-scopes: ["conversations:read"] security: - ApiKeyAuth: [] parameters: - name: messageId in: path required: true schema: {type: string} responses: "200": description: Workspace-scoped message. content: application/json: schema: $ref: "#/components/schemas/Message" default: $ref: "#/components/responses/Error" /messages/{messageId}/cancel-send: post: tags: [Messages] summary: Cancel a pending send description: Attempts cancellation before delivery. Returns 409 if the message can no longer be canceled. operationId: cancelMessageSend x-mcp-tool: cancel_message_send x-required-scopes: ["messages:send"] security: - ApiKeyAuth: [] parameters: - name: messageId in: path required: true schema: {type: string} responses: "204": description: Pending send canceled. default: $ref: "#/components/responses/Error" /webhooks: get: tags: - Webhooks summary: List webhook subscriptions operationId: listWebhooks x-mcp-tool: list_webhooks x-required-scopes: ["webhooks:manage"] security: - ApiKeyAuth: [] parameters: - name: status in: query schema: $ref: "#/components/schemas/WebhookStatus" - name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 25 - $ref: "#/components/parameters/Cursor" responses: "200": description: Webhook subscriptions. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ListWebhooksResponse" default: $ref: "#/components/responses/Error" post: tags: - Webhooks summary: Create a webhook subscription operationId: createWebhook x-mcp-tool: create_webhook x-required-scopes: ["webhooks:manage"] security: - ApiKeyAuth: [] parameters: - $ref: "#/components/parameters/IdempotencyKey" requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateWebhookRequest" responses: "201": description: Created webhook subscription. The signing secret is returned only in this response. headers: X-Request-Id: $ref: "#/components/headers/RequestId" Idempotency-Replayed: $ref: "#/components/headers/IdempotencyReplayed" content: application/json: schema: $ref: "#/components/schemas/WebhookSubscription" default: $ref: "#/components/responses/Error" /webhooks/{webhookId}: patch: tags: - Webhooks summary: Update a webhook subscription operationId: updateWebhook x-mcp-tool: update_webhook x-required-scopes: ["webhooks:manage"] security: - ApiKeyAuth: [] parameters: - name: webhookId in: path required: true schema: type: string example: wh_x6q5vl75f5d53m7oet2k4r5w6a requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateWebhookRequest" responses: "200": description: Updated webhook subscription. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/WebhookSubscription" default: $ref: "#/components/responses/Error" delete: tags: - Webhooks summary: Delete a webhook subscription operationId: deleteWebhook x-mcp-tool: delete_webhook x-required-scopes: ["webhooks:manage"] security: - ApiKeyAuth: [] parameters: - name: webhookId in: path required: true schema: type: string example: wh_x6q5vl75f5d53m7oet2k4r5w6a responses: "204": description: Deleted webhook subscription. headers: X-Request-Id: $ref: "#/components/headers/RequestId" default: $ref: "#/components/responses/Error" /webhooks/{webhookId}/test: post: tags: - Webhooks summary: Send a test delivery description: > Sends a synthetic `webhook.test` event through the real delivery path (same headers and HMAC signature) and returns the result synchronously. Use this to verify your endpoint before real traffic. operationId: testWebhook x-mcp-tool: test_webhook x-required-scopes: ["webhooks:manage"] security: - ApiKeyAuth: [] parameters: - name: webhookId in: path required: true schema: type: string example: wh_x6q5vl75f5d53m7oet2k4r5w6a responses: "200": description: Test delivery result. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/WebhookTestResult" default: $ref: "#/components/responses/Error" /webhooks/{webhookId}/deliveries: get: tags: - Webhooks summary: List recent deliveries description: Recent delivery attempts for one subscription, newest first. operationId: listWebhookDeliveries x-mcp-tool: list_webhook_deliveries x-required-scopes: ["webhooks:manage"] security: - ApiKeyAuth: [] parameters: - name: webhookId in: path required: true schema: type: string example: wh_x6q5vl75f5d53m7oet2k4r5w6a - name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 25 - $ref: "#/components/parameters/Cursor" responses: "200": description: Delivery attempts ordered by most recent. headers: X-Request-Id: $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ListWebhookDeliveriesResponse" default: $ref: "#/components/responses/Error" components: securitySchemes: ApiKeyAuth: type: http scheme: bearer bearerFormat: Protodesk API key description: > Use a workspace API key in the Authorization header: Bearer pk_.... Older pk_live_ and pk_test_ keys remain valid; their prefixes do not isolate data or select a separate environment. parameters: IdempotencyKey: name: Idempotency-Key in: header required: false description: > Optional retry key for safely replaying create requests. Reusing the same key with a different request body returns 409. schema: type: string minLength: 8 maxLength: 255 example: 8db8e596-7c1a-4fd5-a728-4d6c99f4e66b Cursor: name: cursor in: query required: false description: > Opaque keyset cursor from a prior response's nextCursor. Omit for the first page. Malformed or tampered cursors return 400. schema: type: string headers: RequestId: description: Request id for tracing this request in logs and support. schema: type: string example: req_x6q5vl75f5d53m7oet2k4r5w6a IdempotencyReplayed: description: Present with true when a response was replayed from an idempotency key. schema: type: string enum: - "true" RateLimitLimit: description: Request budget for the route class (read or write) applied to this request. schema: type: integer example: 40 RateLimitRemaining: description: Requests remaining in the current budget after this request. schema: type: integer example: 39 RateLimitReset: description: Seconds until the budget refills to its full limit. schema: type: integer example: 1 RetryAfter: description: Seconds to wait before retrying, sent with 429 responses. schema: type: integer example: 1 responses: Error: description: >- Error response. All /v1 responses carry RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset headers. A 429 (rate_limit.exceeded) additionally carries Retry-After. Request bodies exceeding the route-class limit (1KB reads, 64KB writes) return 413 (request.body_too_large). headers: X-Request-Id: $ref: "#/components/headers/RequestId" RateLimit-Limit: $ref: "#/components/headers/RateLimitLimit" RateLimit-Remaining: $ref: "#/components/headers/RateLimitRemaining" RateLimit-Reset: $ref: "#/components/headers/RateLimitReset" Retry-After: $ref: "#/components/headers/RetryAfter" content: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" schemas: AttachmentUpload: type: object required: [uploadId, status] properties: uploadId: { type: string } status: { type: string, enum: [pending, ready] } url: { type: string, format: uri, description: "Temporary PUT URL, present only while pending." } method: { type: string, enum: [PUT] } headers: type: object properties: Content-Type: { type: string } Content-Length: { type: string } expiresAt: { type: string, format: date-time } maxSizeBytes: { type: integer } attachment: type: object description: Present only when ready. Use this object in a message's attachments array. required: [url, filename, contentType, sizeBytes] properties: url: { type: string, description: Durable /api/media/ reference; not a public download URL. } filename: { type: string } contentType: { type: string } sizeBytes: { type: integer } EvelinChatCreate: example: { conversationId: "CONVERSATION_ID" } type: object additionalProperties: false required: [conversationId] properties: conversationId: { type: string, minLength: 1 } EvelinChatRename: example: { title: "Billing follow-up" } type: object additionalProperties: false required: [title] properties: title: { type: string, minLength: 1, maxLength: 120 } EvelinChatQuestion: example: { question: "Summarize this conversation and suggest the next step." } type: object additionalProperties: false required: [question] properties: question: { type: string, minLength: 1, maxLength: 4000 } retryOfTurnId: { type: string, description: "Retry only the latest failed turn, with its identical question and attachments." } clarificationTurnId: { type: string, description: "The preceding turn whose clarification is being answered; mutually exclusive with retryOfTurnId." } approvalMode: { type: string, enum: [ask_before_changes], default: ask_before_changes } attachments: type: array maxItems: 4 items: type: object additionalProperties: false required: [reference, filename, contentType, sizeBytes] properties: reference: { type: string, description: "An existing workspace media reference; arbitrary URLs and other workspaces are rejected." } filename: { type: string } contentType: { type: string } sizeBytes: { type: integer, minimum: 1 } EvelinActionUndo: example: { proposalIds: ["PROPOSAL_ID"] } type: object additionalProperties: false required: [proposalIds] properties: proposalIds: type: array minItems: 1 maxItems: 6 uniqueItems: true items: { type: string, minLength: 1 } EvelinChat: type: object required: [id, conversationId, title, status, createdAt, updatedAt, lastActivityAt, retentionExpiresAt] properties: id: { type: string } conversationId: { type: string } title: { type: string } status: { type: string, enum: [active, archived] } createdAt: { type: string, format: date-time } updatedAt: { type: string, format: date-time } lastActivityAt: { type: string, format: date-time } retentionExpiresAt: { type: string, format: date-time } archivedAt: { type: string, format: date-time } EvelinChatList: type: array items: { $ref: "#/components/schemas/EvelinChat" } EvelinChatTurn: type: object required: [id, sessionId, conversationId, runId, status, question, createdAt, updatedAt, expiresAt] properties: id: { type: string } sessionId: { type: string } conversationId: { type: string } runId: { type: string } status: { type: string } question: { type: string } approvalMode: { type: string } retryOfTurnId: { type: string } answer: type: object description: "Completed answer with citations, draft proposals and actionProposals. Read the exact action proposal before confirming its id. An answer may be withheld if its retained sources are no longer accessible." additionalProperties: true properties: answer: { type: string, description: "Answer text for the user." } warnings: { type: array, items: { type: string } } citations: { type: array, items: { type: object, additionalProperties: true } } proposals: { type: array, description: "Draft suggestions, not executed changes.", items: { type: object, additionalProperties: true } } actionProposals: type: array items: type: object additionalProperties: true properties: id: { type: string, description: "Use this exact ID for confirmation after user review." } type: { type: string } target: { type: object, additionalProperties: true } before: { type: object, additionalProperties: true } after: { type: object, additionalProperties: true } confirmationRequired: { type: boolean } actionConfirmations: type: array items: { $ref: "#/components/schemas/EvelinActionReceipt" } createdAt: { type: string, format: date-time } updatedAt: { type: string, format: date-time } expiresAt: { type: string, format: date-time } EvelinChatTurnList: type: array items: { $ref: "#/components/schemas/EvelinChatTurn" } EvelinActionReceipt: type: object description: "Durable receipt of the exact proposed change; check its target, before, after and undo status." required: [id, proposalId, runId, effectId, type, target, before, after, confirmedAt] properties: id: { type: string } proposalId: { type: string } runId: { type: string } effectId: { type: string } type: { type: string } target: { type: object, additionalProperties: true } before: { type: object, additionalProperties: true } after: { type: object, additionalProperties: true } confirmedAt: { type: string, format: date-time } undo: type: object properties: status: { type: string, enum: [available, changed, unavailable, undone] } undoneAt: { type: string, format: date-time } EvelinActionReceiptList: type: array items: { $ref: "#/components/schemas/EvelinActionReceipt" } EvelinFeedbackRequest: type: object additionalProperties: false required: [expectedRevision, targetVersion, signal] properties: expectedRevision: { type: integer, minimum: 0 } targetVersion: { type: string, minLength: 64, maxLength: 64 } signal: { type: string, enum: [helpful, needs_improvement] } reason: { type: string, enum: ["", incorrect_information, missing_details, wrong_tone, repeated_question, other, accurate, clear, useful, good_tone] } correction: { type: string, maxLength: 1000 } visibility: { type: string, enum: [personal, workspace], default: personal, description: "Workspace shares the answer and correction with workspace reviewers." } EvelinFeedbackWithdrawal: type: object additionalProperties: false required: [expectedRevision, targetVersion] properties: expectedRevision: { type: integer, minimum: 0 } targetVersion: { type: string, minLength: 64, maxLength: 64 } EvelinFeedbackState: type: object required: [revision, visibility, targetVersion, feedback] properties: revision: { type: integer, minimum: 0 } visibility: { type: string, enum: [personal, workspace] } targetVersion: { type: string } feedback: type: [object, "null"] properties: revision: { type: integer } targetVersion: { type: string } signal: { type: string, enum: [helpful, needs_improvement] } reason: { type: string } correction: { type: string } sharedWithReviewers: { type: boolean } updatedAt: { type: string, format: date-time } review: type: object description: Reporter-safe review outcome; no reviewer identity or internal notes. properties: feedbackRevision: { type: integer } status: { type: string } decision: { type: string } reviewedAt: { type: string, format: date-time } EvelinGrounding: type: object required: [state, reason, confidence] properties: state: { type: string, enum: [grounded, weak, no-match] } reason: { type: string } confidence: { type: number } EvelinNextAction: type: object required: [kind, reason] properties: kind: type: string enum: [answer, acknowledge, clarify, escalate, wait, resolve] description: Acknowledge recommends a conversation-only receipt confirmation, without product or policy claims. It does not send a reply or resolve the conversation. reason: { type: string } EvelinEmptyRequest: type: object additionalProperties: false EvelinReplyRequest: type: object additionalProperties: false properties: strategy: { type: string, enum: [recommended, direct_answer, answer_and_clarify, clarify_first, guided_steps, troubleshoot, deescalate, confirm_and_close, escalate] } instruction: { type: string, maxLength: 1000 } overrideNoReply: { type: boolean, description: Explicitly request a draft even if the reply guard recommends no reply. } EvelinGroundingRequest: type: object additionalProperties: false required: [draft] properties: draft: { type: string, minLength: 1, maxLength: 20000 } EvelinTranslationRequest: type: object additionalProperties: false required: [targetLanguage] properties: targetLanguage: { type: string, minLength: 1, maxLength: 100, example: id } EvelinRewriteRequest: type: object additionalProperties: false required: [mode, draft] properties: mode: { type: string, enum: [improve, polish, translate] } draft: { type: string, minLength: 1, maxLength: 20000 } targetLanguage: { type: string, minLength: 1, maxLength: 100 } tone: { type: string, maxLength: 200 } preset: { type: string, enum: [expand, shorten, friendly, professional] } EvelinCreatedRun: type: object required: [id, kind, status, trigger, conversationId, agentId, agentVersionId, createdAt] properties: id: { type: string } kind: { type: string } status: { type: string } trigger: type: object properties: type: { type: string } conversationId: { type: string } agentId: { type: string } agentVersionId: { type: string } createdAt: { type: string, format: date-time } EvelinRunStatus: type: object required: [id, status, conversationId, createdAt, completedAt, creditsUsed, errorCategory] properties: id: { type: string } status: { type: string, description: Poll until a terminal run status. } conversationId: { type: string } createdAt: { type: string, format: date-time } completedAt: { type: [string, "null"], format: date-time } creditsUsed: { type: integer, minimum: 0 } errorCategory: { type: string, description: Empty when the run has no recorded error. } EvelinResult: type: object description: Typed artifact projection for the requested kind. Reply and follow-up drafts expose body; rewrites expose text. Fact reviews expose verdict, claims and evidenceSources. Review translations expose body and reviewLanguage. Reply guard exposes shouldDraft, reasonCode and reason. Results never imply delivery. properties: artifactId: { type: string } runId: { type: string } conversationId: { type: string } sourceMessageId: { type: string } body: { type: string } text: { type: string } changed: { type: boolean } mode: { type: string } targetLanguage: { type: string } outputLanguage: { type: string } reviewLanguage: { type: string } sourceLanguage: { type: string } sourceDraftArtifactId: { type: string } sourceDraftContentHash: { type: string } sourceDraftHash: { type: string } contextArtifactId: { type: string } strategy: { type: string } strategyLabel: { type: string } grounding: { $ref: "#/components/schemas/EvelinGrounding" } citations: { type: array, items: { type: object } } claims: { type: array, items: { type: object } } evidenceSources: { type: array, items: { type: object } } verdict: { type: string } shouldDraft: { type: boolean } reasonCode: { type: string } reason: { type: string } overrideApplied: { type: boolean } knowledgeOutdated: { type: boolean } createdAt: { type: string, format: date-time } expiresAt: { type: [string, "null"], format: date-time } EvelinContext: type: [object, "null"] properties: customerWants: { type: string } currentState: { type: string } nextBestAction: { $ref: "#/components/schemas/EvelinNextAction" } grounding: { $ref: "#/components/schemas/EvelinGrounding" } citations: { type: array, items: { type: object } } openQuestions: { type: array, items: { type: string } } unsupportedRisks: { type: array, items: { type: object } } sourceMessageId: { type: string } generatedAt: { type: string, format: date-time } knowledgeOutdated: { type: boolean } AuthoringNewArticle: type: object additionalProperties: false required: [slug, locale, tags, featured, content] properties: slug: { type: string, minLength: 1, maxLength: 160 } locale: { type: string } collectionId: { type: string, description: Existing collection ID from authoring context; empty means intentionally Uncategorized. } tags: type: array maxItems: 20 uniqueItems: true items: { type: string } featured: { type: boolean, description: Editable review default; publishing still requires a human decision. } reviewNote: { type: string, maxLength: 2000, description: Internal uncertainty or reviewer notes; not customer content. } content: { $ref: "#/components/schemas/AuthoringContent" } AuthoringArticleSet: type: object required: [id, name, items, reviewUrl, publicationChanged] properties: id: { type: string } name: { type: string } items: type: array items: allOf: - { $ref: "#/components/schemas/AuthoringProposal" } - type: object properties: articleSetId: { type: string } locale: { type: string } title: { type: string } reviewUrl: { type: string } publicationChanged: { type: boolean, enum: [false] } AuthoringContent: type: object additionalProperties: false required: [title, excerpt, bodyMarkdown] properties: title: { type: string } excerpt: { type: string } bodyMarkdown: { type: string } AuthoringArticle: type: object properties: tags: { type: array, items: { type: string } } featured: { type: boolean } id: { type: string } title: { type: string } slug: { type: string } locale: { type: string } status: { type: string, enum: [draft, published] } revision: { type: integer, format: int64 } excerpt: { type: string } bodyMarkdown: { type: string } collectionId: { type: [string, "null"] } HelpCollection: type: object properties: id: { type: string } parentId: { type: [string, "null"] } slug: { type: string } name: { type: string } description: { type: string } position: { type: integer } articleCount: { type: integer } revision: { type: integer } createdAt: { type: string, format: date-time } updatedAt: { type: string, format: date-time } translations: type: array items: type: object properties: locale: { type: string } slug: { type: string } name: { type: string } description: { type: string } revision: { type: integer } AuthoringProposal: type: object properties: kind: { type: string, enum: [update, create] } articleSetId: { type: string } articleSetName: { type: string } newArticle: type: [object, "null"] properties: slug: { type: string } collectionId: { type: string } tags: { type: array, items: { type: string } } featured: { type: boolean } reviewNote: { type: string } id: { type: string } articleId: { type: string } status: { type: string, enum: [pending, approved, dismissed] } baseRevision: { type: integer, format: int64 } currentRevision: { type: [integer, "null"], format: int64 } content: { $ref: "#/components/schemas/AuthoringContent" } reviewUrl: { type: string } publicationChanged: { type: boolean } Me: type: object required: - apiKeyId - workspaceId - scopes properties: apiKeyId: type: string example: key_o5mj4q7beg32vkjcd76a37t5zy workspaceId: type: string example: wsp_f46kbkltuw6uzkj7y2j4bbxlbe scopes: type: array items: $ref: "#/components/schemas/APIKeyScope" CreateApiKeyRequest: type: object required: - name - scopes properties: name: type: string description: Human-readable label for the key. example: CI deploy environment: type: string description: Defaults to the calling key's environment. enum: - live - test scopes: type: array minItems: 1 items: $ref: "#/components/schemas/APIKeyScope" CreatedApiKey: type: object description: A newly created key. The secret is shown once and never again. required: - id - name - environment - scopes - display - secret properties: id: type: string example: key_o5mj4q7beg32vkjcd76a37t5zy name: type: string environment: type: string enum: - live - test scopes: type: array items: $ref: "#/components/schemas/APIKeyScope" display: type: string description: Masked key preview safe to display. example: pk_abcdefgh...wxyz secret: type: string description: The raw API key. Store it now; it cannot be retrieved later. example: pk_o5mj4q7beg32vkjcd76a37t5zyabcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrst ApiKey: type: object description: API key metadata without secret material. required: - id - name - environment - scopes - display - createdAt properties: id: type: string name: type: string environment: type: string enum: - live - test scopes: type: array items: $ref: "#/components/schemas/APIKeyScope" display: type: string createdAt: type: string format: date-time lastUsedAt: type: string format: date-time nullable: true revokedAt: type: string format: date-time nullable: true ListApiKeysResponse: type: object required: - data properties: data: type: array items: $ref: "#/components/schemas/ApiKey" APIKeyScope: type: string description: Workspace API key permission scope. enum: - customers:read - customers:write - conversations:read - conversations:write - messages:write - attachments:read - attachments:write - webhooks:manage - realtime:read - ai:use - keys:manage - help:read - help:drafts:write - help:articles:write - help:articles:publish - help:manage CreateCustomerRequest: type: object properties: externalId: type: string description: Your stable id for this customer. example: renter_123 name: type: string example: Maya email: type: string format: email example: maya@example.com phone: type: string example: "+628123456789" company: type: string example: Bali Villa Hub language: type: string example: id timezone: type: string example: Asia/Jakarta metadata: type: object additionalProperties: true description: > Arbitrary customer metadata. Evelin reads account context only from the reserved tenant object and only permits id, name, plan, status, domain, region, createdAt, and updatedAt; every other key remains excluded from AI tool results. example: tier: vip source: villa_owner_app UpdateCustomerRequest: type: object description: Omitted fields are left unchanged. Empty externalId clears the external id. minProperties: 1 properties: externalId: type: string description: Your stable id for this customer. Send an empty string to clear it. example: renter_123 name: type: string example: Maya email: type: string format: email example: maya@example.com phone: type: string example: "+628123456789" company: type: string example: Bali Villa Hub language: type: string example: id timezone: type: string example: Asia/Jakarta avatarUrl: type: string claimedEmail: type: string description: An email the customer claimed but that is not verified. emailStatus: type: string description: Email deliverability/suppression state. lastActiveChannel: type: string example: whatsapp firstContactAt: type: string format: date-time lastSeenAt: type: string format: date-time tags: type: array items: type: string createdAt: type: string format: date-time description: Backdates the record when it is first created (import semantics); ignored if the customer already exists. metadata: type: object additionalProperties: true description: > Replaces the full customer metadata object. Evelin reads account context only from the reserved tenant object and only permits id, name, plan, status, domain, region, createdAt, and updatedAt. example: tier: gold UpsertCustomerRequest: type: object description: > externalId is the natural key and is required. On update, only the provided fields are written; omitted fields are left unchanged. required: - externalId properties: externalId: type: string description: Your stable id for this customer; the upsert key. example: renter_123 name: type: string example: Maya email: type: string format: email example: maya@example.com phone: type: string example: "+628123456789" company: type: string example: Bali Villa Hub language: type: string example: id timezone: type: string example: Asia/Jakarta metadata: type: object additionalProperties: true description: > Replaces the full customer metadata object. Evelin reads account context only from the reserved tenant object and only permits id, name, plan, status, domain, region, createdAt, and updatedAt. example: tier: gold CreateIdentityRequest: type: object required: - channel - identifier properties: channel: type: string description: Channel the identity belongs to (open string, e.g. whatsapp, slack, email). example: whatsapp identifier: type: string description: The identity value on that channel. example: "+628123456789" verified: type: boolean default: false description: > True asserts your system authenticated this identity, allowing auto-merge. Recorded with verifiedAt. claimedEmail: type: string description: A claimed (unverified) email; display and merge-suggestion input only. displayName: type: string avatarUrl: type: string primary: type: boolean default: false ChannelIdentity: type: object required: - id - customerId - channel - identifier - verified - primary - createdAt - updatedAt properties: id: type: string example: cid_x6q5vl75f5d53m7oet2k4r5w6a customerId: type: string channel: type: string example: whatsapp identifier: type: string example: "+628123456789" verified: type: boolean verifiedAt: type: string format: date-time nullable: true claimedEmail: type: [string, "null"] displayName: type: [string, "null"] avatarUrl: type: [string, "null"] lastActiveAt: type: string format: date-time nullable: true primary: type: boolean createdAt: type: string format: date-time updatedAt: type: string format: date-time ListIdentitiesResponse: type: object required: - data properties: data: type: array items: $ref: "#/components/schemas/ChannelIdentity" IdentityConflict: type: object description: > Returned with 409 when a (channel, identifier) is already claimed. The owning customerId is workspace-scoped, so it is safe to return. required: - error - customerId properties: error: $ref: "#/components/schemas/Error" customerId: type: string example: cus_x6q5vl75f5d53m7oet2k4r5w6a Customer: type: object description: > Every key is always present; optional fields are null when unset (never empty strings). required: - id - externalId - name - email - phone - company - language - timezone - avatarUrl - claimedEmail - emailStatus - lastActiveChannel - firstContactAt - lastSeenAt - tags - metadata - createdAt - updatedAt properties: id: type: string example: cus_x6q5vl75f5d53m7oet2k4r5w6a externalId: type: [string, "null"] example: renter_123 name: type: string example: Maya email: type: [string, "null"] example: maya@example.com phone: type: [string, "null"] example: "+628123456789" company: type: [string, "null"] example: Bali Villa Hub language: type: [string, "null"] example: id timezone: type: [string, "null"] example: Asia/Jakarta avatarUrl: type: [string, "null"] example: https://cdn.example.com/avatars/maya.png claimedEmail: type: [string, "null"] description: An email the customer claimed but that is not verified. emailStatus: type: [string, "null"] description: Email deliverability/suppression state (e.g. active, unsubscribed, bounced). lastActiveChannel: type: [string, "null"] example: whatsapp firstContactAt: type: [string, "null"] format: date-time lastSeenAt: type: [string, "null"] format: date-time tags: type: array items: type: string conversationCount: type: integer description: Number of conversations for this customer. Present on list responses only. metadata: type: object additionalProperties: true createdAt: type: string format: date-time updatedAt: type: string format: date-time ConversationStatus: type: string description: > Workspace-defined status key. Every workspace is seeded with `open`, `pending`, `resolved`, and `closed`; workspaces can define their own. Use `GET /v1/statuses` to list the keys valid for your workspace. Unknown keys are rejected with `conversations.unknown_status`. pattern: "^[a-z0-9][a-z0-9_-]{0,62}$" example: open ConversationPriority: type: string description: > Workspace-defined priority key. Every workspace is seeded with `low`, `normal`, `high`, and `urgent`; workspaces can define their own. Use `GET /v1/priorities` to list the keys valid for your workspace. Unknown keys are rejected with `conversations.unknown_priority`. pattern: "^[a-z0-9][a-z0-9_-]{0,62}$" example: normal Assignee: type: object description: > Who the conversation is assigned to. Set `id` to an empty string to clear the assignment. required: - id properties: type: type: string enum: - user - ai-agent default: user id: type: string description: External assignee id, such as an owner, agent, or AI agent id. example: owner_456 Status: type: object description: Workspace-defined conversation status. required: - key - label - color - icon - type - position - isDefault properties: key: type: string description: Immutable key referenced by conversation `status`. example: open label: type: string example: Open color: type: string example: "#22c55e" icon: type: string example: "" type: type: string description: > `active` statuses are in progress; `completed` statuses end the conversation and are valid resolution targets. enum: - active - completed position: type: integer isDefault: type: boolean description: Default status for its type. Priority: type: object description: Workspace-defined conversation priority level. required: - key - label - color - position - isDefault properties: key: type: string description: Immutable key referenced by conversation `priority`. example: normal label: type: string example: Normal color: type: string example: "" position: type: integer isDefault: type: boolean ListStatusesResponse: type: object required: - data properties: data: type: array items: $ref: "#/components/schemas/Status" ListPrioritiesResponse: type: object required: - data properties: data: type: array items: $ref: "#/components/schemas/Priority" ExternalObject: type: object description: Snapshot of your business object attached to the conversation. required: - type - id - title - url - metadata properties: type: type: string example: villa_listing id: type: string example: listing_123 title: type: [string, "null"] example: 2BR Villa in Canggu url: type: [string, "null"] format: uri example: https://example.com/listings/listing_123 metadata: type: object additionalProperties: true example: ownerId: owner_456 agencyId: agency_789 CreateConversationRequest: type: object required: - customerId - subject properties: externalId: type: string description: Your stable id for this conversation. example: inquiry_123 subject: type: string minLength: 1 example: Inquiry for 2BR Villa in Canggu status: $ref: "#/components/schemas/ConversationStatus" default: open priority: $ref: "#/components/schemas/ConversationPriority" default: normal channel: type: string default: api example: whatsapp customerId: type: string example: cus_x6q5vl75f5d53m7oet2k4r5w6a assignee: $ref: "#/components/schemas/Assignee" team: type: string description: External team or routing hint. example: canggu-rentals tags: type: array items: type: string example: - villa - lead externalObject: $ref: "#/components/schemas/ExternalObject" metadata: type: object additionalProperties: true example: source: owner_app channelIdentityId: type: string description: The channel identity that opened this conversation (provenance). example: cid_x6q5vl75f5d53m7oet2k4r5w6a routing: $ref: "#/components/schemas/ConversationRouting" ConversationRouting: type: object description: > Routing hints resolved at create time. Unresolved hints never fail the create — they leave the conversation unassigned and are recorded in the response's routingNotes for debugging. properties: teamKey: type: string description: Team key to route to. Falls back to the workspace fallback team if unknown. example: canggu-rentals assigneeId: type: string description: Assign directly to this assignee id. assigneeType: type: string enum: - user - ai-agent default: user assigneeExternalId: type: string description: Your external id for an assignee (recorded as a suggestion; not yet resolved). priority: type: string tags: type: array items: type: string strategy: type: string description: Routing strategy hint (e.g. round_robin). Recorded for future use. UpdateConversationRequest: type: object description: > Omitted fields are left unchanged. Tags, externalObject, and metadata replace their full stored values when provided. minProperties: 1 properties: subject: type: string minLength: 1 example: Inquiry for 2BR Villa in Canggu status: $ref: "#/components/schemas/ConversationStatus" priority: $ref: "#/components/schemas/ConversationPriority" channel: type: string minLength: 1 example: whatsapp assignee: $ref: "#/components/schemas/Assignee" team: type: string description: External team or routing hint. example: canggu-rentals tags: type: array items: type: string example: - urgent - villa externalObject: $ref: "#/components/schemas/ExternalObject" metadata: type: object additionalProperties: true example: source: triage AssignConversationRequest: type: object description: > Requires assignee or team. Send an assignee with an empty id, or an empty team string, to clear that field. minProperties: 1 properties: assignee: $ref: "#/components/schemas/Assignee" team: type: string description: External team or routing hint. example: canggu-rentals ResolveConversationRequest: type: object properties: status: type: string description: > Workspace-defined completed-type status key. Defaults to the workspace's default completed status (`resolved` for seeded workspaces). Active-type keys are rejected with `conversations.invalid_resolution_status`. example: resolved Conversation: type: object description: > Every key is always present; optional fields are null when unset. required: - id - externalId - subject - status - priority - channel - customerId - assignee - team - tags - externalObject - metadata - channelIdentityId - createdAt - updatedAt properties: id: type: string example: conv_x6q5vl75f5d53m7oet2k4r5w6a externalId: type: [string, "null"] example: inquiry_123 subject: type: string example: Inquiry for 2BR Villa in Canggu status: $ref: "#/components/schemas/ConversationStatus" priority: $ref: "#/components/schemas/ConversationPriority" channel: type: string example: whatsapp customerId: type: string example: cus_x6q5vl75f5d53m7oet2k4r5w6a assignee: oneOf: - $ref: "#/components/schemas/Assignee" - type: "null" description: Null when the conversation is unassigned. team: type: [string, "null"] example: canggu-rentals tags: type: array items: type: string externalObject: oneOf: - $ref: "#/components/schemas/ExternalObject" - type: "null" description: Null when no external object is attached. metadata: type: object additionalProperties: true channelIdentityId: type: [string, "null"] description: The channel identity that opened this conversation, if any. example: cid_x6q5vl75f5d53m7oet2k4r5w6a routingNotes: type: object additionalProperties: true nullable: true description: Routing hints and resolution reasons recorded at create time (debugging). createdAt: type: string format: date-time updatedAt: type: string format: date-time ListConversationsResponse: type: object required: - data - hasMore properties: data: type: array items: $ref: "#/components/schemas/Conversation" hasMore: $ref: "#/components/schemas/HasMore" nextCursor: $ref: "#/components/schemas/NextCursor" MessageSender: type: string enum: - customer - agent - ai - system Attachment: type: object description: > A file on a message, referenced by URL (and/or your own id). Every key is always present; unset fields are null. Provide at least id or url. required: - id - url - filename - contentType - sizeBytes - metadata properties: id: type: [string, "null"] description: Your external attachment id, if any. example: file_123 url: type: [string, "null"] format: uri example: https://example.com/files/inquiry.png filename: type: [string, "null"] example: inquiry.png contentType: type: [string, "null"] example: image/png sizeBytes: type: integer format: int64 minimum: 0 example: 12345 metadata: type: object additionalProperties: true example: kind: screenshot WebhookStatus: type: string enum: - active - disabled Assignment: type: object required: - id - conversationId - assignee - role - status - offeredAt - createdAt - updatedAt properties: id: type: string example: asg_x6q5vl75f5d53m7oet2k4r5w6a conversationId: type: string assignee: $ref: "#/components/schemas/Assignee" role: type: string example: owner status: type: string enum: - offered - accepted - declined - active - revoked - completed - expired offeredAt: type: string format: date-time respondedAt: type: string format: date-time nullable: true resolvedAt: type: string format: date-time nullable: true expiresAt: type: string format: date-time nullable: true metadata: type: object additionalProperties: true createdAt: type: string format: date-time updatedAt: type: string format: date-time ListAssignmentsResponse: type: object required: - data properties: data: type: array items: $ref: "#/components/schemas/Assignment" WebhookEvent: type: string enum: - customer.created - customer.updated - conversation.created - conversation.updated - conversation.assigned - conversation.resolved - conversation.read - message.created - message.delivered - message.failed WebhookTestResult: type: object required: - eventId - delivered properties: eventId: type: string description: Synthetic test event id included in the delivery headers. example: evt_x6q5vl75f5d53m7oet2k4r5w6a delivered: type: boolean responseStatus: type: [integer, "null"] description: HTTP status returned by your endpoint. Null when the request never completed. example: 200 responseSnippet: type: [string, "null"] description: First 512 bytes of the response body or failure reason. WebhookDelivery: type: object description: One delivery attempt series for one event and subscription. required: - id - eventId - eventType - attempt - status - createdAt - completedAt properties: id: type: string example: whd_x6q5vl75f5d53m7oet2k4r5w6a eventId: type: string example: evt_x6q5vl75f5d53m7oet2k4r5w6a eventType: $ref: "#/components/schemas/WebhookEvent" attempt: type: integer description: Attempts made so far (max 6, with 30s/2m/10m/1h/6h backoff). status: type: string enum: - pending - sending - delivered - failed responseStatus: type: [integer, "null"] description: Last HTTP status from your endpoint. Null when the request never completed. responseSnippet: type: [string, "null"] description: First 512 bytes of the last response body or failure reason. createdAt: type: string format: date-time completedAt: oneOf: - type: string format: date-time - type: "null" description: Null while retries are still scheduled. ListWebhookDeliveriesResponse: type: object required: - data - hasMore properties: data: type: array items: $ref: "#/components/schemas/WebhookDelivery" hasMore: $ref: "#/components/schemas/HasMore" nextCursor: $ref: "#/components/schemas/NextCursor" CreateWebhookRequest: type: object required: - url - events properties: url: type: string format: uri description: HTTPS endpoint that will receive signed webhook deliveries. example: https://example.com/protodesk/webhooks events: type: array minItems: 1 items: $ref: "#/components/schemas/WebhookEvent" example: - message.created - conversation.resolved status: $ref: "#/components/schemas/WebhookStatus" default: active metadata: type: object additionalProperties: true example: source: owner_app UpdateWebhookRequest: type: object minProperties: 1 properties: url: type: string format: uri description: HTTPS endpoint that will receive signed webhook deliveries. example: https://example.com/protodesk/webhooks events: type: array minItems: 1 items: $ref: "#/components/schemas/WebhookEvent" status: $ref: "#/components/schemas/WebhookStatus" metadata: type: object additionalProperties: true description: Replaces the full webhook metadata object. example: source: owner_app WebhookSubscription: type: object required: - id - url - events - status - metadata - createdAt - updatedAt properties: id: type: string example: wh_x6q5vl75f5d53m7oet2k4r5w6a url: type: string format: uri example: https://example.com/protodesk/webhooks events: type: array items: $ref: "#/components/schemas/WebhookEvent" status: $ref: "#/components/schemas/WebhookStatus" secret: type: [string, "null"] description: Signing secret; returned only when the webhook is created, null on reads. example: whsec_pz6kyr2ftwsn6c6q3a4oky2d35txmgaj3t35u2quy4ielzqro3za metadata: type: object additionalProperties: true createdAt: type: string format: date-time updatedAt: type: string format: date-time ListWebhooksResponse: type: object required: - data - hasMore properties: data: type: array items: $ref: "#/components/schemas/WebhookSubscription" hasMore: $ref: "#/components/schemas/HasMore" nextCursor: $ref: "#/components/schemas/NextCursor" MessageAttachmentInput: type: object additionalProperties: false properties: id: {type: string} url: {type: string} filename: {type: string} contentType: {type: string} sizeBytes: {type: integer, minimum: 0} metadata: type: object additionalProperties: true SendMessageRequest: type: object additionalProperties: false properties: body: type: string description: Reply text; required unless attachments are provided. internal: type: boolean default: false description: Store an internal note without customer delivery. sendAt: type: string format: date-time description: Optional future email delivery time, at most 30 days ahead. attachments: type: array items: $ref: "#/components/schemas/MessageAttachmentInput" description: Durable /api/media references owned by this workspace; arbitrary URLs are not accepted for sending. SendMessageResponse: type: object required: [message, queued, undoMsec] properties: message: $ref: "#/components/schemas/Message" queued: type: boolean description: True only when provider delivery has been durably queued. undoMsec: type: integer description: Normal undo window in milliseconds; zero for scheduled or unqueued replies. scheduledFor: type: string format: date-time CreateMessageRequest: type: object description: Requires body or at least one attachment. properties: externalId: type: string description: Your stable id for this message. example: msg_123 sender: $ref: "#/components/schemas/MessageSender" default: customer body: type: string example: Hi, is this villa available for yearly rental? channel: type: string default: api example: whatsapp attachments: type: array items: $ref: "#/components/schemas/MessageAttachmentInput" metadata: type: object additionalProperties: true example: source: owner_app internal: type: boolean default: false description: True for internal notes that should not be sent to the customer. Message: type: object description: > Every key is always present; optional fields are null when unset. required: - id - externalId - conversationId - sender - body - channel - attachments - metadata - internal - createdAt properties: id: type: string example: msg_x6q5vl75f5d53m7oet2k4r5w6a externalId: type: [string, "null"] example: msg_123 conversationId: type: string example: conv_x6q5vl75f5d53m7oet2k4r5w6a sender: $ref: "#/components/schemas/MessageSender" body: type: string example: Hi, is this villa available for yearly rental? channel: type: string example: whatsapp attachments: type: array items: $ref: "#/components/schemas/Attachment" metadata: type: object additionalProperties: true internal: type: boolean createdAt: type: string format: date-time ListMessagesResponse: type: object required: - data - hasMore properties: data: type: array items: $ref: "#/components/schemas/Message" hasMore: $ref: "#/components/schemas/HasMore" nextCursor: $ref: "#/components/schemas/NextCursor" ListCustomersResponse: type: object required: - data - hasMore properties: data: type: array items: $ref: "#/components/schemas/Customer" hasMore: $ref: "#/components/schemas/HasMore" nextCursor: $ref: "#/components/schemas/NextCursor" HasMore: type: boolean description: True when more rows exist beyond this page. NextCursor: type: [string, "null"] description: >- Opaque cursor for the next page. Pass it as the cursor query param. Null when hasMore is false. ErrorEnvelope: type: object required: - error properties: error: $ref: "#/components/schemas/Error" Error: type: object required: - code - message - requestId properties: code: type: string example: route.not_found message: type: string example: Not found requestId: type: string example: req_x6q5vl75f5d53m7oet2k4r5w6a