{"openapi":"3.0.0","paths":{"/v1/org/{org_id}/server":{"get":{"description":"`server.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nThe organization's machines. Each `id` is a `server_id` for creating an agent; pick one whose `status` is ONLINE.","operationId":"listServers","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"page","required":true,"in":"query","schema":{"type":"number"}},{"name":"limit","required":true,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListServerDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List servers","tags":["🖥️ Infrastructure / Servers"]},"post":{"description":"`server.create` — `OWNER` `ADMIN`\n\nReturns the provisioning token only once.","operationId":"createServer","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateServerDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetServerDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Create server","tags":["🖥️ Infrastructure / Servers"]}},"/v1/org/{org_id}/server/{id}/regenerate-provisioning-token":{"post":{"description":"`server.edit` — `OWNER` `ADMIN`\n\nInvalidates the previous one.","operationId":"regenerateServerProvisioningToken","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetServerDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Regenerate provisioning token","tags":["🖥️ Infrastructure / Servers"]}},"/v1/org/{org_id}/approval":{"get":{"description":"`approval.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nApprovals the caller may see, most recent first. Among direct chats, a member only sees their own.","operationId":"listApprovals","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"page","required":true,"in":"query","schema":{"type":"number"}},{"name":"limit","required":true,"in":"query","schema":{"type":"number"}},{"name":"only_pending","required":true,"in":"query","schema":{"type":"boolean"}},{"name":"agent_id","required":false,"in":"query","schema":{"type":"number"}},{"name":"from","required":false,"in":"query","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListApprovalDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List approvals","tags":["💬 Communication / Approvals"]}},"/v1/org/{org_id}/approval/audience":{"get":{"description":"`approval.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nFor each pending approval in `ids` (comma-separated, up to 100) that the caller can see: the members who can see it too, whether each may decide it and whether the notice reached them.","operationId":"listApprovalAudience","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"ids","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListApprovalAudienceDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Who an approval concerns","tags":["💬 Communication / Approvals"]}},"/v1/org/{org_id}/approval/{id}/resolve":{"patch":{"description":"`approval.resolve` — `OWNER` `ADMIN` `OPERATOR`\n\nMarks the approval as RESOLVED. The underlying issue (e.g. the OAuth token) must be fixed first — this only changes the status.","operationId":"resolveApproval","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveApprovalDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetApprovalDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Resolve approval","tags":["💬 Communication / Approvals"]}},"/v1/org/{org_id}/approval/{id}/answer":{"patch":{"description":"`approval.resolve` — `OWNER` `ADMIN` `OPERATOR`\n\nAnswers a question (#308) by picking options and/or free text. The agent receives the answer as a turn in the thread it asked from.","operationId":"answerQuestion","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnswerQuestionDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetApprovalDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Answer an agent question","tags":["💬 Communication / Approvals"]}},"/v1/org/{org_id}/approval/{id}/dismiss":{"patch":{"description":"`approval.resolve` — `OWNER` `ADMIN` `OPERATOR`\n\nMarks the approval as DISMISSED without acting on it. Use it when the request no longer applies (e.g. agent deleted).","operationId":"dismissApproval","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetApprovalDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Dismiss approval","tags":["💬 Communication / Approvals"]}},"/v1/org/{org_id}/agent/{agent_id}/conversation":{"get":{"description":"`agent.output.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`","operationId":"listConversations","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"agent_id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GetConversationWithNotifyDto"}}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List agent conversations","tags":["💬 Communication / Conversations"]}},"/v1/org/{org_id}/agent/{agent_id}/conversation/{conversation_id}/message":{"get":{"description":"`agent.output.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nCursor-based pagination, scrolls upward.","operationId":"listMessages","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"conversation_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"before_seq","required":false,"in":"query","schema":{"type":"string"}},{"name":"after_seq","required":false,"in":"query","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"tool_detail","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListMessageDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Paginated messages of a conversation","tags":["💬 Communication / Conversations"]}},"/v1/org/{org_id}/agent/{agent_id}/conversation/{conversation_id}/message/search":{"get":{"description":"`agent.output.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nSearch across a conversation's full history — returns the seq of matching messages.","operationId":"searchConversation","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"conversation_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"q","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Search the entire history","tags":["💬 Communication / Conversations"]}},"/v1/org/{org_id}/conversation":{"get":{"description":"`conversation.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nAll conversations of the organization — unified, filtered, and paginated view.","operationId":"listOrgConversations","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":false,"in":"query","description":"Exact conversation id (deep-link).","schema":{"minimum":1,"type":"number"}},{"name":"agent_id","required":false,"in":"query","description":"Filter by the owning agent id.","schema":{"minimum":1,"type":"number"}},{"name":"contact_id","required":false,"in":"query","description":"Filter by contact id.","schema":{"minimum":1,"type":"number"}},{"name":"channel_id","required":false,"in":"query","description":"Filter by a specific channel id.","schema":{"minimum":1,"type":"number"}},{"name":"transport","required":false,"in":"query","description":"Filter by channel transport (TELEGRAM, GMAIL, DISCORD…).","schema":{"minLength":1,"maxLength":32,"type":"string"}},{"name":"status","required":false,"in":"query","description":"Filter by conversation status (OPEN, PENDING, RESOLVED, ARCHIVED).","schema":{"type":"string","enum":["OPEN","PENDING","RESOLVED","ARCHIVED"]}},{"name":"model","required":false,"in":"query","description":"Filter by the model the next turn runs on, or the sentinel \"inherited\" for conversations with no model override (still inheriting the agent default), or \"pinned\" for the ones that set their own model or connection.","schema":{"minLength":1,"maxLength":120,"type":"string"}},{"name":"ref","required":false,"in":"query","description":"Opaque conversation ref (case-insensitive).","schema":{"minLength":1,"maxLength":40,"type":"string"}},{"name":"search","required":false,"in":"query","description":"Free-text search over the contact name + channel name.","schema":{"minLength":1,"maxLength":200,"type":"string"}},{"name":"sort","required":false,"in":"query","description":"Sort column: activity (last message), created (opened), contact (name), agent (name), channel (transport), model, status. Default: activity.","schema":{"type":"string","enum":["activity","created","contact","agent","channel","model","status"]}},{"name":"order","required":false,"in":"query","description":"Sort direction. Default: desc.","schema":{"type":"string","enum":["asc","desc"]}},{"name":"limit","required":false,"in":"query","description":"Page size (1–200, default 50).","schema":{"minimum":1,"maximum":200,"type":"number"}},{"name":"offset","required":false,"in":"query","description":"Rows to skip (default 0).","schema":{"minimum":0,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationListPageDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List all conversations","tags":["💬 Communication / Conversations"]}},"/v1/org/{org_id}/agent":{"get":{"description":"`agent.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`","operationId":"listAgents","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"page","required":true,"in":"query","schema":{"type":"number"}},{"name":"limit","required":true,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListAgentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List agents","tags":["🤖 Agents / Agents"]},"post":{"description":"`agent.create` — `OWNER` `ADMIN` `OPERATOR`\n\nCreated with STOPPED status until it's launched through the WS gateway.","operationId":"createAgent","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAgentDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAgentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Create agent","tags":["🤖 Agents / Agents"]}},"/v1/org/{org_id}/agent/{id}":{"get":{"description":"`agent.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`","operationId":"getAgent","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAgentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Agent detail","tags":["🤖 Agents / Agents"]},"delete":{"description":"`agent.delete` — `OWNER` `ADMIN`\n\nOnly while it is STOPPED or in ERROR.","operationId":"deleteAgent","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Delete agent","tags":["🤖 Agents / Agents"]},"patch":{"description":"`agent.edit` — `OWNER` `ADMIN` `OPERATOR`","operationId":"updateAgent","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAgentDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAgentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Edit agent metadata","tags":["🤖 Agents / Agents"]}},"/v1/org/{org_id}/agent/{id}/start":{"post":{"description":"`agent.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nLaunches the agent's container on its server (returns instantly as SPAWNING).","operationId":"startAgent","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAgentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Start agent","tags":["🤖 Agents / Agents"]}},"/v1/org/{org_id}/agent/{id}/stop":{"post":{"description":"`agent.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nStops the agent's container (returns instantly as STOPPING).","operationId":"stopAgent","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAgentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Stop agent","tags":["🤖 Agents / Agents"]}},"/v1/org/{org_id}/agent/{id}/pause":{"post":{"description":"`agent.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nReturns instantly as PAUSING.","operationId":"pauseAgent","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAgentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Pause agent","tags":["🤖 Agents / Agents"]}},"/v1/org/{org_id}/agent/{id}/resume":{"post":{"description":"`agent.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nResumes the agent from PAUSED (returns instantly as RESUMING). Requires a confirmed email.","operationId":"resumeAgent","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAgentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Resume paused agent","tags":["🤖 Agents / Agents"]}},"/v1/org/{org_id}/agent/{id}/reset":{"post":{"description":"`agent.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nMoves an agent in ERROR back to STOPPED (no-op on healthy agents).","operationId":"resetAgent","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAgentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Clear agent error state","tags":["🤖 Agents / Agents"]}},"/v1/agent-image":{"get":{"description":"Only the available ones, unless the rest are explicitly requested. Global catalogue, no organization in the path, and needs no scope, so a service token can read it too. Each `id` is an `image_id` for creating an agent.","operationId":"listAgentImages","parameters":[{"name":"page","required":true,"in":"query","schema":{"type":"number"}},{"name":"limit","required":true,"in":"query","schema":{"type":"number"}},{"name":"role_key","required":false,"in":"query","schema":{"type":"string"}},{"name":"include_unavailable","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListAgentImageDto"}}}}},"security":[{"JWT":[]}],"summary":"🔒 List agent images","tags":["🖥️ Infrastructure / Agent images"]}},"/v1/org":{"get":{"description":"With a service token the list holds exactly one: the organization the token belongs to. Its `id` is the `org_id` every `/org/{org_id}/…` route needs — read it here instead of asking for it.","operationId":"listOrganizations","parameters":[{"name":"page","required":true,"in":"query","schema":{"type":"number"}},{"name":"limit","required":true,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListOrganizationDto"}}}}},"security":[{"JWT":[]}],"summary":"🔒 List my organizations","tags":["👤 Account & org / Organizations"]}},"/v1/org/{org_id}":{"get":{"description":"`organization.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nYou must be a member.","operationId":"getOrganization","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetOrganizationDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Organization detail","tags":["👤 Account & org / Organizations"]}},"/v1/org/{org_id}/agent/{agent_id}/connector/{connector_id}":{"post":{"description":"`connector.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nAssigns ONE connector to this agent without replacing the others (incremental, idempotent).","operationId":"addAgentConnector","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"agent_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"connector_id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Assign connector to agent","tags":["🔌 Resources / Connectors (agent shortcut)"]},"delete":{"description":"`connector.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nUnassigns ONE connector from this agent leaving the rest untouched (incremental, idempotent).","operationId":"removeAgentConnector","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"agent_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"connector_id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Unassign connector from agent","tags":["🔌 Resources / Connectors (agent shortcut)"]}},"/v1/org/{org_id}/memory":{"get":{"description":"`memory.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nOptionally filtered by scope / agent / orphaned.","operationId":"listMemories","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"scope","required":false,"in":"query","description":"Restrict to one scope","schema":{"type":"string","enum":["SHORT_TERM","LONG_TERM"]}},{"name":"scopes","required":false,"in":"query","description":"Memory scopes to keep (SHORT_TERM/LONG_TERM). Repeat or CSV. Wins over the legacy single-scope `scope` if both are sent.","schema":{"type":"array","items":{"type":"string","enum":["SHORT_TERM","LONG_TERM"]}}},{"name":"agent_id","required":false,"in":"query","description":"Restrict to memories assigned to this agent (handy for the per-agent view).","schema":{"minimum":1,"type":"number"}},{"name":"unassigned","required":false,"in":"query","description":"When true, only return memories with zero assignments (orphans). Default false.","schema":{"type":"boolean"}},{"name":"archived","required":false,"in":"query","description":"When true, return ONLY archived (soft-deleted) memories. Default false → live memories only. The two sets never mix; the trash view in the operator UI uses this flag.","schema":{"type":"boolean"}},{"name":"search","required":false,"in":"query","description":"Free-text search across name + description (case-insensitive substring).","schema":{"type":"string"}},{"name":"sort_by","required":false,"in":"query","description":"Sort column.","schema":{"type":"string","enum":["updated_at","name","created_at"]}},{"name":"sort_dir","required":false,"in":"query","description":"Sort direction.","schema":{"type":"string","enum":["asc","desc"]}},{"name":"limit","required":false,"in":"query","description":"Page size (1-200). Default 200 (folder UI loads all).","schema":{"minimum":1,"maximum":200,"type":"number"}},{"name":"offset","required":false,"in":"query","description":"Offset (>= 0). Default 0.","schema":{"minimum":0,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListMemoryDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List the organization's memories","tags":["🧩 Resources / Memories"]},"post":{"description":"`memory.edit` — `OWNER` `ADMIN` `OPERATOR`","operationId":"createMemory","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMemoryDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMemoryWithContentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Create memory","tags":["🧩 Resources / Memories"]}},"/v1/org/{org_id}/memory/{id}":{"get":{"description":"`memory.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nWith content and assignments.","operationId":"getMemory","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMemoryWithContentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Memory detail","tags":["🧩 Resources / Memories"]},"delete":{"description":"`memory.delete` — `OWNER` `ADMIN` `OPERATOR`\n\nMarks the assigned agents to sync.","operationId":"deleteMemory","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Delete memory","tags":["🧩 Resources / Memories"]},"patch":{"description":"`memory.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nMarks the assigned agents to sync.","operationId":"updateMemory","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMemoryDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMemoryWithContentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Edit memory","tags":["🧩 Resources / Memories"]}},"/v1/org/{org_id}/memory/{id}/lock":{"get":{"description":"`memory.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nWhether the memory is read-only and which members and agents may still change it (OWNER/ADMIN always may and are not listed).","operationId":"getMemoryLock","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemoryLockDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Read-only flag and editors of a memory","tags":["🧩 Resources / Memories"]},"put":{"description":"`memory.grant` — `OWNER` `ADMIN`\n\nReplaces the read-only flag and both editor lists. Needs memory.grant and managing every agent that has the memory; editor agents must be ones you manage.","operationId":"setMemoryLock","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemoryLockDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemoryLockDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Mark a memory read-only and choose its editors","tags":["🧩 Resources / Memories"]}},"/v1/org/{org_id}/memory/{id}/agents":{"put":{"description":"`memory.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nSets the full list (diff of add+remove). Returns the memory with refreshed assignments.","operationId":"setMemoryAgents","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetMemoryAgentsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMemoryWithContentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Set the memory's agents","tags":["🧩 Resources / Memories"]}},"/v1/org/{org_id}/memory/{id}/archive":{"post":{"description":"`memory.delete` — `OWNER` `ADMIN` `OPERATOR`\n\nSoft delete: it disappears from listings and spawn payloads, but the assignment rows survive so unarchive can cleanly restore ownership.","operationId":"archiveMemory","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMemoryWithContentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Archive memory","tags":["🧩 Resources / Memories"]}},"/v1/org/{org_id}/memory/{id}/unarchive":{"post":{"description":"`memory.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nAssigned agents regain access instantly.","operationId":"unarchiveMemory","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMemoryWithContentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Restore memory","tags":["🧩 Resources / Memories"]}},"/v1/org/{org_id}/agent/{agent_id}/channel":{"get":{"description":"`agent.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`","operationId":"listChannels","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"agent_id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GetChannelDto"}}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List the agent's external channels","tags":["💬 Communication / Channels"]}},"/v1/org/{org_id}/channel":{"get":{"description":"`agent.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nAll of the organization's external channels; optionally filtered by agent.","operationId":"listOrgChannels","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"agent_id","required":false,"in":"query","description":"Restrict to channels of this agent (handy for the per-agent view).","schema":{"minimum":1,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GetChannelDto"}}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List external channels","tags":["💬 Communication / Channels"]},"post":{"description":"`agent.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nConnects an external channel (e.g. a Telegram bot) to an agent. Requires a confirmed email.","operationId":"createOrgChannel","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateChannelGlobalDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetChannelDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Connect external channel to an agent","tags":["💬 Communication / Channels"]}},"/v1/org/{org_id}/channel/{id}":{"get":{"description":"`agent.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`","operationId":"getOrgChannel","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetChannelDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Channel detail","tags":["💬 Communication / Channels"]},"delete":{"description":"`agent.edit` — `OWNER` `ADMIN` `OPERATOR`","operationId":"deleteOrgChannel","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Disconnect channel","tags":["💬 Communication / Channels"]},"patch":{"description":"`agent.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nName, scope, token. Requires a confirmed email.","operationId":"updateOrgChannel","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateChannelDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetChannelDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Edit channel","tags":["💬 Communication / Channels"]}},"/v1/org/{org_id}/channel/{id}/whitelist":{"post":{"description":"`agent.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nAdds an origin id to the channel's inbound whitelist.","operationId":"addOrgChannelWhitelist","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddToWhitelistDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetChannelDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Add origin to the whitelist","tags":["💬 Communication / Channels"]}},"/v1/org/{org_id}/channel/{id}/whitelist/{origin_id}":{"delete":{"description":"`agent.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nRemoves an origin id from the channel's inbound whitelist.","operationId":"removeOrgChannelWhitelist","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}},{"name":"origin_id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetChannelDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Remove origin from the whitelist","tags":["💬 Communication / Channels"]}},"/v1/transport":{"get":{"description":"","operationId":"listSupportedTransports","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"type":"object"}}}}}},"security":[{"JWT":[]}],"summary":"🔒 Traffic direction of each transport","tags":["💬 Communication / Channels"]}},"/v1/org/{org_id}/activity":{"get":{"description":"`activity.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nWhat every agent the caller may see has on its plate: open activities, plus the recent finished ones with `status=all`. Details follow access to the conversation each one lives in; without it, `visible` is false and they come blank.","operationId":"listOrgActivities","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"status","required":false,"in":"query","description":"`open` (default): what is on the plate now. `all`: plus the most recent finished ones.","schema":{"example":"open","type":"string","enum":["open","all"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GetActivityDto"}}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List activities","tags":["🧩 Resources / Activities"]}},"/v1/org/{org_id}/agent/{agent_id}/activity":{"get":{"description":"`activity.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nWhat this agent has on its plate: open activities, plus the recent finished ones with `status=all`. 404 when the caller may not see the agent.","operationId":"listAgentActivities","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"agent_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"status","required":false,"in":"query","description":"`open` (default): what is on the plate now. `all`: plus the most recent finished ones.","schema":{"example":"open","type":"string","enum":["open","all"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GetActivityDto"}}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List an agent's activities","tags":["🧩 Resources / Activities"]}},"/v1/org/{org_id}/host":{"get":{"description":"`host.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nMachines paired for consent-based remote support (kj-host). OWNER/ADMIN see every device; any other member only the ones they are assigned to. `tag_id` keeps only the devices carrying that tag.","operationId":"listHosts","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"page","required":true,"in":"query","schema":{"type":"number"}},{"name":"limit","required":true,"in":"query","schema":{"type":"number"}},{"name":"tag_id","required":false,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListHostDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List hosts","tags":["🖥️ Infrastructure / Hosts"]},"post":{"description":"`host.create` — `OWNER` `ADMIN`\n\nReturns the provisioning token only once.","operationId":"createHost","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateHostDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetHostDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Create host","tags":["🖥️ Infrastructure / Hosts"]}},"/v1/org/{org_id}/host/{id}/regenerate-provisioning-token":{"post":{"description":"`host.edit` — `OWNER` `ADMIN`\n\nInvalidates the previous one.","operationId":"regenerateHostProvisioningToken","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetHostDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Regenerate provisioning token","tags":["🖥️ Infrastructure / Hosts"]}},"/v1/org/{org_id}/credential":{"get":{"description":"`credential.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nOptionally filtered by agent / orphaned / archived. Field VALUES are never returned.","operationId":"listCredentials","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"access","required":false,"in":"query","description":"team = open to the team (team_can_use); private = only its access list. Omit for any.","schema":{"type":"string","enum":["team","private"]}},{"name":"agent_id","required":false,"in":"query","description":"Restrict to credentials assigned to this agent (per-agent view).","schema":{"minimum":1,"type":"number"}},{"name":"unassigned","required":false,"in":"query","description":"When true, only return credentials with zero assignments. Default false.","schema":{"type":"boolean"}},{"name":"created_by_agent","required":false,"in":"query","description":"When true, only return credentials an agent saved itself. Default false.","schema":{"type":"boolean"}},{"name":"archived","required":false,"in":"query","description":"When true, return ONLY archived (soft-deleted) credentials. Default false → live only.","schema":{"type":"boolean"}},{"name":"search","required":false,"in":"query","description":"Free-text search across name + description (case-insensitive substring).","schema":{"type":"string"}},{"name":"kind","required":false,"in":"query","description":"Free-form kind tag(s) to keep (e.g. \"login\", \"api-key\", \"ssh-key\"). Repeat the param or pass a CSV. Omit for any.","schema":{"type":"array","items":{"type":"string"}}},{"name":"sort_by","required":false,"in":"query","description":"Sort column.","schema":{"type":"string","enum":["updated_at","name","created_at","last_used_at"]}},{"name":"sort_dir","required":false,"in":"query","description":"Sort direction.","schema":{"type":"string","enum":["asc","desc"]}},{"name":"limit","required":false,"in":"query","description":"Page size (1-200). Default 50.","schema":{"minimum":1,"maximum":200,"type":"number"}},{"name":"offset","required":false,"in":"query","description":"Offset (>= 0). Default 0.","schema":{"minimum":0,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListCredentialDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List credentials","tags":["🧩 Resources / Credentials"]},"post":{"description":"`credential.edit` — `OWNER` `ADMIN` `OPERATOR`","operationId":"createCredential","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCredentialDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetCredentialWithFieldsDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Create credential","tags":["🧩 Resources / Credentials"]}},"/v1/org/{org_id}/credential/{id}":{"get":{"description":"`credential.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nField metadata (label + type, WITHOUT values) and assignments.","operationId":"getCredential","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetCredentialWithFieldsDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Credential details","tags":["🧩 Resources / Credentials"]},"delete":{"description":"`credential.delete` — `OWNER` `ADMIN` `OPERATOR`","operationId":"deleteCredential","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}},{"name":"pause_crons","required":false,"in":"query","description":"Pause the live scheduled tasks that inject this credential instead of refusing","schema":{"type":"boolean"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Delete credential","tags":["🧩 Resources / Credentials"]},"patch":{"description":"`credential.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nName / fields / type.","operationId":"updateCredential","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCredentialDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetCredentialWithFieldsDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Edit credential","tags":["🧩 Resources / Credentials"]}},"/v1/org/{org_id}/credential/{id}/field/{field_id}/reveal":{"get":{"description":"`credential.reveal` — `OWNER` `ADMIN` `OPERATOR`\n\nDecrypts and returns the value of ONE field (for the operator's view/copy action). Gated on credential.reveal (separate from edit) — and, if set, the credential's operator ACL.","operationId":"revealCredentialField","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}},{"name":"field_id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevealCredentialFieldDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Reveal a field value","tags":["🧩 Resources / Credentials"]}},"/v1/org/{org_id}/credential/{id}/agents":{"put":{"description":"`credential.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nSets the full list (add+remove diff).","operationId":"setCredentialAgents","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}},{"name":"pause_crons","required":false,"in":"query","description":"Pause the live scheduled tasks that inject this credential instead of refusing","schema":{"type":"boolean"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetCredentialAgentsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetCredentialWithFieldsDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Set credential agents","tags":["🧩 Resources / Credentials"]}},"/v1/org/{org_id}/credential/{id}/archive":{"post":{"description":"`credential.delete` — `OWNER` `ADMIN` `OPERATOR`\n\nSoft delete: disappears from listings by default; assignment rows survive so unarchive can restore membership.","operationId":"archiveCredential","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}},{"name":"pause_crons","required":false,"in":"query","description":"Pause the live scheduled tasks that inject this credential instead of refusing","schema":{"type":"boolean"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetCredentialWithFieldsDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Archive credential","tags":["🧩 Resources / Credentials"]}},"/v1/org/{org_id}/credential/{id}/unarchive":{"post":{"description":"`credential.edit` — `OWNER` `ADMIN` `OPERATOR`","operationId":"unarchiveCredential","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetCredentialWithFieldsDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Restore credential","tags":["🧩 Resources / Credentials"]}},"/v1/org/{org_id}/agent/{agent_id}/credential/{credential_id}":{"post":{"description":"`credential.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nAssigns ONE credential to this agent without replacing its other assignments (incremental, idempotent). Safe for wiring up service tokens — no read-modify-write of the full set.","operationId":"addAgentCredential","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"agent_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"credential_id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Assign credential to agent","tags":["🧩 Resources / Credentials (agent shortcut)"]},"delete":{"description":"`credential.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nUnassigns ONE credential from this agent, leaving the rest intact (incremental, idempotent).","operationId":"removeAgentCredential","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"agent_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"credential_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"pause_crons","required":false,"in":"query","description":"Pause the live scheduled tasks that inject this credential instead of refusing","schema":{"type":"boolean"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Unassign credential from agent","tags":["🧩 Resources / Credentials (agent shortcut)"]}},"/v1/org/{org_id}/skill":{"get":{"description":"`skill.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nOptionally filtered by agent / orphaned / archived.","operationId":"listSkills","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"agent_id","required":false,"in":"query","description":"Restrict to skills assigned to this agent (handy for the per-agent view).","schema":{"minimum":1,"type":"number"}},{"name":"unassigned","required":false,"in":"query","description":"When true, only return skills with zero assignments. Default false.","schema":{"type":"boolean"}},{"name":"archived","required":false,"in":"query","description":"When true, return ONLY archived (soft-deleted) skills. Default false → live skills only.","schema":{"type":"boolean"}},{"name":"search","required":false,"in":"query","description":"Free-text search across name + description (case-insensitive substring).","schema":{"type":"string"}},{"name":"effort","required":false,"in":"query","description":"Per-skill effort overrides to keep (LOW/MEDIUM/HIGH/XHIGH/MAX). Repeat or CSV.","schema":{"type":"array","items":{"type":"string","enum":["LOW","MEDIUM","HIGH","XHIGH","MAX"]}}},{"name":"sort_by","required":false,"in":"query","description":"Sort column.","schema":{"type":"string","enum":["updated_at","name","created_at"]}},{"name":"sort_dir","required":false,"in":"query","description":"Sort direction.","schema":{"type":"string","enum":["asc","desc"]}},{"name":"limit","required":false,"in":"query","description":"Page size (1-200). Default 50.","schema":{"minimum":1,"maximum":200,"type":"number"}},{"name":"offset","required":false,"in":"query","description":"Offset (>= 0). Default 0.","schema":{"minimum":0,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListSkillDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List the organization's skills","tags":["🧩 Resources / Skills"]},"post":{"description":"`skill.edit` — `OWNER` `ADMIN` `OPERATOR`","operationId":"createSkill","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSkillDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSkillWithContentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Create skill","tags":["🧩 Resources / Skills"]}},"/v1/org/{org_id}/skill/{id}/entry":{"get":{"description":"`skill.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nThe files that travel next to SKILL.md. Without content.","operationId":"listSkillEntries","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GetSkillEntryDto"}}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Skill files","tags":["🧩 Resources / Skills"]},"put":{"description":"`skill.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nCreates or replaces it, and re-seeds the agents that have the skill.","operationId":"putSkillEntry","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutSkillEntryDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSkillEntryContentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Write skill file","tags":["🧩 Resources / Skills"]},"delete":{"description":"`skill.edit` — `OWNER` `ADMIN` `OPERATOR`","operationId":"deleteSkillEntry","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}},{"name":"path","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Delete skill file","tags":["🧩 Resources / Skills"]}},"/v1/org/{org_id}/skill/{id}/entry/content":{"get":{"description":"`skill.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nOne file, with its content.","operationId":"getSkillEntry","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}},{"name":"path","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSkillEntryContentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Skill file","tags":["🧩 Resources / Skills"]}},"/v1/org/{org_id}/skill/{id}":{"get":{"description":"`skill.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nWith content and assignments.","operationId":"getSkill","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSkillWithContentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Skill detail","tags":["🧩 Resources / Skills"]},"delete":{"description":"`skill.delete` — `OWNER` `ADMIN` `OPERATOR`\n\nMarks the assigned agents to sync.","operationId":"deleteSkill","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Delete skill","tags":["🧩 Resources / Skills"]},"patch":{"description":"`skill.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nMarks the assigned agents to sync.","operationId":"updateSkill","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSkillDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSkillWithContentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Edit skill","tags":["🧩 Resources / Skills"]}},"/v1/org/{org_id}/skill/import":{"post":{"description":"`skill.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nBrings SKILL.md and its support files (#725 phase 3) as a new, unassigned skill — same guardrails as writing one by hand: the secret detector, size caps and versioning.","operationId":"importSkillFromGit","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportSkillFromGitDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportedSkillDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Import a skill from a github.com repo/folder","tags":["🧩 Resources / Skills"]}},"/v1/org/{org_id}/skill/{id}/lock":{"get":{"description":"`skill.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nWhether the skill is read-only and which members and agents may still change it (OWNER/ADMIN always may and are not listed).","operationId":"getSkillLock","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeLockDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Read-only flag and editors of a skill","tags":["🧩 Resources / Skills"]},"put":{"description":"`skill.grant` — `OWNER` `ADMIN`\n\nReplaces the read-only flag and both editor lists. Needs skill.grant and managing every agent that has the skill; editor agents must be ones you manage.","operationId":"setSkillLock","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeLockDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeLockDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Mark a skill read-only and choose its editors","tags":["🧩 Resources / Skills"]}},"/v1/org/{org_id}/skill/{id}/agents":{"put":{"description":"`skill.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nSets the full list (diff of add+remove). Returns the skill with refreshed assignments.","operationId":"setSkillAgents","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetSkillAgentsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSkillWithContentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Set the skill's agents","tags":["🧩 Resources / Skills"]}},"/v1/org/{org_id}/skill/{id}/archive":{"post":{"description":"`skill.delete` — `OWNER` `ADMIN` `OPERATOR`\n\nSoft delete: it disappears from listings and spawn payloads, but the assignment rows survive so unarchive can cleanly restore ownership.","operationId":"archiveSkill","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSkillWithContentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Archive skill","tags":["🧩 Resources / Skills"]}},"/v1/org/{org_id}/skill/{id}/unarchive":{"post":{"description":"`skill.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nAssigned agents regain access instantly.","operationId":"unarchiveSkill","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSkillWithContentDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Restore skill","tags":["🧩 Resources / Skills"]}},"/v1/org/{org_id}/mcp":{"get":{"description":"`mcp.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nOptionally filtered by agent / orphaned / archived.","operationId":"listMcpServers","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"agent_id","required":false,"in":"query","description":"Restrict to servers assigned to this agent (handy for the per-agent view).","schema":{"minimum":1,"type":"number"}},{"name":"unassigned","required":false,"in":"query","description":"When true, only return servers with zero assignments. Default false.","schema":{"type":"boolean"}},{"name":"archived","required":false,"in":"query","description":"When true, return ONLY archived (soft-deleted) servers. Default false → live only.","schema":{"type":"boolean"}},{"name":"search","required":false,"in":"query","description":"Free-text search across name + description (case-insensitive substring).","schema":{"type":"string"}},{"name":"transport","required":false,"in":"query","description":"Transport kind(s) to keep (STDIO / SSE / HTTP). Repeat the param or pass a CSV.","schema":{"type":"array","items":{"type":"string","enum":["STDIO","SSE","HTTP"]}}},{"name":"sort_by","required":false,"in":"query","description":"Sort column.","schema":{"type":"string","enum":["updated_at","name","created_at"]}},{"name":"sort_dir","required":false,"in":"query","description":"Sort direction.","schema":{"type":"string","enum":["asc","desc"]}},{"name":"limit","required":false,"in":"query","description":"Page size (1-200). Default 50.","schema":{"minimum":1,"maximum":200,"type":"number"}},{"name":"offset","required":false,"in":"query","description":"Offset (>= 0). Default 0.","schema":{"minimum":0,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListMcpDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List MCP servers","tags":["🧩 Resources / MCP servers"]},"post":{"description":"`mcp.edit` — `OWNER` `ADMIN` `OPERATOR`","operationId":"createMcpServer","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMcpDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMcpWithAssignmentsDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Register MCP server","tags":["🧩 Resources / MCP servers"]}},"/v1/org/{org_id}/mcp/{id}":{"get":{"description":"`mcp.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nIncludes its assignments.","operationId":"getMcpServer","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMcpWithAssignmentsDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 MCP server details","tags":["🧩 Resources / MCP servers"]},"delete":{"description":"`mcp.delete` — `OWNER` `ADMIN` `OPERATOR`\n\nMarks the assigned agents for sync.","operationId":"deleteMcpServer","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Delete MCP server","tags":["🧩 Resources / MCP servers"]},"patch":{"description":"`mcp.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nMarks the assigned agents for sync.","operationId":"updateMcpServer","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMcpDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMcpWithAssignmentsDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Edit MCP server","tags":["🧩 Resources / MCP servers"]}},"/v1/org/{org_id}/mcp/{id}/agents":{"put":{"description":"`mcp.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nSets the full list (add+remove diff). Returns the server with refreshed assignments.","operationId":"setMcpServerAgents","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetMcpAgentsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMcpWithAssignmentsDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Set MCP server agents","tags":["🧩 Resources / MCP servers"]}},"/v1/org/{org_id}/mcp/{id}/archive":{"post":{"description":"`mcp.delete` — `OWNER` `ADMIN` `OPERATOR`\n\nSoft delete: disappears from listings and spawn payloads, but assignment rows survive so unarchive can cleanly restore membership.","operationId":"archiveMcpServer","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMcpWithAssignmentsDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Archive MCP server","tags":["🧩 Resources / MCP servers"]}},"/v1/org/{org_id}/mcp/{id}/unarchive":{"post":{"description":"`mcp.edit` — `OWNER` `ADMIN` `OPERATOR`\n\nAssigned agents regain access instantly.","operationId":"unarchiveMcpServer","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMcpWithAssignmentsDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Restore MCP server","tags":["🧩 Resources / MCP servers"]}},"/v1/model":{"get":{"description":"","operationId":"listModels","parameters":[{"name":"all","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GetModelDto"}}}}}},"security":[{"JWT":[]}],"summary":"🔒 List global model catalog","tags":["🧬 Models"]}},"/v1/model/gateway/{provider}":{"get":{"description":"","operationId":"listGatewayModels","parameters":[{"name":"provider","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GetGatewayModelDto"}}}}}},"security":[{"JWT":[]}],"summary":"🔒 List a gateway's models","tags":["🧬 Models"]}},"/v1/org/{org_id}/cost-analytics":{"get":{"description":"`billing.view` — `OWNER` `ADMIN`\n\nCost report computed from per-message usage: totals, time series, and breakdown by agent/channel/transport/scope/conversation/contact/model, split by token type, with date windows and arbitrary filters.","operationId":"getCostAnalytics","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"from","required":false,"in":"query","description":"Lower bound (ISO date) on the message timestamp. Default: 30 days ago.","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"Upper bound (ISO date), exclusive. Default: now.","schema":{"type":"string"}},{"name":"bucket","required":false,"in":"query","description":"Time-bucket granularity of the series.","schema":{"type":"string","enum":["hour","day","week","month"]}},{"name":"group_by","required":false,"in":"query","description":"Dimension the breakdown is grouped by.","schema":{"type":"string","enum":["agent","channel","transport","scope","conversation","contact","model","connection","cron"]}},{"name":"agent_id","required":false,"in":"query","description":"Filter to a single agent.","schema":{"minimum":1,"type":"number"}},{"name":"channel_id","required":false,"in":"query","description":"Filter to a single channel.","schema":{"minimum":1,"type":"number"}},{"name":"transport","required":false,"in":"query","description":"Filter to a single transport.","schema":{"type":"string","enum":["INTERNAL","WHATSAPP","GMAIL","DISCORD","TELEGRAM","SLACK","WEBHOOK","EMAIL","GITHUB","DRIVE","CALENDAR","HOST","WORDPRESS"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CostAnalyticsDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Detailed cost report","tags":["📊 Observability / Billing"]}},"/v1/org/{org_id}/agent/{agent_id}/backup":{"get":{"description":"`backup.view` — `OWNER` `ADMIN`","operationId":"listAgentBackups","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"agent_id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GetBackupDto"}}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List agent volume backups","tags":["🧩 Resources / Backups"]},"post":{"description":"`backup.manage` — `OWNER` `ADMIN`\n\nCopies the agent's /home volume to R2 (asynchronous — poll the list until COMPLETED).","operationId":"createAgentBackup","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"agent_id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetBackupDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Start agent volume backup","tags":["🧩 Resources / Backups"]}},"/v1/org/{org_id}/agent/{agent_id}/backup/{id}/restore":{"post":{"description":"`backup.manage` — `OWNER` `ADMIN`\n\nRestores a backup onto the agent's volume (DESTRUCTIVE — overwrites the current /home). Asynchronous.","operationId":"restoreAgentBackup","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"agent_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetBackupDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Restore backup onto the volume","tags":["🧩 Resources / Backups"]}},"/v1/org/{org_id}/agent/{agent_id}/backup/{id}":{"delete":{"description":"`backup.manage` — `OWNER` `ADMIN`\n\nDeletes the R2 object and the row.","operationId":"deleteAgentBackup","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"agent_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Delete backup","tags":["🧩 Resources / Backups"]}},"/v1/org/{org_id}/backup":{"get":{"description":"`backup.view` — `OWNER` `ADMIN`\n\nThe organization's backups, newest first, one page at a time (`page` from 1, `limit` 50 by default, 200 at most). Without `page` and `limit` it answers the old way, every backup as a plain array — kept for panels opened before pagination; use the paged form.","operationId":"listOrgBackups","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"page","required":false,"in":"query","schema":{"type":"number"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List all backups","tags":["🧩 Resources / Backups"]}},"/v1/org/{org_id}/backup/unknown-objects":{"get":{"description":"`backup.manage` — `OWNER` `ADMIN`\n\nObjects that are in the organization's bucket and that no backup row claims. Read-only: nothing is deleted or adopted — the bucket belongs to someone else and we only look.","operationId":"listUnknownBackupObjects","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Unclaimed objects in the backup bucket","tags":["🧩 Resources / Backups"]}},"/v1/org/{org_id}/backup-destination":{"get":{"description":"`backup.view` — `OWNER` `ADMIN`\n\nThe bucket this organization writes its backups to, and whether backups are even happening. Never returns the secret key.","operationId":"getBackupDestination","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Organization backup destination","tags":["🧩 Resources / Backups"]},"put":{"description":"`backup.manage` — `OWNER` `ADMIN`\n\nPoints the organization at its own S3-compatible bucket. The destination is TESTED before saving (a test object is written and deleted); a bucket that can't be written to, or whose credential can't delete, is rejected with 400 instead of failing on the night the backup runs.","operationId":"setBackupDestination","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetBackupDestinationDto"}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Configure own backup bucket","tags":["🧩 Resources / Backups"]},"delete":{"description":"`backup.manage` — `OWNER` `ADMIN`\n\nNothing is deleted from the client's bucket — new backups simply stop being made, and the existing ones can no longer be restored through us.","operationId":"removeBackupDestination","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Forget backup destination","tags":["🧩 Resources / Backups"]}},"/v1/org/{org_id}/agent/{agent_id}/activity/{id}/refresh":{"post":{"description":"`agent.input.send` — `OWNER` `ADMIN` `OPERATOR`\n\nSends the agent a reminder about this activity in its conversation; the answer is the agent's next write on it. 404 when the caller may not see the agent or the conversation, 409 when it's closed, already asked or the agent isn't running, 429 more than once a minute.","operationId":"refreshActivity","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"agent_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"202":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Ask the agent for an update","tags":["🧩 Resources / Activities"]}},"/v1/org/{org_id}/agent/{agent_id}/activity/{id}/link/{link_id}":{"patch":{"description":"`agent.input.send` — `OWNER` `ADMIN` `OPERATOR`\n\nTurns automatic merging on or off for a pull request the activity follows: with it, the agent is told to merge as soon as the PR is green and approved; without it, a person is told it is ready. 404 when the caller may not see the agent, the activity or the link.","operationId":"updateActivityLink","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"agent_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}},{"name":"link_id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateActivityLinkDto"}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Change how a followed link is handled","tags":["🧩 Resources / Activities"]}},"/v1/org/{org_id}/audit":{"get":{"description":"`audit.view` — `OWNER` `ADMIN`\n\nAppend-only log of sensitive actions, most recent first. Filters: actor_kind, action, resource_type/id, date range. Paginated.","operationId":"listAudit","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"actor_kind","required":false,"in":"query","description":"Filter by actor kind.","schema":{"type":"string","enum":["HUMAN","AGENT","SERVICE","SYSTEM"]}},{"name":"action","required":false,"in":"query","description":"Filter by action (substring match, e.g. \"credential\").","schema":{"minLength":1,"maxLength":64,"type":"string"}},{"name":"resource_type","required":false,"in":"query","description":"Filter by resource type (e.g. \"credential\").","schema":{"minLength":1,"maxLength":32,"type":"string"}},{"name":"resource_id","required":false,"in":"query","description":"Filter by resource id.","schema":{"minimum":1,"type":"number"}},{"name":"agent_id","required":false,"in":"query","description":"Everything about one agent: actions ON it OR done BY it.","schema":{"minimum":1,"type":"number"}},{"name":"from","required":false,"in":"query","description":"Lower bound (ISO date) on created_at.","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"Upper bound (ISO date) on created_at.","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"Page size (1–200, default 50).","schema":{"minimum":1,"maximum":200,"type":"number"}},{"name":"offset","required":false,"in":"query","description":"Offset for pagination (default 0).","schema":{"minimum":0,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListAuditDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List the organization's audit log","tags":["📊 Observability / Audit"]}},"/v1/org/{org_id}/agent/{agent_id}/logs":{"get":{"description":"`agent.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nMost recent first. Paginated (limit/offset); `from`/`to` (ISO dates) bound created_at.","operationId":"listAgentLogs","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"agent_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"limit","required":true,"in":"query","schema":{"type":"number"}},{"name":"offset","required":true,"in":"query","schema":{"type":"number"}},{"name":"from","required":false,"in":"query","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListAuditDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 An agent's audit feed","tags":["📊 Observability / Audit"]}},"/v1/org/{org_id}/billing":{"get":{"description":"`billing.view` — `OWNER` `ADMIN`\n\nOrganization totals (cost_micro + tokens) aggregated across all agents, with a per-agent breakdown.","operationId":"getBilling","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingSummaryDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Live billing summary","tags":["📊 Observability / Billing"]}},"/v1/org/{org_id}/billing/agents":{"get":{"description":"`billing.view` — `OWNER` `ADMIN`\n\nHow many agents are paid for, how many exist, how low it can go, and what change is scheduled for renewal.","operationId":"getContractedAgents","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractedAgentsDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Contracted agents and headroom","tags":["📊 Observability / Billing"]}},"/v1/org/{org_id}/billing/usage":{"get":{"description":"`billing.view` — `OWNER` `ADMIN`\n\nHow much of each plan limit is in use: external channels (shared by all the agents), devices and servers. The same counts that refuse one more.","operationId":"getPlanUsage","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanUsageDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Plan limits in use","tags":["📊 Observability / Billing"]}},"/v1/org/{org_id}/cronjob":{"get":{"description":"`cronjob.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`","operationId":"listCronjobs","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"include_internal","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GetCronjobDto"}}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List organization cronjobs","tags":["⏰ Automation / Scheduled jobs"]},"post":{"description":"`cronjob.create` — `OWNER` `ADMIN` `OPERATOR`\n\nRequires a confirmed email: a cronjob spends on its own schedule.","operationId":"createCronjob","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCronjobDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetCronjobDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Create cronjob","tags":["⏰ Automation / Scheduled jobs"]}},"/v1/org/{org_id}/agent/{agent_id}/cronjob":{"get":{"description":"`cronjob.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`","operationId":"listAgentCronjobs","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"agent_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"include_internal","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GetCronjobDto"}}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List an agent's cronjobs","tags":["⏰ Automation / Scheduled jobs"]}},"/v1/org/{org_id}/cronjob/{id}":{"get":{"description":"`cronjob.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`","operationId":"getCronjob","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetCronjobDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Cronjob details","tags":["⏰ Automation / Scheduled jobs"]},"delete":{"description":"`cronjob.delete` — `OWNER` `ADMIN` `OPERATOR`","operationId":"deleteCronjob","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Delete cronjob","tags":["⏰ Automation / Scheduled jobs"]},"patch":{"description":"`cronjob.edit` — `OWNER` `ADMIN` `OPERATOR`","operationId":"updateCronjob","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCronjobDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetCronjobDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Edit cronjob","tags":["⏰ Automation / Scheduled jobs"]}},"/v1/org/{org_id}/cronjob/{id}/runs":{"get":{"description":"`cronjob.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`","operationId":"listCronjobRuns","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CronjobRunDto"}}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Cronjob run history","tags":["⏰ Automation / Scheduled jobs"]}},"/v1/org/{org_id}/cronjob/{id}/execute":{"post":{"description":"`cronjob.execute` — `OWNER` `ADMIN` `OPERATOR`\n\nRequires a confirmed email: triggers a turn that spends.","operationId":"executeCronjob","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"202":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Run cronjob now","tags":["⏰ Automation / Scheduled jobs"]}},"/v1/capability":{"get":{"description":"","operationId":"listCapabilities","parameters":[{"name":"page","required":true,"in":"query","schema":{"type":"number"}},{"name":"limit","required":true,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListCapabilityDto"}}}}},"security":[{"JWT":[]}],"summary":"🔒 List global capability catalogue","tags":["🤖 Agents / Capabilities"]}},"/v1/org/{org_id}/inbox/contact":{"get":{"description":"`agent.output.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nInbox contacts grouped with their conversations.","operationId":"listInbox","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"scope","required":false,"in":"query","description":"mine = contacts assigned to the authenticated user; all = every contact accessible in the org","schema":{"default":"all","type":"string","enum":["mine","all"]}},{"name":"status","required":false,"in":"query","description":"Filter conversations by status. ALL returns OPEN + CLOSED.","schema":{"default":"open","type":"string","enum":["open","pending","resolved","all"]}},{"name":"agent_id","required":false,"in":"query","description":"Restrict to a single agent","schema":{"minimum":1,"type":"number"}},{"name":"channel_id","required":false,"in":"query","description":"Restrict to a single channel","schema":{"minimum":1,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListInboxDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List Agent Desk contacts","tags":["💬 Communication / Inbox"]}},"/v1/org/{org_id}/inbox/contact/{contact_id}":{"get":{"description":"`agent.output.view` — `OWNER` `ADMIN` `OPERATOR` `VIEWER`\n\nThe contact and all of its conversations.","operationId":"getInboxContact","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"contact_id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetInboxContactDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Agent Desk contact detail","tags":["💬 Communication / Inbox"]}},"/v1/org/{org_id}/inbox/assignees":{"get":{"description":"`agent.input.send` — `OWNER` `ADMIN` `OPERATOR`\n\nCandidates for the Agent Desk selector (members + agents available for the Agent Desk).","operationId":"listInboxAssignees","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListInboxAssigneesDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 List Agent Desk assignees","tags":["💬 Communication / Inbox"]}},"/v1/org/{org_id}/inbox/conversation/{conversation_id}/status":{"patch":{"description":"`agent.input.send` — `OWNER` `ADMIN` `OPERATOR`\n\nAgent Desk conversation: open / pending / resolved.","operationId":"setInboxConversationStatus","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"conversation_id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetConversationStatusDto"}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Change conversation status","tags":["💬 Communication / Inbox"]}},"/v1/org/{org_id}/inbox/contact/{contact_id}/assignee":{"patch":{"description":"`agent.input.send` — `OWNER` `ADMIN` `OPERATOR`\n\nSets or clears the Agent Desk assignee of an external contact.","operationId":"setInboxAssignee","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"contact_id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetAssigneeDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetInboxContactDto"}}}}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Assign or release an Agent Desk contact","tags":["💬 Communication / Inbox"]}},"/v1/org/{org_id}/inbox/conversation/{conversation_id}/reply-mode":{"patch":{"description":"`agent.input.send` — `OWNER` `ADMIN` `OPERATOR`\n\nWhether the agent sends what it writes in this Agent Desk conversation, or leaves it as a draft for a person. A conversation starts with its channel's mode; this changes that one conversation from now on.","operationId":"setInboxConversationReplyMode","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"conversation_id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetConversationReplyModeDto"}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Send or draft in a conversation","tags":["💬 Communication / Inbox"]}},"/v1/org/{org_id}/inbox/draft/{message_id}":{"delete":{"description":"`agent.input.send` — `OWNER` `ADMIN` `OPERATOR`\n\nThe draft never reaches the customer. The message is kept hidden from the thread rather than deleted, so what was rejected can still be looked up.","operationId":"discardInboxDraft","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"message_id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Discard a draft reply","tags":["💬 Communication / Inbox"]},"patch":{"description":"`agent.input.send` — `OWNER` `ADMIN` `OPERATOR`\n\nRewrites a reply the agent left as a draft on a supervised channel (reply_mode = DRAFT) before a person sends it. The original text is kept.","operationId":"updateInboxDraft","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"message_id","required":true,"in":"path","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDraftDto"}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Rewrite a draft reply","tags":["💬 Communication / Inbox"]}},"/v1/org/{org_id}/inbox/draft/{message_id}/send":{"post":{"description":"`agent.input.send` — `OWNER` `ADMIN` `OPERATOR`\n\nQueues the draft for delivery like any other reply. Two people pressing send at once produce exactly one send; the loser gets a 409.","operationId":"sendInboxDraft","parameters":[{"name":"org_id","required":true,"in":"path","schema":{"type":"number"}},{"name":"message_id","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"201":{"description":""}},"security":[{"bearer":[]},{"JWT":[]}],"summary":"🔒 Send a reviewed draft","tags":["💬 Communication / Inbox"]}},"/v1/mcp-catalog":{"get":{"description":"","operationId":"listMcpCatalog","parameters":[{"name":"all","required":false,"in":"query","schema":{"type":"string"}},{"name":"i18n","required":false,"in":"query","schema":{"type":"string"}},{"name":"locale","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GetMcpCatalogDto"}}}}}},"security":[{"JWT":[]}],"summary":"🔒 List global MCP catalog","tags":["🔌 MCP Catalog"]}}},"info":{"title":"Kujira API","description":"Control-plane API: organizations, agents, resources (skills, memories, MCP servers, credentials), communication (channels, inbox, approvals), scheduled jobs, audit and billing.\n\n## Authentication\n\nIntegrations authenticate with a **service token** on every request:\n\n```\nAuthorization: Bearer kjtk_live_…\n```\n\nThe token is created from the panel (**Settings → Service tokens**), belongs to **one** organization and carries its own permissions (scopes). The full secret is shown only once, at creation — store it in a secrets manager. It can be revoked at any time from the same panel.\n\n## Routes and organizations\n\nAll routes live under the `/v1` prefix. An organization's resources live under `/v1/org/{org_id}/…`: a token only sees its own organization — requesting another one's returns **404**, without revealing whether it exists.\n\nEach endpoint states the permission it requires (`resource.action`, e.g. `agent.view`). A token without that scope gets **403**.\n\n## Errors\n\nError responses share a common shape:\n\n```json\n{ \"status\": 403, \"code\": \"PERMISSION_DENIED\", \"message\": \"…\" }\n```\n\n`code` is stable and what you should branch on in code; `message` is a supporting text. Validation errors add an `errors` list with the per-field detail.\n\n## Rate limits\n\n60-second windows per IP: **600 requests** on any route as a general ceiling, with stricter quotas on sensitive surfaces (authentication **10/min**, public webhooks **120/min**, support **20/min**). Once a limit is exceeded, the API responds **429** with the `Retry-After` header.\n\n## More help\n\nPlatform guides and concepts: [docs.kujira.so](https://docs.kujira.so).","version":"0.1.0","contact":{}},"tags":[],"servers":[{"url":"https://api.kujira.so","description":"Production"}],"components":{"securitySchemes":{"bearer":{"name":"Service token","description":"Service token (`kjtk_live_…`), created from the panel under Settings → Service tokens. Belongs to one organization and carries its own permissions.","type":"http","scheme":"bearer"},"JWT":{"name":"JWT token","description":"A person's session in the panel. Not usable for integrations.","type":"http","scheme":"bearer"}},"schemas":{"GetServerDto":{"type":"object","properties":{"id":{"type":"number","description":"Server id","example":1},"organization_id":{"type":"number","description":"Organization id","example":1},"name":{"type":"string","description":"Display name","example":"hetzner-fra-01"},"description":{"type":"string","nullable":true,"description":"Free-form description"},"ip":{"type":"string","nullable":true,"description":"IPv4 or IPv6 address (auto-detected from the supervisor handshake)","example":"49.12.44.201"},"os":{"type":"string","nullable":true,"description":"Operating system label"},"cpu":{"type":"number","nullable":true,"description":"CPU cores"},"ram_mb":{"type":"number","nullable":true,"description":"RAM in MB"},"default_agent_memory_mb":{"type":"number","nullable":true,"description":"Default container RAM cap (MB) inherited by this server’s agents. Null = no limit.","example":4096},"default_agent_cpu":{"type":"number","nullable":true,"description":"Default container CPU cap (cores) inherited by this server’s agents. Null = no limit.","example":2},"default_agent_disk_gb":{"type":"number","nullable":true,"description":"Default soft disk ceiling (whole GB) inherited by this server’s agents. Null = none.","example":20},"status":{"enum":["UNPROVISIONED","ONLINE","WARNING","OFFLINE"],"type":"string","description":"Lifecycle status","example":"UNPROVISIONED"},"load":{"type":"number","nullable":true,"description":"Load average (0..1) reported by the agent"},"cpu_percent":{"type":"number","nullable":true,"description":"Instantaneous CPU usage (0..100) reported by the agent"},"ram_percent":{"type":"number","nullable":true,"description":"RAM usage (0..100) reported by the agent"},"alerts_enabled":{"type":"boolean","description":"Whether the resource-alert watcher is active for this server (KUJI-136)","example":true},"alert_ram_pct":{"type":"number","nullable":true,"description":"RAM alert threshold (%). Overrides the org default; null = inherit (org default → 85).","example":85},"alert_cpu_pct":{"type":"number","nullable":true,"description":"CPU alert threshold (%). Overrides the org default; null = inherit (org default → 90).","example":90},"alert_load_factor":{"type":"number","nullable":true,"description":"Load alert factor (load > factor × cores). Overrides the org default; null = inherit (org default → 1.5).","example":1.5},"uptime_seconds":{"type":"number","nullable":true,"description":"Uptime in seconds reported by the agent"},"last_seen_at":{"format":"date-time","type":"string","nullable":true,"description":"Last heartbeat timestamp"},"provisioning_expires_at":{"format":"date-time","type":"string","nullable":true,"description":"Provisioning expires at"},"supervisor_outdated":{"type":"boolean","description":"Whether the supervisor runs a version BEHIND the platform target (same comparison the upgrade endpoint gates on). False when current or when the comparison is unknown.","example":false},"agents_count":{"type":"number","description":"Number of agents bound to this server","example":3},"has_ssh_key":{"type":"boolean","description":"Whether an SSH key has been generated for this server","example":true},"tenancy":{"enum":["DEDICATED","SHARED"],"type":"string","description":"Who the server serves: its own organization, or a partner and its clients","example":"DEDICATED"},"shared_ready":{"type":"boolean","nullable":true,"description":"Whether the machine can be shared, as its supervisor reports. Null = never reported (older supervisor)."},"shared_missing":{"description":"What the machine lacks to be shared: userns_remap, disk_quota, network_rules","type":"array","items":{"type":"string"}},"shared_accepted_at":{"format":"date-time","type":"string","nullable":true,"description":"When sharing was accepted"},"shared_terms_current":{"type":"boolean","description":"Whether the accepted warning is the current one. False on a shared server means it takes no new client agents until accepted again.","example":false},"provisioning_token":{"type":"string","description":"Provisioning token (only at create/regenerate time)"},"created_at":{"format":"date-time","type":"string","description":"Created at"},"updated_at":{"format":"date-time","type":"string","description":"Updated at"}},"required":["id","organization_id","name","status","alerts_enabled","supervisor_outdated","agents_count","has_ssh_key","tenancy","shared_missing","shared_terms_current","created_at","updated_at"]},"ListServerDto":{"type":"object","properties":{"data":{"description":"Items in the current page","type":"array","items":{"$ref":"#/components/schemas/GetServerDto"}},"total":{"type":"number","description":"Total number of items matching the query","example":42},"page":{"type":"number","description":"Current page (1-indexed)","example":1},"limit":{"type":"number","description":"Page size","example":20}},"required":["data","total","page","limit"]},"CreateServerDto":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"description":"Display name (unique within the org)","example":"hetzner-fra-01"},"description":{"type":"string","maxLength":500,"description":"Free-form description","example":"Main VPS in Frankfurt"},"ip":{"type":"string","description":"IPv4 or IPv6 address. Optional — auto-detected from the supervisor handshake on connect (and refreshed on every reconnect, so dynamic IPs stay current).","example":"49.12.44.201"},"os":{"type":"string","maxLength":80,"description":"Operating system label","example":"Debian 12"},"cpu":{"type":"number","minimum":1,"description":"CPU cores","example":8},"ram_mb":{"type":"number","minimum":1,"description":"RAM in MB","example":32768},"default_agent_memory_mb":{"type":"number","nullable":true,"minimum":64,"maximum":1048576,"description":"Default container RAM cap (MB) for this server’s agents. Null clears it (no limit).","example":4096},"default_agent_cpu":{"type":"number","nullable":true,"minimum":0.1,"maximum":256,"description":"Default container CPU cap (cores) for this server’s agents. Null clears it (no limit).","example":2},"default_agent_disk_gb":{"type":"number","nullable":true,"minimum":1,"maximum":100000,"description":"Default soft disk ceiling (whole GB) for this server’s agents: measured every 60 s, warning at 80 %, agent stopped at 100 %. Null clears it (none).","example":20},"alerts_enabled":{"type":"boolean","description":"Enable/disable the resource-alert watcher for this server.","example":true},"alert_ram_pct":{"type":"number","nullable":true,"minimum":1,"maximum":100,"description":"RAM alert threshold (%). Null clears the override (inherit org default).","example":85},"alert_cpu_pct":{"type":"number","nullable":true,"minimum":1,"maximum":100,"description":"CPU alert threshold (%). Null clears the override (inherit org default).","example":90},"alert_load_factor":{"type":"number","nullable":true,"minimum":0.1,"maximum":100,"description":"Load alert factor (load > factor × cores). Null clears the override (inherit org default).","example":1.5}},"required":["name"]},"GetApprovalDto":{"type":"object","properties":{"id":{"type":"number","description":"Approval id","example":1},"organization_id":{"type":"number","description":"Organization id","example":1},"agent_id":{"type":"number","description":"Agent id this approval belongs to","example":1},"kind":{"enum":["OAUTH_RENEWAL","ACTION_REQUEST","QUESTION"],"type":"string","description":"What kind of approval this is","example":"OAUTH_RENEWAL"},"status":{"enum":["PENDING","RESOLVED","DISMISSED","EXPIRED"],"type":"string","description":"Lifecycle state of the approval","example":"PENDING"},"metadata":{"type":"object","additionalProperties":true,"nullable":true,"description":"Kind-specific data (free-form JSON). OAUTH_RENEWAL leaves this null today; other kinds will pack amount/target/etc here."},"conversation_id":{"type":"number","nullable":true,"description":"The operator conversation the agent asked from (null = no thread context). Lets the panel render the approval as an inline card in that chat."},"expires_at":{"format":"date-time","type":"string","nullable":true,"description":"When this PENDING approval auto-expires (null = never)"},"created_at":{"format":"date-time","type":"string","description":"Created at"},"resolved_at":{"format":"date-time","type":"string","nullable":true,"description":"When the approval was resolved (null while PENDING)"},"resolved_by":{"type":"number","nullable":true,"description":"User id that resolved or dismissed it","example":1}},"required":["id","organization_id","agent_id","kind","status","created_at"]},"ListApprovalDto":{"type":"object","properties":{"data":{"description":"Items in the current page","type":"array","items":{"$ref":"#/components/schemas/GetApprovalDto"}},"total":{"type":"number","description":"Total number of items matching the query","example":3},"page":{"type":"number","description":"Current page (1-indexed)","example":1},"limit":{"type":"number","description":"Page size","example":20},"decidable_change_ids":{"description":"Ids of the pending configuration changes asked by an agent (metadata.category = agent_change) on this page that the caller may decide — it takes the same permission and access as making the change from the panel. Any other agent change on the page is not theirs to decide.","example":[12],"type":"array","items":{"type":"number"}}},"required":["data","total","page","limit","decidable_change_ids"]},"ApprovalAudiencePersonDto":{"type":"object","properties":{"user_id":{"type":"number","description":"User id","example":25},"name":{"type":"string","description":"Display name","example":"Nadya"},"avatar_url":{"type":"string","nullable":true,"description":"Avatar path (backend proxy), or null","example":"/v1/user/25/avatar?v=1"},"can_decide":{"type":"boolean","description":"Whether this person may decide it (approve or reject), not only see it: the approval permission and, for a configuration change, the permission to make that change.","example":true},"notified":{"type":"boolean","description":"Whether the notice of this approval reached this person (bell, email or phone, per their preferences).","example":true}},"required":["user_id","name","avatar_url","can_decide","notified"]},"ApprovalAudienceDto":{"type":"object","properties":{"approval_id":{"type":"number","description":"Approval id","example":3583},"people":{"description":"Members of the organization who can see it, with whether each may decide it and whether the notice reached them. People who decide first, then the rest.","type":"array","items":{"$ref":"#/components/schemas/ApprovalAudiencePersonDto"}}},"required":["approval_id","people"]},"ListApprovalAudienceDto":{"type":"object","properties":{"data":{"description":"One entry per requested approval the caller can see","type":"array","items":{"$ref":"#/components/schemas/ApprovalAudienceDto"}}},"required":["data"]},"ResolveApprovalDto":{"type":"object","properties":{"selected":{"minItems":1,"maxItems":20,"description":"Only for an approval with several changes (metadata.change.items): the ids to apply. Omitted = all of them.","example":["a1b2c3d4"],"type":"array","items":{"type":"string","maxLength":40}},"scope":{"enum":["once","always"],"type":"string","description":"Only for a credential used on a host (metadata.category = credential_destination): «always» saves the host as a destination (only when metadata.always is true), «once» allows it in that conversation for an hour. Omitted = once.","example":"once"}}},"AnswerQuestionDto":{"type":"object","properties":{"selected":{"maxItems":8,"description":"Chosen options as 0-based indices into metadata.options. A single-choice question keeps the first one. May be empty for a free-text answer.","example":[0],"type":"array","items":{"type":"number","minimum":0}},"free_text":{"type":"string","maxLength":2000,"description":"Free-text nuance or full answer (\"bórrala pero deja los volúmenes\").","example":"bórrala pero deja los volúmenes"},"seal":{"type":"object","additionalProperties":true,"description":"From the Kujira desktop app only: its signature over this answer (kj-answer/1). The turn the answer opens then counts as sent from the app."}}},"ConversationConnectionDto":{"type":"object","properties":{"id":{"type":"number","description":"Connection id","example":7},"name":{"type":"string","description":"Connection name","example":"OpenRouter"},"provider":{"type":"string","description":"Provider key","example":"openrouter"},"model":{"type":"string","nullable":true,"description":"Model the connection carries, when it carries one (OpenRouter, Ollama).","example":"deepseek/deepseek-v4-flash"}},"required":["id","name","provider","model"]},"ConversationContactDto":{"type":"object","properties":{"id":{"type":"number","description":"Contact id","example":1},"kind":{"type":"string","description":"Contact kind","example":"INTERNAL_USER"},"name":{"type":"string","description":"Contact display name","example":"Alice"},"user_id":{"type":"number","nullable":true,"description":"User id (internal_user only)"}},"required":["id","kind","name"]},"ConversationChannelDto":{"type":"object","properties":{"id":{"type":"number","description":"Channel id","example":1},"transport":{"type":"string","description":"Channel transport","example":"TELEGRAM"},"name":{"type":"string","description":"Channel display name","example":"Chat interno"}},"required":["id","transport","name"]},"ConversationNotifyChannelDto":{"type":"object","properties":{"id":{"type":"number","description":"Channel id","example":3},"name":{"type":"string","description":"Channel display name","example":"Soporte WhatsApp"},"transport":{"type":"string","description":"Channel transport","example":"WHATSAPP"}},"required":["id","name","transport"]},"GetConversationWithNotifyDto":{"type":"object","properties":{"id":{"type":"number","description":"Conversation id","example":1},"ref":{"type":"string","description":"Opaque public reference — paste it to jump to this conversation.","example":"MZROL9UC"},"contact_id":{"type":"number","description":"Contact id","example":1},"channel_id":{"type":"number","description":"Channel this thread lives on","example":1},"session_id":{"type":"string","description":"Claude session UUID for --resume","example":"a1b2c3d4-..."},"status":{"type":"string","description":"Status","example":"OPEN"},"model":{"type":"string","nullable":true,"description":"Last Claude model seen in this conversation (slug). Null until first reply.","example":"claude-haiku-4-5"},"model_override":{"type":"string","nullable":true,"description":"Operator/agent-set model override for this session (KJ-150). Null = inherit the agent default. Distinct from `model` (what last ran).","example":"claude-haiku-4-5"},"connection":{"nullable":true,"description":"Connection this conversation runs with when it is not the one of its agent. Null = the connection of the agent.","type":"object","allOf":[{"$ref":"#/components/schemas/ConversationConnectionDto"}]},"effort_override":{"type":"string","nullable":true,"description":"Reasoning effort set for this conversation (#277): LOW/MEDIUM/HIGH/XHIGH/MAX. Null = inherit the agent default.","example":"HIGH"},"is_operator_session":{"type":"boolean","description":"Whether this is an operator multi-session chat thread (KUJI-52).","example":true},"temporary":{"type":"boolean","description":"Temporary session: the agent reads as usual but leaves no trace from it, and it is emptied when closed or after 24 h without activity.","example":false},"private_device":{"type":"boolean","description":"The session used a person’s private computer in the last 2 hours: what the agent writes outside it needs that person’s approval. No other session ever reads it.","example":false},"parent_conversation_id":{"type":"number","nullable":true,"description":"The session this one was opened from (#635): set when the agent split a job into parallel sessions with `session_create`. Null = opened on its own.","example":42},"forked_from_conversation_id":{"type":"number","nullable":true,"description":"The session this one was forked from: the operator forked it from a message and it starts with that session’s context up to there. Null = not a fork.","example":42},"title":{"type":"string","nullable":true,"description":"Session title for an operator session (\"Bug del login\"). Null = unnamed."},"icon":{"type":"string","nullable":true,"description":"Lucide icon name tagging the session (curated set). Null = default."},"color":{"type":"string","nullable":true,"description":"Theme palette color key tagging the session. Null = default."},"pinned":{"type":"boolean","description":"Pinned to the top of the operator session list."},"section_id":{"type":"number","nullable":true,"description":"The section of the session list it sits in. Null = no section."},"last_action":{"type":"string","nullable":true,"description":"Per-session last activity snippet (\"Ejecutando: …\"). Null until any."},"last_action_at":{"format":"date-time","type":"string","nullable":true,"description":"When last_action was recorded."},"contact":{"description":"Contact summary","allOf":[{"$ref":"#/components/schemas/ConversationContactDto"}]},"channel":{"description":"Channel summary (separates per-channel views)","allOf":[{"$ref":"#/components/schemas/ConversationChannelDto"}]},"context_tokens":{"type":"number","nullable":true,"description":"KUJI-87: live context size (input+cache tokens of the last turn). Null until the first result.","example":65000},"context_window":{"type":"number","description":"The model's context window (denominator of the saturation bar).","example":1000000},"reset_threshold":{"type":"number","nullable":true,"description":"Effective context cap (eje A) — marker for \"session compacts here\". Per-conversation override first, else the agent/org resolution. Null = no cap.","example":300000},"context_cap_tokens":{"type":"number","nullable":true,"description":"Per-conversation context-cap override. Null = inherit the agent/org cap; 0 = no cap for this conversation; >0 = compact at this many tokens.","example":500000},"last_turn_failed_at":{"format":"date-time","type":"string","nullable":true,"description":"When this conversation's LAST turn died without answering (null = its last turn did not fail). Cleared by its next successful turn."},"last_turn_error":{"type":"string","nullable":true,"description":"Why that last turn failed, in words for a person (the same sentence as the turn-failure alert, secrets redacted). Null when it did not fail.","example":null},"opened_at":{"format":"date-time","type":"string","description":"Opened at"},"last_activity_at":{"format":"date-time","type":"string","description":"Last activity"},"debug_visible_until":{"format":"date-time","type":"string","nullable":true,"description":"Debug opt-in expiry. Future = exposed to admins/service for troubleshooting; null/past = private."},"support_session_at":{"format":"date-time","type":"string","nullable":true,"description":"When this thread was opened from a platform support visit (null = it was not). Such threads stay readable by the org owners and admins."},"notify_channels":{"description":"Channels that escalate to this conversation (their notify destination). Always an array: empty when none, or when the caller can't see the agent's channels.","type":"array","items":{"$ref":"#/components/schemas/ConversationNotifyChannelDto"}}},"required":["id","ref","contact_id","channel_id","session_id","status","model","model_override","connection","effort_override","is_operator_session","temporary","private_device","parent_conversation_id","forked_from_conversation_id","title","icon","color","pinned","section_id","last_action","last_action_at","contact","channel","context_window","last_turn_failed_at","last_turn_error","opened_at","last_activity_at","debug_visible_until","support_session_at","notify_channels"]},"MessageDeliveryDto":{"type":"object","properties":{"status":{"type":"string","description":"PENDING | SENDING | SENT | FAILED | DRAFTED | HELD | CANCELED","example":"SENT"},"attempts":{"type":"number","description":"Delivery attempts so far","example":1},"error":{"type":"string","nullable":true,"description":"Last transport error, when it failed"},"sent_at":{"format":"date-time","type":"string","nullable":true,"description":"When the transport accepted it"}},"required":["status","attempts"]},"MessageAttachmentDto":{"type":"object","properties":{"id":{"type":"number","description":"Attachment id","example":42},"filename":{"type":"string","description":"Original filename","example":"report.csv"},"mime_type":{"type":"string","description":"MIME type","example":"image/png"},"size_bytes":{"type":"number","description":"Size in bytes","example":12345},"url":{"type":"string","description":"Backend proxy URL to stream the bytes (auth required).","example":"/org/2/attachment/42"},"uploaded_by_agent_id":{"type":"number","nullable":true,"description":"Agent that attached it (attachment_send), else null."},"live":{"type":"boolean","description":"The agent keeps this file up to date: the same attachment, new versions."},"version":{"type":"number","description":"Current version of the file","example":1},"content_updated_at":{"type":"string","nullable":true,"description":"When the agent last changed the file, if it ever did."}},"required":["id","filename","mime_type","size_bytes","url"]},"GetMessageDto":{"type":"object","properties":{"id":{"type":"number","description":"Message id","example":1},"conversation_id":{"type":"number","description":"Conversation id","example":1},"seq":{"type":"number","description":"Monotonic sequence within conversation","example":1},"role":{"type":"string","description":"Role","example":"ASSISTANT"},"kind":{"type":"string","description":"Kind","example":"TEXT"},"via_channel_id":{"type":"number","nullable":true,"description":"Channel the message arrived/left through"},"via_identity_id":{"type":"number","nullable":true,"description":"Identity that sent the message"},"author_user_id":{"type":"number","nullable":true,"description":"Human author (operator) user id. NULL for agent output and external contacts."},"author_name":{"type":"string","nullable":true,"description":"Author display name when author_user_id is set."},"status":{"type":"string","description":"DELIVERED or PENDING (auth failure / offline)","example":"DELIVERED"},"delivery":{"nullable":true,"description":"Delivery to the customer channel (Telegram, mail…) for messages that go out through one. NULL for everything else — internal threads, inbound messages. Distinct from status, which is about whether it reached the agent.","type":"object","allOf":[{"$ref":"#/components/schemas/MessageDeliveryDto"}]},"content":{"type":"object","description":"Raw event content (stream-json shape)"},"attachments":{"description":"Files attached to this message (operator upload or the agent attachment_send).","type":"array","items":{"$ref":"#/components/schemas/MessageAttachmentDto"}},"created_at":{"format":"date-time","type":"string","description":"Created at"}},"required":["id","conversation_id","seq","role","kind","status","content","created_at"]},"ListMessageDto":{"type":"object","properties":{"data":{"description":"Messages","type":"array","items":{"$ref":"#/components/schemas/GetMessageDto"}},"total":{"type":"number","description":"Total messages in conversation"}},"required":["data","total"]},"ConversationAgentDto":{"type":"object","properties":{"id":{"type":"number","description":"Agent id","example":5},"name":{"type":"string","description":"Agent name","example":"soki"},"avatar_url":{"type":"string","nullable":true,"description":"Avatar proxy URL (null = use generative)"},"provider":{"type":"string","nullable":true,"description":"Provider the agent's container actually booted against, so the picker can offer only models it can run. Resolved by the spawn's own precedence: its pinned connection, else the catalogue provider of the model it runs (its own, or its image default), else the org's single shared connection. Null when there is no way to tell — it used to read \"anthropic\" there, which was a guess dressed as an answer.","example":"anthropic"},"effort_override":{"type":"string","nullable":true,"description":"The agent's own reasoning effort (#277/#581): what a conversation on \"Auto\" actually inherits. Null = the agent has none set either, and the CLI's own default (`high`) applies.","example":"HIGH"}},"required":["id","name","avatar_url","effort_override"]},"ConversationListItemDto":{"type":"object","properties":{"id":{"type":"number","description":"Conversation id","example":1},"ref":{"type":"string","description":"Opaque public reference — paste it to jump to this conversation.","example":"MZROL9UC"},"status":{"type":"string","description":"Status","example":"OPEN"},"model":{"type":"string","nullable":true,"description":"Last Claude model seen (slug). Null until first reply.","example":"claude-haiku-4-5"},"model_override":{"type":"string","nullable":true,"description":"Operator/agent-set model override for this conversation (KJ-22). Null = inherit the agent default. Distinct from `model` (what last ran).","example":"claude-haiku-4-5"},"connection":{"nullable":true,"description":"Connection this conversation runs with when it is not the one of its agent. Null = the connection of the agent.","type":"object","allOf":[{"$ref":"#/components/schemas/ConversationConnectionDto"}]},"effort_override":{"type":"string","nullable":true,"description":"Reasoning effort set for this conversation (#277): LOW/MEDIUM/HIGH/XHIGH/MAX. Null = inherit the agent default.","example":"HIGH"},"effective_model":{"type":"string","nullable":true,"description":"The model this conversation will actually run its NEXT turn with, resolved server-side by the shared precedence chain (conversation → agent → image). Lets a row say what it runs on without waiting for a first reply.","example":"claude-opus-5"},"effective_connection_id":{"type":"number","nullable":true,"description":"The connection that will serve the NEXT turn: the conversation's own, or the one annotated on its agent. Lets a row mark WHICH of several connections of the same provider its default model belongs to. Null when the agent inherits without an annotated connection (nothing is deduced per row).","example":3},"context_tokens":{"type":"number","nullable":true,"description":"Tokens of context this conversation is currently carrying. Null until its first turn.","example":96000},"context_window":{"type":"number","description":"The effective model's context window, so the row can turn `context_tokens` into a percentage without knowing the model catalogue.","example":1000000},"reset_threshold":{"type":"number","nullable":true,"description":"Tokens at which this conversation compacts in place, resolved by the same chain every turn uses (conversation → agent → organization). Null = Kujira does not compact it.","example":150000},"context_cap_tokens":{"type":"number","nullable":true,"description":"This conversation's own context cap: null inherits the agent/organization, 0 = no cap, otherwise the tokens it compacts at.","example":null},"last_turn_failed_at":{"format":"date-time","type":"string","nullable":true,"description":"When this conversation's LAST turn died without answering (null = its last turn did not fail). Cleared by its next successful turn."},"last_turn_error":{"type":"string","nullable":true,"description":"Why that last turn failed, in words for a person (the same sentence as the turn-failure alert, secrets redacted). Null when it did not fail.","example":null},"agent":{"nullable":true,"description":"Owning agent (null if unassigned)","type":"object","allOf":[{"$ref":"#/components/schemas/ConversationAgentDto"}]},"contact":{"description":"Contact summary","allOf":[{"$ref":"#/components/schemas/ConversationContactDto"}]},"channel":{"description":"Channel summary","allOf":[{"$ref":"#/components/schemas/ConversationChannelDto"}]},"last_activity_at":{"format":"date-time","type":"string","description":"Last activity"},"debug_visible_until":{"format":"date-time","type":"string","nullable":true,"description":"Debug opt-in expiry (KUJI-41 Fase 4). When in the future, the owner has exposed this personal conversation to admins/service for troubleshooting. Null = private."}},"required":["id","ref","status","model","model_override","connection","effort_override","effective_model","effective_connection_id","context_tokens","context_window","reset_threshold","context_cap_tokens","last_turn_failed_at","last_turn_error","agent","contact","channel","last_activity_at"]},"ConversationListPageDto":{"type":"object","properties":{"rows":{"description":"Conversations in this page","type":"array","items":{"$ref":"#/components/schemas/ConversationListItemDto"}},"total":{"type":"number","description":"Total matching the filters","example":1234},"limit":{"type":"number","description":"Page size","example":50},"offset":{"type":"number","description":"Offset","example":0}},"required":["rows","total","limit","offset"]},"CredentialInEffectDto":{"type":"object","properties":{"provider":{"type":"string","nullable":true,"description":"The provider its model resolves to: where the credential is looked up. Null when there is no way to know — no saved connection, no model the catalogue recognizes, and no own credential that says so.","example":"anthropic"},"source":{"enum":["OWN","ORG","NONE"],"type":"string","description":"Whose it is: OWN the one pasted into this agent, ORG the organization's connector, NONE none usable — with NONE the agent does not start.","example":"ORG"},"kind":{"type":"string","nullable":true,"description":"How the organization's is paid for (SUBSCRIPTION / API_KEY) or how the agent's own authenticates (OAUTH / API_KEY). Null with source NONE.","example":"SUBSCRIPTION"},"connection_id":{"type":"number","nullable":true,"description":"WHICH connection answers, when one does (source ORG): the pinned one, or the one the agent inherits. Null with OWN and NONE. With several connections per provider, the provider alone no longer names the one the agent is running on.","example":12}},"required":["source"]},"AgentLimitsDto":{"type":"object","properties":{"memory_mb":{"type":"number","nullable":true,"description":"RAM ceiling in MB.","example":2048},"cpu":{"type":"number","nullable":true,"description":"CPU ceiling in cores.","example":2},"disk_gb":{"type":"number","nullable":true,"description":"Soft disk ceiling in GB.","example":20}},"required":["memory_mb","cpu","disk_gb"]},"AgentLatestActivityDto":{"type":"object","properties":{"text":{"type":"string","description":"The activity line, as the conversation stores it.","example":"Ejecutando: docker pull"},"at":{"format":"date-time","type":"string","description":"When it was recorded."}},"required":["text","at"]},"NotAnsweringDto":{"type":"object","properties":{"code":{"type":"string","description":"Code for the cause, stable even if the text gets rewritten.","example":"oauth_org_not_allowed"},"reason":{"type":"string","description":"The cause as a short label, for a pill or a chip.","example":"Claude Code desactivado en la cuenta"},"detail":{"type":"string","description":"The cause explained, with what needs to be done to fix it.","example":"the Anthropic account has Claude Code access disabled, so no turn of this agent gets to start"},"since":{"format":"date-time","type":"string","description":"Since when it has not been answering."}},"required":["code","reason","detail","since"]},"GetAgentDto":{"type":"object","properties":{"id":{"type":"number","description":"Agent id","example":1},"organization_id":{"type":"number","description":"Organization id","example":1},"server_id":{"type":"number","description":"Server id this agent runs on","example":1},"image_id":{"type":"number","description":"AgentImage id pinned to this agent","example":1},"name":{"type":"string","description":"Display name","example":"sales-bot-eu"},"description":{"type":"string","nullable":true,"description":"Free-form description"},"role":{"type":"string","nullable":true,"description":"What the agent is FOR. Null while nobody has said: the AI Doctor proposes one from the identity and a person confirms it.","example":"sales"},"status":{"enum":["SPAWNING","RUNNING","IDLE","INPUT","PAUSING","PAUSED","RESUMING","STOPPING","STOPPED","ERROR","DELETING"],"type":"string","description":"Lifecycle status — driven by the supervisor once running","example":"STOPPED"},"viewer_access":{"nullable":true,"enum":["CHAT","MANAGE"],"type":"string","description":"The requesting member's access level on this agent (KUJI-41 F2): 'MANAGE' (admin/service or a MANAGE grant), 'CHAT' (view+converse only), or null. Lets the UI hide admin actions / filter assignment pickers. Undefined for internal/no-session reads.","example":"MANAGE"},"viewer_hidden":{"type":"boolean","description":"Whether the requesting person has hidden this agent from their own view. A personal preference: it grants or removes no access. Undefined for internal/no-session reads; always false for service tokens.","example":false},"viewer_position":{"type":"number","nullable":true,"description":"Where the requesting person placed this agent in their own order (0 = first), or null if they never placed it: those go after, by id. Undefined for internal/no-session reads.","example":0},"credential_choice":{"enum":["API_KEY","INHERIT","SUBSCRIPTION"],"type":"string","description":"DEPRECATED and no longer read: what the agent runs on is the connection in `model_provider_id`, not a credential type. Kept in the response with its stored value while the column is still there; it decides nothing. See `credential_in_effect`.","example":"INHERIT"},"model_provider_id":{"type":"number","nullable":true,"description":"The organization connection this agent starts with. It used to be inferred from its model name; now it is stored, because an organization can have SEVERAL connections of the same provider (one Ollama per machine) and \"uses Ollama\" does not say which one. Null = still inferred, which is what happens when the organization has no active connection for its provider or when the agent runs with the credential pasted into it directly. When it is null, `credential_in_effect.connection_id` says which one it inherits.","example":12},"credential_in_effect":{"description":"Which credential this agent would start with RIGHT NOW, already resolved. It is the answer to \"where does it get its credentials from?\", and it comes from the control layer precisely so the panel does not have to rebuild it: the rule — its own wins, but only if it is for the provider its model resolves to today — is easy to get wrong, and written twice it drifts out of sync (#616).","allOf":[{"$ref":"#/components/schemas/CredentialInEffectDto"}]},"model_override":{"type":"string","nullable":true,"description":"Claude model slug override. Null → use whatever the image defaults to.","example":"claude-sonnet-4-6"},"resolved_model":{"type":"string","nullable":true,"description":"Effective model the agent would spawn with now (override ?? image default). Null = claude binary default.","example":"claude-opus-4-8"},"context_floor_tokens":{"type":"number","description":"Tokens the agent loads on EVERY step before anyone speaks (memories, skills, base prompt). Lets the list compare the fleet without opening each context map. Approximate — the exact breakdown is the context manifest.","example":67000},"context_floor_memory_tokens":{"type":"number","description":"How much of that floor is short-term memories, the part usually worth moving.","example":46000},"context_floor_recommended_tokens":{"type":"number","description":"What we advise not to exceed at boot, measured against whatever really limits this agent (its session-reset cap, or the model window when uncapped).","example":60000},"context_floor_cap_tokens":{"type":"number","nullable":true,"description":"The session-reset cap this agent compacts at, or null when uncapped.","example":300000},"running_model":{"type":"string","nullable":true,"description":"Model the agent was last actually spawned with. Differs from resolved_model → a restart is pending to apply the change.","example":"claude-opus-4-7"},"running_provider":{"type":"string","nullable":true,"description":"Provider the running container was started against, recorded from the credential it actually used. Differs from credential_in_effect.provider → a restart is pending to switch provider. null = never started, or started before this was recorded.","example":"openrouter"},"analysis_score":{"type":"number","nullable":true,"description":"Latest config-analysis score 0-100 (KUJI-90). null if never analyzed.","example":72},"effort_override":{"nullable":true,"enum":["LOW","MEDIUM","HIGH","XHIGH","MAX"],"type":"string","description":"Reasoning-effort override → CLI --effort (LOW/MEDIUM/HIGH/XHIGH/MAX). The modern unified depth control; supersedes thinking on adaptive models. Null = not set (claude defaults to high).","example":"HIGH"},"thinking_enabled":{"type":"boolean","nullable":true,"description":"Force --thinking on or off for this agent. Null → use the image default.","example":true},"language":{"type":"string","nullable":true,"description":"BCP-47 / ISO language code appended to the system prompt by the image start script.","example":"es"},"response_style":{"enum":["DEFAULT","EXTENDED","SUMMARIZED","MINIMAL"],"type":"string","description":"Reply DETAIL (KUJI-53/62): DEFAULT (platform default), EXTENDED (full), SUMMARIZED (concise), or MINIMAL (just the essential).","example":"DEFAULT"},"response_format":{"enum":["PROSE","SCANNABLE"],"type":"string","description":"Reply FORMAT (KUJI-62): PROSE (normal sentences) or SCANNABLE (headline + bullets, actionable first).","example":"PROSE"},"single_message":{"type":"boolean","description":"Reply MESSAGES (KUJI-62): false = may narrate steps as separate messages; true = work silently, send ONE message per turn.","example":false},"memory_review_enabled":{"type":"boolean","description":"Weekly memory review (early Monday morning, agent time zone). Every change it finds is proposed as an approval card, never applied on its own.","example":false},"no_emoji":{"type":"boolean","description":"Reply EMOJI (KUJI-62): false = may use emojis where they aid scanning; true = NEVER use emojis (customer-facing agents).","example":false},"approval_ask_threshold":{"type":"number","description":"Approval \"ask\" threshold (0-100): the agent acts alone below it and asks the operator (approval_request with a self-assessed level) at or above it.","example":70},"approval_block_threshold":{"type":"number","description":"Approval \"block\" threshold (0-100, >= ask): at or above it the agent waits for the human no matter what; in the [ask, block) band it asks but auto-proceeds after approval_timeout_minutes.","example":90},"approval_timeout_minutes":{"type":"number","description":"Minutes the agent waits for a decision in the auto-proceed band before continuing on its own.","example":60},"approval_categories":{"type":"array","items":{"required":true,"description":"Always-ask overrides (on top of the thresholds): free-text actions that ALWAYS require approval regardless of the agent's level estimate. Empty = thresholds only.","example":["Spend money: purchases, payments or subscriptions","Deactivate a customer"],"type":"array","items":{"type":"string"}}},"disabled_tool_groups":{"description":"Tool families this agent may NOT use. Empty = it can use everything.","type":"array","items":{"type":"string"}},"memory_mb_override":{"type":"number","nullable":true,"description":"RAM ceiling for the agent container in MB. Null = inherit the server default (or unlimited if the server sets none). Per-agent override that wins over the server default.","example":2048},"cpu_override":{"type":"number","nullable":true,"description":"CPU ceiling for the agent container in cores. Null = inherit the server default (or unlimited if the server sets none). Per-agent override that wins over the server default.","example":2},"disk_gb_override":{"type":"number","nullable":true,"description":"Soft disk ceiling in whole GB. Null = inherit the server default (or none). Measured by the supervisor every ~60 s; the agent is stopped at 100 %.","example":20},"limits":{"description":"The limits in effect (override, else server default; null = none): what the container runs with after its next start.","allOf":[{"$ref":"#/components/schemas/AgentLimitsDto"}]},"disk_used_mb":{"type":"number","nullable":true,"description":"Disk the agent used at the last report, in MB (volumes + writable layer). Null = never reported.","example":16384},"disk_limit_mb":{"type":"number","nullable":true,"description":"Disk ceiling the supervisor applied at the last report, in MB.","example":20480},"disk_level":{"type":"string","nullable":true,"description":"Level at the last report: ok, warn (>= 80 %) or full (stopped at 100 %).","enum":["ok","warn","full"],"example":"ok"},"disk_reported_at":{"format":"date-time","type":"string","nullable":true,"description":"When the supervisor last reported the agent disk."},"hub_available":{"type":"boolean","description":"Whether this agent is listed as an assignee candidate in the external-conversations Agent Desk. Defaults to false so internal-only agents stay invisible to customers.","example":true},"voice_enabled":{"type":"boolean","description":"Whether the voice channel (TTS replies) is enabled for this agent.","example":false},"network_privileged":{"type":"boolean","description":"Privileged networking (KJ-156): container gets /dev/net/tun + CAP_NET_ADMIN so the agent can run a VPN (Tailscale/WireGuard/…). Applied on the next spawn.","example":false},"voice_id":{"type":"string","nullable":true,"description":"ElevenLabs voice id for this agent's TTS, or null.","example":"dNjJKg63Fr5AXwIdkATa"},"voice_speed":{"type":"number","nullable":true,"description":"Speaking speed of the agent's voice (0.7–1.2), or null for normal (1).","example":1.05},"backup_schedule":{"enum":["INHERIT","OFF","CUSTOM"],"type":"string","description":"Scheduled-backup mode: INHERIT (org default), OFF (disabled), or CUSTOM (own schedule).","example":"INHERIT"},"timezone":{"type":"string","nullable":true,"description":"Custom backup cron (UTC) when backup_schedule=CUSTOM. Null otherwise.","example":"0 */6 * * *"},"backup_retention":{"type":"number","nullable":true,"description":"Custom retention (SCHEDULED backups to keep) when CUSTOM. Null otherwise.","example":10},"cost_guard_mode":{"enum":["INHERIT","OFF","CUSTOM"],"type":"string","description":"Cost-guard mode (KUJI-51): INHERIT (org default), OFF (disabled), or CUSTOM (own threshold).","example":"INHERIT"},"cost_guard_tokens_per_hour":{"type":"number","nullable":true,"description":"Custom hourly token threshold when cost_guard_mode=CUSTOM. Null otherwise.","example":500000},"cost_guard_action":{"nullable":true,"enum":["NOTIFY","PAUSE"],"type":"string","description":"Custom guard action (NOTIFY/PAUSE) when CUSTOM. Null = inherit."},"session_reset_mode":{"enum":["INHERIT","OFF","CUSTOM"],"type":"string","description":"Session auto-reset mode (KUJI-51): INHERIT (org default), OFF (never), or CUSTOM (own token cap).","example":"INHERIT"},"session_reset_tokens":{"type":"number","nullable":true,"description":"Own compaction threshold in tokens when CUSTOM (capped to the model window on use). Null otherwise.","example":300000},"context_window":{"type":"number","description":"Context window of the model this agent would spawn with (resolved_model), the connection's declared window first.","example":1000000},"session_reset_effective_tokens":{"type":"number","description":"Tokens at which this agent's sessions compact, resolved from its mode and the org share of its model window (#698). 0 = Kujira doesn't compact (the CLI still auto-compacts at the edge of the window).","example":300000},"session_reset_inherited_tokens":{"type":"number","description":"What session_reset_effective_tokens would be in INHERIT mode, whatever the current mode: lets a client show what inheriting means for this agent before switching to it.","example":300000},"last_backup_at":{"format":"date-time","type":"string","nullable":true,"description":"Timestamp of the agent's most recent COMPLETED backup. Null if never backed up."},"container_id":{"type":"string","nullable":true,"description":"Container id assigned by Docker (null when not running)"},"last_action":{"type":"string","nullable":true,"description":"Last action reported by the agent","example":"Analyzing diff in src/auth/session.ts"},"last_action_code":{"nullable":true,"enum":["supervisor_offline","container_missing","migration_failed","agent_ready","supervisor_reconnected","operator_reset","no_container","image_pull_failed","volume_seed_failed","docker_run_failed","spawn_failed","image_update_failed","stop_after_pull_failed","recreate_failed","stop_failed","pause_failed","resume_failed","restore_failed","drain_wait","disk_limit_reached"],"type":"string","description":"Machine code for last_action, for the panel to translate. Null when the text has no code (activity, progress steps, an older supervisor): show last_action as is.","example":"container_missing"},"last_action_at":{"format":"date-time","type":"string","nullable":true,"description":"Timestamp of the last reported action"},"latest_activity":{"nullable":true,"description":"Most recent activity in this agent's conversations that the requester can see. `last_action` only carries the lifecycle; this is what the agent is actually doing. Null when there is none; undefined for internal/no-session reads.","type":"object","allOf":[{"$ref":"#/components/schemas/AgentLatestActivityDto"}]},"auth_failed_at":{"format":"date-time","type":"string","nullable":true,"description":"When the provider rejected this agent's token, or null if it works. Cleared as soon as the agent completes a turn."},"not_answering":{"nullable":true,"description":"Why this agent is not answering anyone, or null if it is answering. Not the same as status: the container can be RUNNING and healthy while every turn dies on arrival. It clears itself as soon as a turn completes.","type":"object","allOf":[{"$ref":"#/components/schemas/NotAnsweringDto"}]},"uptime_seconds":{"type":"number","nullable":true,"description":"Container uptime in seconds (null when not running)"},"tokens_used":{"type":"string","description":"Total tokens consumed by the agent (string to preserve BigInt precision)","example":"142000"},"cost_micro":{"type":"string","description":"Accumulated cost in 1e-6 currency units (string to preserve BigInt precision). 2.84 in currency = \"2840000\".","example":"2840000"},"usage_status":{"type":"string","nullable":true,"description":"Latest Anthropic rate-limit status for this agent (ALLOWED / ALLOWED_WARNING / EXCEEDED). Null when no snapshot has been received yet.","example":"ALLOWED"},"usage_resets_at":{"format":"date-time","type":"string","nullable":true,"description":"Wall-clock moment Anthropic will reset the current rate-limit window. Null when no snapshot has been received yet."},"usage_rate_limit_type":{"type":"string","nullable":true,"description":"Which rate-limit bucket triggered the event: FIVE_HOUR (rolling 5h window) or WEEKLY (Max-plan weekly cap).","example":"FIVE_HOUR"},"usage_overage_status":{"type":"string","nullable":true,"description":"Anthropic \"overage\" status (pay-as-you-go burst credit). REJECTED with overageDisabledReason \"org_level_disabled\" is the textbook \"the OAuth token belongs to a different Anthropic org than the operator expects\" diagnostic.","example":"ALLOWED"},"usage_overage_resets_at":{"format":"date-time","type":"string","nullable":true,"description":"Wall-clock moment the overage window resets."},"usage_is_overage":{"type":"boolean","nullable":true,"description":"True when the agent is currently consuming the overage allowance.","example":false},"usage_updated_at":{"format":"date-time","type":"string","nullable":true,"description":"Server timestamp when the usage snapshot was last refreshed."},"usage_windows":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"utilization":{"type":"number"},"resets_at":{"type":"string"}},"required":["key","utilization","resets_at"]}},"sync_pending":{"type":"boolean","description":"True when skills/memories changed and the supervisor has not yet picked them up","example":false},"avatar_url":{"type":"string","nullable":true,"description":"Avatar URL — points at the backend proxy that streams from R2. Includes a v=<timestamp> cache-buster so a replacement is picked up on the next render. Null when no avatar has been uploaded.","example":"/org/1/agent/3/avatar?v=1779650000000"},"template_id":{"type":"number","nullable":true,"description":"Marketplace template it was created from. Provenance only: what it copied is the organization’s. Null when created from scratch or the template was deleted.","example":1},"created_at":{"format":"date-time","type":"string","description":"Created at"},"updated_at":{"format":"date-time","type":"string","description":"Updated at"},"backup_cron":{"type":"string","nullable":true},"backup_freeze":{"type":"boolean"}},"required":["id","organization_id","server_id","image_id","name","status","credential_choice","credential_in_effect","response_style","response_format","single_message","memory_review_enabled","no_emoji","approval_ask_threshold","approval_block_threshold","approval_timeout_minutes","approval_categories","limits","hub_available","voice_enabled","network_privileged","voice_id","voice_speed","backup_schedule","cost_guard_mode","session_reset_mode","context_window","session_reset_effective_tokens","session_reset_inherited_tokens","tokens_used","cost_micro","sync_pending","created_at","updated_at","backup_cron","backup_freeze"]},"ListAgentDto":{"type":"object","properties":{"data":{"description":"Items in the current page","type":"array","items":{"$ref":"#/components/schemas/GetAgentDto"}},"total":{"type":"number","description":"Total number of items matching the query","example":42},"page":{"type":"number","description":"Current page (1-indexed)","example":1},"limit":{"type":"number","description":"Page size","example":20}},"required":["data","total","page","limit"]},"CreateAgentDto":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"pattern":"^[^\\r\\n]+$","description":"Display name (unique within the org)","example":"sales-bot-eu"},"description":{"type":"string","maxLength":500,"description":"Free-form description","example":"Handles EU leads in the sales pipeline"},"role":{"type":"string","maxLength":80,"description":"What the agent is FOR: «sales», «customer support», «clinic reception». Free text, not a closed list. Left empty, the AI Doctor proposes one from the agent identity and a person confirms it — it is never rewritten on its own.","example":"sales"},"server_id":{"type":"number","minimum":1,"description":"Server where the agent will run. Must belong to the same org. Discover it with `GET /org/{org_id}/server`.","example":1},"image_id":{"type":"number","minimum":1,"description":"AgentImage version pinned to this agent. Must be available. Omit it to use the catalogue default (the image with `is_default: true` in `GET /agent-image`), which is the usual choice.","example":1},"model_provider_id":{"type":"number","minimum":1,"description":"The organization connection this agent boots with. Omit and it is resolved from the model: the default connection of its provider. Pass an id to say which one: with several connections of the same provider, \"runs on Ollama\" does not say which machine. Must belong to this organization and be turned on. Naming one needs `model_provider.view` on top of `agent.create`: choosing from the list means being allowed to see it.","example":12},"model_override":{"type":"string","maxLength":80,"pattern":"^$|^[A-Za-z0-9._[\\]-]+$","description":"Model this agent runs, from the catalogue (any provider). Required unless `model_provider_id` is a connection that brings its own model (Ollama, OpenRouter); without either the agent is rejected with MODEL_REQUIRED. The image does not carry a model — a model of any provider is valid here as long as the org has a connection for it.","example":"claude-sonnet-5"},"template_id":{"type":"number","minimum":1,"description":"Marketplace agent template to start from (`GET /org/{org_id}/marketplace/template`). Its skills land in the library and its memories as free memories, all assigned to the agent; its identity and soul replace the generic ones. Everything copied belongs to the organization and is edited freely — editing the template never touches this agent. It brings no model, server or credentials: those are still chosen here. 404 when the organization has no marketplace or the template is not published.","example":1}},"required":["name","server_id"]},"UpdateAgentDto":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"pattern":"^[^\\r\\n]+$","description":"Display name (unique within the org)","example":"sales-bot-eu"},"image_id":{"type":"number","minimum":1,"description":"Switch the agent to a different image from the catalogue (e.g. base → flex). The image must be available. Changing it only updates the row; call POST /:id/update-image to pull the new tag and recreate the container — the volume, session_id and conversations are preserved. The server cannot be changed this way.","example":3},"description":{"type":"string","maxLength":500,"description":"Free-form description"},"role":{"type":"string","maxLength":80,"description":"What the agent is FOR: «sales», «customer support», «clinic reception». Free text, not a closed list. Left empty, the AI Doctor proposes one from the agent identity and a person confirms it — it is never rewritten on its own.","example":"sales"},"timezone":{"type":"string","nullable":true,"description":"IANA time zone the schedules are read in (\"Europe/Madrid\"). Overrides the organization zone for the crons of this agent; empty inherits. Rejected if the runtime does not know the zone.","example":"Europe/Madrid"},"credential_choice":{"enum":["API_KEY","INHERIT","SUBSCRIPTION"],"type":"string","description":"DEPRECATED and ignored since an organization can have several connections per provider: a credential TYPE no longer identifies one. Use `model_provider_id` to say which connection the agent runs on. Still accepted so a client that sends it keeps working, but it changes nothing.","example":"INHERIT"},"model_provider_id":{"type":"number","nullable":true,"minimum":1,"description":"The organization connection this agent boots with. Pass an id to pin one — the only way to choose between several connections of the SAME provider (one Ollama per machine), where \"use Ollama\" doesn't say which. Null goes back to resolving it from the agent's model. Omit and it is recomputed whenever the model changes and the current connection stops serving it. Must belong to this organization and be turned on; choosing among the connections that already exist needs `agent.edit` plus `model_provider.view` (naming one means being allowed to see the list), creating or rotating one still needs `model_provider.edit`.","example":12},"model_override":{"type":"string","maxLength":80,"pattern":"^$|^[A-Za-z0-9._[\\]-]+$","description":"Model slug override (e.g. claude-sonnet-4-6, MiniMax-M3). Must be a slug from the catalogue. Empty string clears the override and returns the agent to the image default.","example":"claude-sonnet-4-6"},"apply_model_to_conversations":{"type":"boolean","description":"Also return every conversation of this agent to its model: clears each conversation's own model and connection, so all of them run on the agent's from their next turn. Not stored on the agent.","example":true},"thinking_enabled":{"type":"boolean","nullable":true,"description":"Force --thinking on or off for this agent. Pass null to clear the override and fall back to the image default.","example":true},"effort_override":{"nullable":true,"enum":["LOW","MEDIUM","HIGH","XHIGH","MAX"],"type":"string","description":"Reasoning-effort override → CLI --effort (LOW/MEDIUM/HIGH/XHIGH/MAX). Pass null to clear and fall back to the claude default (high). Supersedes --thinking on adaptive models.","example":"HIGH"},"language":{"type":"string","maxLength":10,"pattern":"^$|^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})?$","description":"BCP-47 / ISO language code. Empty string clears the override.","example":"es"},"approval_ask_threshold":{"type":"number","minimum":0,"maximum":100,"description":"Approval \"ask\" threshold (0-100): the agent acts alone below it, asks at/above it. Takes effect on the next spawn/restart.","example":70},"approval_block_threshold":{"type":"number","minimum":0,"maximum":100,"description":"Approval \"block\" threshold (0-100, kept >= ask server-side): at/above it the agent waits no matter what; between ask and block it auto-proceeds after the timeout.","example":90},"approval_timeout_minutes":{"type":"number","minimum":1,"description":"Minutes to wait for a decision in the auto-proceed band before continuing.","example":60},"approval_categories":{"type":"array","items":{"required":false,"maxItems":50,"description":"Always-ask overrides (on top of the thresholds): free-text actions that ALWAYS require approval. Empty array = thresholds only. Blank items are dropped server-side.","example":["Gastar dinero: compras, pagos o suscripciones","Dar de baja a un cliente"],"type":"array","items":{"type":"string","maxLength":300}}},"disabled_tool_groups":{"type":"array","items":{"type":"string","enum":["credentials","scheduling","mcp_servers","third_party_access","self_management","outbound","teammates","knowledge","activities","personal_devices"]},"description":"Tool families this agent may NOT use (#302). Empty = it can use everything, which is how every agent behaved before this existed. Enforced server-side on every MCP call, not just by not announcing them.","example":["credentials","mcp_servers"]},"mcp_http_bridge":{"type":"boolean","nullable":true,"description":"Obsoleto: se acepta y se ignora (el puente MCP se ha retirado).","example":true},"response_style":{"enum":["DEFAULT","EXTENDED","SUMMARIZED","MINIMAL"],"type":"string","description":"Reply DETAIL (KUJI-53/62). DEFAULT follows the platform default; EXTENDED = full; SUMMARIZED = concise; MINIMAL = just the essential.","example":"DEFAULT"},"response_format":{"enum":["PROSE","SCANNABLE"],"type":"string","description":"Reply FORMAT (KUJI-62). PROSE = normal sentences; SCANNABLE = headline + bullets, actionable first.","example":"PROSE"},"single_message":{"type":"boolean","description":"Reply MESSAGES (KUJI-62). false = may narrate steps; true = work silently, send ONE message per turn.","example":false},"memory_review_enabled":{"type":"boolean","description":"Weekly memory review. Every change it finds is proposed as an approval card, never applied on its own.","example":false},"no_emoji":{"type":"boolean","description":"Reply EMOJI (KUJI-62). false = may use emojis where they help; true = NEVER use emojis (customer-facing).","example":false},"memory_mb_override":{"type":"number","nullable":true,"minimum":64,"maximum":131072,"description":"RAM ceiling override for this agent in MB. Null clears the override (falls back to the server default, or unlimited if the server sets none).","example":2048},"cpu_override":{"type":"number","nullable":true,"minimum":0.1,"maximum":128,"description":"CPU ceiling override for this agent in cores. Null clears the override (falls back to the server default, or unlimited if the server sets none).","example":2},"disk_gb_override":{"type":"number","nullable":true,"minimum":1,"maximum":100000,"description":"Soft disk ceiling override for this agent in whole GB (volumes + writable layer; the supervisor stops the agent at 100 %). Null clears the override (falls back to the server default, or none).","example":20},"hub_available":{"type":"boolean","description":"Whether this agent appears in the Agent Desk assignee picker. False (default) hides it from external-conversations workflows.","example":true},"voice_enabled":{"type":"boolean","description":"Enable the voice channel (TTS replies) for this agent (KUJI — voice).","example":true},"network_privileged":{"type":"boolean","description":"Privileged networking (KJ-156): spawn the container with /dev/net/tun + CAP_NET_ADMIN so the agent can run a VPN. Powerful — opt-in. Applies on the next spawn (needs a recreate).","example":false},"isolated":{"type":"boolean","description":"Obsoleto: se acepta y se ignora (todo agente corre en la variante aislada de su imagen si la tiene).","example":false},"voice_id":{"type":"string","description":"ElevenLabs voice id for this agent's TTS. Empty string clears it.","example":"dNjJKg63Fr5AXwIdkATa"},"voice_speed":{"type":"number","minimum":0.7,"maximum":1.2,"description":"Speaking speed of the agent's voice (ElevenLabs `speed`, 0.7–1.2; 1 = normal). A call can still change it for one conversation (`voice_set`).","example":1.05},"backup_schedule":{"enum":["INHERIT","OFF","CUSTOM"],"type":"string","description":"Scheduled-backup mode (KUJI-43). INHERIT (default) uses the org schedule; OFF disables backups for this agent; CUSTOM uses backup_cron/backup_retention below.","example":"INHERIT"},"backup_cron":{"type":"string","nullable":true,"description":"Custom backup schedule (cron, UTC) when backup_schedule=CUSTOM. Empty/null clears it.","example":"0 */6 * * *"},"backup_retention":{"type":"number","nullable":true,"minimum":1,"maximum":365,"description":"How many SCHEDULED backups to keep for this agent (when CUSTOM).","example":10},"backup_freeze":{"type":"boolean","description":"Freeze this agent while its disk is read, so the copy cannot catch a file half-written. It is unreachable for the duration of the tar (minutes on a big volume) and its open connections may time out, so it is off by default and the call is the operator's.","example":false},"cost_guard_mode":{"enum":["INHERIT","OFF","CUSTOM"],"type":"string","description":"Cost-guard mode (KUJI-51). INHERIT (default) uses the org guard; CUSTOM uses the threshold/action below; OFF drops both. On a standard plan OFF does NOT leave the agent unguarded: it falls back to the plan's hourly ceiling, which cannot be switched off from here. Only ENTERPRISE, which negotiates its own, can run with no guard at all.","example":"INHERIT"},"cost_guard_tokens_per_hour":{"type":"number","nullable":true,"minimum":0,"description":"Custom hourly token threshold when cost_guard_mode=CUSTOM. Null/0 clears your number; on a standard plan the plan's ceiling then applies. A value BELOW the plan's is honoured — tightening the guard is always allowed, switching it off is not.","example":500000},"cost_guard_action":{"nullable":true,"enum":["NOTIFY","PAUSE"],"type":"string","description":"Custom guard action (NOTIFY/PAUSE) when CUSTOM."},"session_reset_mode":{"enum":["INHERIT","OFF","CUSTOM"],"type":"string","description":"Conversation compaction mode (KUJI-51). INHERIT (default) uses the org share of the model window; OFF never compacts; CUSTOM uses session_reset_tokens below.","example":"INHERIT"},"session_reset_tokens":{"type":"number","nullable":true,"minimum":20000,"maximum":900000,"description":"Own compaction threshold in tokens when CUSTOM (20k–900k). Capped to the model's window on use.","example":300000}},"required":["approval_categories"]},"AgentImageCapabilityDto":{"type":"object","properties":{"id":{"type":"number","description":"Capability id","example":1},"key":{"type":"string","description":"Stable slug — the agent-base/supervisor look at this.","example":"playwright"},"label":{"type":"string","description":"Display name","example":"Playwright + Chromium"},"description":{"type":"string","nullable":true,"description":"Long description"},"icon":{"type":"string","nullable":true,"description":"Lucide icon name hint for the frontend","example":"theater"}},"required":["id","key","label"]},"GetAgentImageDto":{"type":"object","properties":{"id":{"type":"number","description":"AgentImage id","example":1},"role_key":{"type":"string","description":"Role key (stable identifier across versions)","example":"sales"},"version":{"type":"string","description":"Image version","example":"0.1.0"},"image_tag":{"type":"string","description":"Fully qualified image tag in the registry","example":"ghcr.io/calltek/kj-agent-sales:0.1.0"},"name":{"type":"string","description":"Display name","example":"Sales Agent"},"description":{"type":"string","nullable":true,"description":"Free-form description"},"icon":{"type":"string","nullable":true,"description":"Icon hint for the UI"},"color":{"type":"string","nullable":true,"description":"Color hint for the UI (hex)"},"default_memory_mb":{"type":"number","description":"Default container memory limit in MB","example":512},"default_cpu":{"type":"number","description":"Default container CPU share","example":0.5},"is_available":{"type":"boolean","description":"False to hide from selectors without removing the row"},"supports_isolation":{"type":"boolean","description":"True when an `-isolation` variant of this image is published — gates the agent \"restricted mode\" toggle.","example":false},"is_default":{"type":"boolean","description":"The image a new agent gets when the create omits `image_id`. At most one image is the default, and it is always available."},"capabilities":{"description":"Capabilities pre-installed in the image (internet, playwright, git, …). Read-only from the panel; managed by the Kujira team.","type":"array","items":{"$ref":"#/components/schemas/AgentImageCapabilityDto"}},"avatar_url":{"type":"string","nullable":true,"description":"Avatar URL (cache-busted) or null; the UI falls back to the icon.","example":"/agent-image/8/avatar?v=1717000000000"},"created_at":{"format":"date-time","type":"string","description":"Created at"},"updated_at":{"format":"date-time","type":"string","description":"Updated at"}},"required":["id","role_key","version","image_tag","name","default_memory_mb","default_cpu","is_available","supports_isolation","is_default","capabilities","created_at","updated_at"]},"ListAgentImageDto":{"type":"object","properties":{"data":{"description":"Items in the current page","type":"array","items":{"$ref":"#/components/schemas/GetAgentImageDto"}},"total":{"type":"number","description":"Total number of items matching the query","example":3},"page":{"type":"number","description":"Current page (1-indexed)","example":1},"limit":{"type":"number","description":"Page size","example":20}},"required":["data","total","page","limit"]},"GetOrganizationDto":{"type":"object","properties":{"id":{"type":"number","description":"Organization id","example":1},"name":{"type":"string","description":"Organization name","example":"Acme Labs"},"plan":{"enum":["FREE","BASIC","PRO","ENTERPRISE"],"type":"string","description":"Subscription plan","example":"FREE"},"avatar_url":{"type":"string","nullable":true,"description":"Avatar URL — points at the backend proxy that streams from R2. Includes a v=<timestamp> cache-buster so a replacement is picked up on the next render. Null when no avatar has been uploaded.","example":"/org/1/avatar?v=1779650000000"},"viewer_role":{"nullable":true,"enum":["OWNER","ADMIN","OPERATOR","VIEWER","SERVICE"],"type":"string","description":"The requesting user's role in this organization. Lets the frontend gate edit/delete UI. Null when the role can't be resolved.","example":"OWNER"},"viewer_permissions":{"description":"The viewer's effective permission patterns — what the backend will actually evaluate for them. Includes wildcards (`*`) and negations. Empty when the viewer is not a member. Use this to gate the UI; re-deriving it from the role is wrong as soon as a member's permissions are customised.","example":["agent.view","agent.edit"],"type":"array","items":{"type":"string"}},"has_elevenlabs_key":{"type":"boolean","description":"Whether an ElevenLabs API key is configured for the voice channel (KUJI — voice). The key itself is never returned.","example":false},"has_groq_key":{"type":"boolean","description":"Whether a Groq API key is configured for speech-to-text. The key itself is never returned. Without it, audio is transcribed with the ElevenLabs key if there is one; with neither, inbound voice messages are NOT transcribed — there is no platform-wide key behind them.","example":false},"has_google_oauth_app":{"type":"boolean","description":"Whether the organization uses its own Google app for connectors (#310). Without it, Google accounts cannot be connected. The secret is never returned.","example":false},"google_oauth_client_id":{"type":"string","nullable":true,"description":"The client_id of its Google app, so it can be shown and checked that it is the right one. Not secret: it travels in every authorization URL.","example":"1234-abc.apps.googleusercontent.com"},"timezone":{"type":"string","description":"Org-wide default backup schedule (cron, UTC). Null = no default; agents inherit it unless they override.","nullable":true,"example":"0 3 * * *"},"trial_ends_at":{"format":"date-time","type":"string","nullable":true,"description":"When the PRO trial ends (#395). Null = not on trial: never was, or it already ended."},"grace_until":{"format":"date-time","type":"string","nullable":true,"description":"Until when whatever no longer fits the plan is tolerated, after any downgrade (expired trial, non-payment, or a staff change). Past that date what is over the limit gets STOPPED — nothing is ever deleted. Null = no pending downgrade. This is the countdown the panel shows."},"grace_from_plan":{"nullable":true,"enum":["FREE","BASIC","PRO","ENTERPRISE"],"type":"string","description":"Which plan that downgrade came from, so it can be reported (\"from PRO to FREE\")."},"kind":{"enum":["STANDARD","PARTNER"],"type":"string","description":"PARTNER = sells Kujira to its own clients, and its own agents run under its package. STANDARD = everyone else."},"partner_id":{"type":"number","nullable":true,"description":"The partner this organization is a client of. Null = a direct client of Kujira. A partner's client is billed by its partner, and takes its limits from the partner's package."},"partner_name":{"type":"string","nullable":true,"description":"Name of the partner this organization is a client of. Null = a direct client of Kujira."},"viewer_via_partner":{"type":"boolean","description":"Whether the viewer is inside as someone of the owning partner (`partner.client.operate`) rather than as one of its own members."},"limits":{"type":"object","properties":{"max_agents":{"type":"number","nullable":true},"max_servers":{"type":"number","nullable":true},"channels_per_agent":{"type":"number","nullable":true},"min_cron_interval_minutes":{"type":"number","nullable":true},"min_mail_poll_minutes":{"type":"number","nullable":true},"doctor_fixes":{"type":"boolean"},"cron_llm_runs_per_month":{"type":"number","nullable":true},"file_versions":{"type":"number","nullable":true}},"required":["max_agents","max_servers","channels_per_agent","min_cron_interval_minutes","min_mail_poll_minutes","doctor_fixes","cron_llm_runs_per_month","file_versions"]},"support_access_until":{"format":"date-time","type":"string","nullable":true,"description":"Until when platform support can access this organization. Null or past = cannot. Granted by the owner and expires on its own."},"support_access_granted_by":{"type":"number","nullable":true,"description":"Who granted that access and when, so the customer can review it."},"support_access_granted_at":{"format":"date-time","type":"string","nullable":true},"customer_type":{"nullable":true,"enum":["BUSINESS","INDIVIDUAL"],"type":"string","description":"Business or individual (#417). Decides which data is required: a business must provide all of it, an individual under €400 only the country, because their simplified invoice does not carry the rest. `null` = not declared.","example":"BUSINESS"},"tax_name":{"type":"string","nullable":true,"description":"Name or legal entity name shown on the invoice (#417).","example":"Acme Labs SL"},"tax_id":{"type":"string","nullable":true,"description":"Tax ID (NIF/CIF/VAT). Optional: an individual may not have one.","example":"B12345678"},"tax_country":{"type":"string","nullable":true,"description":"Country in ISO 3166-1 alpha-2. This is what decides the invoice VAT, even without a tax ID: a consumer is charged the rate of THEIR country.","example":"ES"},"tax_address":{"type":"string","nullable":true,"description":"Billing address."},"tax_postal_code":{"type":"string","nullable":true,"description":"Billing postal code."},"tax_city":{"type":"string","nullable":true,"description":"Billing city."},"vat_validated":{"type":"boolean","nullable":true,"description":"Whether the tax ID was checked against VIES and came back valid. This is the proof that justifies a 0% intra-community rate."},"vat_validated_at":{"format":"date-time","type":"string","nullable":true,"description":"When that check was performed."},"backup_retention":{"type":"number","nullable":true,"description":"How many SCHEDULED backups to keep per agent (org default).","example":7},"cost_guard_tokens_per_hour":{"type":"number","nullable":true,"description":"Org-wide cost guardrail: max spend per agent per hour (1e-6 units). Null/0 = off.","example":500000},"cost_guard_action":{"enum":["NOTIFY","PAUSE"],"type":"string","description":"What the cost guard does on breach: NOTIFY or PAUSE.","example":"NOTIFY"},"session_reset_percent":{"type":"number","description":"Org-wide default: compact a live conversation once its context passes this percentage of its model's context window (#698). 100 = Kujira doesn't compact (Claude Code does at the edge). Agents inherit unless they override.","example":100},"session_reset_tokens":{"type":"number","nullable":true,"description":"Optional org token cap (#698): compact at this many tokens even if the percentage comes later. Null = no cap.","example":300000},"transcript_retention_days":{"type":"number","nullable":true,"description":"Transcript retention override in days (KUJI-113). null = the retention is derived from the plan default. Only ENTERPRISE orgs can set it.","example":365},"contracted_agents":{"type":"number","nullable":true,"description":"Agents the org pays for (#394, per-agent pricing). Governs how many agents and servers it may create. null = not contracted yet: paid plans fall back to the hard cap (50), FREE is always 1. Staff-set until billing exists.","example":3},"max_channels_per_agent":{"type":"number","nullable":true,"description":"Per-org override of the external-channel allowance per contracted agent (#394). null = plan default (BASIC 3, PRO/ENTERPRISE 5). Staff-set.","example":10},"max_hosts":{"type":"number","nullable":true,"description":"Per-org override of the device (kj-host) allowance. null = plan default (FREE 1, BASIC 5, PRO 10, ENTERPRISE bespoke). Staff-set.","example":10},"cron_llm_runs_per_month":{"type":"number","nullable":true,"description":"Per-org override of monthly cron runs that wake the agent (#394). null = plan default. Staff-set. Not enforced yet.","example":20000},"file_versions":{"type":"number","nullable":true,"description":"Per-org override of how many versions are kept per memory and per skill (#405). null = plan default. Staff-set; only ENTERPRISE reads it.","example":50},"version_retention":{"enum":["SPREAD","RECENT"],"type":"string","description":"Which versions of a memory or skill survive once its history is full (#405). SPREAD keeps them spaced across time — reaches months back with less grain in the last days; RECENT keeps only the newest. Chosen by the organization in Settings.","example":"SPREAD"},"agent_changes":{"enum":["AUTO","PERMISSIONS","DENY"],"type":"string","description":"Unattended mode for memories and skills: what an agent does with a change to them when no team member is behind the turn (a webhook, an email, a scheduled task, another agent). AUTO: it makes it. PERMISSIONS: it asks for approval. DENY: it does not make it and asks nobody (memories include its own). A channel may set its own. With a team member behind the turn their permissions always decide.","example":"PERMISSIONS"},"agent_changes_crons":{"enum":["AUTO","PERMISSIONS","DENY"],"type":"string","description":"Unattended mode for scheduled tasks. Same as agent_changes.","example":"PERMISSIONS"},"agent_changes_credentials":{"enum":["AUTO","PERMISSIONS","DENY"],"type":"string","description":"Unattended mode for credentials (credential_write): PERMISSIONS (asks for approval) or DENY (neither done nor asked).","example":"PERMISSIONS"},"agent_changes_mcp":{"enum":["AUTO","PERMISSIONS","DENY"],"type":"string","description":"Unattended mode for MCP servers (mcp_add / mcp_remove): PERMISSIONS (asks for approval) or DENY (neither done nor asked).","example":"PERMISSIONS"},"agent_changes_agent":{"enum":["AUTO","PERMISSIONS","DENY"],"type":"string","description":"Unattended mode for agent self-configuration (avatar today): PERMISSIONS (asks for approval) or DENY (neither done nor asked).","example":"PERMISSIONS"},"agent_changes_devices":{"enum":["AUTO","PERMISSIONS","DENY"],"type":"string","description":"Unattended mode for linking devices (a customer of the Agent Desk is always asked): PERMISSIONS (asks for approval) or DENY (neither done nor asked).","example":"PERMISSIONS"},"auto_archive_days":{"type":"number","nullable":true,"description":"Days without activity after which an open conversation is archived on its own (moved to RESOLVED, like the Archive button). Pinned ones are left alone. null = never.","example":30},"server_alert_ram_pct":{"type":"number","nullable":true,"description":"Org default RAM alert threshold (%) for servers (KUJI-136). null = built-in default (85).","example":85},"server_alert_cpu_pct":{"type":"number","nullable":true,"description":"Org default CPU alert threshold (%) for servers. null = built-in default (90).","example":90},"server_alert_load_factor":{"type":"number","nullable":true,"description":"Org default load alert factor (load > factor × cores). null = built-in default (1.5).","example":1.5},"features":{"type":"array","items":{"type":"string","enum":["browser","graph3d","deepseek","wordpress","marketplace"]},"description":"Features in staged rollout that this organization has. What is not listed does not exist for it: the panel hides it and its routes answer 404.","example":["browser"]},"disabled_modules":{"type":"array","items":{"type":"string","enum":["devices","browser"]},"description":"Modules this organization has switched off in Settings. Empty = every module on.","example":[]},"modules":{"type":"array","items":{"type":"string","enum":["devices","browser"]},"description":"Modules this organization has on: offered to it (a module behind a feature needs it) and not switched off. What is not listed does not exist for it: the panel hides it, its routes answer 404 and its agents do not get its tools.","example":["devices"]},"created_at":{"format":"date-time","type":"string","description":"Created at"},"updated_at":{"format":"date-time","type":"string","description":"Updated at"},"backup_cron":{"type":"string","nullable":true}},"required":["id","name","plan","viewer_permissions","has_elevenlabs_key","has_groq_key","has_google_oauth_app","kind","partner_id","partner_name","viewer_via_partner","limits","cost_guard_action","session_reset_percent","session_reset_tokens","transcript_retention_days","contracted_agents","max_channels_per_agent","max_hosts","cron_llm_runs_per_month","file_versions","version_retention","agent_changes","agent_changes_crons","agent_changes_credentials","agent_changes_mcp","agent_changes_agent","agent_changes_devices","auto_archive_days","server_alert_ram_pct","server_alert_cpu_pct","server_alert_load_factor","features","disabled_modules","modules","created_at","updated_at","backup_cron"]},"ListOrganizationDto":{"type":"object","properties":{"data":{"description":"Items in the current page","type":"array","items":{"$ref":"#/components/schemas/GetOrganizationDto"}},"total":{"type":"number","description":"Total number of items matching the query","example":42},"page":{"type":"number","description":"Current page (1-indexed)","example":1},"limit":{"type":"number","description":"Page size","example":20}},"required":["data","total","page","limit"]},"GetMemoryDto":{"type":"object","properties":{"id":{"type":"number","description":"Memory id","example":1},"name":{"type":"string","description":"Filename in /home/agent/.claude/memories/","example":"guia-marca.md"},"description":{"type":"string","nullable":true,"description":"One-line summary of what the memory is and when to use it (the skills \"name + description\" pattern). Shown in the UI without opening the file and given to the agent on recall. Null when unset (legacy/free memories).","example":"Brand voice and tone guidelines for customer replies."},"scope":{"enum":["SHORT_TERM","LONG_TERM"],"type":"string","description":"SHORT_TERM = injected at spawn time (working memory). LONG_TERM = referenced on demand via MCP tool (not yet runtime).","example":"SHORT_TERM"},"content_hash":{"type":"string","description":"SHA-256 of `content`, lets the supervisor skip a no-op rewrite"},"assignment_count":{"type":"number","description":"Number of agents this memory is assigned to. Zero = unassigned (orphan).","example":3},"created_by":{"type":"number","nullable":true,"description":"Creator user id"},"created_at":{"format":"date-time","type":"string","description":"Created at"},"updated_at":{"format":"date-time","type":"string","description":"Updated at"},"archived_at":{"format":"date-time","type":"string","nullable":true,"description":"When the memory was archived (soft-deleted). Null on live memories. Archived memories are excluded from default listings and never written to a container volume."},"canonical_kind":{"nullable":true,"enum":["IDENTITY","SOUL"],"type":"string","description":"Set when this is a canonical per-agent memory (auto-seeded on agent creation). IDENTITY = identity.md, SOUL = soul.md. Free memories return null. Canonical memories cannot be archived, deleted, or unassigned from their owning agent.","example":"IDENTITY"},"read_only":{"type":"boolean","description":"Read-only: only its editors (and OWNER/ADMIN) may change it. Reading is unaffected.","example":false}},"required":["id","name","scope","content_hash","assignment_count","created_at","updated_at","read_only"]},"ListMemoryDto":{"type":"object","properties":{"rows":{"description":"Page rows","type":"array","items":{"$ref":"#/components/schemas/GetMemoryDto"}},"total":{"type":"number","description":"Total matching rows across all pages","example":31},"limit":{"type":"number","description":"Echo of the requested page size","example":200},"offset":{"type":"number","description":"Echo of the requested offset","example":0}},"required":["rows","total","limit","offset"]},"MemoryAssignedAgentDto":{"type":"object","properties":{"id":{"type":"number","description":"Agent id","example":5},"name":{"type":"string","description":"Agent name","example":"Captain"},"avatar_url":{"type":"string","nullable":true,"description":"Avatar url (proxy endpoint)"},"assigned_at":{"format":"date-time","type":"string","description":"When the memory was assigned to this agent"}},"required":["id","name","assigned_at"]},"GetMemoryWithContentDto":{"type":"object","properties":{"id":{"type":"number","description":"Memory id","example":1},"name":{"type":"string","description":"Filename in /home/agent/.claude/memories/","example":"guia-marca.md"},"description":{"type":"string","nullable":true,"description":"One-line summary of what the memory is and when to use it (the skills \"name + description\" pattern). Shown in the UI without opening the file and given to the agent on recall. Null when unset (legacy/free memories).","example":"Brand voice and tone guidelines for customer replies."},"scope":{"enum":["SHORT_TERM","LONG_TERM"],"type":"string","description":"SHORT_TERM = injected at spawn time (working memory). LONG_TERM = referenced on demand via MCP tool (not yet runtime).","example":"SHORT_TERM"},"content_hash":{"type":"string","description":"SHA-256 of `content`, lets the supervisor skip a no-op rewrite"},"assignment_count":{"type":"number","description":"Number of agents this memory is assigned to. Zero = unassigned (orphan).","example":3},"created_by":{"type":"number","nullable":true,"description":"Creator user id"},"created_at":{"format":"date-time","type":"string","description":"Created at"},"updated_at":{"format":"date-time","type":"string","description":"Updated at"},"archived_at":{"format":"date-time","type":"string","nullable":true,"description":"When the memory was archived (soft-deleted). Null on live memories. Archived memories are excluded from default listings and never written to a container volume."},"canonical_kind":{"nullable":true,"enum":["IDENTITY","SOUL"],"type":"string","description":"Set when this is a canonical per-agent memory (auto-seeded on agent creation). IDENTITY = identity.md, SOUL = soul.md. Free memories return null. Canonical memories cannot be archived, deleted, or unassigned from their owning agent.","example":"IDENTITY"},"read_only":{"type":"boolean","description":"Read-only: only its editors (and OWNER/ADMIN) may change it. Reading is unaffected.","example":false},"content":{"type":"string","description":"Markdown content of the memory"},"assignments":{"description":"Agents this memory is currently assigned to","type":"array","items":{"$ref":"#/components/schemas/MemoryAssignedAgentDto"}},"viewer_can_edit":{"type":"boolean","description":"Whether the viewer may change it: every agent that has it under their management and, if read-only, being one of its editors. Absent = true (older backend)."},"viewer_can_grant":{"type":"boolean","description":"Whether the viewer reaches it whole (manages every agent that has it), needed to mark it read-only or choose its editors (plus memory.grant)."}},"required":["id","name","scope","content_hash","assignment_count","created_at","updated_at","read_only","content","assignments"]},"CreateMemoryDto":{"type":"object","properties":{"name":{"type":"string","minLength":4,"maxLength":512,"description":"S3-style key written to /home/agent/.claude/memories/<name>. May contain `/` to nest under folders. Must end in `.md`. The parent folder is created automatically if missing.","example":"comercial/oferta-q2.md"},"content":{"type":"string","minLength":0,"maxLength":200000,"description":"Markdown content","example":"# Guía de marca\n\n- Tono cordial…"},"description":{"type":"string","minLength":0,"maxLength":1024,"description":"One-line summary of what the memory is and when to use it (skills \"name + description\" pattern). Shown in the UI and given to the agent on recall. Optional — keep the body clean markdown, the summary lives here.","example":"Brand voice and tone guidelines for customer replies."},"scope":{"enum":["SHORT_TERM","LONG_TERM"],"type":"string","description":"SHORT_TERM = injected at /home/agent/.claude/memories/ on spawn. LONG_TERM = MCP-fetched on demand (runtime TBD).","example":"SHORT_TERM"},"force":{"type":"boolean","description":"Override the credential-in-content check (#309). When the detector flags a likely API key / PEM / connection string, the save is rejected with a 400 — pass `force: true` only if you know the match is an example, not a real secret. The detector still audits the attempt either way.","example":false}},"required":["name","content","scope"]},"UpdateMemoryDto":{"type":"object","properties":{"name":{"type":"string","minLength":4,"maxLength":512,"description":"Filename (full path including folders)","example":"ventas/oferta-q2.md"},"content":{"type":"string","minLength":0,"maxLength":200000,"description":"Markdown content"},"description":{"type":"string","minLength":0,"maxLength":1024,"description":"One-line summary (skills \"name + description\" pattern). Pass an empty string to clear it.","example":"Brand voice and tone guidelines for customer replies."},"scope":{"enum":["SHORT_TERM","LONG_TERM"],"type":"string"},"force":{"type":"boolean","description":"Override the credential-in-content check (#309). When the detector flags a likely API key / PEM / connection string, the save is rejected with a 400 — pass `force: true` only if you know the match is an example, not a real secret. The detector still audits the attempt either way.","example":false}}},"MemoryLockDto":{"type":"object","properties":{"read_only":{"type":"boolean","description":"Read-only: only the people and agents below (and OWNER/ADMIN) may change it. Reading is unaffected.","example":true},"member_ids":{"uniqueItems":true,"description":"Members (user ids) who may still change it. Replaces the list.","example":[7],"type":"array","items":{"type":"number","minimum":1}},"agent_ids":{"uniqueItems":true,"description":"Agents that may still change it. An ask from another agent only passes if every agent in the chain is here.","example":[3],"type":"array","items":{"type":"number","minimum":1}}},"required":["read_only","member_ids","agent_ids"]},"SetMemoryAgentsDto":{"type":"object","properties":{"agent_ids":{"uniqueItems":true,"description":"Full list of agents this memory should be assigned to. The service syncs: new ids are added, missing ids are removed. Pass [] to detach the memory from every agent.","example":[3,5],"type":"array","items":{"type":"number","minimum":1}}},"required":["agent_ids"]},"GetWebhookConfigDto":{"type":"object","properties":{"outbound_url":{"type":"string","nullable":true,"description":"Outbound POST URL"},"has_outbound_secret":{"type":"boolean","description":"Whether an outbound HMAC secret is set","example":false},"has_inbound_secret":{"type":"boolean","description":"Whether an inbound HMAC secret is set","example":true},"signature_header":{"type":"string","nullable":true,"description":"Header the inbound signature (plain HMAC-SHA256 hex) travels in. Null = default (x-kujira-signature). Plane: x-plane-signature.","example":"x-plane-signature"},"echo_filter":{"type":"object","properties":{"actor_paths":{"type":"array","items":{"type":"string"}},"self_ids":{"type":"array","items":{"type":"string"}}},"required":["actor_paths","self_ids"]},"contact_mapping":{"type":"object","properties":{"id_paths":{"type":"array","items":{"type":"string"}},"name_paths":{"type":"array","items":{"type":"string"}},"email_paths":{"type":"array","items":{"type":"string"}},"phone_paths":{"type":"array","items":{"type":"string"}}},"required":["id_paths","name_paths","email_paths","phone_paths"]}},"required":["has_outbound_secret","has_inbound_secret","echo_filter"]},"GetGithubOAuthDto":{"type":"object","properties":{"authorized":{"type":"boolean","description":"Whether the channel is authorized to act on GitHub","example":true},"user_login":{"type":"string","nullable":true,"description":"The GitHub account the channel posts as (login). Null when not authorized.","example":"n0v4-SYS"},"expires_at":{"type":"string","nullable":true,"description":"When the current access token expires (ISO). Null = non-expiring / unauthorized."}},"required":["authorized"]},"GetGithubConfigDto":{"type":"object","properties":{"installation_id":{"type":"number","nullable":true,"description":"The GitHub App installation id this channel rides.","example":82345678},"repos":{"description":"Repo full_names listened to. ['*'] = all repos.","example":["calltek/kj-backend"],"type":"array","items":{"type":"string"}},"events":{"description":"Event types listened to. ['*'] = all subscribed events.","example":["pull_request","pull_request_review"],"type":"array","items":{"type":"string"}},"listen_scope":{"type":"string","description":"Listening scope: \"all\" = every event of the configured repos/events wakes the agent; \"involved\" = only events that concern it (review requested / assigned / @-mentioned / its own PRs), GitHub-notifications style.","enum":["all","involved"],"example":"all"},"raw_payload":{"type":"boolean","description":"Whether each event carries the full raw webhook payload. False by default — it is ~3.000 tokens per event that the agent re-reads on every step, for ~61 that mean something (#272).","example":false},"oauth":{"description":"Per-channel OAuth identity: whether the channel is authorized to post on GitHub AS a bot account, and as whom (Fase 3).","allOf":[{"$ref":"#/components/schemas/GetGithubOAuthDto"}]}},"required":["repos","events","listen_scope","raw_payload","oauth"]},"GetGoogleConnectionDto":{"type":"object","properties":{"connected":{"type":"boolean","description":"Whether the agent has a Google account connected","example":true},"google_email":{"type":"string","nullable":true,"description":"The connected Gmail address. Null when not connected.","example":"soporte@gmail.com"}},"required":["connected"]},"GetDriveConfigDto":{"type":"object","properties":{"folder_id":{"type":"string","description":"Google Drive folder id being watched","example":"1AbCdEfGhIjKlMnOpQrStUvWxYz"},"folder_name":{"type":"string","description":"Folder display name","example":"Facturas"}},"required":["folder_id","folder_name"]},"GetCalendarEntryDto":{"type":"object","properties":{"id":{"type":"string","description":"Google Calendar id","example":"primary"},"name":{"type":"string","description":"Calendar display name","example":"Agenda Calltek"}},"required":["id","name"]},"GetCalendarConfigDto":{"type":"object","properties":{"calendar_whitelist":{"description":"Calendar allowlist. EMPTY = every calendar of the connected account is processed; entries restrict to those calendars.","type":"array","items":{"$ref":"#/components/schemas/GetCalendarEntryDto"}},"reminder_lead_minutes":{"type":"number","nullable":true,"description":"Reminder lead in minutes (a heads-up before each event starts). Null = changes only.","example":15}},"required":["calendar_whitelist"]},"GetWhatsappConfigDto":{"type":"object","properties":{"linked":{"type":"boolean","description":"Whether an account is linked (someone scanned the code).","example":true},"phone_number":{"type":"string","nullable":true,"description":"The linked account's phone number.","example":"34600111222"},"display_name":{"type":"string","nullable":true,"description":"The name the account displays on WhatsApp.","example":"Sergio"},"linked_at":{"type":"string","nullable":true,"description":"When it was linked.","example":"2026-08-31T10:00:00.000Z"}},"required":["linked","phone_number","display_name","linked_at"]},"GetWordpressConfigDto":{"type":"object","properties":{"site_url":{"type":"string","description":"The site URL the channel was created for.","example":"https://tienda.ejemplo.com"},"paired_at":{"type":"string","nullable":true,"description":"When the plugin last proved it holds the secret (hello). Null = not paired yet.","example":"2026-11-09T10:00:00.000Z"},"pairing_pending":{"type":"boolean","description":"Whether a pairing code is outstanding (not redeemed, not expired).","example":false},"wp_version":{"type":"string","nullable":true,"description":"WordPress version reported at hello.","example":"6.9"},"php_version":{"type":"string","nullable":true,"description":"PHP version reported at hello.","example":"8.3.12"},"plugin_version":{"type":"string","nullable":true,"description":"Plugin version reported at hello.","example":"0.1.0"},"config_version":{"type":"number","description":"Grows with every change; the plugin reports the one it holds."},"events":{"description":"The events the channel listens to.","type":"array","items":{"type":"string"}},"preset":{"type":"string","nullable":true,"description":"The preset applied when the site pairs. Null once paired (see act)."},"act":{"type":"object","properties":{"groups":{"type":"object","additionalProperties":{"type":"string"}},"escalate_untrusted":{"type":"boolean"}},"required":["groups","escalate_untrusted"]}},"required":["site_url","paired_at","pairing_pending","wp_version","php_version","plugin_version","config_version","events","preset","act"]},"GetNotifyConversationDto":{"type":"object","properties":{"id":{"type":"number","description":"Conversation id","example":42},"ref":{"type":"string","nullable":true,"description":"Public conversation ref. Null when the reader cannot see the conversation.","example":"MZROL9UC"},"title":{"type":"string","nullable":true,"description":"Session title (operator sessions). Null = unnamed/default, or the reader cannot see the conversation.","example":"Avisos infra"},"contact_name":{"type":"string","nullable":true,"description":"Contact on the other side (the session owner). Kept when the reader cannot see the conversation; null when there is no name.","example":"Jonatan"},"visible":{"type":"boolean","description":"Whether the reader can see this conversation. False = another member's private session: the destination and its owner are shown, but its title and ref are withheld.","example":true},"transport":{"enum":["INTERNAL","WHATSAPP","GMAIL","DISCORD","TELEGRAM","SLACK","WEBHOOK","EMAIL","GITHUB","DRIVE","CALENDAR","HOST","WORDPRESS"],"type":"string","description":"Transport of the conversation channel","example":"TELEGRAM"}},"required":["id","visible","transport"]},"GetChannelDto":{"type":"object","properties":{"id":{"type":"number","description":"Channel id","example":1},"agent_id":{"type":"number","description":"Owning agent id","example":3},"transport":{"enum":["INTERNAL","WHATSAPP","GMAIL","DISCORD","TELEGRAM","SLACK","WEBHOOK","EMAIL","GITHUB","DRIVE","CALENDAR","HOST","WORDPRESS"],"type":"string","description":"Transport","example":"TELEGRAM"},"scope":{"enum":["EXTERNAL","INTERNAL"],"type":"string","description":"Scope: EXTERNAL (Agent Desk-facing) or INTERNAL (operators/rooms)","example":"EXTERNAL"},"agent_changes":{"nullable":true,"enum":["AUTO","PERMISSIONS","DENY"],"type":"string","description":"Unattended mode for memories and skills on this channel's threads with no team member behind them. Null = the organization's.","example":null},"agent_changes_crons":{"nullable":true,"enum":["AUTO","PERMISSIONS","DENY"],"type":"string","description":"Same for scheduled tasks. Null = the organization's.","example":null},"agent_changes_credentials":{"nullable":true,"enum":["AUTO","PERMISSIONS","DENY"],"type":"string","description":"Unattended mode for credentials (credential_write) on this channel: PERMISSIONS or DENY. Null = the organization's.","example":null},"agent_changes_mcp":{"nullable":true,"enum":["AUTO","PERMISSIONS","DENY"],"type":"string","description":"Unattended mode for MCP servers (mcp_add / mcp_remove) on this channel: PERMISSIONS or DENY. Null = the organization's.","example":null},"agent_changes_agent":{"nullable":true,"enum":["AUTO","PERMISSIONS","DENY"],"type":"string","description":"Unattended mode for the agent's own configuration (avatar today) on this channel: PERMISSIONS or DENY. Null = the organization's.","example":null},"agent_changes_devices":{"nullable":true,"enum":["AUTO","PERMISSIONS","DENY"],"type":"string","description":"Unattended mode for linking devices (a customer of the Agent Desk is always asked) on this channel: PERMISSIONS or DENY. Null = the organization's.","example":null},"direction":{"enum":["INBOUND","OUTBOUND","BIDIRECTIONAL"],"type":"string","description":"Traffic direction: INBOUND (receive only), OUTBOUND (send only), BIDIRECTIONAL (both).","example":"BIDIRECTIONAL"},"name":{"type":"string","description":"Display name","example":"Soporte Telegram"},"target":{"type":"string","description":"Transport-side target (bot @username for Telegram)","example":"@kujira_support_bot"},"host_id":{"type":"number","nullable":true,"description":"HOST transport: the kj-host device this channel links to. Null otherwise.","example":12},"host_deleted":{"type":"object","properties":{"name":{"type":"string"},"at":{"type":"string"}},"required":["name","at"]},"owner_contact_id":{"type":"number","nullable":true,"description":"HOST transport: the contact who owns this device. A device with an owner is private — only that person and the device reach it. Null otherwise.","example":42},"owner_user_id":{"type":"number","nullable":true,"description":"HOST transport: when the owner is a team member, their user id. Null otherwise.","example":7},"host_layers":{"nullable":true,"type":"array","items":{"type":"string","enum":["OBSERVE","FILES","TERMINAL","SCREEN","CHAT","CONTROL","BROWSER"]},"description":"HOST transport: the layers this agent may use on the device (the ceiling over what the person grants). Null otherwise.","example":["OBSERVE","CHAT","SCREEN"]},"host_control_minutes":{"type":"number","nullable":true,"description":"HOST transport: how long a mouse-and-keyboard session of this agent may last (minutes). Null otherwise.","example":60},"host_trust_chains":{"type":"boolean","nullable":true,"description":"HOST transport: acts without a prompt when another agent or a scheduled task asked, on a chain the team started. Null otherwise.","example":false},"host_events":{"nullable":true,"type":"array","items":{"type":"string","enum":["CONNECTION","PERMISSIONS","RESOURCES","PRESENCE","POWER","BATTERY"]},"description":"HOST transport: the device events that wake this agent (CONNECTION, PERMISSIONS, RESOURCES, PRESENCE, POWER, BATTERY). Null otherwise.","example":["CONNECTION","PERMISSIONS"]},"bot_username":{"type":"string","nullable":true,"description":"Bot username (Telegram)"},"has_token":{"type":"boolean","description":"Whether a bot token is configured (the token itself is never returned)","example":true},"has_signing_secret":{"type":"boolean","description":"SLACK: whether the signing secret is configured (the secret is never returned). Distinguishes \"not yet configured\" from \"one is already set, leave it blank to keep it\".","example":true},"whitelist":{"description":"Inbound whitelist: origin chat/channel ids this channel accepts messages from (Telegram chat id / Discord channel id). Empty means the channel accepts nothing — ids are added explicitly.","example":["1512401528445730899"],"type":"array","items":{"type":"string"}},"label_whitelist":{"description":"GMAIL only: label allowlist. Empty = every inbound email is processed; non-empty = only mail carrying at least one of these Gmail labels gets through (the rest is marked read and skipped). Always empty for other transports.","example":["Kujira"],"type":"array","items":{"type":"string"}},"always_draft":{"type":"boolean","description":"EMAIL/GMAIL: everything the agent writes is left in the mailbox drafts folder instead of being sent. Binds internal channels only (customer threads are reviewed in the Agent Desk). Always false for other transports.","example":false},"recipient_whitelist":{"description":"EMAIL/GMAIL: whom the agent may write to (addresses or *@domain). Empty = anyone; to anyone else it leaves a draft. Always empty for other transports.","example":["*@example.com"],"type":"array","items":{"type":"string"}},"mention_off":{"description":"Per-chat-id mention exceptions. Every group chat_id requires an @bot mention by DEFAULT before forwarding; chat_ids in this list opt OUT of that — the agent answers every message in those groups. DMs are never gated. Empty list means \"default everywhere\": all groups require a mention.","example":["-100500"],"type":"array","items":{"type":"string"}},"webhook_registered":{"type":"boolean","description":"Whether the inbound webhook has been registered with the provider","example":true},"inbound_path":{"type":"string","nullable":true,"description":"For WEBHOOK channels: the relative inbound path external systems POST to (the secret IS the URL — not a provider auth token). Null for token-based transports, whose webhook_secret stays private.","example":"/webhooks/webhook/ab12cd34"},"invite_url":{"type":"string","nullable":true,"description":"For DISCORD channels: the bot invite URL (built from the public Application ID) with minimal messaging permissions. Null for other transports or when the client_id is unknown.","example":"https://discord.com/oauth2/authorize?client_id=123&scope=bot&permissions=68608"},"webhook":{"nullable":true,"description":"For WEBHOOK channels: editable config (outbound URL, whether secrets are set, signature header + prefix). Secrets are never returned. Null for non-WEBHOOK transports.","type":"object","allOf":[{"$ref":"#/components/schemas/GetWebhookConfigDto"}]},"github":{"nullable":true,"description":"For GITHUB channels: the installation + repo/event filters. Null for other transports.","type":"object","allOf":[{"$ref":"#/components/schemas/GetGithubConfigDto"}]},"google":{"nullable":true,"description":"For GMAIL/DRIVE/CALENDAR channels: the agent's Google connection status. Null for other transports.","type":"object","allOf":[{"$ref":"#/components/schemas/GetGoogleConnectionDto"}]},"drive":{"nullable":true,"description":"For DRIVE channels: the watched folder. Null for other transports.","type":"object","allOf":[{"$ref":"#/components/schemas/GetDriveConfigDto"}]},"calendar":{"nullable":true,"description":"For CALENDAR channels: the watched calendar. Null for other transports.","type":"object","allOf":[{"$ref":"#/components/schemas/GetCalendarConfigDto"}]},"whatsapp":{"nullable":true,"description":"For WHATSAPP channels: which account is linked. Null for other transports.","type":"object","allOf":[{"$ref":"#/components/schemas/GetWhatsappConfigDto"}]},"wordpress":{"nullable":true,"description":"WORDPRESS: the site and its pairing.","type":"object","allOf":[{"$ref":"#/components/schemas/GetWordpressConfigDto"}]},"instructions":{"type":"string","nullable":true,"description":"Operating instructions for an EVENT channel (WEBHOOK/GITHUB/DRIVE/CALENDAR) or a mail channel (EMAIL/GMAIL): what the agent does with the events/emails from this channel, injected as a <system-reminder> per message. Null when unset or for chat transports.","example":"Review PRs where you are requested; approve if clean, else comment."},"default_assignee_mode":{"enum":["OWNER_AGENT","USER","UNASSIGNED"],"type":"string","description":"Who a NEW conversation on this channel is assigned to: OWNER_AGENT (the agent answers from the first message), USER (it lands on default_assignee_user_id and the agent is not handed the turn automatically) or UNASSIGNED (it waits in the Agent Desk queue). Conversations already open keep whatever assignee they have — this only decides the next ones. It is ROUTING, not containment: the agent still owns the channel, so it can read these conversations and write to them through its tools; what changes is who gets the turn by default. Only meaningful on EXTERNAL conversational channels — on event channels (WEBHOOK/GITHUB/DRIVE/CALENDAR/HOST) and INTERNAL ones the agent always takes the turn, and any other mode is 400.","example":"OWNER_AGENT"},"default_assignee_user_id":{"type":"number","nullable":true,"description":"The person new conversations go to. Only set when default_assignee_mode is USER. They must be a member of this organization WITH access to the channel agent — otherwise the conversation would land on a desk they cannot open.","example":7},"reply_mode":{"enum":["SEND","DRAFT"],"type":"string","description":"SEND = the agent sends what it writes (default). DRAFT holds EVERYTHING the agent emits on this channel — not just its answer to a customer: a channel_send notice or an approval request is held too, waiting for someone to send it from the inbox. So do not put an internal channel in DRAFT expecting its notices to keep arriving. What a PERSON writes from the panel is never held.","example":"SEND"},"model_override":{"type":"string","nullable":true,"description":"Default model for this channel's conversations (what one with no model of its own runs on, ahead of the agent's). Null = the agent's.","example":"claude-haiku-4-5"},"model_connection":{"type":"object","properties":{"id":{"type":"number"},"name":{"type":"string"},"provider":{"type":"string"},"model":{"type":"string","nullable":true}},"required":["id","name","provider","model"]},"handoff_user_id":{"type":"number","nullable":true,"description":"Where a conversation goes when the agent says it cannot help (its `conversation_handoff` tool). Null = no destination: the hand-off leaves the thread unassigned in the queue.","example":7},"notify_conversation_id":{"type":"number","nullable":true,"description":"Escalation destination (#567): the INTERNAL conversation of this same agent where notices/approval prompts born on this channel land (channel_send without an explicit target resolves here). Null = not configured.","example":42},"notify_conversation":{"nullable":true,"description":"Summary of the escalation destination, to name it in the panel. Null when unset.","type":"object","allOf":[{"$ref":"#/components/schemas/GetNotifyConversationDto"}]},"is_active":{"type":"boolean","description":"Active flag","example":true},"inactive_reason":{"type":"string","nullable":true,"description":"Why the channel was deactivated — set by the DRIVE/CALENDAR poller when it cannot get a usable token for the linked connector (missing link or needs reconnecting). Always null while is_active."},"created_at":{"format":"date-time","type":"string","description":"Created at"},"last_activity_at":{"format":"date-time","type":"string","nullable":true,"description":"Last activity at"},"alert_recipient_user_ids":{"description":"Who receives the alerts raised in this channel THREADS THAT BELONG TO NOBODY — a group room, a scheduled task, an unassigned Agent Desk contact. Threads that do belong to someone (an employee chatting with the agent) notify that person instead and never read this. EMPTY MEANS NOBODY, not everybody: what was never configured does not interrupt anyone.","example":[4,9],"type":"array","items":{"type":"number"}}},"required":["id","agent_id","transport","scope","direction","name","target","has_token","has_signing_secret","whitelist","label_whitelist","always_draft","recipient_whitelist","mention_off","webhook_registered","wordpress","default_assignee_mode","reply_mode","is_active","created_at","alert_recipient_user_ids"]},"EchoFilterDto":{"type":"object","properties":{"actor_paths":{"description":"Dot-paths into the payload where the event's actor lives (provider-specific, arrays are traversed). E.g. [\"sender.login\"] for GitHub, [\"history_items.user.id\"] for ClickUp.","type":"array","items":{"type":"string","maxLength":256}},"self_ids":{"description":"The agent's OWN identities in that system. An inbound event whose actor matches one of these is dropped. E.g. [\"n0v4-SYS\"].","type":"array","items":{"type":"string","maxLength":256}}}},"ContactMappingDto":{"type":"object","properties":{"id_paths":{"description":"Dot-paths to the person's id, e.g. [\"customer.id\"]. Required to map contacts.","type":"array","items":{"type":"string","maxLength":256}},"name_paths":{"description":"Dot-paths to the person's name.","type":"array","items":{"type":"string","maxLength":256}},"email_paths":{"description":"Dot-paths to the person's email.","type":"array","items":{"type":"string","maxLength":256}},"phone_paths":{"description":"Dot-paths to the person's phone.","type":"array","items":{"type":"string","maxLength":256}}}},"WebhookConfigDto":{"type":"object","properties":{"outbound_url":{"type":"string","maxLength":2048,"format":"uri","description":"URL the agent POSTs to (outbound)."},"outbound_secret":{"type":"string","nullable":true,"maxLength":255,"description":"Shared secret to HMAC-sign outbound posts. On PATCH: a string sets it, null removes it, omitting/\"\" keeps the current one."},"inbound_secret":{"type":"string","nullable":true,"maxLength":255,"description":"HMAC secret for INBOUND verification. When set, every incoming POST must carry a valid HMAC-SHA256 of the raw body in the configured header. On PATCH: a string sets it, null removes it (disabling verification), omitting/\"\" keeps the current one. Stored encrypted."},"signature_header":{"type":"string","maxLength":128,"description":"Header carrying the inbound signature (plain HMAC-SHA256 hex). Defaults to x-kujira-signature. Plane uses x-plane-signature, GitHub x-hub-signature-256."},"echo_filter":{"description":"Anti-echo: where the actor lives in the payload + the agent's own ids. Events whose actor is the agent are dropped. On PATCH the whole block is replaced; an empty block clears the filter.","allOf":[{"$ref":"#/components/schemas/EchoFilterDto"}]},"contact_mapping":{"nullable":true,"description":"Customer-facing channels only: where the person who writes lives in the payload, so each one is their own contact and thread in the Agent Desk. On PATCH the whole block is replaced; null or no id paths turns it off.","type":"object","allOf":[{"$ref":"#/components/schemas/ContactMappingDto"}]}}},"ImapConfigDto":{"type":"object","properties":{"host":{"type":"string","maxLength":255},"port":{"type":"number","minimum":1,"maximum":65535},"user":{"type":"string","maxLength":255},"pass":{"type":"string","maxLength":1024},"tls":{"type":"boolean"}},"required":["host","port","user","pass"]},"SmtpConfigDto":{"type":"object","properties":{"host":{"type":"string","maxLength":255},"port":{"type":"number","minimum":1,"maximum":65535},"user":{"type":"string","maxLength":255},"pass":{"type":"string","maxLength":1024},"from":{"type":"string","maxLength":320,"format":"email"}},"required":["host","port","user","pass","from"]},"EmailConfigDto":{"type":"object","properties":{"imap":{"description":"IMAP receive config","allOf":[{"$ref":"#/components/schemas/ImapConfigDto"}]},"smtp":{"description":"SMTP send config","allOf":[{"$ref":"#/components/schemas/SmtpConfigDto"}]}},"required":["imap","smtp"]},"GithubChannelConfigDto":{"type":"object","properties":{"installation_id":{"type":"number","description":"The org's GitHub App installation id this channel rides.","example":82345678},"repos":{"description":"Repo full_names to listen to. Empty/omit = all repos of the installation.","example":["calltek/kj-backend"],"type":"array","items":{"type":"string","maxLength":140}},"events":{"description":"Event types to listen to (pull_request, issues, push…). Empty/omit = all subscribed events.","example":["pull_request","pull_request_review"],"type":"array","items":{"type":"string","maxLength":60}},"listen_scope":{"enum":["all","involved"],"type":"string","description":"Listening scope, GitHub-notifications style. \"all\" (default): every event of the configured repos/events wakes the agent. \"involved\": only events that concern the agent — review requested of it, assigned to it, @-mentioned, or activity on a PR/issue it authored. Needs the agent's GitHub connector (its identity) to resolve; with no connector it falls back to \"all\" (fail-open, never a missed review).","example":"involved"},"raw_payload":{"type":"boolean","description":"Attach the full raw webhook payload to each event. Off by default: measured on production it is ~3.000 tokens per event of API template URLs and repeated user objects, for ~61 tokens of meaning — and the agent re-reads it on every step of that turn. Turn it on only for a channel whose skill genuinely parses an exotic field.","example":false}},"required":["installation_id"]},"WordpressChannelConfigDto":{"type":"object","properties":{"site_url":{"type":"string","maxLength":500,"description":"The site URL, HTTPS on its default port. A subdirectory install keeps its path. It is what the plugin has to report when it pairs.","example":"https://tienda.ejemplo.com"},"preset":{"enum":["READ_ONLY","EDITOR","FULL","FULL_NO_ASK"],"type":"string","description":"What the agent may do once the site is paired. Default FULL (Control total). Ignored on an INBOUND channel, which only listens."}},"required":["site_url"]},"DriveChannelConfigDto":{"type":"object","properties":{"folder_id":{"type":"string","minLength":1,"maxLength":255,"description":"Google Drive folder id to watch (from the folder picker). New or modified files in it become events for the agent.","example":"1AbCdEfGhIjKlMnOpQrStUvWxYz"},"folder_name":{"type":"string","maxLength":255,"description":"Folder display name (labels the Agent Desk thread and the event text).","example":"Facturas"}},"required":["folder_id"]},"CalendarChannelConfigDto":{"type":"object","properties":{"reminder_lead_minutes":{"type":"number","minimum":1,"maximum":1440,"description":"Optional reminder lead: the agent also gets a heads-up this many minutes before each event starts (one per occurrence). Omit/0 = changes only.","example":15}}},"CreateChannelGlobalDto":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120,"description":"Display name for the channel","example":"Soporte Telegram"},"scope":{"enum":["EXTERNAL","INTERNAL"],"type":"string","description":"Scope. INTERNAL (default): you and your team — never creates external contacts, never in the Agent Desk. EXTERNAL: reaches customers and surfaces in the Agent Desk; only where the transport allows it and only on plans that include the Agent Desk (PRO and up), otherwise 403 PLAN_FEATURE_UNAVAILABLE.","example":"INTERNAL"},"direction":{"enum":["INBOUND","OUTBOUND","BIDIRECTIONAL"],"type":"string","description":"Traffic direction. BIDIRECTIONAL (default): receives and replies. INBOUND: receives only (the agent acts via tools/API, never replies down the channel). OUTBOUND: sends only, never ingests inbound (pure notifications; a WEBHOOK OUTBOUND channel requires outbound_url). Event channels (GITHUB/DRIVE/CALENDAR) are always INBOUND.","example":"BIDIRECTIONAL"},"transport":{"enum":["INTERNAL","WHATSAPP","GMAIL","DISCORD","TELEGRAM","SLACK","WEBHOOK","EMAIL","GITHUB","DRIVE","CALENDAR","HOST","WORDPRESS"],"type":"string","description":"Transport. TELEGRAM/DISCORD use a bot token; WEBHOOK uses webhook config; EMAIL uses imap/smtp config.","example":"TELEGRAM"},"bot_token":{"type":"string","minLength":10,"description":"Bot token for TELEGRAM/DISCORD/SLACK (validated against the provider and stored encrypted). Not used for WEBHOOK or EMAIL.","example":"123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"},"signing_secret":{"type":"string","minLength":10,"description":"Slack app signing secret. Required for SLACK: used to verify that every inbound message really comes from Slack. Stored encrypted.","example":"your-app-signing-secret"},"destination":{"type":"string","minLength":1,"maxLength":255,"description":"Destination the agent sends to. Required for DISCORD (target text-channel / DM id). Ignored for Telegram.","example":"123456789012345678"},"connector_id":{"type":"number","minimum":1,"description":"Id of an existing GOOGLE connector for GMAIL/DRIVE/CALENDAR (from the org's connector list). Picks which of the org's Google accounts this channel operates through — and becomes the agent's active Google identity, same as PUT .../channel/:id/connector. Omit to keep the agent's current one (or connect a new account afterwards, for GMAIL).","example":4},"webhook":{"description":"WEBHOOK transport config (outbound).","allOf":[{"$ref":"#/components/schemas/WebhookConfigDto"}]},"email":{"description":"EMAIL transport config (imap + smtp).","allOf":[{"$ref":"#/components/schemas/EmailConfigDto"}]},"github":{"description":"GITHUB transport config (installation + repo/event filters).","allOf":[{"$ref":"#/components/schemas/GithubChannelConfigDto"}]},"wordpress":{"description":"WORDPRESS transport config (the site and the starting preset).","allOf":[{"$ref":"#/components/schemas/WordpressChannelConfigDto"}]},"drive":{"description":"DRIVE transport config (the folder to watch).","allOf":[{"$ref":"#/components/schemas/DriveChannelConfigDto"}]},"calendar":{"description":"CALENDAR transport config (the calendar to watch + reminder lead).","allOf":[{"$ref":"#/components/schemas/CalendarChannelConfigDto"}]},"host_id":{"type":"number","minimum":1,"description":"HOST transport: the kj-host device this «device» channel links the agent to. N channels per device (one per agent). Defaults: scope INTERNAL, direction INBOUND.","example":12},"owner_contact_id":{"type":"number","minimum":1,"description":"HOST transport: whose machine this is — a contact of this agent. A device with an owner is PRIVATE: only that person (from any channel of theirs) and the device itself reach it, and its chat is their own conversation. Null/omitted = the team machine. For a team member, owner_user_id.","example":42},"owner_user_id":{"type":"number","minimum":1,"description":"HOST transport: a team member as the owner (instead of owner_contact_id) — resolved to their own contact with this agent. They must reach the agent.","example":7},"host_layers":{"type":"array","items":{"type":"string","enum":["OBSERVE","FILES","TERMINAL","SCREEN","CHAT","CONTROL","BROWSER"]},"description":"HOST transport: the layers this agent may use on the device — a ceiling over what the person grants there (it never grants anything). Omitted = all but BROWSER and CHAT (opt-in).","example":["OBSERVE","CHAT","SCREEN"]},"host_control_minutes":{"type":"number","minimum":5,"maximum":240,"description":"HOST transport: how long a mouse-and-keyboard session of this agent may last, in minutes (a timeout for an agent stuck in a loop; the person ends it before). Omitted = 60.","example":60},"host_trust_chains":{"type":"boolean","description":"HOST transport: let this agent act on the device WITHOUT a prompt when another agent or a scheduled task asked it — only on a chain the team started (a member, or a cron a member created), never for mouse-and-keyboard control, and the Ask/Automatic mode of the device still applies. Omitted = off.","example":false},"host_events":{"type":"array","items":{"type":"string","enum":["CONNECTION","PERMISSIONS","RESOURCES","PRESENCE","POWER","BATTERY"]},"description":"HOST transport: what happening on the device wakes this agent with a system turn — CONNECTION (it disconnects for a few minutes / comes back), PERMISSIONS (the person changes «Active», the granted layers or the mode), RESOURCES (CPU, RAM or disk over the organization's thresholds), PRESENCE (the person is back at the machine), POWER (it powered on or resumed), BATTERY (low and discharging) — the last three only if the device's app can detect them (Host.events_supported). Whatever the direction. Every kind needs OBSERVE granted on the machine and in the channel ceiling: without it nothing is delivered. Omitted = CONNECTION + PERMISSIONS; [] = none. Only for a HOST channel.","example":["CONNECTION","PERMISSIONS"]},"instructions":{"type":"string","maxLength":8000,"description":"Operating instructions for an EVENT channel (WEBHOOK/GITHUB/DRIVE/CALENDAR) or a mail channel (EMAIL/GMAIL): what the agent should do with the events/emails from this channel. Injected as a <system-reminder> with every forwarded message, so it never bloats the agent's permanent context. Markdown, optional. Ignored for chat transports (Telegram/Discord act on memory).","example":"Review PRs where you are requested; approve if clean, else comment."},"model_override":{"type":"string","maxLength":80,"description":"Default model for this channel's conversations: what one with no model of its own runs on, ahead of the agent's. A model of the agent's provider. Omit or empty = the agent's.","example":"claude-haiku-4-5"},"model_provider_id":{"type":"number","nullable":true,"minimum":1,"description":"Connection the default model runs on, for one of another provider than the agent's (same rule as a conversation's own connection; needs model_provider.view). Omitted with a model of another provider: that provider's default connection.","example":12},"agent_id":{"type":"number","minimum":1,"description":"Agent this channel belongs to","example":1}},"required":["name","transport","agent_id"]},"UpdateGithubConfigDto":{"type":"object","properties":{"repos":{"description":"Repo full_names to listen to. Empty = all repos of the installation.","example":["calltek/kj-backend"],"type":"array","items":{"type":"string","maxLength":140}},"events":{"description":"Event types to listen to. Empty = all subscribed events.","example":["pull_request","issue_comment"],"type":"array","items":{"type":"string","maxLength":60}},"listen_scope":{"enum":["all","involved"],"type":"string","description":"Listening scope: \"all\" = every event; \"involved\" = only events that concern the agent (review requested / assigned / @-mentioned / its own PRs), GitHub-notifications style.","example":"involved"},"raw_payload":{"type":"boolean","description":"Attach the full raw webhook payload to each event. Off by default: measured on production it is ~3.000 tokens per event of API template URLs and repeated user objects, for ~61 tokens of meaning — and the agent re-reads it on every step of that turn. Turn it on only for a channel whose skill genuinely parses an exotic field.","example":false}}},"UpdateChannelDto":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120,"description":"Display name","example":"Soporte Telegram"},"agent_changes":{"nullable":true,"enum":["AUTO","PERMISSIONS","DENY"],"type":"string","description":"Unattended mode for memories and skills on this channel (AUTO / PERMISSIONS); null = the organization's. Changing it requires organization.edit."},"agent_changes_crons":{"nullable":true,"enum":["AUTO","PERMISSIONS","DENY"],"type":"string","description":"Same for scheduled tasks; null = the organization's. Changing it requires organization.edit."},"agent_changes_credentials":{"nullable":true,"enum":["AUTO","PERMISSIONS","DENY"],"type":"string","description":"Unattended mode for credentials (credential_write) on this channel: PERMISSIONS or DENY (never AUTO); null = the organization's. Changing it requires organization.edit."},"agent_changes_mcp":{"nullable":true,"enum":["AUTO","PERMISSIONS","DENY"],"type":"string","description":"Unattended mode for MCP servers (mcp_add / mcp_remove) on this channel: PERMISSIONS or DENY (never AUTO); null = the organization's. Changing it requires organization.edit."},"agent_changes_agent":{"nullable":true,"enum":["AUTO","PERMISSIONS","DENY"],"type":"string","description":"Unattended mode for the agent's own configuration (avatar today) on this channel: PERMISSIONS or DENY (never AUTO); null = the organization's. Changing it requires organization.edit."},"agent_changes_devices":{"nullable":true,"enum":["AUTO","PERMISSIONS","DENY"],"type":"string","description":"Unattended mode for linking devices (a customer of the Agent Desk is always asked) on this channel: PERMISSIONS or DENY (never AUTO); null = the organization's. Changing it requires organization.edit."},"scope":{"enum":["EXTERNAL","INTERNAL"],"type":"string","description":"Scope: EXTERNAL (Agent Desk-facing) or INTERNAL (operators/rooms)"},"owner_contact_id":{"type":"number","nullable":true,"minimum":1,"description":"HOST transport: whose machine this is — a contact of this agent. A device with an owner is PRIVATE: only that person and the device itself reach it, and its chat is their own conversation. null clears it; omitted keeps it. For a team member, owner_user_id.","example":42},"owner_user_id":{"type":"number","nullable":true,"minimum":1,"description":"HOST transport: a team member as the owner (instead of owner_contact_id) — resolved to their own contact with this agent. null clears the owner.","example":7},"host_layers":{"type":"array","items":{"type":"string","enum":["OBSERVE","FILES","TERMINAL","SCREEN","CHAT","CONTROL","BROWSER"]},"description":"HOST transport: the layers this agent may use on the device — a ceiling over what the person grants there (it never grants anything). Omitted keeps it.","example":["OBSERVE","CHAT","SCREEN"]},"host_control_minutes":{"type":"number","minimum":5,"maximum":240,"description":"HOST transport: how long a mouse-and-keyboard session of this agent may last, in minutes (a timeout for an agent stuck in a loop; the person ends it before). Omitted keeps it.","example":60},"host_trust_chains":{"type":"boolean","description":"HOST transport: let this agent act on the device WITHOUT a prompt when another agent or a scheduled task asked it — only on a chain the team started (a member, or a cron a member created), never for mouse-and-keyboard control, and the Ask/Automatic mode of the device still applies. Omitted keeps it.","example":false},"host_events":{"type":"array","items":{"type":"string","enum":["CONNECTION","PERMISSIONS","RESOURCES","PRESENCE","POWER","BATTERY"]},"description":"HOST transport: what happening on the device wakes this agent with a system turn — CONNECTION, PERMISSIONS, RESOURCES, PRESENCE, POWER, BATTERY. The whole list ([] = none). Omitted keeps it. Only for a HOST channel.","example":["CONNECTION","PERMISSIONS","RESOURCES"]},"direction":{"enum":["INBOUND","OUTBOUND","BIDIRECTIONAL"],"type":"string","description":"Traffic direction: INBOUND (receive only), OUTBOUND (send only), BIDIRECTIONAL (both). Switching a WEBHOOK to OUTBOUND requires an outbound_url. Ignored for event channels (GITHUB/DRIVE/CALENDAR), which stay INBOUND."},"bot_token":{"type":"string","minLength":10,"description":"New bot token to rotate to. Validated + stored encrypted; never returned."},"signing_secret":{"type":"string","minLength":10,"description":"New Slack signing secret. Stored encrypted and never returned. Leave empty to keep the existing one."},"mention_off":{"uniqueItems":true,"description":"Per-chat-id mention exceptions. Group chat_ids in this list opt OUT of the require-mention default — the agent answers every message in them. Send the FULL intended list (the server replaces, not merges). No effect on DMs.","type":"array","items":{"type":"string"}},"webhook":{"description":"WEBHOOK transport config. Editable in place: outbound URL/secret, inbound HMAC secret, signature header + prefix. Secrets are only rewritten when present (empty/omitted = keep the current one).","allOf":[{"$ref":"#/components/schemas/WebhookConfigDto"}]},"github":{"description":"GITHUB transport config, editable in place: the listening scope (all/involved) and the repo/event filters. Only present fields change.","allOf":[{"$ref":"#/components/schemas/UpdateGithubConfigDto"}]},"instructions":{"type":"string","maxLength":8000,"description":"Operating instructions for an EVENT channel (WEBHOOK/GITHUB/DRIVE/CALENDAR) or a mail channel (EMAIL/GMAIL), injected as a <system-reminder> with each forwarded message. A string sets it, an empty string clears it, omitting keeps the current one."},"default_assignee_mode":{"enum":["OWNER_AGENT","USER","UNASSIGNED"],"type":"string","description":"Who a NEW conversation on this channel is assigned to. OWNER_AGENT (default) = the agent that owns the channel answers from the first message; USER = it lands on the desk of default_assignee_user_id and the turn is not dispatched to the agent, so a person triages it first; UNASSIGNED = nobody, it waits in the Agent Desk queue. Only affects conversations born AFTER the change — the ones already open keep their assignee. This decides WHO IS ASSIGNED and whether the turn is dispatched automatically; it does NOT restrict what the owner agent can read or send on the channel (it keeps owning these contacts). 400 on event channels (WEBHOOK/GITHUB/DRIVE/CALENDAR/HOST) and INTERNAL ones for anything other than OWNER_AGENT: there the turn always reaches the agent, so the setting would be inert."},"default_assignee_user_id":{"type":"number","nullable":true,"minimum":1,"description":"The person new conversations go to. Required when default_assignee_mode is USER (and only allowed there — moving to any other mode clears it). Must be a member of this organization AND have access to the channel agent, since the Agent Desk is scoped per agent: assigning to someone outside that scope would park the conversation on a desk they cannot open. Omitting it keeps the current one; null is only accepted together with a mode other than USER, which clears it anyway.","example":7},"reply_mode":{"enum":["SEND","DRAFT"],"type":"string","description":"Does the agent SEND what it writes here, or leave it for a person to review? SEND (default) is what channels do today. DRAFT holds EVERYTHING the agent emits on this channel — not just its answer to a customer: a channel_send notice or an approval request is held too, waiting for someone to send it from the inbox. So do not put an internal channel in DRAFT expecting its notices to keep arriving. What a PERSON writes from the panel is never held (whoever writes it already is the review). Applies to what is written AFTER the change."},"reply_mode_all_conversations":{"type":"boolean","description":"With reply_mode (or alone, with the current one): apply it to every conversation already on this channel, not only to the ones it opens from now on. Without it, existing conversations keep their own mode.","example":false},"always_draft":{"type":"boolean","description":"EMAIL/GMAIL on an internal channel: leave everything the agent writes in the mailbox drafts folder instead of sending it.","example":true},"recipient_whitelist":{"maxItems":200,"description":"EMAIL/GMAIL: whom the agent may write to — addresses (ana@example.com) or domains (*@example.com). What it writes to anyone else is left as a draft. Replaces the whole list; [] lets it write to anyone.","example":["*@example.com"],"type":"array","items":{"type":"string"}},"model_override":{"type":"string","maxLength":80,"description":"Default model for this channel's conversations: what one with no model of its own runs on, ahead of the agent's. One of another provider runs on a connection (model_provider_id, or that provider's default). Empty string, with no connection, clears it (they run on the agent's again).","example":"claude-haiku-4-5"},"model_provider_id":{"type":"number","nullable":true,"minimum":1,"description":"Connection the default model runs on (same rule as a conversation’s own connection; needs model_provider.view). null = the agent’s connection.","example":12},"handoff_user_id":{"type":"number","nullable":true,"minimum":1,"description":"Where a conversation goes when the AGENT says it cannot help (its `conversation_handoff` tool). Must be a member of this organization WITH access to the channel agent — same scope rule as default_assignee_user_id, and it bites harder here: the hand-off also drops the agent, so a destination outside that scope leaves a customer thread owned by someone who cannot open it. null = no destination: a hand-off just leaves the thread unassigned in the Agent Desk queue. Omitting keeps the current one. Independent of default_assignee_mode.","example":7},"notify_conversation_id":{"type":"number","nullable":true,"minimum":1,"description":"Escalation destination (#567): id of an INTERNAL conversation of this same agent (operator thread / internal room) where notices and approval prompts born on this channel land. Must live on a channel that can send (not inbound-only) and never on a customer thread. null clears it, omitting keeps the current one.","example":42},"is_active":{"type":"boolean","description":"Active flag. A DRIVE/CALENDAR channel the poller could not run (see inactive_reason on the channel) is set to false automatically — flip it back to true after fixing the underlying connector to resume polling; that also clears inactive_reason. Setting this to false stops the channel from ingesting or sending — it is a containment switch, not a credential revocation: it does NOT rotate webhook_secret or invalidate a Discord/Telegram bot token. If the channel was compromised (leaked secret, stolen token), delete it (or rotate the bot token) instead of just disabling it. When rotating a Discord bot token, write the new one HERE FIRST and only revoke the old one in Discord afterwards: the replica that owns the socket closes it on its next lease check because the stored token no longer matches, so rotating here is what actually kills the old connection. Revoking in Discord first still works, but the ugly way: the socket reconnects against a dead credential until Discord rejects it (close code 4004), and the channel then DEACTIVATES ITSELF with an inactive_reason — so you end up having to flip this back to true by hand after storing the new token. DISCORD takes effect within ~15s when turning off (the bot's gateway session is closed on its next lease check) and up to ~30s when turning back on (picked up by the periodic connect sweep) — other transports take effect immediately. That ~15s is only the socket teardown: containment itself is immediate on every replica — ingestion, outbound sends and HITL approval buttons all read the flag per message, so nothing gets through in the meantime. GITHUB: turning this off does not defer webhook deliveries for replay later — GitHub does not retry a webhook that got a 2xx, so events that arrive while the channel is off are dropped for good."}}},"AddToWhitelistDto":{"type":"object","properties":{"origin_id":{"type":"string","minLength":1,"maxLength":255,"description":"Origin chat/channel id to accept inbound from (Telegram chat id / Discord channel id). Added to the channel whitelist.","example":"1512401528445730899"}},"required":["origin_id"]},"GetActivityOriginDto":{"type":"object","properties":{"kind":{"enum":["INTERNAL_CHAT","CHANNEL","CRONJOB","AGENT","SUBAGENT"],"type":"string","description":"Where it came from"},"label":{"type":"string","description":"Channel, cron room or contact name — as people recognise it","example":"kj-backend"},"transport":{"type":"string","nullable":true,"description":"Channel transport when kind is CHANNEL","example":"GITHUB"}},"required":["kind","label"]},"GetActivityExternalRefDto":{"type":"object","properties":{"label":{"type":"string","description":"Link text","example":"calltek/kj-app#551"},"url":{"type":"string","description":"An http(s) link, or a path inside the panel","example":"https://github.com/calltek/kj-app/pull/551"}},"required":["label","url"]},"GetActivityLinkDto":{"type":"object","properties":{"id":{"type":"number","description":"Link id","example":1},"kind":{"type":"string","description":"What it is","enum":["GITHUB_PR"]},"ref":{"type":"string","description":"Its name where it lives","example":"calltek/kj-app#551"},"url":{"type":"string","description":"Where it is","example":"https://github.com/calltek/kj-app/pull/551"},"state":{"enum":["OPEN","DRAFT","MERGED","CLOSED"],"type":"string","description":"Where it stands"},"automerge":{"type":"boolean","description":"Merged as soon as it is green and approved"},"facts":{"type":"object","additionalProperties":true,"nullable":true,"description":"The live picture as last read (for a PR: title, additions, deletions, checks, reviews, mergeable)"},"synced_at":{"format":"date-time","type":"string","nullable":true,"description":"Last read"}},"required":["id","kind","ref","url","state","automerge"]},"GetActivityEventDto":{"type":"object","properties":{"kind":{"enum":["CREATED","NOTE","WAITING","RESUMED","REFRESH_REQUESTED","REMINDED","ABANDONED","DONE","FAILED","CANCELLED"],"type":"string","description":"What happened"},"at":{"format":"date-time","type":"string","description":"When"},"text":{"type":"string","nullable":true,"description":"A line of detail"}},"required":["kind","at"]},"GetActivityDto":{"type":"object","properties":{"id":{"type":"number","description":"Activity id","example":1},"agent_id":{"type":"number","description":"Agent it belongs to","example":1},"status":{"enum":["RUNNING","WAITING","DONE","FAILED","CANCELLED"],"type":"string","description":"Where it is in its life"},"created_at":{"format":"date-time","type":"string","description":"Created at"},"updated_at":{"format":"date-time","type":"string","description":"Last change"},"visible":{"type":"boolean","description":"Whether the viewer can see the conversation it lives in — and so its details"},"title":{"type":"string","nullable":true,"description":"What it is"},"summary":{"type":"string","nullable":true,"description":"Where it stands"},"icon":{"type":"string","nullable":true,"description":"Icon name (session set)"},"color":{"type":"string","nullable":true,"description":"Colour key (session set)"},"origin":{"nullable":true,"description":"Where it came from","type":"object","allOf":[{"$ref":"#/components/schemas/GetActivityOriginDto"}]},"conversation_id":{"type":"number","nullable":true,"description":"The conversation the work happens in"},"parent_id":{"type":"number","nullable":true,"description":"Parent activity"},"external_ref":{"nullable":true,"description":"What it is about","type":"object","allOf":[{"$ref":"#/components/schemas/GetActivityExternalRefDto"}]},"wait_reason":{"nullable":true,"enum":["APPROVAL","CONTACT_REPLY","CHILDREN","SCHEDULED_CHECK","MERGE"],"type":"string","description":"What a WAITING activity waits on"},"next_check_at":{"format":"date-time","type":"string","nullable":true,"description":"When a WAITING activity looks again"},"finished_at":{"format":"date-time","type":"string","nullable":true,"description":"When it closed"},"links":{"description":"What it follows outside Kujira (empty when not visible)","type":"array","items":{"$ref":"#/components/schemas/GetActivityLinkDto"}},"events":{"description":"The latest 50 lines of its own timeline, oldest first (empty when not visible)","type":"array","items":{"$ref":"#/components/schemas/GetActivityEventDto"}}},"required":["id","agent_id","status","created_at","updated_at","visible","links","events"]},"HostOwnerDto":{"type":"object","properties":{"id":{"type":"number","description":"User id","example":7},"name":{"type":"string","description":"Name","example":"Ana"},"avatar_url":{"type":"string","nullable":true,"description":"The person's avatar (backend proxy path), or null when they have none","example":"/v1/user/7/avatar?v=1717000000000"}},"required":["id","name"]},"HostFilesLimitsDto":{"type":"object","properties":{"all":{"type":"boolean","description":"Every folder is allowed (the list is then ignored)","example":false},"folders":{"description":"Folders FILES may touch","example":[],"type":"array","items":{"type":"string"}}},"required":["all","folders"]},"HostTerminalLimitsDto":{"type":"object","properties":{"all":{"type":"boolean","description":"Every command is allowed (the list is then ignored)","example":false},"commands":{"description":"Commands TERMINAL may run","example":[],"type":"array","items":{"type":"string"}}},"required":["all","commands"]},"HostAccessLimitsDto":{"type":"object","properties":{"files":{"description":"FILES bounds","allOf":[{"$ref":"#/components/schemas/HostFilesLimitsDto"}]},"terminal":{"description":"TERMINAL bounds","allOf":[{"$ref":"#/components/schemas/HostTerminalLimitsDto"}]}},"required":["files","terminal"]},"GetHostTagDto":{"type":"object","properties":{"id":{"type":"number","description":"Tag id","example":1},"name":{"type":"string","description":"Name","example":"Oficina Madrid"},"color":{"type":"string","description":"Theme palette color key","enum":["accent","emerald","sky","violet","amber","rose","red","teal","fuchsia","slate"],"example":"sky"}},"required":["id","name","color"]},"GetHostDto":{"type":"object","properties":{"id":{"type":"number","description":"Host id","example":1},"organization_id":{"type":"number","description":"Organization id","example":1},"name":{"type":"string","description":"Display name","example":"portatil-ana"},"owner":{"nullable":true,"description":"The member who is the device's chat person (its «device» channels follow them); null = nobody. It no longer decides who sees or uses the device — see assignees. Kept for compatibility; it will go.","type":"object","allOf":[{"$ref":"#/components/schemas/HostOwnerDto"}]},"assignees":{"description":"The people assigned to the device: besides OWNER/ADMIN, only they see it, and a team member only gets an agent to use it on their own turns if they are assigned.","type":"array","items":{"$ref":"#/components/schemas/HostOwnerDto"}},"sealed":{"type":"boolean","description":"Always false. A device is shown whole to whoever may see it, and not at all to anyone else. Kept for compatibility; it will go.","example":false},"hostname":{"type":"string","nullable":true,"description":"Hostname reported by the binary"},"machine_id":{"type":"string","nullable":true,"description":"Machine identity reported by the binary (unique per org)"},"os":{"type":"string","nullable":true,"description":"Operating system label"},"host_version":{"type":"string","nullable":true,"description":"kj-host binary version"},"status":{"enum":["UNPAIRED","ONLINE","OFFLINE"],"type":"string","description":"Lifecycle status","example":"UNPAIRED"},"disabled":{"type":"boolean","description":"Whether the host is cut (disabled) — the handshake refuses it","example":false},"active":{"type":"boolean","description":"Whether the «active» switch is on in the binary. When off, an agent can only read data (OBSERVE) and ask the person to switch on; when on, it may use the allowed layers.","example":false},"agents_enabled":{"type":"boolean","description":"Kujira's own switch for this device. When off, no agent reaches it (tools, chat, events) whatever the person granted; it stays connected and its consent stays as it is.","example":true},"allowed_layers":{"type":"array","items":{"type":"string","enum":["OBSERVE","FILES","TERMINAL","SCREEN","CHAT","CONTROL","BROWSER"]},"description":"Access layers the person ticked in the binary, reported by the machine. What any agent linked to this device may use (with the active switch on).","example":["OBSERVE"]},"limits":{"description":"Bounds of the acting layers, set by the person in the binary: the folders FILES may touch and the commands TERMINAL may run, or all of either. Nothing allowed by default.","allOf":[{"$ref":"#/components/schemas/HostAccessLimitsDto"}]},"action_mode":{"enum":["ASK","AUTO"],"type":"string","description":"How the acting layers run, chosen by the person in the binary: ASK (each write or command waits for them to allow it) or AUTO (runs inside the limits without asking).","example":"ASK"},"events_supported":{"type":"array","items":{"type":"string","enum":["CONNECTION","PERMISSIONS","RESOURCES","PRESENCE","POWER","BATTERY"]},"description":"The device events this machine's app can produce on its operating system (PRESENCE, POWER, BATTERY), as a «device» channel subscribes to them. Empty = none (an older app, or nothing detectable there): a subscription to a missing one never fires.","example":["PRESENCE","POWER"]},"events_disabled":{"type":"array","items":{"type":"string","enum":["CONNECTION","PERMISSIONS","RESOURCES","PRESENCE","POWER","BATTERY"]},"description":"The device events the person switched off on the machine: no agent hears them, whatever its «device» channel subscribes to. Empty = none switched off.","example":["PRESENCE"]},"last_seen_at":{"format":"date-time","type":"string","nullable":true,"description":"Last heartbeat timestamp"},"cpu_percent":{"type":"number","nullable":true,"description":"CPU usage (0..100), last telemetry"},"ram_percent":{"type":"number","nullable":true,"description":"RAM usage (0..100), last telemetry"},"disk_percent":{"type":"number","nullable":true,"description":"Disk usage (0..100), last telemetry"},"uptime_seconds":{"type":"number","nullable":true,"description":"Uptime in seconds, last telemetry"},"provisioning_expires_at":{"format":"date-time","type":"string","nullable":true,"description":"Provisioning token expiry"},"needs_new_token":{"type":"boolean","description":"The host is UNPAIRED with its provisioning token already spent (e.g. revoked from the machine): re-pairing needs a fresh token regenerated from the panel, not the existing expiry.","example":false},"tags":{"description":"The organization's tags on this device, by name (also on another member's private device)","type":"array","items":{"$ref":"#/components/schemas/GetHostTagDto"}},"provisioning_token":{"type":"string","description":"Provisioning token (only at create/regenerate time)"},"created_at":{"format":"date-time","type":"string","description":"Created at"},"updated_at":{"format":"date-time","type":"string","description":"Updated at"}},"required":["id","organization_id","name","assignees","sealed","status","disabled","active","agents_enabled","allowed_layers","limits","action_mode","events_supported","events_disabled","needs_new_token","tags","created_at","updated_at"]},"ListHostDto":{"type":"object","properties":{"data":{"description":"Items in the current page","type":"array","items":{"$ref":"#/components/schemas/GetHostDto"}},"total":{"type":"number","description":"Total number of items matching the query","example":42},"page":{"type":"number","description":"Current page (1-indexed)","example":1},"limit":{"type":"number","description":"Page size","example":20}},"required":["data","total","page","limit"]},"CreateHostDto":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"description":"Display name (unique within the org)","example":"portatil-ana"},"hostname":{"type":"string","maxLength":120,"description":"Free-form hostname hint (the binary refreshes it on connect)","example":"ANA-LAPTOP"},"owner_user_id":{"type":"number","minimum":1,"description":"The device's chat person, a member: they are assigned to it from the start. Omitted = nobody assigned (OWNER/ADMIN see it and assign people).","example":7}},"required":["name"]},"CredentialAgentDto":{"type":"object","properties":{"id":{"type":"number","description":"Agent id","example":5},"name":{"type":"string","description":"Agent name","example":"Captain"},"avatar_url":{"type":"string","nullable":true,"description":"Avatar url (proxy endpoint)"}},"required":["id","name"]},"CredentialMemberDto":{"type":"object","properties":{"user_id":{"type":"number","description":"User id","example":7},"name":{"type":"string","description":"User name","example":"Ana"},"email":{"type":"string","description":"User email","example":"ana@acme.com"},"avatar_url":{"type":"string","nullable":true,"description":"Avatar url (proxy endpoint)"},"level":{"enum":["USE","MANAGE"],"type":"string","description":"USE = an agent may use it in their turns; MANAGE = also see, reveal, edit and assign it."},"granted_at":{"format":"date-time","type":"string","description":"When access was granted"}},"required":["user_id","name","email","level","granted_at"]},"GetCredentialDto":{"type":"object","properties":{"id":{"type":"number","description":"Credential id","example":1},"name":{"type":"string","description":"Unique name per org","example":"STRIPE_SECRET"},"description":{"type":"string","nullable":true,"description":"Description"},"kind":{"type":"string","nullable":true,"description":"Free-form kind for the UI"},"destinations":{"description":"Where this credential is meant to be used. Empty = not set.","type":"array","items":{"type":"string"}},"usable_with_clients":{"type":"boolean","description":"Whether the agent may use it in a turn with a client (external contact).","example":false},"team_can_use":{"type":"boolean","description":"Whether any member may have an agent use it in their turns (team credential). Off = only the people on its access list and OWNER/ADMIN.","example":false},"assignment_count":{"type":"number","description":"Number of agents this credential is assigned to. Zero = unassigned.","example":3},"agents":{"description":"Agents this credential is assigned to (id, name, avatar). Enough to render them on a list row — the full assignment records, with who granted them and when, come with the detail endpoint.","type":"array","items":{"$ref":"#/components/schemas/CredentialAgentDto"}},"field_count":{"type":"number","description":"Number of fields","example":2},"created_by":{"type":"number","nullable":true,"description":"Creator user id"},"created_by_agent_id":{"type":"number","nullable":true,"description":"Agent that saved it (credential_write). Null when a person or a token did."},"created_at":{"format":"date-time","type":"string","description":"Created at"},"updated_at":{"format":"date-time","type":"string","description":"Updated at"},"archived_at":{"format":"date-time","type":"string","nullable":true,"description":"When archived (soft-deleted). Null on live credentials."},"last_used_at":{"format":"date-time","type":"string","nullable":true,"description":"Last time an agent got its value (read, keychain, browser fill, cron env). Null = never."},"members":{"description":"Operators with access, each at a level. Empty = OWNER/ADMIN only. With a manager and not open to the team it is that person's private credential: OWNER/ADMIN see it but do not open it.","type":"array","items":{"$ref":"#/components/schemas/CredentialMemberDto"}}},"required":["id","name","destinations","usable_with_clients","team_can_use","assignment_count","agents","field_count","created_at","updated_at","members"]},"ListCredentialDto":{"type":"object","properties":{"rows":{"description":"Page rows","type":"array","items":{"$ref":"#/components/schemas/GetCredentialDto"}},"total":{"type":"number","description":"Total matching rows across all pages","example":47},"limit":{"type":"number","description":"Echo of the requested page size","example":50},"offset":{"type":"number","description":"Echo of the requested offset","example":0}},"required":["rows","total","limit","offset"]},"CredentialFieldDto":{"type":"object","properties":{"id":{"type":"number","description":"Field id","example":10},"label":{"type":"string","description":"Field label","example":"api_key"},"type":{"enum":["TEXT","USERNAME","PASSWORD","EMAIL","URL","API_KEY","OTP","NOTE","SSH_PRIVATE_KEY","SSH_PUBLIC_KEY"],"type":"string","description":"Field type"},"position":{"type":"number","description":"Display order","example":0},"has_value":{"type":"boolean","description":"Whether a value is set. The value itself is never returned here.","example":true}},"required":["id","label","type","position","has_value"]},"CredentialAssignedAgentDto":{"type":"object","properties":{"id":{"type":"number","description":"Agent id","example":5},"name":{"type":"string","description":"Agent name","example":"Captain"},"avatar_url":{"type":"string","nullable":true,"description":"Avatar url (proxy endpoint)"},"assigned_at":{"format":"date-time","type":"string","description":"When the credential was assigned to this agent"}},"required":["id","name","assigned_at"]},"GetCredentialWithFieldsDto":{"type":"object","properties":{"id":{"type":"number","description":"Credential id","example":1},"name":{"type":"string","description":"Unique name per org","example":"STRIPE_SECRET"},"description":{"type":"string","nullable":true,"description":"Description"},"kind":{"type":"string","nullable":true,"description":"Free-form kind for the UI"},"destinations":{"description":"Where this credential is meant to be used. Empty = not set.","type":"array","items":{"type":"string"}},"usable_with_clients":{"type":"boolean","description":"Whether the agent may use it in a turn with a client (external contact).","example":false},"team_can_use":{"type":"boolean","description":"Whether any member may have an agent use it in their turns (team credential). Off = only the people on its access list and OWNER/ADMIN.","example":false},"assignment_count":{"type":"number","description":"Number of agents this credential is assigned to. Zero = unassigned.","example":3},"agents":{"description":"Agents this credential is assigned to (id, name, avatar). Enough to render them on a list row — the full assignment records, with who granted them and when, come with the detail endpoint.","type":"array","items":{"$ref":"#/components/schemas/CredentialAgentDto"}},"field_count":{"type":"number","description":"Number of fields","example":2},"created_by":{"type":"number","nullable":true,"description":"Creator user id"},"created_by_agent_id":{"type":"number","nullable":true,"description":"Agent that saved it (credential_write). Null when a person or a token did."},"created_at":{"format":"date-time","type":"string","description":"Created at"},"updated_at":{"format":"date-time","type":"string","description":"Updated at"},"archived_at":{"format":"date-time","type":"string","nullable":true,"description":"When archived (soft-deleted). Null on live credentials."},"last_used_at":{"format":"date-time","type":"string","nullable":true,"description":"Last time an agent got its value (read, keychain, browser fill, cron env). Null = never."},"members":{"description":"Operators with access, each at a level. Empty = OWNER/ADMIN only. With a manager and not open to the team it is that person's private credential: OWNER/ADMIN see it but do not open it.","type":"array","items":{"$ref":"#/components/schemas/CredentialMemberDto"}},"fields":{"description":"Fields (label + type only, no values)","type":"array","items":{"$ref":"#/components/schemas/CredentialFieldDto"}},"assignments":{"description":"Agents this credential is assigned to","type":"array","items":{"$ref":"#/components/schemas/CredentialAssignedAgentDto"}},"viewer_can_open":{"type":"boolean","description":"Whether whoever asked can open it: reveal, edit, assign to an agent or change who has it. False = they only see it, and may archive, delete or remove it from agents."},"viewer_assignable_agent_ids":{"description":"Agents whoever asked can give this credential to or take it from: the ones they manage, plus, for a private credential they manage, the ones they chat with. Absent = every agent they see (older backend).","type":"array","items":{"type":"number"}}},"required":["id","name","destinations","usable_with_clients","team_can_use","assignment_count","agents","field_count","created_at","updated_at","members","fields","assignments"]},"RevealCredentialFieldDto":{"type":"object","properties":{"id":{"type":"number","description":"Field id","example":10},"label":{"type":"string","description":"Field label","example":"api_key"},"type":{"enum":["TEXT","USERNAME","PASSWORD","EMAIL","URL","API_KEY","OTP","NOTE","SSH_PRIVATE_KEY","SSH_PUBLIC_KEY"],"type":"string","description":"Field type"},"value":{"type":"string","description":"Decrypted plaintext value"}},"required":["id","label","type","value"]},"CredentialFieldInput":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":255,"description":"Field label, e.g. \"username\", \"api_key\", \"host\"."},"type":{"enum":["TEXT","USERNAME","PASSWORD","EMAIL","URL","API_KEY","OTP","NOTE","SSH_PRIVATE_KEY","SSH_PUBLIC_KEY"],"type":"string","description":"Field type"},"value":{"type":"string","minLength":0,"maxLength":20000,"description":"Plaintext value. Encrypted server-side, never returned."},"position":{"type":"number","minimum":0,"description":"Display order (0-based)."}},"required":["label","type","value"]},"CreateCredentialDto":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":255,"description":"Unique name per org. Letters, digits, . _ -","example":"STRIPE_SECRET"},"description":{"type":"string","minLength":0,"maxLength":512,"description":"One-line description."},"kind":{"type":"string","minLength":0,"maxLength":64,"description":"Free-form kind for the UI icon/summary (login / api-key / database …).","example":"api-key"},"destinations":{"description":"Where this credential is meant to be used: URLs, IPs or host names. The agent receives them and knows where the key belongs. Empty = not set, which blocks nothing.","example":["https://api.stripe.com","dashboard.stripe.com"],"type":"array","items":{"type":"string"}},"usable_with_clients":{"type":"boolean","description":"Whether the agent may use this credential in a turn with a client (an external contact). Off = for that turn it does not exist. Default false.","example":false},"team_can_use":{"type":"boolean","description":"Team credential: any member may have an agent use it in the turns they open, also members who join later. Off = private: only the people on its access list (and OWNER/ADMIN). Default false; the creator is listed as MANAGE.","example":false},"fields":{"description":"The credential fields.","type":"array","items":{"$ref":"#/components/schemas/CredentialFieldInput"}}},"required":["name","fields"]},"UpdateCredentialDto":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":255,"description":"New name. Optional."},"description":{"type":"string","minLength":0,"maxLength":512,"description":"New description. Optional."},"kind":{"type":"string","minLength":0,"maxLength":64,"description":"New kind. Optional."},"destinations":{"description":"Replace the full list of destinations. Omit to leave it untouched; pass [] to clear it.","type":"array","items":{"type":"string"}},"usable_with_clients":{"type":"boolean","description":"Whether the agent may use this credential in a turn with a client (an external contact). Off = for that turn it does not exist. Default false.","example":true},"fields":{"description":"Replace the full set of fields. Omit to leave fields untouched; pass [] to clear them. Each entry is the plaintext field; values are re-encrypted.","type":"array","items":{"$ref":"#/components/schemas/CredentialFieldInput"}}}},"SetCredentialAgentsDto":{"type":"object","properties":{"agent_ids":{"uniqueItems":true,"description":"Full list of agents this credential should be assigned to. The service syncs: new ids are added, missing ids removed. Pass [] to detach from every agent.","example":[3,5],"type":"array","items":{"type":"number","minimum":1}}},"required":["agent_ids"]},"GetSkillDto":{"type":"object","properties":{"id":{"type":"number","description":"Skill id","example":1},"name":{"type":"string","description":"Slug name — becomes the directory under /home/agent/.claude/skills/<name>/","example":"code-review"},"description":{"type":"string","description":"One-line summary, mirrored into the SKILL.md frontmatter description.","example":"Review a diff for correctness and style."},"model":{"type":"string","nullable":true,"description":"Per-skill model override (SKILL.md frontmatter `model:`). Null = inherit the agent spawn model.","example":"claude-haiku-4-5"},"effort":{"nullable":true,"enum":["LOW","MEDIUM","HIGH","XHIGH","MAX"],"type":"string","description":"Per-skill effort override (SKILL.md frontmatter `effort:`). Null = inherit the session effort. One of LOW|MEDIUM|HIGH|XHIGH|MAX.","example":"LOW"},"content_hash":{"type":"string","description":"SHA-256 of `content`, lets the supervisor skip a no-op rewrite"},"assignment_count":{"type":"number","description":"Number of agents this skill is assigned to. Zero = unassigned.","example":3},"created_by":{"type":"number","nullable":true,"description":"Creator user id"},"created_at":{"format":"date-time","type":"string","description":"Created at"},"updated_at":{"format":"date-time","type":"string","description":"Updated at"},"archived_at":{"format":"date-time","type":"string","nullable":true,"description":"When the skill was archived (soft-deleted). Null on live skills. Archived skills are excluded from default listings and never seeded to a container volume."},"read_only":{"type":"boolean","description":"Read-only: only its editors (and OWNER/ADMIN) may change it. Reading is unaffected.","example":false}},"required":["id","name","description","content_hash","assignment_count","created_at","updated_at","read_only"]},"ListSkillDto":{"type":"object","properties":{"rows":{"description":"Page rows","type":"array","items":{"$ref":"#/components/schemas/GetSkillDto"}},"total":{"type":"number","description":"Total matching rows across all pages","example":31},"limit":{"type":"number","description":"Echo of the requested page size","example":50},"offset":{"type":"number","description":"Echo of the requested offset","example":0}},"required":["rows","total","limit","offset"]},"SkillAssignedAgentDto":{"type":"object","properties":{"id":{"type":"number","description":"Agent id","example":5},"name":{"type":"string","description":"Agent name","example":"Captain"},"avatar_url":{"type":"string","nullable":true,"description":"Avatar url (proxy endpoint)"},"assigned_at":{"format":"date-time","type":"string","description":"When the skill was assigned to this agent"}},"required":["id","name","assigned_at"]},"GetSkillWithContentDto":{"type":"object","properties":{"id":{"type":"number","description":"Skill id","example":1},"name":{"type":"string","description":"Slug name — becomes the directory under /home/agent/.claude/skills/<name>/","example":"code-review"},"description":{"type":"string","description":"One-line summary, mirrored into the SKILL.md frontmatter description.","example":"Review a diff for correctness and style."},"model":{"type":"string","nullable":true,"description":"Per-skill model override (SKILL.md frontmatter `model:`). Null = inherit the agent spawn model.","example":"claude-haiku-4-5"},"effort":{"nullable":true,"enum":["LOW","MEDIUM","HIGH","XHIGH","MAX"],"type":"string","description":"Per-skill effort override (SKILL.md frontmatter `effort:`). Null = inherit the session effort. One of LOW|MEDIUM|HIGH|XHIGH|MAX.","example":"LOW"},"content_hash":{"type":"string","description":"SHA-256 of `content`, lets the supervisor skip a no-op rewrite"},"assignment_count":{"type":"number","description":"Number of agents this skill is assigned to. Zero = unassigned.","example":3},"created_by":{"type":"number","nullable":true,"description":"Creator user id"},"created_at":{"format":"date-time","type":"string","description":"Created at"},"updated_at":{"format":"date-time","type":"string","description":"Updated at"},"archived_at":{"format":"date-time","type":"string","nullable":true,"description":"When the skill was archived (soft-deleted). Null on live skills. Archived skills are excluded from default listings and never seeded to a container volume."},"read_only":{"type":"boolean","description":"Read-only: only its editors (and OWNER/ADMIN) may change it. Reading is unaffected.","example":false},"content":{"type":"string","description":"Markdown body of the SKILL.md (without frontmatter)"},"assignments":{"description":"Agents this skill is currently assigned to","type":"array","items":{"$ref":"#/components/schemas/SkillAssignedAgentDto"}},"viewer_can_edit":{"type":"boolean","description":"Whether the viewer may change it (every agent that has it under their management and, if read-only, being an editor). Absent = true."},"viewer_can_grant":{"type":"boolean","description":"Whether the viewer reaches it whole, needed (with skill.grant) to mark it read-only or choose its editors."}},"required":["id","name","description","content_hash","assignment_count","created_at","updated_at","read_only","content","assignments"]},"GetSkillEntryDto":{"type":"object","properties":{"path":{"type":"string","description":"Path inside the skill folder.","example":"references/api.md"},"size":{"type":"number","description":"Size in bytes (utf-8).","example":1024},"content_hash":{"type":"string","description":"sha256 of the content."},"updated_at":{"type":"string","description":"Last write, ISO-8601."}},"required":["path","size","content_hash","updated_at"]},"GetSkillEntryContentDto":{"type":"object","properties":{"path":{"type":"string","description":"Path inside the skill folder.","example":"references/api.md"},"size":{"type":"number","description":"Size in bytes (utf-8).","example":1024},"content_hash":{"type":"string","description":"sha256 of the content."},"updated_at":{"type":"string","description":"Last write, ISO-8601."},"content":{"type":"string","description":"The file itself (utf-8 text)."}},"required":["path","size","content_hash","updated_at","content"]},"PutSkillEntryDto":{"type":"object","properties":{"path":{"type":"string","minLength":1,"maxLength":255,"description":"Path inside the skill folder. Relative, letters/digits/dot/dash/underscore only, up to 6 levels. SKILL.md is the skill body and is edited from the skill itself.","example":"references/api.md"},"content":{"type":"string","minLength":0,"maxLength":200000,"description":"The file itself (utf-8 text)."}},"required":["path","content"]},"CreateSkillDto":{"type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":255,"description":"Slug name. Lowercase letters, digits, hyphens. Becomes the directory under /home/agent/.claude/skills/<name>/.","example":"code-review"},"description":{"type":"string","minLength":1,"maxLength":512,"description":"One-line summary (mirrored into SKILL.md frontmatter description).","example":"Review a diff for correctness and style."},"content":{"type":"string","minLength":0,"maxLength":200000,"description":"Markdown body of the SKILL.md (without frontmatter).","example":"# Code review\n\nWhen asked to review…"},"model":{"type":"string","minLength":0,"maxLength":64,"description":"Optional per-skill model override → SKILL.md frontmatter `model:`. When the skill is active the agent switches model for the rest of the turn. Null/omit = inherit the agent spawn model.","example":"claude-haiku-4-5"},"effort":{"enum":["LOW","MEDIUM","HIGH","XHIGH","MAX"],"type":"string","description":"Optional per-skill effort override → SKILL.md frontmatter `effort:`. Same \"rest of the turn\" semantics as model. Omit = inherit the session effort.","example":"LOW"},"force":{"type":"boolean","description":"Override the credential-in-content check (#309). When the detector flags a likely API key / PEM / connection string, the save is rejected with a 400 — pass `force: true` only if you know the match is an example, not a real secret. The detector still audits the attempt either way.","example":false}},"required":["name","description","content"]},"ImportSkillFromGitDto":{"type":"object","properties":{"url":{"type":"string","format":"uri","minLength":1,"maxLength":2048,"description":"A github.com URL: the repo root, or a /tree/<ref>/<folder> (or /blob/.../SKILL.md) pointing at the folder that holds SKILL.md.","example":"https://github.com/anthropics/skills/tree/main/pdf"},"name":{"type":"string","minLength":2,"maxLength":255,"description":"Slug name for the new skill. Defaults to the repo/folder name.","example":"pdf-editing"}},"required":["url"]},"ImportedSkillDto":{"type":"object","properties":{"id":{"type":"number","description":"Skill id","example":1},"name":{"type":"string","description":"Slug name — becomes the directory under /home/agent/.claude/skills/<name>/","example":"code-review"},"description":{"type":"string","description":"One-line summary, mirrored into the SKILL.md frontmatter description.","example":"Review a diff for correctness and style."},"model":{"type":"string","nullable":true,"description":"Per-skill model override (SKILL.md frontmatter `model:`). Null = inherit the agent spawn model.","example":"claude-haiku-4-5"},"effort":{"nullable":true,"enum":["LOW","MEDIUM","HIGH","XHIGH","MAX"],"type":"string","description":"Per-skill effort override (SKILL.md frontmatter `effort:`). Null = inherit the session effort. One of LOW|MEDIUM|HIGH|XHIGH|MAX.","example":"LOW"},"content_hash":{"type":"string","description":"SHA-256 of `content`, lets the supervisor skip a no-op rewrite"},"assignment_count":{"type":"number","description":"Number of agents this skill is assigned to. Zero = unassigned.","example":3},"created_by":{"type":"number","nullable":true,"description":"Creator user id"},"created_at":{"format":"date-time","type":"string","description":"Created at"},"updated_at":{"format":"date-time","type":"string","description":"Updated at"},"archived_at":{"format":"date-time","type":"string","nullable":true,"description":"When the skill was archived (soft-deleted). Null on live skills. Archived skills are excluded from default listings and never seeded to a container volume."},"read_only":{"type":"boolean","description":"Read-only: only its editors (and OWNER/ADMIN) may change it. Reading is unaffected.","example":false},"content":{"type":"string","description":"Markdown body of the SKILL.md (without frontmatter)"},"assignments":{"description":"Agents this skill is currently assigned to","type":"array","items":{"$ref":"#/components/schemas/SkillAssignedAgentDto"}},"viewer_can_edit":{"type":"boolean","description":"Whether the viewer may change it (every agent that has it under their management and, if read-only, being an editor). Absent = true."},"viewer_can_grant":{"type":"boolean","description":"Whether the viewer reaches it whole, needed (with skill.grant) to mark it read-only or choose its editors."},"entries":{"description":"Support files that came with the SKILL.md.","type":"array","items":{"$ref":"#/components/schemas/GetSkillEntryDto"}},"skipped":{"description":"Files in the source folder that were left out, and why (not text, too large, bad path).","example":["assets/logo.png (not text)"],"type":"array","items":{"type":"string"}}},"required":["id","name","description","content_hash","assignment_count","created_at","updated_at","read_only","content","assignments","entries","skipped"]},"UpdateSkillDto":{"type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":255,"description":"New slug name (renames the skill directory). Optional.","example":"code-review"},"description":{"type":"string","minLength":1,"maxLength":512,"description":"New one-line summary. Optional."},"content":{"type":"string","minLength":0,"maxLength":200000,"description":"New markdown body. Optional."},"model":{"type":"string","minLength":0,"maxLength":64,"description":"Per-skill model override → SKILL.md frontmatter `model:`. Empty string clears it (inherit spawn model). Optional.","example":"claude-haiku-4-5"},"effort":{"type":"string","enum":["LOW","MEDIUM","HIGH","XHIGH","MAX"],"description":"Per-skill effort override → SKILL.md frontmatter `effort:`. Pass an empty string to clear it. Optional."},"force":{"type":"boolean","description":"Override the credential-in-content check (#309). When the detector flags a likely API key / PEM / connection string, the save is rejected with a 400 — pass `force: true` only if you know the match is an example, not a real secret. The detector still audits the attempt either way.","example":false}}},"KnowledgeLockDto":{"type":"object","properties":{"read_only":{"type":"boolean","description":"Read-only: only the people and agents below (and OWNER/ADMIN) may change it. Reading is unaffected.","example":true},"member_ids":{"uniqueItems":true,"description":"Members (user ids) who may still change it. Replaces the list.","example":[7],"type":"array","items":{"type":"number","minimum":1}},"agent_ids":{"uniqueItems":true,"description":"Agents that may still change it. An ask from another agent only passes if every agent in the chain is here.","example":[3],"type":"array","items":{"type":"number","minimum":1}}},"required":["read_only","member_ids","agent_ids"]},"SetSkillAgentsDto":{"type":"object","properties":{"agent_ids":{"uniqueItems":true,"description":"Full list of agents this skill should be assigned to. The service syncs: new ids are added, missing ids are removed. Pass [] to detach the skill from every agent.","example":[3,5],"type":"array","items":{"type":"number","minimum":1}}},"required":["agent_ids"]},"McpAgentDto":{"type":"object","properties":{"id":{"type":"number","description":"Agent id","example":5},"name":{"type":"string","description":"Agent name","example":"Captain"},"avatar_url":{"type":"string","nullable":true,"description":"Avatar url (proxy endpoint)"}},"required":["id","name"]},"GetMcpDto":{"type":"object","properties":{"id":{"type":"number","description":"MCP server id","example":1},"name":{"type":"string","description":"Slug name — the key in the .mcp.json mcpServers object.","example":"github"},"description":{"type":"string","description":"One-line summary.","example":"Lee repos, crea issues"},"transport":{"enum":["STDIO","SSE","HTTP"],"type":"string","description":"Transport","example":"STDIO"},"command":{"type":"string","nullable":true,"description":"STDIO command"},"args":{"description":"STDIO argv","type":"array","items":{"type":"string"}},"url":{"type":"string","nullable":true,"description":"SSE/HTTP endpoint"},"auth":{"description":"Names of the env vars configured on this server (the \"auth\" the agent gets). Values are NEVER returned — they live encrypted at rest.","example":["GITHUB_TOKEN"],"type":"array","items":{"type":"string"}},"assignment_count":{"type":"number","description":"Number of agents this server is assigned to. Zero = unassigned.","example":3},"agents":{"description":"Agents this server is assigned to (id, name, avatar). Enough to render them on a list row — the full assignment records come with the detail endpoint.","type":"array","items":{"$ref":"#/components/schemas/McpAgentDto"}},"created_by":{"type":"number","nullable":true,"description":"Creator user id"},"created_at":{"format":"date-time","type":"string","description":"Created at"},"updated_at":{"format":"date-time","type":"string","description":"Updated at"},"archived_at":{"format":"date-time","type":"string","nullable":true,"description":"When the server was archived (soft-deleted). Null on live servers. Archived servers are excluded from default listings and never shipped in a spawn payload."}},"required":["id","name","description","transport","args","auth","assignment_count","agents","created_at","updated_at"]},"ListMcpDto":{"type":"object","properties":{"rows":{"description":"Page rows","type":"array","items":{"$ref":"#/components/schemas/GetMcpDto"}},"total":{"type":"number","description":"Total matching rows across all pages","example":23},"limit":{"type":"number","description":"Echo of the requested page size","example":50},"offset":{"type":"number","description":"Echo of the requested offset","example":0}},"required":["rows","total","limit","offset"]},"McpAssignedAgentDto":{"type":"object","properties":{"id":{"type":"number","description":"Agent id","example":5},"name":{"type":"string","description":"Agent name","example":"Captain"},"avatar_url":{"type":"string","nullable":true,"description":"Avatar url (proxy endpoint)"},"assigned_at":{"format":"date-time","type":"string","description":"When the server was assigned to this agent"}},"required":["id","name","assigned_at"]},"GetMcpWithAssignmentsDto":{"type":"object","properties":{"id":{"type":"number","description":"MCP server id","example":1},"name":{"type":"string","description":"Slug name — the key in the .mcp.json mcpServers object.","example":"github"},"description":{"type":"string","description":"One-line summary.","example":"Lee repos, crea issues"},"transport":{"enum":["STDIO","SSE","HTTP"],"type":"string","description":"Transport","example":"STDIO"},"command":{"type":"string","nullable":true,"description":"STDIO command"},"args":{"description":"STDIO argv","type":"array","items":{"type":"string"}},"url":{"type":"string","nullable":true,"description":"SSE/HTTP endpoint"},"auth":{"description":"Names of the env vars configured on this server (the \"auth\" the agent gets). Values are NEVER returned — they live encrypted at rest.","example":["GITHUB_TOKEN"],"type":"array","items":{"type":"string"}},"assignment_count":{"type":"number","description":"Number of agents this server is assigned to. Zero = unassigned.","example":3},"agents":{"description":"Agents this server is assigned to (id, name, avatar). Enough to render them on a list row — the full assignment records come with the detail endpoint.","type":"array","items":{"$ref":"#/components/schemas/McpAgentDto"}},"created_by":{"type":"number","nullable":true,"description":"Creator user id"},"created_at":{"format":"date-time","type":"string","description":"Created at"},"updated_at":{"format":"date-time","type":"string","description":"Updated at"},"archived_at":{"format":"date-time","type":"string","nullable":true,"description":"When the server was archived (soft-deleted). Null on live servers. Archived servers are excluded from default listings and never shipped in a spawn payload."},"assignments":{"description":"Agents this server is currently assigned to","type":"array","items":{"$ref":"#/components/schemas/McpAssignedAgentDto"}}},"required":["id","name","description","transport","args","auth","assignment_count","agents","created_at","updated_at","assignments"]},"CreateMcpDto":{"type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":255,"description":"Slug name. Lowercase letters, digits, hyphens, underscores. Becomes the key in the .mcp.json mcpServers object.","example":"github"},"description":{"type":"string","minLength":1,"maxLength":512,"description":"One-line summary shown in the operator panel.","example":"Lee repos, crea issues, comenta PRs"},"transport":{"enum":["STDIO","SSE","HTTP"],"type":"string","description":"Transport the server speaks. STDIO = local process (command + args). SSE/HTTP = remote endpoint (url).","example":"STDIO"},"command":{"type":"string","minLength":1,"maxLength":512,"description":"STDIO only: the executable to run.","example":"npx"},"args":{"description":"STDIO only: argv passed to the command.","example":["-y","@modelcontextprotocol/server-github"],"type":"array","items":{"type":"string"}},"url":{"type":"string","format":"uri","minLength":1,"maxLength":1024,"description":"SSE/HTTP only: the server endpoint.","example":"https://mcp.notion.com/v1"},"env":{"type":"object","additionalProperties":{"type":"string"},"description":"Env vars to inject into the server process, as a name→value map. Values are encrypted at rest and never returned. The GET DTO exposes only the names (auth).","example":{"GITHUB_TOKEN":"ghp_xxx"}}},"required":["name","description","transport"]},"UpdateMcpDto":{"type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":255,"description":"New slug name. Optional.","example":"github"},"description":{"type":"string","minLength":1,"maxLength":512,"description":"New one-line summary. Optional."},"transport":{"enum":["STDIO","SSE","HTTP"],"type":"string","description":"New transport. Optional."},"command":{"type":"string","minLength":1,"maxLength":512,"description":"STDIO command. Optional."},"args":{"description":"STDIO argv. Optional.","type":"array","items":{"type":"string"}},"url":{"type":"string","minLength":1,"maxLength":1024,"description":"SSE/HTTP endpoint. Optional."},"env":{"type":"object","additionalProperties":{"type":"string"},"description":"Replace the env var set entirely. name→value map; values encrypted at rest. Omit to leave the current env untouched; pass {} to clear it.","example":{"GITHUB_TOKEN":"ghp_xxx"}}}},"SetMcpAgentsDto":{"type":"object","properties":{"agent_ids":{"uniqueItems":true,"description":"Full list of agents this server should be assigned to. The service syncs: new ids are added, missing ids removed. Pass [] to detach from every agent.","example":[3,5],"type":"array","items":{"type":"number","minimum":1}}},"required":["agent_ids"]},"GetModelDto":{"type":"object","properties":{"id":{"type":"number","description":"Model id","example":1},"slug":{"type":"string","description":"Exact model id passed to `claude --model`.","example":"claude-opus-4-8"},"label":{"type":"string","description":"Pretty name shown in the picker.","example":"Opus 4.8 (1M context)"},"sort_order":{"type":"number","description":"Sort order (lower first).","example":10},"active":{"type":"boolean","description":"Whether the model is offered in the picker.","example":true},"provider":{"type":"string","description":"Provider key — which LLM provider serves this model (KUJI-67).","example":"anthropic"},"family":{"type":"string","nullable":true,"description":"Pricing-family key (null = derived from the slug).","example":"opus"},"supports_manual_thinking":{"type":"boolean","description":"Whether this model still uses the manual --thinking on/off toggle. False = reasons adaptively and is driven by --effort (the whole current catalogue); true only for legacy models (Opus 4.5 and earlier). Drives whether the UI shows the thinking control.","example":false},"price_input":{"type":"number","nullable":true,"description":"Price in USD per MILLION input tokens. Null = fall back to the built-in family table. These are COST prices, with no markup.","example":5},"price_output":{"type":"number","nullable":true,"description":"Price in USD per million output tokens. Null = family table.","example":25},"price_cache_read":{"type":"number","nullable":true,"description":"Price in USD per million tokens READ from cache. Null = family table.","example":0.5},"price_cache_write_5m":{"type":"number","nullable":true,"description":"Price per million tokens WRITTEN to the 5-minute cache. Null = family table.","example":6.25},"price_cache_write_1h":{"type":"number","nullable":true,"description":"Price per million tokens WRITTEN to the 1-hour cache. Null = family table.","example":10},"context_window":{"type":"number","nullable":true,"description":"Context window in tokens. Null = derived from the slug by the built-in heuristic.","example":1000000},"created_at":{"format":"date-time","type":"string","description":"Created at"},"updated_at":{"format":"date-time","type":"string","description":"Updated at"}},"required":["id","slug","label","sort_order","active","provider","supports_manual_thinking","price_input","price_output","price_cache_read","price_cache_write_5m","price_cache_write_1h","context_window","created_at","updated_at"]},"GetGatewayModelDto":{"type":"object","properties":{"id":{"type":"string","description":"Exact model id to store in the connection.","example":"anthropic/claude-opus-5"},"label":{"type":"string","description":"The gateway's own name for it.","example":"Anthropic: Claude Opus 5"},"context_window":{"type":"number","description":"Input tokens the model's default route holds, as published by the gateway. This is what Kujira uses instead of asking the operator for it.","example":1000000}},"required":["id","label","context_window"]},"CostFiguresDto":{"type":"object","properties":{"cost_micro":{"type":"string","description":"Cost in 1e-6 currency units.","example":"5000000"},"input_tokens":{"type":"string","description":"Input (uncached) tokens.","example":"120000"},"output_tokens":{"type":"string","description":"Output tokens.","example":"34000"},"cache_read_tokens":{"type":"string","description":"Cache-read tokens.","example":"900000"},"cache_write_tokens":{"type":"string","description":"Cache-write (creation) tokens.","example":"45000"},"total_tokens":{"type":"string","description":"Total tokens (sum of the four above).","example":"1099000"}},"required":["cost_micro","input_tokens","output_tokens","cache_read_tokens","cache_write_tokens","total_tokens"]},"CostSeriesPointDto":{"type":"object","properties":{"cost_micro":{"type":"string","description":"Cost in 1e-6 currency units.","example":"5000000"},"input_tokens":{"type":"string","description":"Input (uncached) tokens.","example":"120000"},"output_tokens":{"type":"string","description":"Output tokens.","example":"34000"},"cache_read_tokens":{"type":"string","description":"Cache-read tokens.","example":"900000"},"cache_write_tokens":{"type":"string","description":"Cache-write (creation) tokens.","example":"45000"},"total_tokens":{"type":"string","description":"Total tokens (sum of the four above).","example":"1099000"},"bucket":{"type":"string","description":"Bucket start (ISO date, UTC).","example":"2026-06-18T00:00:00.000Z"}},"required":["cost_micro","input_tokens","output_tokens","cache_read_tokens","cache_write_tokens","total_tokens","bucket"]},"CostBreakdownRowDto":{"type":"object","properties":{"cost_micro":{"type":"string","description":"Cost in 1e-6 currency units.","example":"5000000"},"input_tokens":{"type":"string","description":"Input (uncached) tokens.","example":"120000"},"output_tokens":{"type":"string","description":"Output tokens.","example":"34000"},"cache_read_tokens":{"type":"string","description":"Cache-read tokens.","example":"900000"},"cache_write_tokens":{"type":"string","description":"Cache-write (creation) tokens.","example":"45000"},"total_tokens":{"type":"string","description":"Total tokens (sum of the four above).","example":"1099000"},"key":{"type":"string","description":"Stable key for the dimension value.","example":"10"},"label":{"type":"string","description":"Human label for the dimension value.","example":"Ailfred"},"agent_id":{"type":"number","nullable":true,"description":"Owning agent id (channel breakdown only; null otherwise). Lets the UI show the agent's avatar.","example":7},"transport":{"type":"string","nullable":true,"description":"Channel transport (channel/conversation breakdown; null otherwise). Lets the UI show the brand logo.","example":"TELEGRAM"},"contact_name":{"type":"string","nullable":true,"description":"Contact name (conversation breakdown only; null otherwise). Seeds the contact's avatar.","example":"Soki"},"person_kind":{"type":"string","nullable":true,"description":"Contact kind (conversation breakdown): EXTERNAL | INTERNAL_USER | INTERNAL_ROOM.","example":"INTERNAL_USER"},"person_avatar_url":{"type":"string","nullable":true,"description":"Real avatar URL of the linked operator (conversation breakdown, INTERNAL_USER with a photo). Null → UI uses the generative avatar.","example":"/user/3/avatar?v=1718900000000"},"message_count":{"type":"string","description":"Number of assistant messages.","example":"991"}},"required":["cost_micro","input_tokens","output_tokens","cache_read_tokens","cache_write_tokens","total_tokens","key","label","message_count"]},"CostAnalyticsDto":{"type":"object","properties":{"from":{"type":"string","description":"Effective lower bound (ISO).","example":"2026-05-20T00:00:00.000Z"},"to":{"type":"string","description":"Effective upper bound (ISO).","example":"2026-06-19T00:00:00.000Z"},"bucket":{"enum":["hour","day","month","week"],"type":"string","description":"Bucket granularity used.","example":"day"},"group_by":{"enum":["agent","channel","scope","connection","transport","model","contact","conversation","cron"],"type":"string","description":"Dimension grouped by.","example":"agent"},"totals":{"description":"Totals over the whole window.","allOf":[{"$ref":"#/components/schemas/CostFiguresDto"}]},"series":{"description":"Cost per time bucket (ascending).","type":"array","items":{"$ref":"#/components/schemas/CostSeriesPointDto"}},"breakdown":{"description":"Cost per dimension value (highest cost first).","type":"array","items":{"$ref":"#/components/schemas/CostBreakdownRowDto"}}},"required":["from","to","bucket","group_by","totals","series","breakdown"]},"GetBackupDto":{"type":"object","properties":{"id":{"type":"number"},"agent_id":{"type":"number"},"agent_name":{"type":"string"},"status":{"enum":["PENDING","FAILED","COMPLETED","UPLOADING"],"type":"string"},"kind":{"enum":["MANUAL","SCHEDULED","PRE_MIGRATION"],"type":"string"},"size_bytes":{"type":"string","nullable":true},"error":{"type":"string","nullable":true},"created_at":{"format":"date-time","type":"string"},"completed_at":{"format":"date-time","type":"string","nullable":true},"object":{"enum":["UNKNOWN","PRESENT","MISSING"],"type":"string"}},"required":["id","agent_id","status","kind","size_bytes","error","created_at","completed_at","object"]},"SetBackupDestinationDto":{"type":"object","properties":{"endpoint":{"type":"string","maxLength":512,"format":"uri","description":"S3-compatible endpoint of the bucket"},"bucket":{"type":"string","minLength":1,"maxLength":255,"description":"Bucket name"},"region":{"type":"string","maxLength":64,"description":"Region. Empty means `auto`, which is what R2 requires."},"access_key_id":{"type":"string","minLength":1,"maxLength":255,"description":"Access key id"},"secret_access_key":{"type":"string","minLength":1,"description":"Secret access key. Stored encrypted, never returned."},"prefix":{"type":"string","maxLength":255,"description":"Optional key prefix inside the bucket"}},"required":["endpoint","bucket","access_key_id","secret_access_key"]},"UpdateActivityLinkDto":{"type":"object","properties":{"automerge":{"type":"boolean","description":"Merge it as soon as it is green and approved"}},"required":["automerge"]},"GetAuditDto":{"type":"object","properties":{"id":{"type":"number","description":"Entry id","example":1},"actor_kind":{"enum":["HUMAN","AGENT","SERVICE","SYSTEM"],"type":"string","description":"Actor kind"},"actor_id":{"type":"number","nullable":true,"description":"Actor id (user/agent), null for SYSTEM"},"actor_label":{"type":"string","nullable":true,"description":"Denormalized actor label (email / agent name)"},"actor_name":{"type":"string","nullable":true,"description":"The actor's CURRENT display name (user name / agent name), resolved at read time. Null for SERVICE/SYSTEM, or when the user/agent no longer exists — fall back to actor_label."},"actor_avatar_url":{"type":"string","nullable":true,"description":"The actor's current avatar URL (user or agent), when it has one."},"action":{"type":"string","description":"Action, \"resource.verb\"","example":"credential.reveal"},"resource_type":{"type":"string","description":"Resource type","example":"credential"},"resource_id":{"type":"number","nullable":true,"description":"Resource id"},"result":{"enum":["SUCCESS","FAILURE"],"type":"string","description":"Result"},"meta":{"type":"object","additionalProperties":true,"nullable":true,"description":"Non-sensitive context (NEVER secrets)"},"created_at":{"format":"date-time","type":"string","description":"When the action happened"}},"required":["id","actor_kind","action","resource_type","result","created_at"]},"ListAuditDto":{"type":"object","properties":{"rows":{"description":"The entries (most recent first)","type":"array","items":{"$ref":"#/components/schemas/GetAuditDto"}},"total":{"type":"number","description":"Total matching the filters (ignoring pagination)"},"limit":{"type":"number","description":"Applied page size"},"offset":{"type":"number","description":"Applied offset"}},"required":["rows","total","limit","offset"]},"BillingSubscriptionDto":{"type":"object","properties":{"status":{"nullable":true,"enum":["TRIALING","ACTIVE","PAST_DUE","CANCELED","ENDED"],"type":"string","description":"Subscription state, or null when there has never been one."},"contracted_agents":{"type":"number","nullable":true,"description":"How many agents are contracted. Set by the team, so it holds even with no card payment.","example":3},"current_period_end":{"type":"string","nullable":true,"description":"When it renews (or when what was paid for runs out)."},"cancel_at_period_end":{"type":"boolean","description":"Whether cancellation is already scheduled for the end of the period.","example":false},"has_customer":{"type":"boolean","description":"Whether this organization has a customer record at the gateway. It is what decides if the customer portal can be opened: no record, nothing to manage.","example":true},"trial_ends_at":{"type":"string","nullable":true,"description":"When the trial ends, if there is one. It lives here and not on the organization so the screen has everything in one place."},"granted":{"type":"boolean","description":"The plan was agreed by hand, not bought by card (ENTERPRISE, or granted by the team). Whoever has one is not shown tariffs: their price was agreed apart.","example":false}},"required":["cancel_at_period_end","has_customer","granted"]},"BillingSummaryDto":{"type":"object","properties":{"plan":{"enum":["FREE","BASIC","PRO","ENTERPRISE"],"type":"string","description":"Organization plan"},"agent_count":{"type":"number","description":"Number of agents in the org","example":3},"subscription":{"description":"Payment state: what is contracted and how the gateway stands.","allOf":[{"$ref":"#/components/schemas/BillingSubscriptionDto"}]}},"required":["plan","agent_count","subscription"]},"ContractedAgentsDto":{"type":"object","properties":{"contracted":{"type":"number","nullable":true,"description":"Agents the organization can have right now. This is the number that decides whether another one can be created.","example":3},"in_use":{"type":"number","description":"Agents that already exist, running or not.","example":3},"running":{"type":"number","description":"Agents that are running, which are the ones that take up a slot.","example":2},"min":{"type":"number","description":"Minimum amount it can be lowered to: the agents that are running, and never less than one.","example":2},"max":{"type":"number","description":"Maximum amount the plan allows.","example":50},"paid_for":{"type":"number","nullable":true,"description":"Agents charged at the gateway. May run ahead of the contracted number for the seconds it takes the payment notification to arrive.","example":4},"period_end":{"format":"date-time","type":"string","nullable":true,"description":"When the subscription renews, which is also when a scheduled reduction takes effect.","example":"2026-09-28T10:00:00.000Z"},"scheduled":{"type":"number","nullable":true,"description":"Reduction already scheduled for the renewal, if any.","example":2},"settling":{"type":"boolean","description":"The charge has already gone through and the contracted number does not reflect it yet. Check again in a few seconds.","example":false},"can_change":{"type":"boolean","description":"Whether the amount can be changed from the panel.","example":true},"blocked_reason":{"nullable":true,"enum":["gateway_not_configured","not_billed","plan_not_sold_by_card","no_subscription","subscription_not_current"],"type":"string","description":"Why it cannot be changed, when it cannot.","example":"no_subscription"}},"required":["contracted","in_use","running","min","max","paid_for","period_end","scheduled","settling","can_change","blocked_reason"]},"LimitUsageDto":{"type":"object","properties":{"used":{"type":"number","description":"How many are in use, counted as the limit counts them.","example":7},"limit":{"type":"number","nullable":true,"description":"The ceiling. Null when there is none.","example":15}},"required":["used","limit"]},"PlanUsageDto":{"type":"object","properties":{"channels":{"description":"External channels, shared by all the agents: the allowance per agent times the agents the organization may have. Device channels and the dashboard chat do not count.","allOf":[{"$ref":"#/components/schemas/LimitUsageDto"}]},"channels_per_agent":{"type":"number","nullable":true,"description":"Channels allowed per agent, which is what the pool is built from.","example":5},"devices":{"description":"Devices (computers with the Kujira app or Companion).","allOf":[{"$ref":"#/components/schemas/LimitUsageDto"}]},"servers":{"description":"Servers where agents run.","allOf":[{"$ref":"#/components/schemas/LimitUsageDto"}]}},"required":["channels","channels_per_agent","devices","servers"]},"CronjobAgentDto":{"type":"object","properties":{"id":{"type":"number","description":"Agent id","example":5},"name":{"type":"string","description":"Agent name","example":"flex-test"}},"required":["id","name"]},"CronjobCredentialDto":{"type":"object","properties":{"id":{"type":"number","description":"Credential id","example":7},"name":{"type":"string","description":"Credential name","example":"stripe"}},"required":["id","name"]},"GetCronjobDto":{"type":"object","properties":{"id":{"type":"number","description":"Cronjob id","example":1},"organization_id":{"type":"number","description":"Organization id","example":2},"agent_id":{"type":"number","description":"Agent id","example":5},"agent":{"description":"Agent summary","allOf":[{"$ref":"#/components/schemas/CronjobAgentDto"}]},"name":{"type":"string","description":"Name","example":"daily-report"},"description":{"type":"string","nullable":true,"description":"Description"},"schedule":{"type":"string","nullable":true,"description":"Cron expression (recurring). Null on a one-shot — see run_at.","example":"0 9 * * 1"},"run_at":{"format":"date-time","type":"string","nullable":true,"description":"One-shot (KJ-190): the instant it fires. Null on a recurring cron."},"expires_at":{"format":"date-time","type":"string","nullable":true,"description":"Stop running after this instant (KJ-190). Null = no deadline."},"max_runs":{"type":"number","nullable":true,"description":"Stop after this many runs (KJ-190). Null = unlimited.","example":3},"run_count":{"type":"number","description":"Scheduled runs so far (KJ-190)","example":0},"kind":{"enum":["INPUT","SCRIPT"],"type":"string","description":"Kind","example":"INPUT"},"status":{"enum":["ACTIVE","PENDING_APPROVAL","DISABLED","EXPIRED"],"type":"string","description":"Status","example":"ACTIVE"},"enabled":{"type":"boolean","description":"Whether the schedule is active","example":true},"internal":{"type":"boolean","description":"The agent's own bookkeeping (monitor/probe): hidden from the cron list unless include_internal=true, and always carries a forced expires_at.","example":false},"message":{"type":"string","nullable":true,"description":"INPUT: injected turn text"},"command":{"type":"string","nullable":true,"description":"SCRIPT: the command"},"timeout_ms":{"type":"number","description":"SCRIPT: timeout (ms)","example":60000},"model":{"type":"string","nullable":true,"description":"INPUT: Claude model slug to run this scheduled turn on (KJ-22). Null = inherit the agent/conversation default.","example":"claude-haiku-4-5"},"report_when":{"enum":["ALWAYS","ON_CHANGE","ON_MATCH","NEVER"],"type":"string","description":"SCRIPT: when output is reported to the agent","example":"ALWAYS"},"report_pattern":{"type":"string","nullable":true,"description":"SCRIPT + ON_MATCH: regex deciding when to notify"},"notify_conversation_id":{"type":"number","nullable":true,"description":"Conversation to report to (contact + channel); null = log only"},"credentials":{"description":"Credentials injected as env vars into a SCRIPT cron (KJ-38). Names only, never values.","type":"array","items":{"$ref":"#/components/schemas/CronjobCredentialDto"}},"last_run_at":{"format":"date-time","type":"string","nullable":true,"description":"Last run timestamp"},"last_status":{"type":"string","nullable":true,"description":"Last run status"},"last_result":{"type":"object","description":"Last run result (exit_code/output for SCRIPT)","nullable":true},"created_by":{"type":"number","nullable":true,"description":"Creator user id (null for MCP/service)"},"is_mine":{"type":"boolean","description":"Whether the requesting user owns this cron (private to them)","example":true},"shared_user_ids":{"description":"User ids this cron is shared with (besides the owner)","example":[3,7],"type":"array","items":{"type":"number"}},"created_at":{"format":"date-time","type":"string","description":"Created at"},"updated_at":{"format":"date-time","type":"string","description":"Updated at"},"timezone":{"type":"string","nullable":true}},"required":["id","organization_id","agent_id","agent","name","run_count","kind","status","enabled","internal","timeout_ms","report_when","credentials","is_mine","shared_user_ids","created_at","updated_at","timezone"]},"CronjobRunDto":{"type":"object","properties":{"id":{"type":"number","description":"Run id","example":1},"status":{"type":"string","description":"Status","example":"SUCCESS"},"exit_code":{"type":"number","nullable":true,"description":"Exit code (SCRIPT)"},"output":{"type":"string","nullable":true,"description":"Truncated stdout/stderr"},"truncated":{"type":"boolean","description":"Whether the output was truncated","example":false},"notified":{"type":"boolean","description":"Whether this run was sent to the notify channel","example":false},"duration_ms":{"type":"number","nullable":true,"description":"Wall-clock duration of the run in ms (KUJI-131). Null for legacy rows. Lets a silent script still show it ran + how long.","example":1240},"created_at":{"format":"date-time","type":"string","description":"When it ran"}},"required":["id","status","truncated","notified","created_at"]},"CreateCronjobDto":{"type":"object","properties":{"agent_id":{"type":"number","minimum":1,"description":"Agent this cronjob runs against. Must belong to the same org.","example":5},"name":{"type":"string","minLength":1,"maxLength":255,"description":"Display name (unique within the org).","example":"daily-report"},"description":{"type":"string","minLength":0,"maxLength":1024,"description":"Free-form description.","example":"Resumen de pedidos cada mañana"},"schedule":{"type":"string","minLength":1,"maxLength":128,"description":"Recurring: raw cron expression (5 fields), validated on save. Mutually exclusive with run_at — send exactly one.","example":"0 9 * * 1"},"run_at":{"type":"string","description":"One-shot (KJ-190): ISO date-time this cron fires ONCE, then expires. Mutually exclusive with schedule. Must be in the future.","example":"2026-08-12T09:00:00.000Z"},"timezone":{"type":"string","nullable":true,"description":"IANA zone the expression is read in (\"Europe/Madrid\"), so \"0 7 * * *\" means seven in the morning THERE, daylight saving included. Omitted/empty inherits the agent zone, then the organization one, then UTC.","example":"Europe/Madrid"},"expires_at":{"type":"string","description":"Stop running after this instant (KJ-190) — the cron flips to EXPIRED, keeping its history. Must be in the future.","example":"2026-09-01T00:00:00.000Z"},"max_runs":{"type":"number","minimum":1,"description":"Stop after this many runs (KJ-190). Omit for unlimited.","example":3},"kind":{"enum":["INPUT","SCRIPT"],"type":"string","description":"INPUT injects a turn into the agent; SCRIPT runs a command in its container.","example":"INPUT"},"internal":{"type":"boolean","description":"Agent bookkeeping (a monitor, a re-check, a probe): kept out of the cron list unless include_internal=true. An internal cron is ALWAYS given a deadline — the one sent, capped at 7 days, or 1 day if none — so a monitor can't outlive whatever it was watching.","example":false},"message":{"type":"string","minLength":1,"maxLength":8192,"description":"For INPUT: the text injected as a scheduled turn.","example":"Genera el reporte de ayer y avísame."},"command":{"type":"string","minLength":1,"maxLength":8192,"description":"For SCRIPT: the shell command run inside the container.","example":"python3 /home/agent/scripts/report.py"},"timeout_ms":{"type":"number","minimum":1000,"maximum":86400000,"description":"For SCRIPT: hard timeout in ms. The longest allowed is a platform setting (6 h by default); asking for more is a 400 CRONJOB_TIMEOUT_TOO_LONG.","example":60000},"report_when":{"enum":["ALWAYS","ON_CHANGE","ON_MATCH","NEVER"],"type":"string","description":"SCRIPT report gate (only applies with notify_conversation_id): ALWAYS / ON_CHANGE (output differs) / ON_MATCH (matches report_pattern) / NEVER. Failures always notify except NEVER. Ignored for INPUT.","example":"ON_CHANGE"},"report_pattern":{"type":"string","minLength":1,"maxLength":512,"description":"SCRIPT + ON_MATCH: regex; the output is reported only when it matches.","example":"\"reales61\":\\s*[1-9]"},"notify_conversation_id":{"type":"number","minimum":1,"description":"Conversation (contact + channel) to report to. SCRIPT delivers its stdout there (non-LLM); INPUT asks the agent to report there. Must be a conversation of this agent. Omit to only log.","example":7},"credential_ids":{"uniqueItems":true,"description":"SCRIPT only (KJ-38): ids of credentials to inject as env vars into the command. Each MUST be assigned to this agent. A field becomes `<CRED>_<FIELD>` (uppercased), plus a bare `<CRED>` when the credential has one field; OTP fields yield the live code. Never written to disk.","example":[7,9],"type":"array","items":{"type":"number"}},"model":{"type":"string","minLength":0,"maxLength":80,"description":"INPUT only (KJ-22): Claude model for this scheduled turn — \"haiku\"/\"sonnet\"/\"opus\" or a full claude-… slug. Routine crons run cheaper. Empty/omitted = inherit the default.","example":"haiku"}},"required":["agent_id","name","kind"]},"UpdateCronjobDto":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":255,"description":"Display name.","example":"daily-report"},"description":{"type":"string","minLength":0,"maxLength":1024,"description":"Free-form description."},"schedule":{"type":"string","minLength":0,"maxLength":128,"description":"Raw cron expression. Send \"\" (empty) together with run_at to turn a recurring cron into a one-shot.","example":"0 9 * * 1"},"run_at":{"type":"string","nullable":true,"description":"One-shot instant (KJ-190), ISO date-time. Null clears it. Setting it re-arms an already-fired one-shot.","example":"2026-08-12T09:00:00.000Z"},"timezone":{"type":"string","nullable":true,"description":"IANA zone the expression is read in (\"Europe/Madrid\"), so \"0 7 * * *\" means seven in the morning THERE, daylight saving included. Empty inherits the agent zone, then the organization one, then UTC.","example":"Europe/Madrid"},"expires_at":{"type":"string","nullable":true,"description":"Stop running after this instant (KJ-190). Null clears the deadline."},"max_runs":{"type":"number","nullable":true,"minimum":1,"description":"Stop after this many runs (KJ-190). Null = unlimited.","example":3},"enabled":{"type":"boolean","description":"Enable/disable the schedule.","example":true},"internal":{"type":"boolean","description":"Mark/unmark as agent bookkeeping (hidden from the list by default). While internal, the cron's deadline is re-capped on every save — you cannot park an internal cron forever by clearing expires_at.","example":false},"message":{"type":"string","minLength":1,"maxLength":8192,"description":"For INPUT: the injected turn text."},"command":{"type":"string","minLength":1,"maxLength":8192,"description":"For SCRIPT: the command."},"timeout_ms":{"type":"number","minimum":1000,"maximum":86400000,"description":"For SCRIPT: timeout in ms. The longest allowed is a platform setting (6 h by default)."},"report_when":{"enum":["ALWAYS","ON_CHANGE","ON_MATCH","NEVER"],"type":"string","description":"SCRIPT report gate (with notify_conversation_id): ALWAYS | ON_CHANGE | ON_MATCH | NEVER."},"report_pattern":{"type":"string","minLength":0,"maxLength":512,"description":"SCRIPT + ON_MATCH: regex; output is reported only when it matches."},"notify_conversation_id":{"type":"number","nullable":true,"minimum":1,"description":"Conversation to report to (contact + channel). null clears it."},"credential_ids":{"uniqueItems":true,"description":"SCRIPT only (KJ-38): replace the set of credentials injected as env vars. Each MUST be assigned to this agent. [] clears them.","example":[7,9],"type":"array","items":{"type":"number"}},"model":{"type":"string","minLength":0,"maxLength":80,"description":"INPUT only (KJ-22): model for this scheduled turn (\"haiku\"/\"sonnet\"/\"opus\" or a claude-… slug). Empty string clears it (inherit).","example":"haiku"}}},"GetCapabilityDto":{"type":"object","properties":{"id":{"type":"number","description":"Capability id","example":1},"key":{"type":"string","description":"Stable slug — the agent-base/supervisor look at this.","example":"playwright"},"label":{"type":"string","description":"Display name","example":"Playwright + Chromium"},"description":{"type":"string","nullable":true,"description":"Long description"},"icon":{"type":"string","nullable":true,"description":"Lucide icon name hint for the frontend","example":"theater"},"created_at":{"format":"date-time","type":"string","description":"Created at"},"updated_at":{"format":"date-time","type":"string","description":"Updated at"}},"required":["id","key","label","created_at","updated_at"]},"ListCapabilityDto":{"type":"object","properties":{"data":{"description":"Items in the current page","type":"array","items":{"$ref":"#/components/schemas/GetCapabilityDto"}},"total":{"type":"number","description":"Total number of items matching the query","example":5},"page":{"type":"number","description":"Current page (1-indexed)","example":1},"limit":{"type":"number","description":"Page size","example":20}},"required":["data","total","page","limit"]},"InboxAgentBadgeDto":{"type":"object","properties":{"id":{"type":"number","description":"Agent id","example":3},"name":{"type":"string","description":"Agent name","example":"Captain"},"avatar_url":{"type":"string","nullable":true,"description":"Avatar url (proxy endpoint)"}},"required":["id","name"]},"InboxAssigneeDto":{"type":"object","properties":{"kind":{"enum":["AGENT","USER"],"type":"string","description":"Discriminator: USER (human operator) or AGENT (Claude agent)","example":"AGENT"},"id":{"type":"number","description":"Assignee id (user.id or agent.id depending on kind)","example":5},"name":{"type":"string","description":"Display name","example":"Captain"},"avatar_url":{"type":"string","nullable":true,"description":"Avatar url for AGENT kind (proxy endpoint), null for USER"}},"required":["kind","id","name"]},"InboxChannelBadgeDto":{"type":"object","properties":{"id":{"type":"number","description":"Channel id","example":2},"transport":{"type":"string","description":"Channel transport","example":"INTERNAL"},"name":{"type":"string","description":"Channel name","example":"Internal operators"}},"required":["id","transport","name"]},"InboxLastMessageDto":{"type":"object","properties":{"id":{"type":"number","description":"Message id","example":42},"role":{"type":"string","description":"Role","example":"ASSISTANT"},"kind":{"type":"string","description":"Message kind","example":"TEXT"},"preview":{"type":"string","nullable":true,"description":"Short text preview (best-effort from content)","example":"Sí, te llamas Maria Test"},"created_at":{"format":"date-time","type":"string","description":"Created at"}},"required":["id","role","kind","created_at"]},"InboxConversationDto":{"type":"object","properties":{"id":{"type":"number","description":"Conversation id","example":7},"status":{"type":"string","description":"Status","example":"OPEN"},"session_id":{"type":"string","description":"Claude session UUID","example":"a1b2…"},"reply_mode":{"type":"string","description":"SEND: the agent sends its replies. DRAFT: they wait as drafts for a person.","example":"SEND"},"opened_at":{"format":"date-time","type":"string","description":"Opened at"},"last_activity_at":{"format":"date-time","type":"string","description":"Last activity at"},"last_message":{"nullable":true,"description":"Last message preview","type":"object","allOf":[{"$ref":"#/components/schemas/InboxLastMessageDto"}]}},"required":["id","status","session_id","reply_mode","opened_at","last_activity_at"]},"InboxContactDto":{"type":"object","properties":{"id":{"type":"number","description":"Contact id","example":12},"kind":{"type":"string","description":"Contact kind","example":"EXTERNAL"},"name":{"type":"string","description":"Contact display name","example":"Maria"},"agent":{"nullable":true,"description":"Legacy \"owner\" agent of this contact (kept for backwards compat — prefer `assignee`).","type":"object","allOf":[{"$ref":"#/components/schemas/InboxAgentBadgeDto"}]},"assignee":{"nullable":true,"description":"Current Agent Desk assignee (human operator or agent). Null when nobody is currently handling the contact (fresh inbound).","type":"object","allOf":[{"$ref":"#/components/schemas/InboxAssigneeDto"}]},"channels":{"description":"Channels the contact is reachable through","type":"array","items":{"$ref":"#/components/schemas/InboxChannelBadgeDto"}},"conversations":{"description":"Conversations sorted by last_activity_at desc","type":"array","items":{"$ref":"#/components/schemas/InboxConversationDto"}},"last_activity_at":{"format":"date-time","type":"string","description":"Most recent activity across the contact"}},"required":["id","kind","name","channels","conversations","last_activity_at"]},"ListInboxDto":{"type":"object","properties":{"data":{"description":"Contacts grouped, each carrying conversation list","type":"array","items":{"$ref":"#/components/schemas/InboxContactDto"}},"total":{"type":"number","description":"Total contacts matched (not conversations)"}},"required":["data","total"]},"InboxIdentityDto":{"type":"object","properties":{"id":{"type":"number","description":"Identity id","example":9},"channel":{"description":"Channel this identity belongs to","allOf":[{"$ref":"#/components/schemas/InboxChannelBadgeDto"}]},"identifier":{"type":"string","description":"Stable identifier (email, phone, user_id, …)","example":"user:42"},"display_name":{"type":"string","nullable":true,"description":"Display name (optional)"},"first_seen_at":{"format":"date-time","type":"string","description":"First seen at"},"last_seen_at":{"format":"date-time","type":"string","description":"Last seen at"}},"required":["id","channel","identifier","first_seen_at","last_seen_at"]},"GetInboxContactDto":{"type":"object","properties":{"id":{"type":"number","description":"Contact id","example":12},"kind":{"type":"string","description":"Contact kind","example":"EXTERNAL"},"name":{"type":"string","description":"Contact display name","example":"Maria"},"user_id":{"type":"number","nullable":true,"description":"Linked user id (operator contacts only)"},"agent":{"nullable":true,"description":"Legacy \"owner\" agent (kept for backwards compat — prefer `assignee`).","type":"object","allOf":[{"$ref":"#/components/schemas/InboxAgentBadgeDto"}]},"assignee":{"nullable":true,"description":"Current Agent Desk assignee. Null if unassigned.","type":"object","allOf":[{"$ref":"#/components/schemas/InboxAssigneeDto"}]},"identities":{"description":"All identities this contact has (one per channel)","type":"array","items":{"$ref":"#/components/schemas/InboxIdentityDto"}},"conversations":{"description":"All conversations (open + closed) ordered by last_activity_at desc","type":"array","items":{"$ref":"#/components/schemas/InboxConversationDto"}},"created_at":{"format":"date-time","type":"string","description":"Created at"},"updated_at":{"format":"date-time","type":"string","description":"Updated at"}},"required":["id","kind","name","identities","conversations","created_at","updated_at"]},"InboxAssigneeUserDto":{"type":"object","properties":{"id":{"type":"number","description":"User id","example":2},"name":{"type":"string","description":"Display name","example":"Alice"},"email":{"type":"string","description":"Email","example":"alice@example.com"},"role":{"type":"string","description":"Membership role","example":"OPERATOR"},"avatar_url":{"type":"string","nullable":true,"description":"Avatar url (proxy endpoint)"}},"required":["id","name","email","role"]},"InboxAssigneeAgentDto":{"type":"object","properties":{"id":{"type":"number","description":"Agent id","example":5},"name":{"type":"string","description":"Agent name","example":"Captain"},"avatar_url":{"type":"string","nullable":true,"description":"Avatar url (proxy endpoint)"}},"required":["id","name"]},"ListInboxAssigneesDto":{"type":"object","properties":{"users":{"description":"Members of the organization eligible to be assigned","type":"array","items":{"$ref":"#/components/schemas/InboxAssigneeUserDto"}},"agents":{"description":"Agents in the organization eligible to be assigned","type":"array","items":{"$ref":"#/components/schemas/InboxAssigneeAgentDto"}}},"required":["users","agents"]},"SetConversationStatusDto":{"type":"object","properties":{"status":{"enum":["open","pending","resolved"],"type":"string","description":"Target status for the conversation. open = reactive / reopen, pending = waiting for action, resolved = closed."}},"required":["status"]},"SetAssigneeDto":{"type":"object","properties":{"user_id":{"type":"number","nullable":true,"minimum":1,"description":"Human operator to assign. Null to clear."},"agent_id":{"type":"number","nullable":true,"minimum":1,"description":"Agent to assign. Null to clear."}}},"SetConversationReplyModeDto":{"type":"object","properties":{"reply_mode":{"enum":["SEND","DRAFT"],"type":"string","description":"SEND: the agent sends what it writes in this conversation. DRAFT: it is kept as a draft for a person to review and send from the Agent Desk."}},"required":["reply_mode"]},"UpdateDraftDto":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":20000,"description":"The text the customer will read. Replaces the draft; the original is kept so what the agent actually wrote is never lost. An empty text is not accepted — discarding a draft is its own endpoint.","example":"Buenos días: su pedido sale mañana por la mañana."}},"required":["text"]},"McpCatalogFieldDto":{"type":"object","properties":{"key":{"type":"string","description":"Logical name.","example":"token"},"label":{"type":"string","description":"Human label shown above the input.","example":"Personal Access Token"},"secret":{"type":"boolean","description":"Rendered as a password input.","example":true},"placeholder":{"type":"string","nullable":true,"description":"Example text for the input placeholder."},"env_var":{"type":"string","nullable":true,"description":"Env var name this field resolves to when not referenced in command/args/url."}},"required":["key","label","secret"]},"GetMcpCatalogDto":{"type":"object","properties":{"id":{"type":"number","description":"Catalog entry id","example":1},"key":{"type":"string","description":"Stable slug","example":"github"},"name":{"type":"string","description":"Display name","example":"GitHub"},"description":{"type":"string","description":"One-line description","example":"Repos, issues, PRs y Actions."},"category":{"enum":["search","data","dev","design","files","infra","productivity","utility"],"type":"string","description":"Filter-chip grouping, as a key from a closed set — the panel writes it in the reader's language.","example":"dev"},"docs_url":{"type":"string","nullable":true,"description":"Where to get what this entry asks for — the tool's setup guide, or its website. Shown next to the fields; null when the entry has no link.","example":"https://docs.github.com/en/authentication"},"avatar_url":{"type":"string","nullable":true,"description":"Uploaded logo. Null falls back to a generic placeholder."},"transport":{"enum":["STDIO","SSE","HTTP"],"type":"string","example":"STDIO"},"command":{"type":"string","nullable":true,"description":"STDIO only."},"args":{"description":"STDIO only argv — may contain {{field.key}} placeholders.","type":"array","items":{"type":"string"}},"url":{"type":"string","nullable":true,"description":"SSE/HTTP only — may contain a {{field.key}} placeholder."},"fields":{"description":"What the picker asks the operator for.","type":"array","items":{"$ref":"#/components/schemas/McpCatalogFieldDto"}},"sort_order":{"type":"number","description":"Sort order (lower first).","example":10},"active":{"type":"boolean","description":"Offered in the picker.","example":true},"created_at":{"format":"date-time","type":"string","description":"Created at"},"updated_at":{"format":"date-time","type":"string","description":"Updated at"},"translations":{"type":"object","additionalProperties":true,"description":"Raw translations by field path and locale, e.g. `{ \"name\": { \"EN\": \"Web search\" } }`. Staff only (`?i18n=1`): everyone else reads the strings above, already in their language.","example":{"name":{"EN":"Web search"}}}},"required":["id","key","name","description","category","transport","args","fields","sort_order","active","created_at","updated_at"]}}}}