# Kindo Documentation — Full Content > Complete documentation content for LLM ingestion. Each section below is one documentation page. --- ## Connecting Integrations Source: https://docs.kindo.ai/connecting-integrations/ These guides walk through the provider-side configuration required to connect specific integrations to Kindo. --- ## Customer Training Session Source: https://docs.kindo.ai/learning/customer-training-session/ ## Session Metadata | Field | Value | | --- | --- | | Format | Virtual (~2.5 hours) | | Audience | Cybersecurity engineers | | Session Type | Pilot cohort (scaling to additional batches) | ## Session Objective Each participant builds a Kindo workflow agent independently by session end, including at least one workflow that uses live integrations through agentic tool calling. :::tip Design principle: get participants into the product within the first 10 minutes. Lead with hands-on experience, then explain what they just experienced. ::: ## Training Agenda ### Block 1 - Platform Orientation (45 min) | Time | Topic | Content | | --- | --- | --- | | 0:00-0:10 | Welcome and Context | Facilitator introduction. Why Kindo, what the customer is building toward, and session objectives. | | 0:10-0:20 | AI Chat First Steps | Live demo: everyone opens Kindo, asks a question in AI Chat. Show model selection and file upload. | | 0:20-0:35 | Platform Overview and Agent Types | What Kindo is, deployment models, chatbots vs workflow vs triggered agents, LLM steps, action steps, API action steps. | | 0:35-0:45 | Integrations Overview | How integrations work. Relevant stack: CrowdStrike, Jira, ServiceNow, Splunk. | Key takeaway: participants have used the product, understand the three major agent types, and know how integrations fit into workflows. ### Transition (5 min) | Time | Activity | Details | | --- | --- | --- | | 0:45-0:50 | Setup and Questions | Open Kindo, confirm access, address any platform questions before exercises begin. | ### Block 2 - Hands-On Workshop (65 min) | Time | Activity | Focus | | --- | --- | --- | | 0:50-1:15 | Exercise A - Firewall Rule Optimizer (25 min) | Prompt engineering, Knowledge Stores, and multi-step workflows | | 1:15-1:20 | Break (5 min) | | | 1:20-1:55 | Exercise B - Universal Security Triage Agent (35 min) | Integrations and agentic tool calling | Key takeaway: participants build two agents end to end — from structured prompting and knowledge stores to integration-powered autonomous triage. ### Block 3 - Apply and Extend (25 min) | Time | Activity | Details | | --- | --- | --- | | 1:55-2:00 | Briefing | Pick your own use case, extend a workshop agent, or fork a prebuilt template. | | 2:00-2:15 | Independent build time | Participants build agents for their own workflows. | | 2:15-2:20 | Show and tell (optional) | 2-3 volunteers share what they built. | ### Block 4 - Wrap-Up and Next Steps (15 min) | Time | Activity | Details | | --- | --- | --- | | 2:20-2:25 | Key concepts recap | Agent types, steps, knowledge stores, prompt engineering, integrations | | 2:25-2:30 | Ongoing resources | Documentation site, learning center, reference materials | | 2:30-2:33 | Scaling plan and action item | Build one agent for your actual work by Friday | | 2:33-2:35 | Q&A and feedback | Open questions and feedback form | :::note Block 2 is the highest-value section. If timing slips earlier in the session, preserve full exercise time. ::: ## Pre-Session Checklist ## Exercises ## Reference Materials --- ## Pre-Session Checklist Source: https://docs.kindo.ai/learning/customer-training-session/pre-session-checklist/ Share this checklist 3-5 days before the session. ## 1. Confirm Account Access :::note If you're already signed into a separate Kindo account, sign out or use an incognito window for the links below. ::: - Look for an email from Kindo with subject: **"You've been invited by bryan+deloittetraining@kindo.ai to join Kindo"** - Click on the link to join the org. - Choose **"Continue with email"** to sign in. Enter your email like this: `{your email}+training@deloitte.com` - For example, if your email is `joe@deloitte.com`, enter `joe+training@deloitte.com` in the email box. - Look for an email from **Kindo** with subject **"Your Kindo sign-in code"** in your email. Enter the code on the sign-in page. - After this it should take you to [app.kindo.ai](https://app.kindo.ai). - If you cannot sign in, contact your training coordinator immediately. ## 2. Browser and Setup - Use the latest Chrome or Edge. - Confirm a stable internet connection. - Use a display setup that supports split-view (training guide on one side, Kindo on the other). ## 3. Prepare a Use Case (Optional) - Think of one workflow from your daily work that you want to automate. - You will have independent build time in Block 3, so this helps you move faster. ## 4. What To Expect - A virtual ~2.5-hour session: orientation, hands-on exercises, and independent build time. - Two workflow agents built during the session. - No prior AI or ML background required. :::tip Prepare one specific workflow question in advance. Participants who bring concrete examples usually get better outcomes during Block 3. ::: ## Session Links - Session overview: [Customer Training Session](/learning/customer-training-session/) - Exercise A: [Firewall Rule Optimizer](/learning/customer-training-session/exercises/firewall-rule-optimizer/) - Exercise B: [Universal Security Triage Agent](/learning/customer-training-session/exercises/universal-triage-agent/) --- ## Learning Center Source: https://docs.kindo.ai/learning/ The Learning Center is where Kindo training programs live. Each session includes a clear agenda, participant setup checklist, hands-on exercises, and reusable reference guides. :::note This section is participant-facing. Facilitator-only operational notes and provisioning details are intentionally excluded. ::: ## Available Sessions ## What You Will Find In Each Session - Session overview with goals, audience, and full agenda timing - Pre-session checklist for access and setup - Hands-on exercises with prompts and troubleshooting - Reference materials you can reuse after training ## Coming Next More sessions and reusable guides are planned, including additional security workflows, cross-functional playbooks, and deeper integration design patterns. --- ## Agents API quickstart Source: https://docs.kindo.ai/apis/agents/quickstart/ The Agents API is Kindo's control-plane surface for **discovering** agents your organization has configured, **triggering** a run, and **polling** the run for its result. It is HTTP+JSON only — there is no SDK shim; any HTTP client works. The flow is: 1. `GET /v1/agents/list` — find the `agentId` you want to run. 2. `GET /v1/agents/{agentId}` — inspect the expected `inputs` labels. 3. `POST /v1/agents/runs` — trigger a run, receive a `runId`. 4. `GET /v1/runs/{runId}` — poll until `status` is terminal. ## Prerequisites - A Kindo API key (see [Authentication](/overview/authentication/)). - At least one agent configured for your organization in the Kindo Terminal. ## 1. Discover an `agentId` ```bash curl https://api.kindo.ai/v1/agents/list \ -H "Authorization: Bearer $KINDO_API_KEY" ``` This endpoint accepts no query parameters and returns the visible agents in a single page, capped at 500 entries. See [Request shape → `GET /v1/agents/list`](/apis/agents/request-shape/#get-v1agentslist) for the cap and envelope details. ```json { "items": [ { "agentId": "18d20df4-d9b3-4cb0-a009-9f11ec9c5d3d", "name": "Daily Security Scan", "description": "Runs the daily vulnerability check workflow.", "createdAt": "2026-03-10T18:45:23.000Z", "creatorName": "Security Team", "metadata": { "userPermissions": ["view", "edit"] } } ], "total": 1 } ``` ## 2. Fetch the input schema `GET /v1/agents/{agentId}` returns the agent's configuration, including the `inputs` array — the exact labels you must use when triggering a run. ```bash curl https://api.kindo.ai/v1/agents/18d20df4-d9b3-4cb0-a009-9f11ec9c5d3d \ -H "Authorization: Bearer $KINDO_API_KEY" ``` ```json { "agentId": "18d20df4-d9b3-4cb0-a009-9f11ec9c5d3d", "name": "Daily Security Scan", "description": "Runs the daily vulnerability check workflow.", "inputs": ["query"], "hasTriggers": false, "modelsInUse": ["claude-sonnet-4-5-20250929"], "recentRunIds": ["7a7ced7d-95a8-416f-95bb-5c0ff3599f53"], "lastRunAtUtc": "2026-03-12T15:00:00.000Z", "metadata": { "userPermissions": ["view", "edit"] } } ``` Each `name` in the run request's `inputs` array must match one of the strings in this `inputs` list exactly. ### Paginating and filtering `recentRunIds` The agent endpoint accepts optional `limit`, `cursor`, `startDate`, and `endDate` query parameters to page through and date-filter the `recentRunIds` list. When any of these is supplied, the paginated path uses a default `limit` of 20 and the response adds a `nextCursor` field: a string when more pages remain, `null` on the last page. Calls without query parameters keep the legacy response shape and legacy run cap. ```bash curl "https://api.kindo.ai/v1/agents/18d20df4-d9b3-4cb0-a009-9f11ec9c5d3d?limit=2&startDate=2026-03-12T00:00:00Z" \ -H "Authorization: Bearer $KINDO_API_KEY" ``` Note: for brevity, this example omits the unchanged agent-config fields (`name`, `description`, `inputs`, `hasTriggers`, `modelsInUse`, and `metadata`) shown above. The response shape is the same as the non-paginated example, plus `nextCursor`. ```json { "agentId": "18d20df4-d9b3-4cb0-a009-9f11ec9c5d3d", "recentRunIds": [ "7a7ced7d-95a8-416f-95bb-5c0ff3599f53", "3c1f4e2a-19b8-4d5e-8a2f-b6d1e0c4f7a9" ], "lastRunAtUtc": "2026-03-12T15:00:00.000Z", "nextCursor": "" } ``` Pass the returned `nextCursor` back as `?cursor=…` to fetch the next page. When you paginate a date-filtered list, re-pass the same `startDate`/`endDate` on every follow-up request — the cursor does not encode the date filters, and dropping them silently changes the page slice. `lastRunAtUtc` is independent of the filters — it always reflects the agent's true most recent run. See [Request shape → `GET /v1/agents/{agentId}`](/apis/agents/request-shape/#get-v1agentsagentid) for the full parameter and response contract. ## 3. Trigger a run ```bash curl -X POST https://api.kindo.ai/v1/agents/runs \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -d '{ "agentId": "18d20df4-d9b3-4cb0-a009-9f11ec9c5d3d", "inputs": [ { "name": "query", "value": "Run the daily security scan" } ] }' ``` The endpoint responds `202 Accepted` with the `runId` and the `conversationId` backing the run: ```json { "runId": "7a7ced7d-95a8-416f-95bb-5c0ff3599f53", "conversationId": "clx9a2b1c0000d4e5f6g7h8i9" } ``` Poll the run with `runId`. Keep the `conversationId` if you want to read the run's full message history via the [Conversations API](/apis/conversations/quickstart/) — the basis for multi-turn continuity across runs. ## 4. Poll the run ```bash curl https://api.kindo.ai/v1/runs/7a7ced7d-95a8-416f-95bb-5c0ff3599f53 \ -H "Authorization: Bearer $KINDO_API_KEY" ``` ```json { "runId": "7a7ced7d-95a8-416f-95bb-5c0ff3599f53", "agentId": "18d20df4-d9b3-4cb0-a009-9f11ec9c5d3d", "conversationId": "clx9a2b1c0000d4e5f6g7h8i9", "createdAtUtc": "2026-03-12T15:00:00.000Z", "endedAtUtc": "2026-03-12T15:00:08.000Z", "result": "{\"role\":\"assistant\",\"parts\":[{\"type\":\"text\",\"text\":\"Security scan complete. No critical findings.\"}]}", "status": "success" } ``` `result` is a JSON-encoded string of the final assistant message payload — parse with `JSON.parse` to recover the structured `{ role, parts, ... }` object. It is `null` while `in_progress`, always `null` on `failure` or `cancelled`, and also `null` on `success` when the run produced no assistant payload. `status` is one of `in_progress`, `success`, `failure`, or `cancelled`. Anything other than `in_progress` is terminal (a `failure` run can be re-run from the Kindo app, returning it to `in_progress` — see [Runs](/apis/agents/runs/)); keep polling while `status` is `in_progress`. See [Runs](/apis/agents/runs/) for the full lifecycle. ## 5. Fetch evaluation results (optional) If the agent has step-level objectives configured, you can retrieve per-step verdicts once the run completes: ```bash curl https://api.kindo.ai/v1/runs/7a7ced7d-95a8-416f-95bb-5c0ff3599f53/evals \ -H "Authorization: Bearer $KINDO_API_KEY" ``` ```json { "runId": "7a7ced7d-95a8-416f-95bb-5c0ff3599f53", "result": "pass", "steps": [ { "stepNumber": 1, "name": "Run the daily security scan", "result": "pass", "explanation": "Kindo completed the scan and found no critical vulnerabilities." } ] } ``` `result` is one of `pass`, `fail`, `pending`, `skipped`, or `unknown`. See [Runs → `GET /v1/runs/{runId}/evals`](/apis/agents/runs/#get-v1runsrunidevals) for the full field reference. ## Next steps - [Request shape](/apis/agents/request-shape/) — every field on every endpoint. - [Runs](/apis/agents/runs/) — run lifecycle, polling cadence, step-level evals, and how runs relate to conversations. - [Errors](/apis/agents/errors/) — common error envelopes. --- ## Chat Completions API quickstart Source: https://docs.kindo.ai/apis/chat-completions/quickstart/ `POST https://api.kindo.ai/v1/chat/completions` speaks the OpenAI [Chat Completions API](https://platform.openai.com/docs/api-reference/chat). Stock OpenAI clients work unmodified. ## Prerequisites - A Kindo API key (see [Authentication](/overview/authentication/)). - A model ID from [`GET /v1/models`](/overview/model-catalog/) — for example, `claude-sonnet-4-5-20250929`. ## openai-python ```python from openai import OpenAI client = OpenAI( base_url="https://api.kindo.ai/v1", api_key=os.environ["KINDO_API_KEY"], ) response = client.chat.completions.create( model="claude-sonnet-4-5-20250929", messages=[ {"role": "user", "content": "Explain Kubernetes pod security policies."} ], ) print(response.choices[0].message.content) ``` ## openai-node ```ts const client = new OpenAI({ baseURL: 'https://api.kindo.ai/v1', apiKey: process.env.KINDO_API_KEY }); const response = await client.chat.completions.create({ model: 'claude-sonnet-4-5-20250929', messages: [ { role: 'user', content: 'Explain Kubernetes pod security policies.' } ] }); console.log(response.choices[0].message.content); ``` ## curl ```bash curl -X POST https://api.kindo.ai/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -d '{ "model": "claude-sonnet-4-5-20250929", "messages": [ { "role": "user", "content": "Explain Kubernetes pod security policies." } ] }' ``` ## Response shape ```json { "id": "chatcmpl-abc123", "object": "chat.completion", "created": 1710000000, "model": "claude-sonnet-4-5-20250929", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "Pod Security Policies were Kubernetes rules..." }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 24, "completion_tokens": 134, "total_tokens": 158 } } ``` ## What you get by default - A single-shot, **stateless** completion. No persistence, no conversation linkage. - Your `messages` flow through verbatim. Kindo does **not** prepend a curated system prompt. - Tools are **only** what you list in `tools`. Kindo does not silently inject hosted tools. Kindo's opt-in extensions (curated system prompt, hosted tools, stateful conversations) are available on `/v1/responses`. See the [Chat Actions guide](/guides/chat-actions/) for that surface. ## Migrating from `llm.kindo.ai` `llm.kindo.ai` is deprecated. Change the subdomain: ```python # Old client = OpenAI(base_url="https://llm.kindo.ai", api_key="...") # New client = OpenAI(base_url="https://api.kindo.ai/v1", api_key="...") ``` The request and response bodies are identical. If you previously sent the bare `api-key:` header, use `Authorization: Bearer` for `api.kindo.ai`. ## Next steps - [Request shape](/apis/chat-completions/request-shape/) — every standard field Kindo honors. - [Streaming](/apis/chat-completions/streaming/) — `stream: true` and SSE. - [Tool use](/apis/chat-completions/tool-use/) — function-calling round trip. - [Errors](/apis/chat-completions/errors/) — error envelope shapes. --- ## Control plane endpoints Source: https://docs.kindo.ai/apis/control-plane/ Beyond the inference protocols, Kindo exposes a small set of **control-plane** endpoints on `api.kindo.ai` for inspecting and configuring the platform's models, integrations, and default connections. They use the same `Authorization: Bearer $KINDO_API_KEY` auth as the inference surfaces. `GET /v1/models` speaks the [OpenAI Models API](https://platform.openai.com/docs/api-reference/models) verbatim — stock OpenAI clients work unmodified. The integration endpoints below are Kindo-specific. ## Models ### `GET /v1/models` Speaks the OpenAI Models API. The response shape is identical to `GET https://api.openai.com/v1/models`, so any stock OpenAI client (`openai-python`, `openai-node`, `client.models.list()` in the SDKs, `curl`, etc.) works against `https://api.kindo.ai/v1/models` unmodified — just swap the base URL and key. The IDs returned here are the exact strings to pass as `model` on `/v1/chat/completions`, `/v1/responses`, and `/v1/messages`. ```bash curl https://api.kindo.ai/v1/models \ -H "Authorization: Bearer $KINDO_API_KEY" ``` ```json { "object": "list", "data": [ { "id": "claude-sonnet-4-5-20250929", "object": "model", "created": 0, "owned_by": "kindo" } ] } ``` For full detail on naming conventions, cross-API model use, and discovery via the Kindo Terminal, see the [Model catalog](/overview/model-catalog/) page. ## Integration connections The Integrations API lets you list integration connections accessible to the API-key holder and pick the preferred (default) connection for each integration. Connections are managed through the Kindo Terminal; this API exposes the read view plus the default-setter. ### `GET /v1/integrations/connections` Returns every connection the caller can see, including shared group connections. Each entry indicates whether it is the preferred default for that integration. ```bash curl https://api.kindo.ai/v1/integrations/connections \ -H "Authorization: Bearer $KINDO_API_KEY" ``` ```json { "data": [ { "id": "conn_abc123", "display_name": "Charlie's Linear", "integration_config_id": "linear", "nango_connection_id": "nango_conn_1", "is_preferred": true, "created_at": "2026-03-10T18:45:23.000Z" } ] } ``` | Field | Type | Notes | | ----------------------- | ------- | ---------------------------------------------------------------------------------------------------------------- | | `id` | string | Connection ID. Pass this as `connection_id` when setting a default. | | `display_name` | string | Display name from the Kindo Terminal. | | `integration_config_id` | string | The `nangoIntegrationId` (e.g. `linear`, `github`). Same value used as `{integration_id}` in the default-setter. | | `nango_connection_id` | string | Upstream Nango connection ID. | | `is_preferred` | boolean | `true` when this connection is the preferred default for `integration_config_id`. | | `created_at` | string | ISO-8601 timestamp. | ### `POST /v1/integrations/connections/{integration_id}/default` Sets the preferred connection for the given integration. The `{integration_id}` path segment is the same value as `integration_config_id` returned by the list endpoint — both refer to the `nangoIntegrationId`. ```bash curl -X POST https://api.kindo.ai/v1/integrations/connections/linear/default \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -d '{ "connection_id": "conn_abc123" }' ``` Response: ```json { "integration_config_id": "linear", "connection_id": "conn_abc123" } ``` Errors: | Status | Cause | | ------ | --------------------------------------------------------------------------------------------------- | | `400` | Invalid body — `connection_id` is required, or `connection_id` does not belong to `integration_id`. | | `403` | The key authenticated, but is not authorized to use the specified connection. | | `404` | The integration connection was not found, or the caller does not have access to it. | ## See also - [Model catalog](/overview/model-catalog/) — naming conventions and cross-API model use. - [Agents API](/apis/agents/quickstart/) — discover and trigger agents that route through the models exposed here. - [MCP API](/apis/mcp/quickstart/) — invoke tools from the same integration connections through Kindo's MCP gateway. --- ## Conversations API quickstart Source: https://docs.kindo.ai/apis/conversations/quickstart/ `GET https://api.kindo.ai/v1/conversations/{conversation_id}/items` speaks the OpenAI [Conversations API](https://platform.openai.com/docs/api-reference/conversations). Stock OpenAI clients work unmodified. The Conversations API is a **read-only** view into the items that make up a Kindo-managed conversation. It's the companion to `/v1/responses` when you run with `store: true` (or supply `conversation`) and want to fetch the persisted history server-side rather than re-reading it from your own application state. Two endpoints: - `GET /v1/conversations/{conversation_id}/items` — list every item in a conversation (messages, reasoning, tool calls, tool results). - `GET /v1/conversations/{conversation_id}/items/{item_id}` — fetch one item. ## Prerequisites - A Kindo API key (see [Authentication](/overview/authentication/)). - A `conversation_id`. The two common ways to get one: - The `response.conversation.id` field returned by a [`POST /v1/responses`](/apis/responses/quickstart/) call with `store: true`. - The Kindo Terminal exposes conversation IDs in the agent / chat-actions view. ## List the items in a conversation ```bash curl https://api.kindo.ai/v1/conversations/conv_abc123/items \ -H "Authorization: Bearer $KINDO_API_KEY" ``` Response: ```json { "object": "list", "data": [ { "id": "msg_xyz789", "type": "message", "role": "user", "content": [ { "type": "input_text", "text": "What is the capital of France?" } ], "status": "completed" }, { "id": "msg_xyz790", "type": "message", "role": "assistant", "content": [ { "type": "output_text", "text": "The capital of France is Paris." } ], "status": "completed" } ], "has_more": false, "first_id": "msg_xyz789", "last_id": "msg_xyz790" } ``` `has_more`, `first_id`, and `last_id` are always present on the list response. Pass `last_id` as `?after=` to fetch the next page; see [Request shape](/apis/conversations/request-shape/#query-parameters) for `limit`, `order`, and `after`. A single assistant turn can produce **multiple top-level items** in `data` — for example a `function_call` (tool use) followed by a `message` are emitted as siblings, not nested. Treat the response as a flat ordered stream and dispatch on each item's `type`. ## Fetch a single item ```bash curl https://api.kindo.ai/v1/conversations/conv_abc123/items/msg_xyz789 \ -H "Authorization: Bearer $KINDO_API_KEY" ``` The response object matches a single entry from `data[]` on the list call. ## When to use this surface - **Auditing** — replay the exact items the model saw, in order, after a run finished. - **Resuming UI state** — fetch a conversation on page load instead of caching it client-side. - **Tool-call analysis** — inspect every `function_call` and `function_call_output` pair that the model emitted. `Conversations` is read-only. To **continue** a stored conversation, send a new `POST /v1/responses` with `conversation: "conv_..."` — see [Responses → Chat Actions extensions](/apis/responses/kindo-extensions/#3-stateful-conversations). ## Next steps - [Request shape](/apis/conversations/request-shape/) — query parameters, response shape, and item types. - [Errors](/apis/conversations/errors/) — the OpenAI-style nested envelope and the `(type, code)` pairs Conversations returns. --- ## MCP API quickstart Source: https://docs.kindo.ai/apis/mcp/quickstart/ Connect any MCP client to Kindo and access permitted tools from connected integrations and first-party Platform tools through one URL with one API key. Tools are available only when your organization's approval setting is **Run automatically**. An organization setting of **Ask before running** or **Block** hides tools and prevents external MCP execution. Personal tool approval settings apply to Kindo chats and agents; configure your tool preferences in your external client. For tools requiring approval, use Kindo chat if manual approvals are enabled for your organization, or contact your administrator. `POST https://api.kindo.ai/v1/mcp` is a remote Model Context Protocol server. Point a spec-conformant MCP client at it and you get a tool catalog filtered by your account's access and organization approval settings. A typical `tools/list` will return entries like: - `linear_create_issue` - `github_search_repositories` - `slack_post_message` - `jira_get_ticket` - `notion_search_pages` Integration tools are prefixed with their integration ID. First-party Platform tools use the `platform_` prefix and require Platform MCP to be configured. Discovery applies [tool availability and policy checks](/apis/mcp/scoping/). Calls also check current access and permission for the requested resource; appearing in the catalog does not guarantee that every call is permitted. ## Prerequisites - A Kindo API key (see [Authentication](/overview/authentication/)). - An MCP-capable client. Anything that speaks the MCP spec works — Claude Code, OpenClaw, Codex CLI, Claude Desktop, Hermes, Cursor, VS Code, or your own code using the official SDK. See [Client setup](/apis/mcp/clients/) for copy-paste recipes. ## First call The smallest possible end-to-end example is a raw `tools/list` with `curl`: ```bash curl -X POST https://api.kindo.ai/v1/mcp \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -d '{ "jsonrpc": "2.0", "method": "tools/list", "id": 1 }' ``` The `Accept: application/json, text/event-stream` header is required by the MCP Streamable HTTP transport. MCP SDKs and the `mcp-remote` bridge set it for you; raw `curl` callers must include it. You'll get back the tools permitted by your organization's discovery and automatic-execution policies, across connected integrations and first-party Platform tools. ```json { "jsonrpc": "2.0", "id": 1, "result": { "tools": [ { "name": "linear_list_issues", "description": "List issues in a Linear team", "inputSchema": { "type": "object", "properties": { "teamId": { "type": "string" } }, "required": ["teamId"] } } ] } } ``` For department and member tools, see the [Platform tool catalog](/reference/tool-catalog/#platform-tools) for availability, limits, and bulk-assignment behavior. ## Next steps - [Client setup](/apis/mcp/clients/) — copy-paste recipes for Claude Code, OpenClaw, Codex CLI, Claude Desktop, Hermes, Cursor, VS Code, and the JS/TS SDK. - [Request shape](/apis/mcp/request-shape/) — required headers, supported JSON-RPC methods, and the tool naming convention. - [Scoping](/apis/mcp/scoping/) — which tools you see in `tools/list`, why the list is what it is, and how to narrow it. --- ## Messages API quickstart Source: https://docs.kindo.ai/apis/messages/quickstart/ `POST https://api.kindo.ai/v1/messages` speaks the Anthropic [Messages API](https://docs.anthropic.com/en/api/messages). Stock Anthropic clients work unmodified. ## Prerequisites - A Kindo API key (see [Authentication](/overview/authentication/)). - A model ID from [`GET /v1/models`](/overview/model-catalog/) — for example, `claude-sonnet-4-5-20250929`. ## Claude Code Point Claude Code at Kindo with two environment variables: ```bash export ANTHROPIC_BASE_URL=https://api.kindo.ai export ANTHROPIC_API_KEY=$KINDO_API_KEY claude ``` Claude Code sends standard Anthropic Messages requests to `https://api.kindo.ai/v1/messages`. Kindo also forwards the `x-claude-code-session-id` header for upstream session continuity. ## Anthropic Python SDK ```python from anthropic import Anthropic client = Anthropic( base_url="https://api.kindo.ai", api_key=os.environ["KINDO_API_KEY"], ) message = client.messages.create( model="claude-sonnet-4-5-20250929", max_tokens=512, messages=[ {"role": "user", "content": "Explain Kubernetes pod security policies."} ], ) print(message.content[0].text) ``` ## Anthropic TypeScript SDK ```ts const client = new Anthropic({ baseURL: 'https://api.kindo.ai', apiKey: process.env.KINDO_API_KEY }); const message = await client.messages.create({ model: 'claude-sonnet-4-5-20250929', max_tokens: 512, messages: [ { role: 'user', content: 'Explain Kubernetes pod security policies.' } ] }); console.log(message.content[0].text); ``` ## curl ```bash curl -X POST https://api.kindo.ai/v1/messages \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -d '{ "model": "claude-sonnet-4-5-20250929", "max_tokens": 512, "messages": [ { "role": "user", "content": "Explain Kubernetes pod security policies." } ] }' ``` The Anthropic SDKs set `anthropic-version` for you. Raw HTTP clients should include it. ## Response shape ```json { "id": "msg_01Aq9w938a90dw8q", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5-20250929", "content": [ { "type": "text", "text": "Pod Security Policies were Kubernetes rules..." } ], "stop_reason": "end_turn", "stop_sequence": null, "usage": { "input_tokens": 24, "output_tokens": 134 } } ``` ## What you get by default - A single-shot, **stateless** response. No persistence. - Your `system` and `messages` flow through verbatim. Kindo does **not** prepend a curated system prompt. - Tools are **only** what you list in `tools`. Kindo does not silently inject hosted tools. Kindo's opt-in extensions (curated system prompt, hosted tools, stateful conversations) are available on `/v1/responses`. See the [Chat Actions guide](/guides/chat-actions/) for that surface. ## Why route Claude Code through Kindo - **Centralized governance** — same auth, model access, audit, DLP, and metering as your other Kindo APIs. - **One API key** — the same Kindo key works for `/v1/messages`, `/v1/responses`, and `/v1/chat/completions`. - **Model consistency** — Claude Code uses the model IDs your organization exposes through Kindo. If Claude Code returns a model-not-found error, hit `GET /v1/models` first and use one of the IDs available to your organization. ## Next steps - [Request shape](/apis/messages/request-shape/) — every standard field Kindo honors. - [Streaming](/apis/messages/streaming/) — Anthropic's SSE event types. - [Tool use](/apis/messages/tool-use/) — `tool_use` / `tool_result` round trip. - [Errors](/apis/messages/errors/) — Anthropic error envelope. --- ## Responses API quickstart Source: https://docs.kindo.ai/apis/responses/quickstart/ `POST https://api.kindo.ai/v1/responses` speaks the OpenAI [Responses API](https://platform.openai.com/docs/api-reference/responses). Stock OpenAI clients work unmodified. Pick the section below that matches the client you already use. ## Prerequisites - A Kindo API key (see [Authentication](/overview/authentication/)). - A model ID from [`GET /v1/models`](/overview/model-catalog/) — for example, `claude-sonnet-4-5-20250929`. ## codex `codex` is the OpenAI CLI built on the Responses API. Point it at Kindo with two environment variables: ```bash export OPENAI_BASE_URL=https://api.kindo.ai/v1 export OPENAI_API_KEY=$KINDO_API_KEY codex ``` That's the entire integration. `codex` builds standard Responses requests and Kindo handles them with stock OpenAI semantics. ## openai-python ```python from openai import OpenAI client = OpenAI( base_url="https://api.kindo.ai/v1", api_key=os.environ["KINDO_API_KEY"], ) response = client.responses.create( model="claude-sonnet-4-5-20250929", input="Explain Kubernetes pod security policies in plain English.", ) print(response.output_text) ``` ## openai-node ```ts const client = new OpenAI({ baseURL: 'https://api.kindo.ai/v1', apiKey: process.env.KINDO_API_KEY }); const response = await client.responses.create({ model: 'claude-sonnet-4-5-20250929', input: 'Explain Kubernetes pod security policies in plain English.' }); console.log(response.output_text); ``` ## curl ```bash curl -X POST https://api.kindo.ai/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -d '{ "model": "claude-sonnet-4-5-20250929", "input": "Explain Kubernetes pod security policies in plain English." }' ``` ## Response shape The response uses the standard OpenAI Responses output: ```json { "id": "resp_abc123", "object": "response", "status": "completed", "model": "claude-sonnet-4-5-20250929", "output": [ { "type": "message", "role": "assistant", "content": [ { "type": "output_text", "text": "Pod Security Policies were Kubernetes rules that controlled what a pod was allowed to do..." } ], "status": "completed" } ], "output_text": "Pod Security Policies were Kubernetes rules that controlled what a pod was allowed to do..." } ``` Raw HTTP responses also include `output_text` at the top level — the SDK exposes it via a convenience field of the same name. ## What you get by default - A single-shot, **stateless** completion. Nothing is persisted on Kindo's side. To carry state across calls, opt in via `store: true`, `conversation`, or `previous_response_id`. - Your `instructions` (or `input`-array `developer` / `system` messages) flow through verbatim. Kindo does **not** prepend its curated system prompt. - Tools are **only** what you list in `tools`. Kindo does not silently inject `kindo_shell`, `kindo_web_search`, or anything else. If you want any of those — stateful conversations, a curated Kindo system prompt, or the Kindo-hosted tool catalog — opt in via the [Chat Actions extensions](/apis/responses/kindo-extensions/). ## Next steps - [Request shape](/apis/responses/request-shape/) — every standard Responses field Kindo honors and how it's forwarded. - [Streaming](/apis/responses/streaming/) — set `stream: true` and consume SSE. - [Tool use](/apis/responses/tool-use/) — function-calling round trip. - [Errors](/apis/responses/errors/) — error envelope shapes. - [Chat Actions extensions](/apis/responses/kindo-extensions/) — opt in to Kindo prompts, tools, and stateful conversations. - [Invoke Kindo agents](/guides/invoke-agents-responses-api/) — run a stored agent by passing `model: "agent/"`. --- ## Prompt and Agent Configuration Management Source: https://docs.kindo.ai/best-practices/prompt-and-agent-configuration/ Your Kindo agents — Chatbots, Workflow Agents, and Triggered Agents — are only as reliable as the prompts, instructions, knowledge files, and integration configurations behind them. As usage grows, these assets become operational infrastructure. They should be managed with the same discipline you apply to application code or business process documentation. This guide covers practical patterns for the full lifecycle of agent configuration management: creation, organization, version control, documentation, backup, recovery, testing, and governance. ## 1. Create Effective Prompts ### Write Prompts Like Instructions for a New Team Member A strong prompt is specific, structured, and testable. Treat agent instructions like a Standard Operating Procedure for a new hire. **Do:** - State the agent's role explicitly - Define the expected output format - Specify boundaries and constraints - Include examples when the task is nuanced **Avoid:** - Vague requests like "analyze this and give me insights" - Conflicting directives like "be thorough" and "keep it under 2 sentences" - Embedding credentials or sensitive values directly in prompt text ### Example Prompt ```text You are a vulnerability triage analyst for our security operations team. ROLE: - Analyze vulnerability scan results from CrowdStrike - Classify each finding by severity and business impact - Recommend remediation priority OUTPUT FORMAT: For each vulnerability, produce a row with these columns: | CVE ID | Severity | Affected Asset | Business Impact | Recommended Action | Priority | RULES: - Only report Critical and High severity findings - If a CVE has a known exploit in the wild, escalate Priority to "Immediate" - If the affected asset is in the DMZ, increase Business Impact by one level - Do not speculate about vulnerabilities not present in the scan data ``` ### Structure Multi-Step Agents Deliberately For Workflow and Triggered Agents: - **Give each step one job.** If a step analyzes, prioritizes, writes a report, and files a ticket, it is doing too much. - **Name steps descriptively.** "Fetch Open Vulnerabilities" is more maintainable than "Step 1". - **Use the right step type.** Use LLM Steps for reasoning, API Action Steps for deterministic reads/writes, and Action Steps for integrated product actions. ### Use Knowledge Store Files Strategically - Attach only files that are relevant to the agent's task - Keep reference files current with a regular review cadence - Prefer structured formats like Markdown, CSV, or JSON when possible ## 2. Organize Your Agent Portfolio ### Use Consistent Naming Conventions A simple pattern works well: ```text [Team/Domain] - [Function] - [Scope] ``` Examples: - `SecOps - Vulnerability Triage - CrowdStrike` - `IAM - Access Review Summary - SailPoint` - `NetOps - Firewall Audit - Cisco FMC` - `IT Helpdesk - Laptop Setup Guide - General` ### Use Descriptions as Real Documentation A good description should answer: 1. What does this agent do? 2. When should someone use it? 3. What integrations does it require? 4. Who owns it? Example: > Performs first-order root cause analysis when a P1/P2 incident is created in ServiceNow. Requires ServiceNow and Splunk integrations. Owned by the Platform Engineering team. Last reviewed: 2026-02-15. ### Group by Function, Not by Creator As your agent library grows, organize by business function rather than by the person who built the agent. Pair shared visibility with clear ownership. ## 3. Version Control ### Why Version Control Matters Prompts are fragile. Small wording changes can create meaningful behavior changes. Without version history, you cannot reliably: - Roll back a change that broke a production workflow - Explain why a prompt was written a certain way - Audit which version of an agent was active on a specific date - Coordinate edits across a team ### Use Kindo's Built-In Version History Kindo captures a version of each agent's configuration every time it is saved. Use the **Version History** panel in the agent builder to: - Browse every prior version in a read-only viewer - Inspect a past version's exact configuration (steps, inputs, triggers, knowledge store) without leaving the builder - Restore the agent to a previous version after a regression - Audit every restore via the `Agent Restored to Previous Version` event in the audit log Restore replaces the agent's content (steps, inputs, triggers, knowledge store) with the selected version's content. Sharing and access controls are intentionally not part of version state — restoring an older version does not change who can see or edit the agent. All triggers (Integration Event, Direct Webhook, Cron) always land **inactive** after a restore and must be explicitly re-enabled, so a months-old version does not silently resume external side effects. Opening the Version History panel, viewing a prior version, and restoring all require **`EDIT_WORKFLOW`** permission on the agent. See [Version History](/features/creating-agents/#version-history) for the full feature reference. ### Git as a Secondary Backup The built-in version history covers day-to-day rollback and audit needs. For an out-of-platform backup — disaster recovery, cross-org templates, or syncing agent configurations into your existing code-review and approval workflows — mirror agent configurations to Git. A practical structure: ```text kindo-agents/ ├── README.md ├── secops/ │ └── vulnerability-triage/ │ ├── agent.yaml │ ├── CHANGELOG.md │ ├── DESIGN.md │ ├── knowledge/ │ └── tests/ ├── iam/ │ └── access-review/ │ ├── agent.yaml │ └── CHANGELOG.md └── templates/ ``` ### What to Capture in the Git Mirror At minimum, track: - Agent name and type - Description - Prompt / instructions - Per-step prompts - Model selections - Integration dependencies - Knowledge file references - Trigger conditions - Sharing and access settings (not captured by Kindo's version history) - Owner and last-reviewed date ## 4. Documentation ### Document the "Why," Not Just the "What" The config explains what the agent does. Documentation should explain why it was designed that way. For business-critical agents, maintain a companion design note covering: - Purpose - Key design decisions - Known limitations - Dependencies - Change history ### Maintain a Central Agent Catalog For teams with many agents, keep an index like this: | Agent | Type | Owner | Integrations | Status | Last Reviewed | | --------------------- | -------- | -------- | ------------ | ------ | ------------- | | Vulnerability Triage | Trigger | SOC Team | CrowdStrike | Active | 2026-02-28 | | Access Review Summary | Workflow | IAM Team | SailPoint | Active | 2026-02-15 | | Firewall Audit | Workflow | NetOps | Cisco FMC | Draft | — | ## 5. Backup and Recovery ### Backup Strategy Kindo's built-in version history is the primary recovery path for in-platform regressions. A separate Git mirror serves as a secondary backup for disaster recovery, cross-org migration, and integration with your existing code-review process. Recommended cadence: | Agent Criticality | Backup Frequency | Method | | ----------------- | -------------------------------------------- | --------------------------------------- | | Business-critical | After every change + weekly snapshot | Git repository | | Standard | After significant changes + monthly snapshot | Git repository or shared document store | | Experimental | Before promoting to production | Git repository | Back up: - Agent configurations - Knowledge Store files referenced by the agent - Integration configuration details (not credentials) - Design notes and changelogs Do **not** store credentials, API keys, or tokens in the repo. ### Recovery Process If an agent needs to be restored: 1. **First, try the built-in version history.** Open the agent's **Version History** panel, identify the last known good version, and select **Restore this version**. This rolls back steps, inputs, triggers, and knowledge store attachments in a single action. Note that all triggers (Integration Event, Direct Webhook, Cron) land inactive after restore — re-enable them once you've verified the restore is correct. - **If restore fails with a "references a deleted X" error** — the target version references a resource (secret, model, step file, integration, or knowledge-store file) that has since been deleted. Recreate the missing resource first, then retry the restore. If recreation isn't an option, fall back to the Git mirror. 2. If the version you need is no longer in Kindo's history (for example, it was pruned by retention or the agent itself was deleted), fall back to your Git mirror: recreate or update the agent in Kindo, reconnect integrations, and re-upload Knowledge Store files. 3. Run a known-good test case before returning the agent to production. 4. Document the recovery action. The audit log records every in-platform restore as an `Agent Restored to Previous Version` event, including the source version and the resulting new version — use this for incident postmortems. ### Self-Managed Deployments For self-managed Kindo deployments, include these in your backup strategy: - PostgreSQL backups for agent configuration, history, and audit data - Object storage backups for Knowledge Store files - Secrets-manager backups and rotation procedures - Periodic restore tests ## 6. Testing and Validation ### Build an Evaluation Habit Before deploying an agent change: - Run a **golden-path test** with known input and expected output - Run **edge-case tests** for empty, malformed, or unusually large input - Re-run golden-path tests after every prompt change to catch regressions ### Store Test Cases Alongside the Agent ```text secops/vulnerability-triage/ ├── agent.yaml ├── knowledge/ └── tests/ ├── golden-path-input.json ├── golden-path-expected.md └── edge-case-empty-findings.json ``` ## 7. Collaboration and Governance ### Establish Ownership Every production agent should have a clearly assigned owner or owning team responsible for: - Keeping prompts and knowledge files current - Reviewing and approving changes - Responding when the agent behaves unexpectedly - Running periodic reviews ### Use Kindo's Governance Controls Use Kindo's built-in controls intentionally: - **RBAC** to control who can create, edit, and run agents - **DLP filters** to reduce sensitive-data exposure - **Audit logs** for compliance and investigation - **Tool access controls** to limit what integrations and actions agents can use - **Model access controls** to align capability, cost, and risk ## 8. Security Considerations ### Secrets Management - Never embed credentials in prompt text - Use Kindo integrations to manage external authentication - Rotate credentials on your standard schedule ### Prompt Injection Awareness If an agent consumes data from external systems, treat that data as untrusted input. Design prompts and tool access around defense in depth. ### Data Classification Be intentional about what documents you attach to agents. If a Knowledge Store contains restricted data, the agent's sharing model and access settings should match that classification. ## Summary Checklist Use this checklist when creating or reviewing an agent: - [ ] Prompts are specific, structured, and testable - [ ] Naming follows a consistent convention - [ ] Description includes purpose, use case, dependencies, and owner - [ ] Steps have clear responsibilities and names - [ ] Knowledge files are relevant and current - [ ] Configuration is version controlled - [ ] Design decisions and limitations are documented - [ ] Backups exist independently of the live environment - [ ] Golden-path and edge-case tests exist - [ ] Access controls match the agent's risk profile - [ ] A person or team owns the agent - [ ] Review cadence is defined This guide reflects recommended practices as of May 2026 and should evolve as Kindo adds more first-class agent management capabilities. --- ## Connect to ServiceNow Source: https://docs.kindo.ai/connecting-integrations/servicenow/ Kindo supports two integration patterns with ServiceNow: - **OAuth 2.0 Client Credentials** — Allows Kindo to authenticate with your ServiceNow instance and access its APIs (e.g., reading and creating incidents). - **Direct Webhook URL** — Allows ServiceNow to send real-time event notifications (e.g., incident created, updated, resolved) to a Kindo agent via an outbound webhook. You can use either or both depending on your use case. --- ## OAuth 2.0 Client Credentials Kindo supports integration with ServiceNow using OAuth 2.0 Client Credentials (CC) for secure authentication. :::caution[Important] This integration is only supported for ServiceNow instances on the **Washington DC release or newer**. Older versions of ServiceNow do not support the Client Credentials grant type for inbound OAuth, so this method will not work there. If your instance is on an older release, please contact Kindo support and we will work with you to provide an alternative authentication solution. ::: ### Prerequisites You must have the **System Administrator** role. For certain steps, you will need to elevate your privileges to `security_admin`. ### Step 1: Enable OAuth Client Credentials Grant Type ### Step 2: Register the OAuth Application ### Next Steps Once these steps are complete, your ServiceNow instance will be ready for integration with Kindo using OAuth 2.0 Client Credentials. 1. In Kindo, open the **Command Center** and, under **Manage Your Integrations**, connect to **ServiceNow**. 2. Enter the **Client ID** and **Client Secret** from Step 2. 3. Provide your ServiceNow instance URL (e.g., `https://.service-now.com`). 4. Click **Connect** to complete the integration. If you need further assistance, please refer to [ServiceNow's documentation](https://www.servicenow.com/docs/bundle/yokohama-platform-security/page/integrate/authentication/concept/oauth-inbound.html) or contact your Kindo support representative. --- ## Direct Webhook URL ServiceNow can send real-time event notifications to a Kindo agent using a Direct Webhook URL. When an incident (or other record) is created or updated, ServiceNow sends a POST request with the record data to your agent's webhook endpoint, triggering the agent automatically. This is useful when you want a Kindo agent to react to ServiceNow events — for example, triaging new incidents, summarizing updates, or escalating based on priority. ### Prerequisites - A Kindo agent with a **Direct Webhook URL** trigger saved. See [Creating Agents — Direct Webhook URL Triggers](/features/creating-agents/#direct-webhook-url-triggers) for setup instructions. Copy the generated webhook URL — you will need it in the steps below. - **System Administrator** access to your ServiceNow instance. ### Step 1: Create an Outbound REST Message ### Step 2: Add an HTTP Method ### Step 3: Create a Business Rule The Business Rule tells ServiceNow *when* to fire the webhook. In this example, it fires whenever an incident is created or updated. ### Step 4: Test the Integration ### Customizing the Trigger - **Different tables**: Replace `Incident [incident]` with any ServiceNow table (e.g., Change Request, Problem, Service Request) and adjust the payload fields accordingly. - **Conditional triggers**: Use the Business Rule's **Filter Conditions** to fire only on specific events — for example, only when priority is Critical, or only when state changes to Resolved. - **Run conditions in Kindo**: You can also add a natural-language **Run Condition** on the Direct Webhook URL trigger in Kindo to filter which payloads actually invoke the agent. See [Creating Agents — Run Condition](/features/creating-agents/#direct-webhook-url-triggers) for details. ### Security Considerations The Direct Webhook URL contains a cryptographically random secret token. Treat this URL as a secret — anyone who has it can trigger your agent. To rotate the token, delete the trigger in Kindo and create a new one, then update the Outbound REST Message endpoint in ServiceNow. For more details, see [Creating Agents — Security Considerations](/features/creating-agents/#security-considerations). --- ## Chat Actions Source: https://docs.kindo.ai/guides/chat-actions/ **Chat Actions** is Kindo's umbrella name for three orthogonal opt-in extensions on `/v1/responses`: 1. **Curated system prompt** — let Kindo prepend its operational prompt. 2. **Kindo-hosted tools** — call `kindo_shell`, `kindo_web_search`, and the rest of the Kindo hosted-tool catalog. 3. **Stateful conversations** — let Kindo persist messages server-side so subsequent turns chain off them. Each is independent. Default behavior on every API surface is **stock provider behavior** — Kindo does not silently inject prompts, tools, or state. You opt in explicitly, per request, when you want Kindo's added value. `/v1/chat/completions` and `/v1/messages` are stock OpenAI and stock Anthropic respectively. They do not accept the Chat Actions opt-ins; if you need the curated prompt, hosted tools, or stateful conversations, use `/v1/responses`. ## Why Chat Actions exist Different teams want different mixes: - A platform team running their own agent loop wants Kindo's governance, but not Kindo's prompt or tools. - An ops team running ad-hoc investigations wants the full Kindo agent — curated prompt, hosted tools, persistent conversation. - A SOC team wants Kindo's tool catalog and statefulness, but their own carefully-tuned system prompt. Bundling these three concerns into a single mode switch (the old `tools: [{type: "kindo_tools"}]` sentinel) collapses the space. Chat Actions splits them apart so each request expresses exactly what it wants. ## How it looks on `/v1/responses` | Concern | Field | | ----------------------------- | -------------------------------------------- | | **Curated system prompt** | `kindo.system_prompt: "agent_default"` | | **Hosted tools (full)** | `tools: [{type: "kindo_tools"}]` | | **Hosted tools (individual)** | `tools: [{type: "kindo_shell"}, ...]` | | **Stateful conversation** | `store: true` + `conversation` (spec-native) | For the field-level spec, see [Responses → Chat Actions extensions](/apis/responses/kindo-extensions/). ## The hosted-tool catalog | Tool type | Purpose | | -------------------------------------- | ------------------------------------------------------------------- | | `kindo_shell` | Run a sandboxed shell command on a Kindo-managed agent workstation. | | `kindo_web_search` | Search the web. | | `kindo_browser_control` | Drive a headless browser session. | | `kindo_browser_screenshot` | Capture a screenshot of the current page. | | `kindo_browser_list_network_requests` | List network requests captured by the browser tool. | | `kindo_browser_export_request_as_curl` | Export a captured request as a runnable curl command. | | `kindo_file_read` | Read a file the agent has access to. | | `kindo_file_write` | Write to a file the agent has access to. | | `kindo_file_list` | List files in an accessible directory. | | `kindo_web_download` | Download a URL to the agent workspace. | | `kindo_files_download_original` | Fetch the original binary of a previously stored file. | | `kindo_files_download_as_text` | Fetch a previously stored file as decoded text. | | `kindo_file_share_with_user` | Share a file with a Kindo user. | | `kindo_vpn_connect` | Connect the agent's network egress to an organization VPN. | | `kindo_vpn_disconnect` | Disconnect from the organization VPN. | | `kindo_dashboard_generate` | Generate a Kindo dashboard. | | `kindo_dashboard_update` | Update a Kindo dashboard. | | `kindo_knowledge_store_search` | Search a Kindo knowledge store. | Tool types not in this list are forwarded verbatim, so new types work as soon as they're hosted. ## The four common combinations All examples below are `/v1/responses` requests. ### A. Stock provider behavior (default) Send no Chat Actions opt-ins. You get exactly what calling OpenAI directly would give you, plus Kindo's governance. ### B. "I want Kindo's tools, but my own prompt" ```json { "tools": [{ "type": "kindo_tools" }] } ``` Hosted tools turn on; your `instructions` / `system` / `messages` flow through verbatim. ### C. "I want Kindo's prompt and tools, but I'll manage state myself" ```json { "store": true, "kindo": { "system_prompt": "agent_default" }, "tools": [{ "type": "kindo_tools" }] } ``` The API auto-creates a conversation and returns the new ID in `conversation.id`. To stay stateless, discard that ID and do not pass it back. ### D. "I want the full Kindo agent experience" ```json { "store": true, "kindo": { "system_prompt": "agent_default" }, "conversation": "conv_...", "tools": [{ "type": "kindo_tools" }] } ``` ## Mixing Kindo-hosted tools with stock function tools You can mix Kindo-hosted entries (`{type: "kindo_*"}`) with stock OpenAI function tools in the same `tools` array on `/v1/responses`. The hosted-tool shape is uniform; the function-tool shape follows the per-API canonical form. See [Responses tool use](/apis/responses/tool-use/) for the function-tool round trip. ## See also - [Responses → Chat Actions extensions](/apis/responses/kindo-extensions/) — full field-level spec. - [Responses quickstart](/apis/responses/quickstart/) — stock-OpenAI baseline before any extensions. --- ## Exercise A - Firewall Rule Optimizer Source: https://docs.kindo.ai/learning/customer-training-session/exercises/firewall-rule-optimizer/ ## Exercise Metadata | Field | Value | | --- | --- | | Duration | ~25 minutes | | Type | Workflow Agent | | Focus | Prompt engineering + Knowledge Stores | ## Scenario Your network security team exports firewall rule reports periodically. These reports contain hundreds of rules accumulated over years, many of which are outdated, overly permissive, or redundant. Manual review is time-consuming and error-prone. ## Objective Build a Kindo Workflow Agent that ingests a firewall rule report, analyzes each rule against security best practices, and produces a structured cleanup report. ## Output Categories | Category | Meaning | Example Indicators | | --- | --- | --- | | RISKY | Significant security exposure | any-to-any rules, inbound to sensitive ports | | OVER-PERMISSIVE | Broader than necessary | allow all ports, overly broad ranges | | UNUSED | Zero or negligible hit counts | 0 hits over 90+ days | | REDUNDANT | Covered by another rule | traffic fully covered by higher-priority rule | | COMPLIANT | Correctly scoped and actively used | specific source/dest, appropriate ports, healthy hit count | ## Prerequisites - Kindo account with access to the training instance - Sample firewall rule report: [Download sample-firewall-rules.txt](/docs/learning/sample-firewall-rules.txt) ## About the Sample Data The sample firewall rule report contains 40 rules representing a realistic corporate firewall accumulated over several years. The rules include a deliberate mix of: - **Clean rules** that are correctly scoped and actively used (web server access, monitoring, security tooling) - **Risky rules** with direct internet exposure to sensitive services (SSH, RDP, MySQL open to `any`) - **Over-permissive rules** with overly broad scope (`any`-to-`any` permits, full internal subnet access) - **Unused rules** with zero hit counts from decommissioned projects, expired contracts, or completed engagements - **Redundant rules** whose traffic is already covered by broader rules at higher priority As you iterate through the prompt engineering steps below, notice how your output quality changes — not just in formatting, but in whether the agent catches subtle issues like dev-to-prod database access or guest WiFi misconfigurations. ## Step 1 - Create the Agent (3 min) {/* Screenshot placeholder: Agents tab -> Create an Agent -> Workflow Agent selected */} ## Step 2 - Set Up the Knowledge Store (3 min) :::tip The Knowledge Store is the agent's reference library. Structured and focused source files produce cleaner and more consistent analysis. CSV works well here because each row is a self-contained rule with consistent columns — the model can process it systematically. ::: {/* Screenshot placeholder: Knowledge Store panel with uploaded file visible */} ## Step 3 - Build the Analysis Prompt Iteratively (12 min) This is the core teaching moment of the exercise. Instead of pasting a final prompt immediately, you will build it in three iterations and compare how each change affects output quality. ### Iteration 1 - Start Simple ```plaintext Analyze the firewall rules in the knowledge store and identify which ones are risky or should be cleaned up. ``` :::note[What to observe] Run this and look at the output critically. You will likely see: - **Vague language** — "this rule might be concerning" instead of a concrete verdict - **Inconsistent structure** — some rules get a paragraph, others get a sentence - **Missing rules** — the model may skip rules it considers "fine" without telling you - **No confidence signal** — you can't tell which findings the model is sure about versus guessing ::: ### Iteration 2 - Add Role and Structure ```plaintext You are a senior network security engineer performing a firewall rule audit. Analyze the firewall rule report in the knowledge store. For each rule, categorize it into exactly ONE of: 1. RISKY - Significant security exposure (e.g., any-to-any rules, inbound to sensitive ports) 2. OVER-PERMISSIVE - Broader than necessary 3. UNUSED - Zero or near-zero hit counts 4. REDUNDANT - Already covered by another rule 5. COMPLIANT - Correctly scoped, actively used, follows best practices Output as a table with columns: Rule ID | Rule Name | Category | Recommendation ``` :::tip[Why this works] Three changes made a big difference: 1. **Role specification** ("senior network security engineer") sets the analysis depth and vocabulary 2. **Explicit categories** constrain the model to consistent buckets instead of freeform commentary 3. **Fixed table format** makes the output readable and usable downstream Compare this output to Iteration 1. You should see every rule accounted for and consistently categorized. ::: ### Iteration 3 - Production Prompt ```plaintext You are a senior network security engineer performing a firewall rule audit. TASK: Analyze the firewall rule report in the knowledge store. For each rule, categorize it into exactly ONE of the following: 1. RISKY - Rules that create significant security exposure (e.g., any-to-any rules, inbound access to sensitive ports from untrusted sources, rules allowing dangerous protocols) 2. OVER-PERMISSIVE - Rules broader than necessary (e.g., allow all ports when specific ports would suffice, overly broad source/destination ranges) 3. UNUSED - Rules with zero or negligible hit counts during the analysis period (0 hits over 90+ days = unused; <10 hits over 90 days = near-unused) 4. REDUNDANT - Rules whose traffic is already fully covered by another rule with equal or higher priority 5. COMPLIANT - Rules that are correctly scoped, actively used, and follow security best practices (specific source/destination, appropriate ports, healthy hit counts) ANALYSIS GUIDELINES: - Consider source, destination, service/port, action (permit/deny), and hit count - For RISKY: any rule allowing inbound traffic from "any" to critical infrastructure ports (22, 3389, 1433, 3306, 5432) should be flagged - For OVER-PERMISSIVE: compare against the principle of least privilege - If a rule could belong to multiple categories, choose the highest-risk category OUTPUT FORMAT: | Rule ID | Rule Name | Category | Confidence | Risk Level | Recommendation | CONFIDENCE LEVELS: - HIGH - Clear indicators support the categorization - MEDIUM - Some ambiguity but categorization is likely correct - LOW - Insufficient data; manual review recommended After the table, provide: 1. Summary count of rules per category 2. Top 5 highest-priority rules to remediate immediately 3. Recommended next steps ``` :::note[Key learning] Compare this output to Iteration 2. The improvements come from: - **Explicit thresholds** (hit count numbers, specific port lists) remove ambiguity - **Analysis guidelines** tell the model *how* to evaluate, not just *what* to look for - **Confidence levels** support triage decisions — auto-remediate HIGH confidence findings, manually review LOW confidence ones - **Structured follow-up sections** make the output actionable for a security operations manager Every constraint you add improves actionability. This is the core prompt engineering lesson: precision in, precision out. ::: ## Step 4 - Add the Report Formatting Step (5 min) Add a second LLM step named `Format Cleanup Report`. :::tip[Why a second step?] Separating analysis from formatting is a key workflow pattern. The first step focuses on *accuracy* (getting the categorization right). The second step focuses on *presentation* (making it useful for a specific audience). This separation also lets you swap the formatting step for different audiences — executive summary, CSV export, or Jira ticket templates — without re-running the analysis. ::: ```plaintext You are formatting the firewall rule analysis into an executive-ready cleanup report. Take the categorized rule analysis from the previous step and produce: SECTION 1 - EXECUTIVE SUMMARY - Total rules analyzed, breakdown by category (count and percentage) - Overall risk assessment (Critical / High / Medium / Low) - Estimated effort to remediate SECTION 2 - IMMEDIATE ACTION ITEMS Top 10 highest-priority rules, sorted by risk: - Rule ID and name - Why it's flagged - Recommended action (remove, tighten, merge, review) - Estimated impact if left unaddressed SECTION 3 - CATEGORY DETAIL For each category, list all rules sorted by confidence level (HIGH first). SECTION 4 - RECOMMENDATIONS - Quick wins (high confidence, low effort) - Rules requiring manual review (low confidence) - Suggested review cadence going forward - Integration recommendations (e.g., connect to CrowdStrike for threat-informed prioritization) Keep the tone professional and direct. This report goes to the security operations manager. ``` ## Step 5 - Run and Review (5 min) ### Quality Checklist - **Categorization accuracy:** categories align with the underlying rule evidence (e.g., FW-019 MySQL from `any` should be RISKY, not just OVER-PERMISSIVE) - **Confidence calibration:** HIGH items are clearly supported, LOW items are truly ambiguous - **Executive utility:** summary is actionable without reading every row - **Recommendation specificity:** concrete next actions instead of generic language like "review this rule" - **Completeness:** all 40 rules are accounted for — none silently skipped ### Iterations to Try - Lower the `UNUSED` threshold and compare volume shifts - Add a new category such as `EXPIRED` for rules past a decommission date - Request output as JSON or CSV instead of markdown table - Upload a second file (a network security policy document) to the Knowledge Store and see if the agent references it during analysis ## What You Learned | Concept | Where You Used It | | --- | --- | | **Knowledge Stores** | Uploaded the firewall CSV as a reference for analysis | | **Prompt engineering** | Iterated from vague to production-quality through three versions | | **Role specification** | Set the model's expertise level and vocabulary | | **Output structuring** | Controlled format with explicit tables, categories, and sections | | **Confidence levels** | Added calibration signals for downstream triage | | **Multi-step workflows** | Separated analysis from formatting for reusability | ## Discussion Points - Which prompt iteration had the biggest impact on output quality, and why? - What additional guardrails are needed before using this in production? - Where could you connect ticketing or trigger automation to close the loop (e.g., auto-create a Jira ticket for each RISKY rule)? - How would you adapt this pattern for a different security domain — vulnerability scan results, IAM policy reviews, or cloud security posture? ## Troubleshooting | Issue | Likely Cause | How To Fix | | --- | --- | --- | | Output is vague and inconsistent | Prompt lacks role, constraints, and schema | Move from Iteration 1 to Iteration 3 prompt structure | | Most rules marked as RISKY | Categories are underspecified or rule data is sparse | Add explicit category definitions and confidence rules | | Missing rules in output | Model hit context limits or skipped rows | Ask for chunked analysis (for example, 100 rules at a time) and merge results | | Confidence levels all HIGH | Prompt does not define evidence standards | Keep the explicit HIGH/MEDIUM/LOW criteria block | | Recommendations are generic | Formatting step not included | Add Step 4 report formatter and require prioritized actions | ## Next Exercise Continue to [Exercise B - Universal Security Triage Agent](/learning/customer-training-session/exercises/universal-triage-agent/). --- ## Getting started Source: https://docs.kindo.ai/overview/getting-started/ Kindo exposes industry-standard inference protocols on a single base URL — `https://api.kindo.ai` — authenticated by a single Kindo API key. Bring whichever client you already use, point it at `api.kindo.ai`, and the rest of your code works unmodified. ## Common inference endpoints | Endpoint | Protocol | Use it when | | ---------------------- | ----------------------- | --------------------------------------------------------------------------------------- | | `/v1/chat/completions` | OpenAI Chat Completions | You're migrating from OpenAI's Chat Completions API or any OpenAI-compatible proxy. | | `/v1/responses` | OpenAI Responses | You're using `codex`, the new OpenAI Responses surface, or want stateful conversations. | | `/v1/messages` | Anthropic Messages | You're using Claude Code, the Anthropic SDK, or any Anthropic-compatible client. | | `/v1/mcp` | Model Context Protocol | You want an MCP client to reach [permitted tools across Kindo integrations](/apis/mcp/scoping/). | All of these accept the **same Kindo API key** — there are no per-protocol credentials. The only difference between using Kindo and calling OpenAI or Anthropic directly is which base URL you point your client at and which key you use. Beyond the inference protocols, Kindo also exposes control-plane endpoints for agents, runs, conversations, models, and integrations — see the **APIs** sidebar for the full list, or jump to: - [Agents API](/apis/agents/quickstart/) — discover, trigger, and poll agent runs. - [Conversations API](/apis/conversations/quickstart/) — read items from a stored conversation. - [Control-plane endpoints](/apis/control-plane/) — `GET /v1/models` and `/v1/integrations/*`. ## Pick your client Kindo follows the upstream specs faithfully. If your client speaks the protocol, it works: | If you use… | Set this base URL | Set this key envvar | API surface | | ------------------------ | ---------------------------------------------------- | ------------------------------------------------ | ------------------------------------------------ | | `codex` | `OPENAI_BASE_URL=https://api.kindo.ai/v1` | `OPENAI_API_KEY=$KINDO_API_KEY` | `/v1/responses` | | `openai-python` | `OpenAI(base_url="https://api.kindo.ai/v1")` | `OpenAI(api_key=os.environ["KINDO_API_KEY"])` | `/v1/chat/completions` (or `client.responses.*`) | | `openai-node` | `new OpenAI({ baseURL: "https://api.kindo.ai/v1" })` | `apiKey: process.env.KINDO_API_KEY` | `/v1/chat/completions` (or `client.responses.*`) | | Anthropic Python SDK | `Anthropic(base_url="https://api.kindo.ai")` | `Anthropic(api_key=os.environ["KINDO_API_KEY"])` | `/v1/messages` | | Anthropic TypeScript SDK | `new Anthropic({ baseURL: "https://api.kindo.ai" })` | `apiKey: process.env.KINDO_API_KEY` | `/v1/messages` | | Claude Code | `ANTHROPIC_BASE_URL=https://api.kindo.ai` | `ANTHROPIC_API_KEY=$KINDO_API_KEY` | `/v1/messages` | | MCP client (spec-conformant) | `https://api.kindo.ai/v1/mcp` | `Authorization: Bearer $KINDO_API_KEY` | `/v1/mcp` | | Raw `curl` | `https://api.kindo.ai` | `Authorization: Bearer $KINDO_API_KEY` | Any of the above | The Anthropic SDKs and Claude Code append `/v1/messages` internally, so the base URL omits `/v1`. Including it would produce a double path segment (`/v1/v1/messages`). ## Why one base URL, many protocols Different teams standardize on different SDKs, and each protocol has different ergonomics: Chat Completions is the broadly-supported baseline, the Responses API adds stateful conversations and typed-output items, Messages is what Anthropic's first-party tools speak, MCP is what spec-conformant tool-calling clients speak. By accepting all of them on one base URL with one API key, Kindo lets every team keep its existing client choice while still routing through a single governance pipeline (auth, model access, audit, DLP, metering). ## Default behavior is stock provider behavior When you call `/v1/chat/completions`, `/v1/responses`, or `/v1/messages` with a stock OpenAI or Anthropic client, you get **stock OpenAI or Anthropic semantics**. Kindo does not silently inject system prompts, hidden tools, or stateful side effects. If you want any of those things on `/v1/responses`, opt in explicitly via the **Chat Actions** extensions: a curated system prompt, the Kindo-hosted tool catalog, and Kindo-managed conversation state. Each is independent and documented at [guides/chat-actions](/guides/chat-actions/). `/v1/chat/completions` and `/v1/messages` are stock OpenAI and stock Anthropic; the Chat Actions opt-ins are not available there. ## Next steps - [Authentication](/overview/authentication/) — header options and key formats. - [Model catalog](/overview/model-catalog/) — discover what model IDs to send. - Pick a quickstart: - [Responses API quickstart](/apis/responses/quickstart/) for `codex` and the OpenAI Responses SDK. - [Chat Completions API quickstart](/apis/chat-completions/quickstart/) for `openai-python` / `openai-node`. - [Messages API quickstart](/apis/messages/quickstart/) for Claude Code and the Anthropic SDKs. - [MCP API quickstart](/apis/mcp/quickstart/) for any spec-conformant MCP client. - [Agents API quickstart](/apis/agents/quickstart/) for triggering Kindo agents over HTTP. - [Chat Actions](/guides/chat-actions/) — opt in to Kindo's curated prompt, hosted tools, or stateful conversations on `/v1/responses`. --- ## Agents API request shape Source: https://docs.kindo.ai/apis/agents/request-shape/ The Agents API is composed of two discovery endpoints, one trigger endpoint, and two run-inspection endpoints covered in [Runs](/apis/agents/runs/). All responses are JSON. Authentication is the same `Authorization: Bearer $KINDO_API_KEY` used elsewhere. ## `GET /v1/agents/list` Lists agents the API-key holder can see. This endpoint accepts no query parameters and returns the visible agents in a single page, capped at 500 entries. The response does not include a `has_more` or cursor field; callers that need more than 500 agents in a single organization should reach out to Kindo. ```bash curl https://api.kindo.ai/v1/agents/list \ -H "Authorization: Bearer $KINDO_API_KEY" ``` Response: ```json { "items": [ { "agentId": "18d20df4-d9b3-4cb0-a009-9f11ec9c5d3d", "name": "Daily Security Scan", "description": "Runs the daily vulnerability check workflow.", "createdAt": "2026-03-10T18:45:23.000Z", "creatorName": "Security Team", "metadata": { "userPermissions": ["view", "edit"] } } ], "total": 1 } ``` Envelope: | Field | Type | Notes | | ------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | `items` | array | One entry per accessible agent. See the item-shape table below. | | `total` | integer | Always equal to `items.length`. This endpoint is non-paginated; `total` is a convenience field, not a counter. Capped at 500 — see note above. | Item shape: | Field | Type | Notes | | ------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | | `agentId` | string | UUID. Use it in `/v1/agents/{agentId}` and `runs` calls. | | `name` | string | Display name from the Kindo Terminal. | | `description` | string | Free-form description. | | `createdAt` | string | ISO-8601 timestamp. | | `creatorName` | string | Name of the user or group that created the agent. | | `metadata` | object | `{ "userPermissions": string[] }`. Entries are the actions the caller may perform on this agent — any subset of `view`, `edit`, `delete`, `share`. | ## `GET /v1/agents/{agentId}` Returns the full agent configuration, including the `inputs` label list you must match when triggering a run. Optional query parameters paginate and filter the `recentRunIds` list. ```bash curl https://api.kindo.ai/v1/agents/18d20df4-d9b3-4cb0-a009-9f11ec9c5d3d \ -H "Authorization: Bearer $KINDO_API_KEY" ``` Query parameters (all optional): | Parameter | Type | Notes | | ----------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `limit` | integer | Maximum number of run IDs to return in `recentRunIds`. Integer between `1` and `100` (inclusive). Defaults to `20` when any pagination/filter query parameter is supplied. | | `cursor` | string | Opaque pagination cursor returned in `nextCursor` on the previous response. Pass it back unchanged to fetch the next page. The encoding is internal and may change — do not parse, modify, or construct cursors yourself. When combined with `startDate`/`endDate`, re-pass the same date filters on every follow-up request; the cursor does not encode them. | | `startDate` | string | Inclusive ISO-8601 lower bound; only runs created at or after this timestamp are returned in `recentRunIds`. | | `endDate` | string | Inclusive ISO-8601 upper bound; only runs created at or before this timestamp are returned in `recentRunIds`. When both are supplied, `startDate` must be less than or equal to `endDate`. | When no query parameters are supplied, the response shape and legacy run cap are preserved (up to 5 `recentRunIds`, no `nextCursor` field). When any of `limit`, `cursor`, `startDate`, or `endDate` is supplied, the paginated path is used: omitted `limit` defaults to `20`, and the response always includes a `nextCursor` field, which is `null` on the last page. Response: ```json { "agentId": "18d20df4-d9b3-4cb0-a009-9f11ec9c5d3d", "name": "Daily Security Scan", "description": "Runs the daily vulnerability check workflow.", "inputs": ["query"], "hasTriggers": false, "modelsInUse": ["claude-sonnet-4-5-20250929"], "recentRunIds": ["7a7ced7d-95a8-416f-95bb-5c0ff3599f53"], "lastRunAtUtc": "2026-03-12T15:00:00.000Z", "metadata": { "userPermissions": ["view", "edit"] } } ``` | Field | Type | Notes | | -------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `agentId` | string | UUID. | | `name` | string | Display name. | | `description` | string | Free-form description. | | `inputs` | array of strings | Each entry is the label the agent expects in `POST /v1/agents/runs`. Returned in ascending alphabetical order, not in agent-configuration order. | | `hasTriggers` | boolean | `true` when the agent has scheduled, event, or webhook triggers configured. Informational — a trigger is an additional way the agent starts and does not prevent running it via `POST /v1/agents/runs`. | | `modelsInUse` | array of strings | Model IDs this agent currently routes through. | | `recentRunIds` | array of strings | Run IDs ordered newest-first. With no query parameters, capped by the legacy default of 5. With pagination/filter parameters, capped by `limit` (default 20, max 100) and filtered by `startDate`/`endDate` when supplied. | | `lastRunAtUtc` | string \| null | ISO-8601 timestamp of the agent's most recent run, or `null` if it has never run. Independent of `startDate`/`endDate` filters — always reflects the true latest run. | | `nextCursor` | string \| null | Present only when one of `limit`, `cursor`, `startDate`, or `endDate` is supplied. A string cursor when more pages remain; `null` when the current page is the last. | | `metadata` | object | `{ "userPermissions": string[] }`. Entries are the actions the caller may perform — any subset of `view`, `edit`, `delete`, `share`. | ## `POST /v1/agents/runs` Triggers an agent execution. ```bash curl -X POST https://api.kindo.ai/v1/agents/runs \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -d '{ "agentId": "18d20df4-d9b3-4cb0-a009-9f11ec9c5d3d", "inputs": [ { "name": "query", "value": "Run the daily security scan" } ] }' ``` Request body: | Field | Required | Type | Notes | | --------- | -------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `agentId` | Yes | string | An `agentId` returned by `GET /v1/agents/list`. | | `inputs` | No | array of objects | Each entry is `{ "name": string, "value": string }`. `name` must match a label from `GET /v1/agents/{agentId}`'s `inputs`. Required labels that are omitted produce a `422`; entries with an unrecognized `name` are silently dropped. | Response (`202 Accepted`): ```json { "runId": "7a7ced7d-95a8-416f-95bb-5c0ff3599f53", "conversationId": "clx9a2b1c0000d4e5f6g7h8i9" } ``` | Field | Type | Notes | | ---------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `runId` | string | UUID for the created run. Poll it via `GET /v1/runs/{runId}`. | | `conversationId` | string | The conversation backing the run. Pass it to `GET /v1/conversations/{conversation_id}/items` to read the message history — the basis for multi-turn continuity. Created together with the run. | A `202` means Kindo accepted the trigger and will execute the run asynchronously. Use [Runs](/apis/agents/runs/) to poll the result. ## See also - [Quickstart](/apis/agents/quickstart/) — the end-to-end discover → trigger → poll → evals flow. - [Runs](/apis/agents/runs/) — `GET /v1/runs/{runId}` shape, lifecycle, and `GET /v1/runs/{runId}/evals` step verdicts. - [Errors](/apis/agents/errors/) — error envelopes for each endpoint. --- ## Chat Completions API request shape Source: https://docs.kindo.ai/apis/chat-completions/request-shape/ `POST /v1/chat/completions` accepts the OpenAI Chat Completions request body. Kindo validates the core fields and forwards the rest verbatim, which keeps the endpoint forward-compatible with new OpenAI request fields. ## Required fields | Field | Type | Notes | | ---------- | ------ | ----------------------------------------------------------------------- | | `model` | string | A model ID from [`GET /v1/models`](/overview/model-catalog/). | | `messages` | array | Must contain at least one entry. See [Messages array](#messages-array). | ## Standard fields | Field | Type | Forwarded? | Notes | | ------------------- | ---------------- | ---------- | ----------------------------------------------------------------------------------- | | `stream` | boolean | — | Consumed by the route handler to switch into SSE mode. | | `temperature` | number | Yes | Sampling temperature. | | `max_tokens` | integer | Yes | Max tokens to generate. | | `top_p` | number | Yes | Nucleus sampling. | | `frequency_penalty` | number | Yes | | | `presence_penalty` | number | Yes | | | `stop` | string \| array | Yes | Stop sequences. | | `tools` | array | Yes | OpenAI tool definitions (see [Tool use](/apis/chat-completions/tool-use/)). | | `tool_choice` | string \| object | Yes | `"auto"`, `"none"`, `"required"`, or `{type: "function", function: {name: "..."}}`. | | `response_format` | object | Yes | E.g. `{type: "json_object"}` or `{type: "json_schema", ...}`. | | `user` | string | Yes | End-user identifier. | | `seed` | integer | Yes | Determinism hint, where the upstream model supports it. | | `logprobs` | boolean / object | Yes | Forwarded to the upstream model. | Anything not listed above passes through Kindo's schema verbatim because the validator uses `passthrough()`. Refer to OpenAI's [Chat Completions spec](https://platform.openai.com/docs/api-reference/chat/create) for full field semantics. ### JSON mode Both variants of `response_format` are accepted: - `{"type": "json_object"}` — any JSON object. - `{"type": "json_schema", "json_schema": {...}}` — forwarded whole, schema included, for models that enforce a schema. How `{"type": "json_object"}` is enforced depends on the provider backing the model you selected: | Provider family | How it is enforced | | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | OpenAI-format (OpenAI, Azure OpenAI, Groq, Mistral, DeepSeek, …) | Forwarded verbatim; the provider enforces JSON mode itself. | | Gemini | Forwarded to the provider's own JSON output mode. | | Claude — direct, on Vertex AI, and on Bedrock | Translated by Kindo, because the provider has no schemaless JSON switch. The translation is not visible in the response. | On those, `choices[0].message.content` is raw JSON you can parse directly, with no markdown code fence, on both streaming and non-streaming requests. Three cases are **not** guaranteed: - **A provider with no JSON mode of its own** — Cohere, for example. `response_format` is accepted and has no effect there, so the model may answer in prose or wrap the JSON in a markdown code fence. Parse defensively if your model group can route to one of these. - **`{"type": "json_object"}` sent together with `tools` or `tool_choice`.** On models without native JSON mode, Kindo will not suppress the tools you asked for in order to force JSON, so the response may be prose. Send a `json_schema`, or omit the tools, when you need a parseable response from that combination. - **`{"type": "json_object"}` sent together with extended thinking (`thinking` or `reasoning_effort`).** Anthropic rejects the forced tool call that JSON mode is translated into when thinking is on, so on models without native JSON mode the response may be prose. Send a `json_schema`, or drop the reasoning parameter, when you need a parseable response. ### Stripped-on-ingress fields Kindo strips these fields from the outgoing upstream request so clients can't spoof governance metadata: - `metadata` - `litellm_metadata` - `proxy_server_request` ## Messages array Each message has the following shape: | Field | Required | Notes | | -------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | `role` | Yes | `system`, `developer`, `user`, `assistant`, or `tool`. | | `content` | No | String, array of content blocks, or `null` (for assistant messages with only `tool_calls`). Arrays are forwarded verbatim and support multimodal blocks. | | `name` | No | Author name. | | `tool_calls` | No | Tool calls generated by the model (present on assistant messages). | | `tool_call_id` | No | Required on `role: "tool"` messages — the ID of the tool call this message is responding to. | `developer` is treated identically to `system` and matches OpenAI's own semantics. ## Kindo extension fields `/v1/chat/completions` is stock OpenAI — Kindo does not accept a `kindo` request block on this surface. Kindo's opt-in extensions (curated system prompt, hosted tools, stateful conversations) are available on `/v1/responses`; see the [Chat Actions guide](/guides/chat-actions/). ## Worked example ```json { "model": "claude-sonnet-4-5-20250929", "messages": [ { "role": "system", "content": "You are a concise security assistant." }, { "role": "user", "content": "Summarize CVE-2024-3094." } ], "tools": [ { "type": "function", "function": { "name": "lookup_cve", "description": "Fetch CVE metadata.", "parameters": { "type": "object", "properties": { "id": { "type": "string" } }, "required": ["id"] } } } ], "tool_choice": "auto", "max_tokens": 512, "temperature": 0.2 } ``` This is stock OpenAI Chat Completions. Kindo accepts it identically to OpenAI's `/v1/chat/completions`. ## See also - [Quickstart](/apis/chat-completions/quickstart/) — minimal client setup. - [Streaming](/apis/chat-completions/streaming/) — SSE protocol. - [Tool use](/apis/chat-completions/tool-use/) — function-calling round trip. - [Errors](/apis/chat-completions/errors/) — error envelope. --- ## Conversations API request shape Source: https://docs.kindo.ai/apis/conversations/request-shape/ Both endpoints speak the OpenAI [Conversations API](https://platform.openai.com/docs/api-reference/conversations) verbatim, exposing the items stored when you run `/v1/responses` with `store: true`. Both are `GET` and return JSON. ## `GET /v1/conversations/{conversation_id}/items` Lists items in the conversation in the order they were appended. ```bash curl https://api.kindo.ai/v1/conversations/conv_abc123/items?limit=50&order=asc \ -H "Authorization: Bearer $KINDO_API_KEY" ``` ### Query parameters | Name | Type | Default | Notes | | ------- | ------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `limit` | integer | `20` | Messages per page. Range `1`–`100`. Each message may expand to multiple items (an assistant turn with a tool call produces a `function_call` plus a `message`), so the returned `data` length may exceed `limit`. | | `order` | string | `desc` | `asc` or `desc`. Sort order by message index. | | `after` | string | — | Cursor: return items appended after this item ID. Pass `last_id` from the previous response. An item ID not present in this conversation produces `400 invalid_cursor`. | ### Response | Field | Type | Notes | | ---------- | -------------- | ------------------------------------------------------------------- | | `object` | string | Always `"list"`. | | `data` | array | One entry per item. See [Item types](#item-types). | | `has_more` | boolean | `true` when another page is available; pass `last_id` as `?after=`. | | `first_id` | string \| null | ID of the first item in `data`, or `null` when `data` is empty. | | `last_id` | string \| null | ID of the last item in `data`, or `null` when `data` is empty. | ### Item types The `type` field on each item determines the rest of its shape. A single assistant turn can produce multiple top-level items (`function_call` siblings of `message`) — treat `data` as a flat ordered stream rather than assuming one item per turn. | `type` | Shape (key fields) | Notes | | ---------------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | `message` | `id`, `role`, `content[]`, `status` | `role` is `user`, `assistant`, or `system`. `content[]` contains typed text/image blocks. | | `reasoning` | `id`, `summary[]` | The model's reasoning summary; `summary[]` entries are `{type: "summary_text", text}`. | | `function_call` | `id`, `call_id`, `name`, `arguments` | A model-emitted function-tool call. | | `function_call_output` | `call_id`, `output` | The matching tool result (echoed back by the caller). Identified by `call_id` — `function_call_output` items do not carry their own `id`. | | `mcp_call` | `call_id`, `server_label`, `name`, `arguments`, `output`, `status` | A server-executed MCP tool call, with its result inline in `output` (`null` while in progress). | | `kindo_call` | `call_id`, `name`, `arguments`, `output`, `status` | A Kindo-hosted tool call (e.g. `kindo_web_search`), with its result inline in `output`. | | `web_search_call` | `call_id`, `status` | A web-search invocation. | | `shell_call` | `call_id`, `name`, `action`, `status` | A sandboxed shell invocation; `action.commands` lists the commands executed. | | `shell_call_output` | `call_id`, `name`, `output[]` | Shell results — `output[]` entries carry `stdout`, `stderr`, and an exit/timeout `outcome`. | The tool-activity and reasoning shapes are the same [Responses output items](/apis/responses/streaming/) the model produced them as — a conversation is the persisted transcript of those items. Conversations backed by agent runs surface the full step-by-step activity this way. `message` items carry a `status` field, currently always `"completed"` (Kindo stores items as they are persisted, not while streaming). Items do **not** carry an `object` field — the outer list envelope has `object: "list"`, but individual items omit it. Dispatch on `type` instead. Unrecognized item types are forwarded verbatim from the underlying store, so new OpenAI Responses item shapes appear without requiring a Kindo upgrade. The API normalizes a `developer` role to `system` before persisting; responses always show `system`. ## `GET /v1/conversations/{conversation_id}/items/{item_id}` Fetches a single item. The response object matches one entry from the `data[]` array on the list endpoint. ```bash curl https://api.kindo.ai/v1/conversations/conv_abc123/items/msg_xyz789 \ -H "Authorization: Bearer $KINDO_API_KEY" ``` ```json { "id": "msg_xyz789", "type": "message", "role": "user", "content": [ { "type": "input_text", "text": "What is the capital of France?" } ], "status": "completed" } ``` ## See also - [Quickstart](/apis/conversations/quickstart/) — list + fetch flow. - [Errors](/apis/conversations/errors/) — the OpenAI-style nested envelope and the `(type, code)` pairs returned by these endpoints. - [Responses → Chat Actions extensions](/apis/responses/kindo-extensions/#3-stateful-conversations) — how a stored conversation is created and continued. --- ## MCP API — client setup Source: https://docs.kindo.ai/apis/mcp/clients/ Every recipe below points an MCP client at `https://api.kindo.ai/v1/mcp` and authenticates with your Kindo API key in the `Authorization` header. Set `KINDO_API_KEY` in your shell, or substitute the literal value. ## Other clients Any spec-conformant MCP client that supports Streamable HTTP will work. Point it at `https://api.kindo.ai/v1/mcp` and send `Authorization: Bearer YOUR_API_KEY`. If your client speaks only stdio, wrap the URL with [`mcp-remote`](https://github.com/geelen/mcp-remote) using the same pattern shown for Claude Desktop. ## Tool surface too large? The bare endpoint shows eligible tools across the integrations your account can reach (see [Scoping](/apis/mcp/scoping/)). For broad-access accounts the catalog can exceed a model's context window. Narrow it with a query filter: ``` https://api.kindo.ai/v1/mcp?integrations=linear,github ``` The filter can only narrow what your account already admits — it never grants new access. See [`kindo-extensions`](/apis/mcp/kindo-extensions/) for the header form. ## Next steps - [Request shape](/apis/mcp/request-shape/) — what to send, what comes back. - [Scoping](/apis/mcp/scoping/) — which tools appear and why. - [Errors](/apis/mcp/errors/) — auth and protocol error envelopes. --- ## Messages API request shape Source: https://docs.kindo.ai/apis/messages/request-shape/ `POST /v1/messages` accepts the Anthropic Messages request body. Kindo validates the core fields and forwards the rest verbatim, which keeps the endpoint forward-compatible with new Anthropic fields. ## Required fields | Field | Type | Notes | | ------------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | `model` | string | A model ID from [`GET /v1/models`](/overview/model-catalog/). | | `messages` | array | Roles must be `user` or `assistant`, must alternate, and the first message must be `user`. Use the top-level `system` field for system prompts. | | `max_tokens` | integer | Positive integer. | ## Standard fields | Field | Type | Forwarded? | Notes | | -------------------- | --------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------- | | `stream` | boolean | — | Consumed by the route handler to switch into SSE mode. | | `system` | string \| array | Yes | Optional system prompt. Use array of content blocks to attach `cache_control` (see [Prompt caching](#prompt-caching)). | | `tools` | array | Yes | Anthropic tool definitions (see [Tool use](/apis/messages/tool-use/)). | | `tool_choice` | object | Yes | Anthropic's standard tool-choice format. | | `temperature` | number | Yes | Sampling control. | | `top_p` | number | Yes | Nucleus sampling. | | `top_k` | integer | Yes | Top-K sampling. | | `stop_sequences` | array | Yes | Stop sequences. | | `context_management` | object | Yes | Anthropic's context-editing config (see [below](#context_management)). | Anything not listed above passes through Kindo's schema verbatim because the validator uses `passthrough()`. Refer to Anthropic's [Messages spec](https://docs.anthropic.com/en/api/messages) for full field semantics. ### `context_management` Kindo forwards `context_management` for every model, regardless of provider. Anthropic-family deployments honor it natively. For other providers, Kindo applies it in-gateway — translating it to the target API's equivalent mechanism where one exists, or polyfilling the behavior where it doesn't. The response echoes `context_management.applied_edits`, describing what was applied. ### Anthropic-specific headers Kindo forwards these headers to the upstream provider when present: - `anthropic-version` - `anthropic-beta` - `x-claude-code-session-id` Anthropic SDKs and Claude Code set the required protocol headers for you. Raw HTTP clients should include `anthropic-version`. ### Stripped-on-ingress fields Kindo strips these fields from the outgoing upstream request: - `metadata` - `litellm_metadata` - `proxy_server_request` ## Messages array | Field | Required | Notes | | --------- | -------- | ---------------------------------------------------------------------------------------------------------------------- | | `role` | Yes | `user` or `assistant`. Must alternate, must start with `user`. | | `content` | Yes | String or array of content blocks. Blocks include `text`, `tool_use`, `tool_result`, `image`, etc. Forwarded verbatim. | System prompts go in the top-level `system` field, not in the `messages` array. ### Compatibility normalization Some OpenAI-compatible gateways and SDK shims emit Anthropic-bound payloads with OpenAI-style role conventions. Kindo normalizes these on ingress (on both `/v1/messages` and `/v1/messages/count_tokens`) rather than rejecting them: - A `system` or `developer` entry inside `messages[]` is hoisted into the top-level `system` field (string content and text-block-array content both work). - A message with a missing `role` defaults to `user`. The strict Anthropic shape above remains the recommended form — normalization exists so unmodified OpenAI-gateway clients work, not as an alternative contract. ## Prompt caching Block-level `cache_control` works through Kindo. Add `cache_control` to Anthropic content blocks and verify cache hits from the response `usage` object. ```json { "model": "claude-sonnet-4-5-20250929", "max_tokens": 512, "system": [ { "type": "text", "text": "You are a release-notes assistant. Always answer in bullet points.", "cache_control": { "type": "ephemeral" } } ], "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Summarize the last three deploys for service api." } ] } ] } ``` Look for these fields in `usage`: - `cache_creation_input_tokens` — tokens written to cache on this request. - `cache_read_input_tokens` — tokens read from cache on subsequent requests. ### Known limitation Top-level `cache_control` is not currently forwarded by LiteLLM's Anthropic TypedDict layer. Use **block-level** `cache_control` instead. If your client requires an Anthropic beta header for prompt caching, use a header value supported by your target model. Internal end-to-end verification confirmed `anthropic-beta: prompt-caching-2024-07-31` works with Kindo's proxy. ## `count_tokens` `POST /v1/messages/count_tokens` counts tokens for a Messages request without generating a completion. ```bash curl https://api.kindo.ai/v1/messages/count_tokens \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -d '{ "model": "claude-sonnet-4-5-20250929", "system": "You are a concise assistant.", "messages": [ { "role": "user", "content": "Summarize the principle of least privilege." } ] }' ``` | Field | Required for `count_tokens` | Notes | | ------------ | --------------------------- | ----------------------------- | | `model` | Yes | | | `messages` | Yes | | | `max_tokens` | Optional | | | `stream` | Not supported | Omit it or set it to `false`. | Response: ```json { "input_tokens": 20 } ``` ## Kindo extension fields `/v1/messages` is stock Anthropic — Kindo does not accept a `kindo` request block on this surface. Kindo's opt-in extensions (curated system prompt, hosted tools, stateful conversations) are available on `/v1/responses`; see the [Chat Actions guide](/guides/chat-actions/). ## See also - [Quickstart](/apis/messages/quickstart/) — minimal client setup. - [Streaming](/apis/messages/streaming/) — SSE event types. - [Tool use](/apis/messages/tool-use/) — `tool_use` / `tool_result` round trip. - [Errors](/apis/messages/errors/) — Anthropic error envelope. --- ## Responses API request shape Source: https://docs.kindo.ai/apis/responses/request-shape/ `POST /v1/responses` accepts the OpenAI Responses request body. The goal is **bit-for-bit compatibility** with stock OpenAI clients — anything you'd send to OpenAI's `/v1/responses` you can send to Kindo's, with the same semantics. This page documents what Kindo recognizes and how each field is forwarded. ## Required fields | Field | Type | Notes | | ------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------ | | `model` | string | A model ID from [`GET /v1/models`](/overview/model-catalog/), or a stored Kindo agent as `agent/` (see below). | | `input` | string \| array of input items | Plain string for single-turn, or array for multi-message / tool round trips. | ### Agent models (`agent/`) Passing `model: "agent/"` invokes one of your stored Kindo agents instead of a raw model. The agent supplies the model, system prompt, and tool set; your `input` is delivered to the run and your `instructions` compose with (append after) the agent's own prompt. Because the agent's configuration is pinned, request fields that would override it — `tools`, a non-`"auto"` `tool_choice`, `conversation`, `previous_response_id`, `background`, and sampling parameters such as `temperature` — are rejected with `400 unsupported_parameter` naming the field. Agent invocations return immediately with `status: "in_progress"` and are polled or streamed. See [Invoke Kindo agents with the Responses API](/guides/invoke-agents-responses-api/) for the full walkthrough. ### `input` array items Each item is one of: | `type` | Required fields | Notes | | ---------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | | `message` | `role`, `content` | `role` is `user`, `system`, `assistant`, or `developer`. `developer` is treated as a system-level instruction (matches OpenAI semantics). | | `function_call` | `call_id`, `name`, `arguments` | Accepted for conversation-history round-tripping. Re-sending one does not re-execute it. | | `function_call_output` | `call_id`, `output` | Returns a function tool's result; pair it with the originating `function_call.call_id`. | ### Message content parts A `message` item's `content` is a plain string or an array of typed parts: | `type` | Key fields | Notes | | ------------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `input_text` | `text` | Plain text. | | `input_image` | `image_url` | A `data:` URL or fetchable `https:` URL. Supported on both the stateless and stateful paths. The OpenAI `detail` hint is accepted; on the stateful path it is not propagated to the model. | | `input_file` | `file_data` / `file_id` / `file_url` | Stateless path only — forwarded to the upstream provider. On the stateful path (`store: true`, `conversation`, or `previous_response_id`), file parts are not carried to the model; send file content as text instead. | **Data Loss Prevention.** If your organization has DLP policies enabled, request text is scanned and redacted in-flight before it reaches the model — this is transparent to the caller. File parts interact with DLP explicitly: an `input_file` whose content cannot be inspected (binary `file_data`, or a `file_id`/`file_url` reference) is **rejected with `400`** rather than passed through unscanned, while inline `file_data` with a text MIME type (`text/*`, JSON, CSV, source code) is decoded, redacted, and forwarded. Without DLP enabled, file parts pass through unmodified on the stateless path. ## Standard fields Every field below is **forwarded verbatim** unless a row says otherwise. Behavior matches OpenAI's published spec. | Field | Type | Forwarded? | Notes | | ---------------------- | ---------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `instructions` | string | Yes | System-level instructions. Flow through unmodified by default. | | `tools` | array | Yes | Mix `function`, `mcp`, and any `kindo_*` tools. Unknown types are forwarded verbatim. `mcp` tools require the stateful path (`store: true` or `conversation`); stateless requests including them are rejected with `400 unsupported_tool_type`. | | `tool_choice` | string \| object | Yes | `"auto"`, `"none"`, `"required"`, `{type: "function", name: "..."}`, or `{type: "allowed_tools", ...}`. | | `stream` | boolean | — | Consumed by the route handler to switch into SSE mode (see [Streaming](/apis/responses/streaming/)). | | `store` | boolean | Yes | When `true`, Kindo persists the response and returns a `conversation.id`. Default `false`. | | `previous_response_id` | string | — | Continues the conversation that produced the referenced stored response (only responses created with `store: true` or `conversation` can be chained). Mutually exclusive with `conversation`. Unknown or inaccessible IDs → `404 previous_response_not_found`. | | `conversation` | string | Yes | Existing Kindo-managed conversation ID to continue. Prior history is loaded server-side — send only the new turn in `input`. Mutually exclusive with `previous_response_id`. | | `parallel_tool_calls` | boolean | Yes | Forwarded to the upstream model. | | `prompt_cache_key` | string | Yes | Forwarded; used by upstream providers that support prompt caching. | | `reasoning` | object | Yes | OpenAI reasoning controls (e.g. `effort`). | | `text` | object | Yes | OpenAI text-output controls (e.g. `format`, `verbosity`). | | `include` | array of strings | Yes | Request additional output items (e.g. encrypted reasoning content). | | `truncation` | string | Yes | OpenAI truncation policy. | | `background` | boolean | Yes\* | OpenAI background-execution flag. \*On the stateful path (`store: true` or `conversation` set), `background: true` is rejected with `400 unsupported_parameter` — background execution is not yet supported on Kindo-managed conversations. Omit it or use `stream: true`. | | `service_tier` | string | Yes | OpenAI service-tier hint. | | `max_output_tokens` | integer | Yes | Standard sampling control. | | `temperature` | number | Yes | Standard sampling control. | | `top_p` | number | Yes | Standard sampling control. | | `n` | integer | Yes | Standard sampling control. | | `stop` | string \| array | Yes | Standard sampling control. | | `metadata` | object | Stored only | Echoed back in the response object. Not forwarded to the model layer. | Anything not listed above passes through Kindo's schema verbatim, so new OpenAI-spec additions land at Kindo automatically when supported by the upstream model. ### Stripped-on-ingress fields Kindo strips these fields from the outgoing upstream request to prevent governance-metadata spoofing: - `metadata` - `litellm_metadata` - `proxy_server_request` (`metadata` is stored and echoed back in the response; it is not forwarded to the model layer — see the row above.) ### Response store pruning When `store: true`, Kindo persists the response object so it can be fetched later via `GET /v1/responses/{id}`. Stored responses are not kept indefinitely — once a response is pruned (whether by retention policy expiry or explicit deletion), subsequent fetches by ID return `404 not_found` with no `error.code` set. This is the same status the endpoint returns for IDs that never existed; the two cases are not distinguished in the response envelope. If you need durable access to a response body, capture it from the original `POST /v1/responses` call rather than relying on the stored copy. See the [`404` row in the errors table](/apis/responses/errors/#status--type--code-reference) for the matching error envelope. ## Read endpoints Two `GET` endpoints accompany `POST /v1/responses` for stored responses: ### `GET /v1/responses/{response_id}` Fetches the response object by ID — the polling mechanism for agent-run responses (`resp_run_*`) and the way to re-read any stored response. Add `?stream=true` (optionally with `starting_after`) to reconnect to the response's SSE stream instead — see [Streaming → resuming a dropped stream](/apis/responses/streaming/#resuming-a-dropped-stream). ### `GET /v1/responses/{response_id}/input_items` Returns the user input that initiated a stored response, as an OpenAI-style list envelope: ```json { "object": "list", "data": [ { "id": "msg_abc123", "type": "message", "role": "user", "content": [{ "type": "input_text", "text": "Summarize CVE-2024-3094." }], "status": "completed" } ], "has_more": false, "first_id": "msg_abc123", "last_id": "msg_abc123" } ``` The list contains the response's originating user message (an empty `data` array when the response had no user input message). `has_more` is always `false` — pagination parameters are not supported on this endpoint. For the full conversation history around a stored response, use the [Conversations API](/apis/conversations/quickstart/) with the response's `conversation.id`. ## Kindo extension fields These are **opt-in** and documented in detail at [Chat Actions extensions](/apis/responses/kindo-extensions/): | Field | Type | Notes | | ------------------------------ | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `kindo.system_prompt` | `"agent_default"` \| `"off"` | When `"agent_default"`, Kindo prepends its curated operational prompt to your `instructions`. On stateful requests (`store: true`, `conversation`, or `previous_response_id`), Kindo includes service-managed context with the composed prompt and supplies the current date and timezone with the triggering input. Default `"off"` does not add either form of service-managed context; client instructions and input remain unchanged. | | `tools[].type: "kindo_tools"` | sugar entry | Expands at request time to the current set of individual `kindo_` entries. | | `tools[].type: "kindo_"` | individual hosted-tool entry | E.g. `kindo_shell`, `kindo_web_search`. See [Chat Actions extensions](/apis/responses/kindo-extensions/) for the full list. | On compacted stateful turns, a service-managed compaction hint may be appended; it follows composed instructions and other service-managed prompt guidance. With `off`, caller instructions are preserved; the separate compaction hint may still be added. The `kindo` block and `kindo_*` tool types are the only Kindo-specific additions to the request body. Everything else on this page is stock OpenAI Responses. ## Worked example ```json { "model": "claude-sonnet-4-5-20250929", "input": [ { "type": "message", "role": "developer", "content": "You are a concise security assistant." }, { "type": "message", "role": "user", "content": "Summarize CVE-2024-3094." } ], "tools": [ { "type": "function", "name": "lookup_cve", "description": "Fetch CVE metadata.", "parameters": { "type": "object", "properties": { "id": { "type": "string" } }, "required": ["id"] } } ], "tool_choice": "auto", "store": true, "max_output_tokens": 512, "temperature": 0.2 } ``` This request is stock OpenAI Responses. Kindo accepts it identically to OpenAI's `/v1/responses`, plus persists the response and returns a `conversation.id` because `store: true` is set. ## See also - [Quickstart](/apis/responses/quickstart/) — minimal client setup. - [Streaming](/apis/responses/streaming/) — SSE event types. - [Tool use](/apis/responses/tool-use/) — function-calling round trip. - [Errors](/apis/responses/errors/) — error envelope. - [Chat Actions extensions](/apis/responses/kindo-extensions/) — `kindo` block, `kindo_*` tools, statefulness opt-ins. --- ## Working with Large Context Source: https://docs.kindo.ai/best-practices/working-with-large-context/ Large language models are powerful, but their biggest practical constraint is the **context window**: the maximum amount of text and tool state they can process in a single request. In Kindo, context includes your instructions, conversation history, and — for agents — tool calls and tool outputs. As that grows, you can hit hard limits, degrade output quality, and increase inference cost because more tokens are processed on every turn. This guide covers practical techniques you can use in Kindo to keep large-context agents reliable. ## How Kindo Helps Kindo mitigates large tool outputs by writing them into the **sandbox** and passing the model a reference to the file rather than injecting the full payload into the prompt. That enables an important pattern: instead of asking the model to read massive blobs, use the sandbox to search, filter, and transform data first — then feed the model only the high-signal subset. ## Technique 1: Reduce Data Volume at the Source The best way to prevent context blowups is to avoid retrieving unnecessary data in the first place. Most systems you integrate with provide filters that narrow results before they ever reach the model. Common query levers include: - Time bounds: last 15 minutes, incident window, since alert fired - Severity: errors only, warnings + errors, critical only - Scope: service, environment, host, region, tenant - Identifiers: incident ID, trace ID, correlation ID - Pattern filters: keywords or error codes - Pagination and limits: fetch small batches first, then expand only if needed The habit to build is: **start narrow, validate the signal, then widen intentionally**. ### Example Prompts ```text Pull logs for only the incident window (10:12–10:47 UTC) and only error-level entries for service=api-gateway in prod. If that returns nothing, expand to warnings+errors. Query the alerting system for alert_id=12345 and fetch only the related events and the top 50 associated log lines, not the entire day. Fetch only authentication-related events for tenant=acme during the last 2 hours and include only timestamp, user, source_ip, action, and result. ``` ## Technique 2: Use the Sandbox to Sift Large Payloads Even with upstream filtering, outputs can still be too large for a model to read efficiently. When the data is in the sandbox, let traditional tools do the heavy lifting. Useful tools include: - `grep` / `rg` for searching by signature, ID, or keyword - `find` for locating generated files - `sed` / `awk` for extracting ranges or fields - Bash scripts for repeatable extraction pipelines - Python scripts for grouping, aggregation, parsing JSON/CSV, and top-N summaries Common patterns: - Search for an error signature and extract a small context window around each match - Group events by correlation ID or error code and output counts plus representative examples - Parse structured logs into a compact report with top errors, affected hosts, and a short timeline ### Example Prompts ```text The tool output was written to a file in the sandbox. Do not read the full file into context. Use grep/rg to find all lines containing "Exception" or "ERROR", group by the most common error prefix, and output: - top 5 error clusters with counts - first/last timestamp per cluster - 2 representative excerpts per cluster Find the sandbox file containing the raw response. If it is JSON, write a short Python script to: - parse it - group by correlation_id - output the 10 largest groups with one-line summaries Then summarize only the script output. ``` ## Technique 3: Use MapReduce-Style Processing When you truly need to process large bodies of content — such as multi-hour logs, many tickets, or large document sets — a single-pass prompt is often brittle. A MapReduce-style approach is more reliable. ### Map Phase Split the input by a natural boundary such as: - time slice - file - service - ticket For each chunk, extract only the minimum useful signal: - key events and timestamps - recurring patterns or clusters - important entities (services, users, IDs) - a few representative examples Write those chunk summaries back to the sandbox as small JSON or Markdown artifacts. ### Reduce Phase Then have a model read only the chunk summaries and synthesize: - timeline - root cause hypothesis - remediation steps - supporting evidence This pattern works especially well when combined with **model tiering**: - use a smaller/cheaper model for extraction - use a stronger reasoning model for the final synthesis ### Example Two-Step Approach **Step 1 — extraction / chunk summaries** ```text We have 12 log chunks in /sandbox/log_chunks/. For each file, produce a compact JSON summary with: - file - time_range - top_errors (signature, count, first_seen, last_seen, short examples) - notable_events - correlation_ids Write one JSON file per chunk to /sandbox/log_summaries/. Do not include raw logs in the output. ``` **Step 2 — final synthesis** ```text Read only the JSON summaries in /sandbox/log_summaries/. Produce: 1. A concise incident timeline 2. The most likely root cause 3. The top 3 remediation steps 4. The key evidence supporting each conclusion If more raw data is required, specify exactly what to grep for and why instead of asking to read everything. ``` ## Recommended Working Style When working with large context in Kindo: 1. Narrow the query upstream first 2. Use the sandbox for search, filtering, and summarization 3. Break large tasks into chunked phases 4. Reserve the strongest model for synthesis, not raw parsing 5. Avoid feeding large raw payloads into the model unless you have already reduced them substantially ## Automatic Compaction Long conversations are compacted automatically. When a conversation approaches the model's context window, Kindo summarizes earlier messages while keeping recent context, so the session can continue without losing the thread — there is no need to start over or manage the context yourself. This happens in the background as you work, and the chat interface marks the conversation where it occurs. The techniques above remain the most effective way to control what enters the context in the first place. ## Future Platform Improvements Kindo can make context management more automatic. Areas that could improve this experience include: - more dynamic prompt loading so only relevant instructions are included per task - deeper prompt-prefix caching optimizations - better durable memory patterns for agents Regardless of future improvements, the most reliable pattern remains: **use traditional compute to reduce the data, then use the model to interpret the reduced result**. --- ## Connect to Microsoft MCP integrations Source: https://docs.kindo.ai/connecting-integrations/microsoft-mcp/ Most Microsoft MCP integrations use the same connection pattern: 1. Create an app registration in Microsoft Entra ID. 2. Create a client secret. 3. Create the integration in Nango and add the client ID and client secret. 4. Copy the OAuth callback URL from Nango and add it to the app registration as a Web redirect URI under **Authentication**. 5. Add the Microsoft API permissions required by the integration. 6. Complete the Microsoft authorization flow. This page covers the common setup. Each Microsoft integration still has its own permission list. Use the permission list provided by Kindo for the integration you are connecting. :::caution[Use the exact permissions for your integration] Do not grant broad Microsoft Graph permissions "just in case." Add only the permissions required for the specific integration and tools you plan to use. ::: ## Applies to Use this guide for Microsoft integrations that authenticate through Microsoft Entra ID, including: - Microsoft Azure - Microsoft Defender - Microsoft Defender for Cloud - Microsoft eDiscovery - Microsoft Entra ID - Microsoft Entra ID Governance - Microsoft Excel Online - Microsoft Fabric - Microsoft Graph Security (Microsoft Defender XDR) - Microsoft Intune - Microsoft Outlook - Microsoft Purview Compliance - Microsoft Purview Data Governance - Microsoft Teams - SharePoint Online Some integrations need extra tenant, endpoint, subscription, or target-user values. If Kindo gives you additional fields for a specific integration, enter them in the Kindo integration form after you complete the common Entra setup. ## Prerequisites - A Microsoft Entra ID tenant. - Permission to create or update app registrations in that tenant. - Permission to grant admin consent if the requested Microsoft API permissions require it. - Access to the Nango dashboard for your Kindo deployment. - The permission list for the Microsoft integration you are connecting. ## Step 1: Create the Entra app registration ## Step 2: Copy the client ID ## Step 3: Create a client secret :::caution[Copy the secret value, not the secret ID] Microsoft only shows the secret **Value** once. Kindo needs the secret value. The secret ID will not work. ::: ## Step 4: Create the integration in Nango ## Step 5: Add the Nango callback URL to the app registration ## Step 6: Add Microsoft API permissions ### How delegated permissions work Delegated permissions act as the signed-in user. The integration's effective access is the **intersection** of two grants: - the permissions granted to the application, and - the permissions the signed-in user already has in the tenant. The integration can never reach data the signed-in user cannot access themselves, and the user's access through the integration is limited to what the registered application's permissions allow. For example, if the app holds `Sites.ReadWrite.All` but the signed-in user only has read access to a SharePoint site, the integration cannot write to that site. ### Integration-specific permissions #### Microsoft Azure This integration authenticates against **Azure Resource Manager**, not Microsoft Graph. An app registration set up for a Graph integration on this page will not work here, and vice versa. The app registration is still created in Microsoft Entra ID: Azure identity lives in Entra, so an Azure Resource Manager token is an Entra token. Add the following instead of the Microsoft Graph permissions: | API | Permission | Type | Admin consent required | | --- | --- | --- | --- | | Azure Resource Manager | `user_impersonation` | Delegated | No | Find **Azure Resource Manager** under the **APIs my organization uses** tab when adding a permission (App ID `797f4846-ba00-4fd7-ba43-dac1f8f63013`). When linking the connection, Kindo also asks for: | Field | Description | | --- | --- | | **Tenant ID** | App registration > Overview > Directory (tenant) ID | | **Subscription ID** | Optional. The default subscription for tools that need one. Management-group and tenant-scoped tools work without it. | The `user_impersonation` permission grants no access to any Azure resource. It only lets Kindo sign you in and request an Azure Resource Manager token on your behalf. What that token can read comes entirely from Azure RBAC, so also assign a role to your account (Azure Portal > Subscriptions > your subscription > **Access control (IAM)** > Add role assignment): | Role | Grants | | --- | --- | | **Reader** | All read tools: subscriptions, resource groups, resources, policy and compliance, RBAC, and management groups | Assign **Reader** at a management group instead to cover every subscription beneath it in one assignment. ##### Microsoft Azure — Application mode Microsoft Azure can also connect in **application mode**, where the integration authenticates as the app itself (client credentials) instead of as a signed-in user. Use the **Microsoft Azure (Application)** integration in Kindo. Set it up as its own integration, with its own app registration, client ID, and client secret — Steps 1 to 4 on this page apply unchanged. Two things differ from the delegated setup above. **Skip Step 6.** Application mode needs no API permissions at all. Azure Resource Manager exposes no application permissions, so there is nothing to add and nothing to consent. Adding Microsoft Graph application permissions grants a service principal no Azure access. **Assign the RBAC role to the app, not to yourself.** With no signed-in user, the service principal is the only identity involved, so it needs the role directly (Azure Portal > Subscriptions > your subscription > **Access control (IAM)** > Add role assignment > **Reader** > Members > **User, group, or service principal**, then search for the app registration by name). Assign at a management group to cover every subscription beneath it. ##### Microsoft Azure — US Government Select the **Microsoft Azure (Government)** integration in Kindo rather than **Microsoft Azure**, and create its app registration in the Azure Government portal at [portal.azure.us](https://portal.azure.us). A commercial app registration cannot be used. Add the same permission, connection fields, and RBAC role as above. Azure has no separate GCC and GCC High tiers — every Azure Government subscription uses this integration. #### Microsoft Defender This integration uses permissions from three APIs: **Microsoft Graph**, **Microsoft Threat Protection**, and **WindowsDefenderATP**. All of them are **Delegated** permissions. Find Microsoft Threat Protection and WindowsDefenderATP under the **APIs my organization uses** tab when adding a permission. Add the following **Microsoft Graph** permissions: | Permission | Description | Admin consent required | | --- | --- | --- | | `User.Read` | Sign in and read user profile | No | Add the following **Microsoft Threat Protection** permissions: | Permission | Description | Admin consent required | | --- | --- | --- | | `Incident.Read` | Read incidents | Yes | | `Incident.ReadWrite` | Read and write incidents | Yes | Add the following **WindowsDefenderATP** permissions: | Permission | Description | Admin consent required | | --- | --- | --- | | `AdvancedQuery.Read` | Run advanced queries | Yes | | `Alert.Read` | Read alerts | Yes | | `Alert.ReadWrite` | Read and write alerts | Yes | | `Machine.Isolate` | Isolate machine | Yes | | `Machine.Read` | Read machine information | Yes | | `Machine.Scan` | Scan machine | Yes | | `Ti.ReadWrite` | Read and write IOCs | Yes | :::caution[Requires an active Defender for Endpoint subscription] This integration also requires an active **Microsoft Defender for Endpoint** subscription on the tenant, separate from the permissions above. ::: ##### Microsoft Defender — GCC High Select the **Microsoft Defender for Endpoint (GCC High)** integration in Kindo rather than **Microsoft Defender**, and create its app registration in the Azure Government portal at [portal.azure.us](https://portal.azure.us). A commercial app registration cannot be used. Add the same permissions listed above. This integration supports GCC High and DoD tenants. GCC tenants are not supported. #### Microsoft Defender for Cloud This is a separate integration and product from Microsoft Defender above — it authenticates against **Azure Resource Manager**, not Microsoft Graph, so its setup differs from the other integrations on this page. An app registration set up for Microsoft Defender will not work here, and vice versa. Add the following permission instead of Microsoft Graph: | API | Permission | Type | Admin consent required | | --- | --- | --- | --- | | Azure Resource Manager | `user_impersonation` | Delegated | No | Find **Azure Resource Manager** under the **APIs my organization uses** tab when adding a permission (App ID `797f4846-ba00-4fd7-ba43-dac1f8f63013`). When linking the connection, Kindo also asks for: | Field | Description | | --- | --- | | **Tenant ID** | App registration > Overview > Directory (tenant) ID | | **Subscription ID** | The Azure subscription to monitor with Defender for Cloud | In addition to the Entra app permission, assign an Azure RBAC role to your account on that subscription (Azure Portal > Subscriptions > your subscription > **Access control (IAM)** > Add role assignment): | Role | Grants | | --- | --- | | **Security Reader** | All read tools (alerts, recommendations, secure score, compliance overview, security coverage) | | **Security Admin** | Everything Security Reader does, plus dismissing/updating alerts | `list_alerts` returns empty until at least one paid Defender plan (e.g. Defender for Servers) is enabled on the subscription — the free tier still covers secure score, recommendations, and compliance overview. ##### Microsoft Defender for Cloud — US Government Select the **Microsoft Defender for Cloud (Government)** integration in Kindo rather than **Microsoft Defender for Cloud**, and create its app registration in the Azure Government portal at [portal.azure.us](https://portal.azure.us). A commercial app registration cannot be used. Add the same permission, connection fields, and RBAC role as above. Some tools return fewer results than in the commercial cloud: | Tool | In Azure Government | | --- | --- | | `list_recommendation_findings` | Machine vulnerability findings aren't included. | | `list_security_coverage` | Defender for AI Services, App Service, and Azure Cosmos DB aren't listed. | | `get_compliance_overview` | The available compliance standards differ. | #### Microsoft eDiscovery Add the following **Microsoft Graph** permissions. All of them are **Delegated** permissions. | Permission | Description | Admin consent required | | --- | --- | --- | | `eDiscovery.Read.All` | Read all eDiscovery objects | Yes | | `eDiscovery.ReadWrite.All` | Read and write all eDiscovery objects | Yes | | `offline_access` | Maintain access to data you have given it access to | No | | `User.Read` | Sign in and read user profile | No | ##### Microsoft eDiscovery — Application mode Microsoft eDiscovery can also connect in **application mode**, where the integration authenticates as the app itself (client credentials) instead of as a signed-in user. This is a separate connection from the standard delegated eDiscovery integration above — set it up as its own Kindo integration if you need automation that runs without a signed-in user. :::caution[Two setup steps, not one] Granting the Microsoft Graph application permissions below is not enough by itself. eDiscovery access is also gated by a separate Microsoft Purview RBAC registration — see Step B below. ::: ###### Step A: Grant the Graph application permissions Add the following **Microsoft Graph** permissions as **Application** permissions (not Delegated) on the app registration: | Permission | Description | Admin consent required | | --- | --- | --- | | `eDiscovery.Read.All` | Read all eDiscovery objects | Yes | | `eDiscovery.ReadWrite.All` | Read and write all eDiscovery objects | Yes | ###### Step B: Register the app in Microsoft Purview's RBAC layer This step happens in Security & Compliance PowerShell, not in Entra ID or the Purview portal UI — service principals cannot be added to eDiscovery role groups directly from the portal. **Prerequisites for Step B:** - The tenant must have **Microsoft Purview eDiscovery (Premium)** licensed. A missing license produces the same `401` symptom even when both steps above are done correctly. - An account with the **Role Management** role in Microsoft Purview, to run the `RoleGroupMember`/`eDiscoveryCaseAdmin` cmdlets above. #### Microsoft Entra ID Add the following **Microsoft Graph** permissions. All of them are **Delegated** permissions. | Permission | Description | Admin consent required | | --- | --- | --- | | `AppRoleAssignment.ReadWrite.All` | Manage app permission grants and app role assignments | Yes | | `AuditLog.Read.All` | Read audit log data | Yes | | `Directory.Read.All` | Read directory data | Yes | | `DirectoryRecommendations.Read.All` | Read Azure AD recommendations | Yes | | `Group.Read.All` | Read all groups | Yes | | `GroupMember.Read.All` | Read group memberships | Yes | | `offline_access` | Maintain access to data you have given it access to | No | | `Policy.Read.All` | Read your organization's policies | Yes | | `Policy.Read.AuthenticationMethod` | Read authentication method policies | Yes | | `Policy.Read.ConditionalAccess` | Read your organization's conditional access policies | No | | `Reports.Read.All` | Read all usage reports | Yes | | `RoleManagement.Read.Directory` | Read directory RBAC settings | Yes | | `User.EnableDisableAccount.All` | Enable and disable user accounts | Yes | | `User.Read` | Sign in and read user profile | No | | `User.Read.All` | Read all users' full profiles | Yes | | `User.ReadBasic.All` | Read all users' basic profiles | No | ##### Microsoft Entra ID — conditional access :::caution[The What If tool needs Entra ID P1] Evaluating a hypothetical sign-in requires a Microsoft Entra ID P1 or P2 licence. Without one it returns *Your tenant is not licensed for this feature.* Office 365 E5 does not include Entra ID P1. ::: Reading conditional access policies and counting their impact from sign-in logs work without a premium licence. A tenant with no conditional access policies is usually running on Security Defaults, which the impact tools report by that name. ##### Microsoft Entra ID — Application mode Microsoft Entra ID can also connect in **application mode**, where the integration authenticates as the app itself (client credentials) instead of as a signed-in user. This is a separate connection from the delegated integration above — set it up as its own Kindo integration if you need automation that runs without a signed-in user. Add the following **Microsoft Graph** permissions as **Application** permissions (not Delegated) on the app registration. The set differs slightly from the delegated list: application mode reads app role assignments with `Directory.Read.All` and resolves users with `User.Read.All`. | Permission | Description | Admin consent required | | --- | --- | --- | | `AuditLog.Read.All` | Read all audit log data | Yes | | `Directory.Read.All` | Read directory data | Yes | | `DirectoryRecommendations.Read.All` | Read all Azure AD recommendations | Yes | | `Group.Read.All` | Read all groups | Yes | | `GroupMember.Read.All` | Read all group memberships | Yes | | `Policy.Read.All` | Read your organization's policies | Yes | | `Policy.Read.AuthenticationMethod` | Read authentication method policies | Yes | | `Policy.Read.ConditionalAccess` | Read your organization's conditional access policies | Yes | | `Reports.Read.All` | Read all usage reports | Yes | | `RoleManagement.Read.Directory` | Read all directory RBAC settings | Yes | | `User.EnableDisableAccount.All` | Enable and disable user accounts | Yes | | `User.Read.All` | Read all users' full profiles | Yes | Two reports are delegated-only: Microsoft does not support application permissions for the authentication-method registration summary or the application sign-in summary, so those two tools fail on an application connection. Everything else works in either mode. #### Microsoft Entra ID Governance Add the following **Microsoft Graph** permissions. All of them are **Delegated** permissions. | Permission | Description | Admin consent required | | --- | --- | --- | | `AccessReview.Read.All` | Read all access reviews that user can access | Yes | | `AccessReview.ReadWrite.All` | Manage all access reviews that user can access | Yes | | `EntitlementManagement.Read.All` | Read all entitlement management resources | Yes | | `EntitlementManagement.ReadWrite.All` | Read and write entitlement management resources | Yes | | `LifecycleWorkflows-Reports.Read.All` | Read all Lifecycle workflows reports | Yes | | `LifecycleWorkflows-Workflow.Activate` | Run workflows on demand in Lifecycle workflows | Yes | | `LifecycleWorkflows-Workflow.ReadBasic.All` | List all workflows in Lifecycle workflows | Yes | | `offline_access` | Maintain access to data you have given it access to | No | | `PrivilegedAssignmentSchedule.Read.AzureADGroup` | Read assignment schedules for access to Azure AD groups | Yes | | `PrivilegedAssignmentSchedule.ReadWrite.AzureADGroup` | Read, create, and delete assignment schedules for access to Azure AD groups | Yes | | `PrivilegedEligibilitySchedule.Read.AzureADGroup` | Read eligibility schedules for access to Azure AD groups | Yes | | `PrivilegedEligibilitySchedule.ReadWrite.AzureADGroup` | Read, create, and delete eligibility schedules for access to Azure AD groups | Yes | | `RoleAssignmentSchedule.Read.Directory` | Read all active role assignments for your company's directory | Yes | | `RoleAssignmentSchedule.ReadWrite.Directory` | Read, update, and delete all active role assignments for your company's directory | Yes | | `RoleEligibilitySchedule.Read.Directory` | Read all eligible role assignments for your company's directory | Yes | | `RoleEligibilitySchedule.ReadWrite.Directory` | Read, update, and delete all eligible role assignments for your company's directory | Yes | | `RoleManagement.Read.Directory` | Read directory RBAC settings | Yes | | `User.Read` | Sign in and read user profile | No | :::caution[PIM and Lifecycle Workflows need extra tenant licensing] - **Privileged Identity Management (PIM)** (role/group assignment and eligibility schedules) requires **Microsoft Entra ID P2** (or Microsoft 365 E5). - **Lifecycle Workflows** requires the separate **Microsoft Entra ID Governance** license. ::: #### Microsoft Excel Online Add the following **Microsoft Graph** permissions. All of them are **Delegated** permissions. | Permission | Description | Admin consent required | | --- | --- | --- | | `Files.Read` | Read user files | No | | `Files.ReadWrite` | Have full access to user files | No | | `offline_access` | Maintain access to data you have given it access to | No | | `User.Read` | Sign in and read user profile | No | #### Microsoft Fabric This integration authenticates as the app itself (a service principal using client credentials) instead of as a signed-in user, so its setup differs from the other integrations on this page: - **No additional API permissions are required.** The default `User.Read` permission that Entra adds to every new app registration is enough, and it does not need admin consent. - **No redirect URI or user sign-in flow is needed.** Skip Step 5, the Callback URL substep of Step 4, and the sign-in portion of Step 7. Use the Fabric connection form below instead of the Step 4 credentials table. - **Access is controlled inside Microsoft Fabric, not in Entra.** Queries run with the service principal's permissions, so grant it only the read access you intend to expose. Configure the service principal in Microsoft Fabric: When linking the Microsoft Fabric account, the connection form asks for the following values: | Field | Description | | --- | --- | | **Tenant ID** | App registration > Overview > Directory (tenant) ID | | **SQL Endpoint Host** | Full `host,port` of the Fabric Lakehouse SQL endpoint, for example `foo.datawarehouse.fabric.microsoft.com,1433` | | **Lakehouse Database** | Database/lakehouse name | | **Client ID** | App registration > Overview > Application (client) ID | | **Client Secret** | App registration > Certificates & secrets > Client secrets > Value | #### Microsoft Graph Security (Microsoft Defender XDR) "Microsoft Graph Security" is Kindo's internal name for this integration (matching the Graph `/security/...` namespace it calls); Microsoft's product name for the same surface is **Defender XDR** (formerly Microsoft 365 Defender) — the unified security portal correlating signals across Defender for Endpoint, Defender for Office 365, Defender for Identity, Defender for Cloud Apps, and Entra ID Protection. It's a different product from both **Microsoft Defender** (Defender for Endpoint specifically, a separate integration on this page) and **Microsoft Defender for Cloud** (Azure's cloud security posture management, unrelated to the Defender XDR family). Add the following **Microsoft Graph** permissions. All of them are **Delegated** permissions. | Permission | Description | Admin consent required | | --- | --- | --- | | `IdentityRiskyUser.Read.All` | Read identity risky user information | Yes | | `IdentityRiskyUser.ReadWrite.All` | Read and write risky user information | Yes | | `offline_access` | Maintain access to data you have given it access to | No | | `SecurityAlert.Read.All` | Read all security alerts | Yes | | `SecurityAlert.ReadWrite.All` | Read and write to all security alerts | Yes | | `SecurityEvents.Read.All` | Read your organization's security events | Yes | | `SecurityIncident.Read.All` | Read incidents | Yes | | `SecurityIncident.ReadWrite.All` | Read and write to incidents | Yes | | `ThreatHunting.Read.All` | Run hunting queries | Yes | | `User.Read` | Sign in and read user profile | No | :::caution[Some tools need extra licensing, not just permissions] - **Risky users** requires **Microsoft Entra ID P2** (or Microsoft 365 E5) to be licensed on the tenant. - **Advanced hunting** device-scoped tables like `DeviceInfo` require **Microsoft Defender for Endpoint** to be licensed. ::: ##### Microsoft Graph Security — Application mode Microsoft Graph Security can also connect in **application mode**, where the integration authenticates as the app itself (client credentials) instead of as a signed-in user. This is a separate connection from the delegated integration above — set it up as its own Kindo integration if you need automation that runs without a signed-in user. Add the same permissions listed above, but as **Application** permissions (not Delegated) on the app registration. Unlike eDiscovery's application mode, this one doesn't need any extra RBAC registration step — the Graph application permissions alone are sufficient. #### Microsoft Intune Add the following **Microsoft Graph** permissions. All of them are **Delegated** permissions. | Permission | Description | Admin consent required | | --- | --- | --- | | `DeviceManagementApps.Read.All` | Read Microsoft Intune apps | Yes | | `DeviceManagementConfiguration.Read.All` | Read Microsoft Intune Device Configuration and Policies | Yes | | `DeviceManagementManagedDevices.PrivilegedOperations.All` | Perform user-impacting remote actions on Microsoft Intune devices | Yes | | `DeviceManagementManagedDevices.Read.All` | Read Microsoft Intune devices | Yes | | `offline_access` | Maintain access to data you have given it access to | No | | `User.Read` | Sign in and read user profile | No | :::caution[Requires an active Intune license on the tenant] This integration also requires **Microsoft Intune** to be licensed on the tenant (bundled with Microsoft 365 E3/E5, Microsoft 365 Business Premium, Enterprise Mobility + Security E3/E5, or standalone Intune Plan 1), separate from the permissions above. ::: ##### Microsoft Intune — Application mode Microsoft Intune can also connect in **application mode**, where the integration authenticates as the app itself (client credentials) instead of as a signed-in user. This is a separate connection from the delegated integration above — set it up as its own Kindo integration if you need automation that runs without a signed-in user. Add the same permissions listed above, but as **Application** permissions (not Delegated) on the app registration. The tenant licensing requirement above applies to this mode too. #### Microsoft Outlook Covers mail, calendar, and contacts in one integration. Add the following **Microsoft Graph** permissions. All of them are **Delegated** permissions. Mail is read and sent as the signed-in user. | Permission | Description | Admin consent required | | --- | --- | --- | | `Calendars.Read` | Read user calendars | No | | `Calendars.Read.Shared` | Read user and shared calendars | No | | `Calendars.ReadBasic` | Read basic details of user calendars | No | | `Calendars.ReadWrite` | Have full access to user calendars | No | | `Contacts.Read` | Read user contacts | No | | `Mail.Read` | Read user mail | No | | `Mail.ReadBasic` | Read user basic mail | No | | `Mail.Send` | Send mail as a user | No | | `offline_access` | Maintain access to data you have given it access to | No | | `People.Read` | Read users' relevant people lists | No | | `User.Read` | Sign in and read user profile | No | #### Microsoft Outlook Calendar Add the following **Microsoft Graph** permissions. All of them are **Delegated** permissions. | Permission | Description | Admin consent required | | --- | --- | --- | | `Calendars.Read` | Read user calendars | No | | `Calendars.Read.Shared` | Read user and shared calendars | No | | `Calendars.ReadBasic` | Read basic details of user calendars | No | | `Calendars.ReadWrite` | Have full access to user calendars | No | | `offline_access` | Maintain access to data you have given it access to | No | | `User.Read` | Sign in and read user profile | No | `find_meeting_times` needs `Calendars.Read.Shared` — it reads attendee availability, which the unshared calendar permissions don't cover. #### Microsoft Purview Compliance Add the following **Microsoft Graph** permissions. All of them are **Delegated** permissions. | Permission | Description | Admin consent required | | --- | --- | --- | | `AuditLogsQuery.Read.All` | Read audit logs data from all services | Yes | | `Content.Process.User` | Process content for data security, governance and compliance | Yes | | `Files.ReadWrite.All` | Have full access to all files user can access | No | | `InformationProtectionPolicy.Read` | Read user sensitivity labels and label policies. | No | | `offline_access` | Maintain access to data you have given it access to | No | | `ProtectionScopes.Compute.User` | Compute Purview policies for an individual user | Yes | | `SecurityAlert.Read.All` | Read all security alerts | Yes | | `SecurityAlert.ReadWrite.All` | Read and write to all security alerts | Yes | | `SecurityIncident.Read.All` | Read incidents | Yes | | `SecurityIncident.ReadWrite.All` | Read and write to incidents | Yes | | `ThreatHunting.Read.All` | Run hunting queries | Yes | | `User.Read` | Sign in and read user profile | No | ##### Microsoft Purview Compliance — Application mode Microsoft Purview Compliance can also connect in **application mode**, where the integration authenticates as the app itself (client credentials) instead of as a signed-in user. This is a separate connection from the delegated integration above — set it up as its own Kindo integration if you need automation that runs without a signed-in user. Tested and confirmed working. Add the following **Microsoft Graph** permissions as **Application** permissions (not Delegated) on the app registration. Two differ from the delegated list: sensitivity labels come from the tenant catalog with `InformationProtectionPolicy.Read.All`, and protection scopes are computed tenant-wide with `ProtectionScopes.Compute.All`. | Permission | Description | Admin consent required | | --- | --- | --- | | `AuditLogsQuery.Read.All` | Read audit logs data from all services | Yes | | `Content.Process.User` | Process content for data security, governance and compliance | Yes | | `Files.ReadWrite.All` | Read and write files in all site collections | Yes | | `InformationProtectionPolicy.Read.All` | Read all published labels and label policies for an organization. | Yes | | `ProtectionScopes.Compute.All` | Compute Purview policies at tenant scope | Yes | | `SecurityAlert.Read.All` | Read all security alerts | Yes | | `SecurityAlert.ReadWrite.All` | Read and write to all security alerts | Yes | | `SecurityIncident.Read.All` | Read all security incidents | Yes | | `SecurityIncident.ReadWrite.All` | Read and write to all security incidents | Yes | | `ThreatHunting.Read.All` | Run hunting queries | Yes | For the `Content.Process.User` / `ProtectionScopes.Compute.User` tools specifically: DLP policies must be created with the `New-DlpComplianceRule` PowerShell cmdlet to apply to Entra-registered (app-only) callers — policies created in the Purview portal UI don't apply to them. #### Microsoft Purview Data Governance This integration uses permissions from two APIs: **Microsoft Graph** and **Microsoft Purview**. All of them are **Delegated** permissions. Find Microsoft Purview under the **APIs my organization uses** tab when adding a permission. Add the following **Microsoft Graph** permission: | Permission | Description | Admin consent required | | --- | --- | --- | | `User.Read` | Sign in and read user profile | No | Add the following **Microsoft Purview** permission: | Permission | Description | Admin consent required | | --- | --- | --- | | `Purview.DelegatedAccess` | Purview Delegated API Access | No | :::caution[Also requires Purview collection role assignments] The permissions above only let the app call the API — the signed-in user also needs Purview data-plane roles assigned on a collection in the [Microsoft Purview governance portal](https://web.purview.azure.com/resource/) (**Data Map** → **Collections** → select the root collection → **Role assignments**): | Role | Grants | | --- | --- | | **Data Curator** | Catalog data plane — entities, glossary, lineage, relationships | | **Data Source Administrator** | Scanning data plane — data sources, scans | | **Collection Admin** | Account/metadata policy data plane — collections management | Only members of the Collection Admin role can assign these roles. ::: #### Microsoft Sentinel (Management) Like Microsoft Defender for Cloud, this integration authenticates against **Azure Resource Manager**, not Microsoft Graph. Add the following permission instead of Microsoft Graph: | API | Permission | Type | Admin consent required | | --- | --- | --- | --- | | Azure Resource Manager | `user_impersonation` | Delegated | No | Find **Azure Resource Manager** under the **APIs my organization uses** tab when adding a permission (App ID `797f4846-ba00-4fd7-ba43-dac1f8f63013`). When linking the connection, Kindo also asks for: | Field | Description | | --- | --- | | **Tenant ID** | App registration > Overview > Directory (tenant) ID | | **Subscription ID** | The subscription holding the Sentinel workspace | | **Resource Group Name** | The resource group holding the workspace | | **Workspace Name** | The Log Analytics workspace Sentinel runs on | All four are required — `Microsoft.SecurityInsights` has no tenant-level surface to fall back on. In addition to the Entra app permission, assign an Azure RBAC role to your account on that workspace (Azure Portal > your workspace > **Access control (IAM)** > Add role assignment): | Role | Grants | | --- | --- | | **Microsoft Sentinel Reader** | All read tools (incidents, alerts, bookmarks, watchlists, analytics rules) | | **Microsoft Sentinel Responder** | Everything Reader does, plus updating incidents and bookmarks | | **Microsoft Sentinel Contributor** | Everything Responder does, plus creating and updating analytics rules and watchlists | ##### Microsoft Sentinel (Management) — US Government Select the **Microsoft Sentinel Management (Government)** integration in Kindo rather than **Microsoft Sentinel (Management)**, and create its app registration in the Azure Government portal at [portal.azure.us](https://portal.azure.us). A commercial app registration cannot be used. Add the same permission, connection fields, and RBAC role as above. #### Microsoft Teams Add the following **Microsoft Graph** permissions. All of them are **Delegated** permissions. | Permission | Description | Admin consent required | | --- | --- | --- | | `Channel.ReadBasic.All` | Read the names and descriptions of channels | No | | `ChannelMessage.Read.All` | Read user channel messages | Yes | | `ChannelMessage.Send` | Send channel messages | No | | `Chat.Create` | Create chats | No | | `Chat.Read` | Read user chat messages | No | | `Chat.ReadBasic` | Read names and members of user chat threads | No | | `ChatMessage.Send` | Send user chat messages | No | | `Files.Read.All` | Read all files that user can access | No | | `Files.ReadWrite.All` | Have full access to all files user can access | No | | `offline_access` | Maintain access to data you have given it access to | No | | `OnlineMeetings.Read` | Read user's online meetings | No | | `OnlineMeetingTranscript.Read.All` | Read all transcripts of online meetings. | Yes | | `Team.ReadBasic.All` | Read the names and descriptions of teams | No | | `User.Read` | Sign in and read user profile | No | | `User.ReadBasic.All` | Read all users' basic profiles | No | ##### Microsoft Teams — meeting transcripts :::caution[Permissions alone are not enough] Microsoft Graph access to meeting transcripts is **off by default in every tenant**. Until an admin turns it on, the transcript tools fail regardless of the permissions granted above. ::: In the Teams admin center, go to **Meetings** > **Meeting settings**. Under **Transcript API access**, turn **Microsoft Graph access** **On**. Then select **Configure** and turn on **Include speaker attribution**, so transcripts identify who said what. The PowerShell equivalent: ```powershell Connect-MicrosoftTeams Set-CsTeamsMeetingConfiguration -EnableGraphTranscriptAccess $true -EnableAttributedTranscripts $true -Identity Global ``` The setting is tenant-wide, not per user. #### OneDrive Add the following **Microsoft Graph** permissions. All of them are **Delegated** permissions. | Permission | Description | Admin consent required | | --- | --- | --- | | `Files.Read` | Read user files | No | | `Files.Read.All` | Read all files that user can access | No | | `Files.ReadWrite.All` | Have full access to all files user can access | No | | `offline_access` | Maintain access to data you have given it access to | No | | `User.Read` | Sign in and read user profile | No | `Files.Read` only covers the signed-in user's own drive. Reading a drive by ID, including a SharePoint document library, needs `Files.Read.All`. #### SharePoint Online Add the following **Microsoft Graph** permissions. All of them are **Delegated** permissions. | Permission | Description | Admin consent required | | --- | --- | --- | | `Files.Read` | Read user files | No | | `Files.ReadWrite` | Have full access to user files | No | | `offline_access` | Maintain access to data you have given it access to | No | | `Sites.Read.All` | Read items in all site collections | No | | `Sites.ReadWrite.All` | Edit or delete items in all site collections | No | | `User.Read` | Sign in and read user profile | No | ##### SharePoint Online — Application mode Use the **SharePoint Online (Application)** integration in Kindo, with its own app registration. Steps 1 to 4 apply unchanged. Add `Sites.ReadWrite.All` as an **Application** permission and grant admin consent. Skip Step 5 and the sign-in portion of Step 7: application mode needs no redirect URI. Enter the Tenant ID alongside the client ID and secret in the connection form. ## Step 7: Authorize and test ## Permission reference Every tool across the Microsoft integrations, with the Microsoft permission it needs, whether it reads or writes, and whether an administrator has to consent. Use it to scope an app registration to exactly the tools you plan to use. How to read the table: - **Delegated** — the integration acts on behalf of a signed-in user. Every integration supports this mode. - **Application** — the integration authenticates as the app itself, with no signed-in user. Only Microsoft Azure, Microsoft eDiscovery, Microsoft Entra ID, Microsoft Graph Security, Microsoft Intune, Microsoft Purview Compliance, and SharePoint Online offer it. - **Admin consent** — Microsoft always requires tenant-wide admin consent for application permissions. The column shows the delegated requirement. - **Roles** — rows labeled as a role are not Entra API permissions. Microsoft Azure, Microsoft Defender for Cloud, and Microsoft Sentinel (Management) authorize through Azure RBAC, and Microsoft Purview Data Governance through Purview collection roles. The signed-in user, or the app itself in application mode, must hold that role as well. Roles are assigned, not consented, so the admin consent column does not apply to them. - A tool that needs more than one permission has one row per permission. :::note Microsoft grants most of these permissions at the API level rather than per operation, so several tools in an integration share one permission. Granting it enables every tool listed against it — check the access column for which of those can write. ::: ## Notes on service principals If you look for an integration's app under **App registrations** and only find it under **Enterprise applications**, you are looking at its **service principal**. - An **app registration** is the blueprint — the global definition of an application. It lives only in the tenant where the app was created. - An **enterprise application** (service principal) is an instance of that blueprint inside each tenant that uses the app. It is the app's actual account in your directory. What you see in your tenant depends on how you use Kindo: - **Kindo SaaS:** the app registration lives in Kindo's tenant. The first time you authorize an integration, Entra ID creates a service principal in your tenant under **Enterprise applications**, and that is what you authenticate against on every subsequent connection. You will not see an app registration in your tenant. - **Self-managed Kindo:** you create the app registration in your own tenant by following the steps on this page. When the app is first authorized, Entra ID still creates a matching service principal under **Enterprise applications**. In both cases, the service principal is what Microsoft Entra ID uses to let the app operate in your tenant: - **It holds the permission grants.** When a user or admin consents, the grant is recorded against the service principal. It is the ledger of what the app is allowed to access in your tenant. - **It is the identity that tokens are issued to.** When Entra ID issues the app an access token for your tenant, the service principal is the actor that token represents. - **It is your admin's control point.** Your admin manages the app under **Enterprise applications**: assign or restrict users, apply Conditional Access policies, disable the app, or revoke its access — all without touching the underlying registration. - **It is where sign-in and audit activity lands.** Sign-in logs and consent events for the app in your tenant attach to the service principal. ## Troubleshooting | Problem | What to check | | --- | --- | | Microsoft says the redirect URI is invalid | Make sure the redirect URI in Entra exactly matches the callback URL shown in Nango, including `https://` and `/oauth/callback`. | | The consent screen does not show the expected permissions | Confirm the permissions were added to the same app registration whose client ID you entered in Nango. Reconnect after changing permissions. | | Kindo reports `Unauthorized` or cannot refresh the connection | Recreate the connection and confirm the integration includes `offline_access` when delegated refresh is required. | | Kindo reports `Forbidden` | The signed-in user may not have access to the Microsoft resource, admin consent may be missing, or the app may not have the required permission. | | A write action fails but read actions work | The app may have read-only permissions. Add only the specific write permission required by the integration. | | Microsoft eDiscovery (Application mode) reports `Unauthorized` | The app also needs to be registered in Microsoft Purview's RBAC layer — see [Application mode](#microsoft-ediscovery--application-mode). | | Microsoft Graph Security risky-user tools report `access denied` | Requires a **Microsoft Entra ID P2** (or Microsoft 365 E5) license. | | Microsoft Intune tools report `authentication failed` | Requires an active **Microsoft Intune** license. | | Microsoft Defender tools report `access denied` | Requires an active **Microsoft Defender for Endpoint** subscription. | | Microsoft Entra ID Governance's PIM or Lifecycle Workflows tools report a licensing error | PIM requires **Microsoft Entra ID P2**; Lifecycle Workflows requires the separate **Microsoft Entra ID Governance** license. | | Microsoft Purview Data Governance tools report access errors even though the permissions are granted and admin-consented | The signed-in user is also missing a Purview collection role (Data Curator, Data Source Administrator, or Collection Admin) — see the Microsoft Purview Data Governance section above. | | SharePoint Online (Application mode) site or file search reports `access denied` | The app holds `Sites.Selected`, which Microsoft does not support on these APIs. Grant `Sites.Read.All` or `Sites.ReadWrite.All` as an Application permission — see [Application mode](#sharepoint-online--application-mode). | ## Security guidance - Use least-privilege permissions for every Microsoft integration. - Rotate client secrets according to your organization's policy. - Remove unused app registrations and old client secrets. - Keep a record of which Kindo integration uses each Entra app registration. ## Related guides - [Connect ServiceNow](/connecting-integrations/servicenow/) --- ## Invoke Kindo agents with the Responses API Source: https://docs.kindo.ai/guides/invoke-agents-responses-api/ Every agent you build in Kindo is invocable through `POST /v1/responses` by passing its ID as the model: ```json { "model": "agent/8e051857-978c-4207-8916-28a3d4c4d605", "input": "..." } ``` The agent supplies the model, system prompt, and tools; you supply the task. Because `model` is a field every OpenAI Responses client can set, this works from stock SDKs with no code changes — the same clients you already point at `api.kindo.ai`. This page takes you from zero to a streamed agent run. ## What you need 1. **A Kindo API key** — see [Authentication](/overview/authentication/). 2. **Your agent's ID.** Two ways to find it: ```bash # Alongside your model catalog (agents appear as agent/) curl "https://api.kindo.ai/v1/models?include_agents=true" \ -H "Authorization: Bearer $KINDO_API_KEY" # Or from the agents list (returns agentId per agent) curl https://api.kindo.ai/v1/agents/list \ -H "Authorization: Bearer $KINDO_API_KEY" ``` You can also copy the agent ID from its URL in the Kindo Terminal. ## Invoke the agent ### openai-python ```python from openai import OpenAI client = OpenAI( base_url="https://api.kindo.ai/v1", api_key=os.environ["KINDO_API_KEY"], ) response = client.responses.create( model="agent/8e051857-978c-4207-8916-28a3d4c4d605", input="Triage INC-4821 and propose a remediation plan.", instructions="Keep the summary to one sentence.", # optional — appends after the agent's own prompt ) print(response.id, response.status) # resp_run_04f0faa6-2e66-4f5a-b49f-6a28daa58d9b in_progress ``` ### curl ```bash curl -X POST https://api.kindo.ai/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -d '{ "model": "agent/8e051857-978c-4207-8916-28a3d4c4d605", "input": "Triage INC-4821 and propose a remediation plan." }' ``` The call returns **immediately** with an in-progress response — agent runs are long-horizon, so you stream or poll rather than block: ```json { "id": "resp_run_04f0faa6-2e66-4f5a-b49f-6a28daa58d9b", "object": "response", "status": "in_progress", "model": "agent/8e051857-978c-4207-8916-28a3d4c4d605", "output": [], "conversation": { "id": "conv_cmqbjvxqv000001ac25z0pje6" }, "metadata": { "kindo_run_id": "04f0faa6-2e66-4f5a-b49f-6a28daa58d9b", "kindo_agent_id": "8e051857-978c-4207-8916-28a3d4c4d605" } } ``` ### How your request composes with the agent - `input` — delivered to the run. If the agent declares exactly one named text input, your text binds to it; if it declares none, your text becomes the run conversation's opening user message (visible context for every step). Agents with two or more named inputs — or a single non-text input — cannot take freeform input (`400 agent_inputs_unmappable`); use [`POST /v1/agents/runs`](/apis/agents/runs/) with named inputs. - `instructions` — optional; appended **after** the agent's own system prompt. - `metadata` — echoed back, merged with the run linkage keys. - Everything pinned by the agent's stored configuration (`tools`, non-`"auto"` `tool_choice`, sampling parameters, `conversation`, `previous_response_id`, `background`) is rejected with `400 unsupported_parameter` naming the field — nothing is silently dropped. ## Stream the run live Set `stream: true` to watch the run as it progresses. The run's activity streams as standard Responses output items — assistant text as live `response.output_text.delta` events, plus reasoning summaries and tool-call items as each step executes: ```bash curl -N -X POST https://api.kindo.ai/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -d '{ "model": "agent/8e051857-978c-4207-8916-28a3d4c4d605", "input": "Triage INC-4821 and propose a remediation plan.", "stream": true }' ``` ``` event: response.created event: response.in_progress event: response.output_item.added ← message / reasoning / tool-call event: response.output_text.delta items, streamed live as each event: response.output_text.done step of the run executes event: response.output_item.done ... event: response.completed ← carries the full final response data: [DONE] ``` Notes on stream semantics: - Steps stream **live, at item granularity**. Assistant text arrives as `response.output_text.delta` events; reasoning summaries stream as `response.reasoning_summary_text.delta`; tool activity appears as typed items (`mcp_call`, `kindo_call`, `shell_call`, `web_search_call`) with their own argument-delta events. See [Streaming](/apis/responses/streaming/) for the event vocabulary. - A step that already finished before you connected is replayed as completed items (`response.output_item.added` + `response.output_item.done` pairs) rather than re-streamed token-by-token. - A failed run ends with `response.failed`; a run cancelled from the Kindo Terminal ends with an `error` event (`code: "run_cancelled"`). Every terminal path is followed by `data: [DONE]`. - Streams are capped at 30 minutes (`error` / `code: "stream_timeout"`). The run itself keeps executing — switch to polling below. - If you disconnect, nothing is lost: the run continues server-side and remains pollable. To re-attach to the stream, reconnect with `GET /v1/responses/{id}?stream=true` — agent-run responses replay from the beginning and then continue live (see [Streaming → resuming](/apis/responses/streaming/#agent-run-responses-replay-from-the-start-only)). ## Poll to completion (non-streaming) Without `stream: true`, take the `id` from the immediate response and poll `GET /v1/responses/{id}` until the status is terminal: ```python response = client.responses.create( model="agent/8e051857-978c-4207-8916-28a3d4c4d605", input="Triage INC-4821 and propose a remediation plan.", ) while response.status == "in_progress": time.sleep(3) response = client.responses.retrieve(response.id) print(response.status) # completed | failed | cancelled print(response.output_text) # the run's final answer ``` ```bash curl https://api.kindo.ai/v1/responses/resp_run_04f0faa6-2e66-4f5a-b49f-6a28daa58d9b \ -H "Authorization: Bearer $KINDO_API_KEY" ``` Statuses: `in_progress` → `completed`, `failed`, or `cancelled`. ## Read the output The response carries the run's activity as standard Responses output items — populated incrementally while the run is `in_progress`, and complete on a terminal status: - `output` — the run's assistant turns as output items: `message` items with `output_text` content, `reasoning` summaries, and tool activity (`mcp_call`, `kindo_call`, `shell_call`, `web_search_call`) where present. - `output_text` — the concatenated final answer text. - `usage` — token usage aggregated across the run. - `metadata.kindo_run_id` / `metadata.kindo_agent_id` — run linkage. The run ID also works with [`GET /v1/runs/{runId}`](/apis/agents/runs/) if you already integrate with the agents API. - `conversation.id` — the run's conversation. Read the full step-by-step transcript with [`GET /v1/conversations/{id}/items`](/apis/conversations/quickstart/). ## Current limitations Stated plainly, so you can design around them today: - **No request `tools` on agent models.** The agent's tool set is pinned by its configuration; additive request tools are planned. - **No `conversation` / `previous_response_id` on agent models**, and agent-run response IDs (`resp_run_*`) are not chainable via `previous_response_id` (you get `404 previous_response_not_found`). Conversation continuity for agent runs — follow-up turns to the same run conversation — is upcoming. - **Version pinning is not available yet.** `agent/@` returns `400 agent_version_pinning_unsupported`; invocations always run the agent's latest configuration. - **Freeform `input` binds to at most one text input.** An agent that declares two or more named inputs — or a single non-text input — returns `400 agent_inputs_unmappable`; run it with [`POST /v1/agents/runs`](/apis/agents/request-shape/#post-v1agentsruns) and named inputs instead. ## Errors | Status | Code | Meaning | | ------ | ------------------------------------- | ---------------------------------------------------------------------------------------------------------- | | `400` | `model_not_found` | Unknown, malformed, **or inaccessible** agent ID — indistinguishable from an unknown model on purpose. | | `400` | `agent_version_pinning_unsupported` | `agent/@` — drop the version suffix. | | `400` | `agent_inputs_unmappable` | The agent declares 2+ named inputs, or a single non-text input — use [`POST /v1/agents/runs`](/apis/agents/runs/). | | `400` | `unsupported_parameter` | A request field conflicts with the agent's pinned configuration; the message names the field. | | `404` | `previous_response_not_found` | You passed a `resp_run_*` ID (or any non-stored response ID) as `previous_response_id`. | | `404` | `not_found` | `GET /v1/responses/resp_run_*` for a run that doesn't exist or that you cannot access. | All errors use the standard [Responses error envelope](/apis/responses/errors/). ## See also - [Responses API quickstart](/apis/responses/quickstart/) — point your SDK at Kindo. - [Request shape](/apis/responses/request-shape/#agent-models-agentagent-id) — `agent/` in the field reference. - [Model catalog](/overview/model-catalog/#listing-agents-as-models) — discover agents with `include_agents=true`. - [Agents API](/apis/agents/quickstart/) — the control-plane surface (list agents, named-input runs, run results). --- ## Exercise B - Universal Security Triage Agent Source: https://docs.kindo.ai/learning/customer-training-session/exercises/universal-triage-agent/ ## Exercise Metadata | Field | Value | | --- | --- | | Duration | ~35 minutes | | Type | Workflow Agent with Integrations | | Focus | Agentic tool calling across security stack | :::tip This is the star exercise for the session: one runbook prompt, one LLM step, five tools, autonomous investigation flow. ::: ## Scenario SOC analysts often pivot across SIEM, EDR, identity, firewall, and threat-intel tools manually. That process can take 30-60 minutes per alert and delays incident response during alert volume spikes. ## Objective Build a single-step Kindo Workflow Agent that performs end-to-end triage across the security stack and synthesizes a justified disposition. The agent should autonomously: - Extract entities from an alert (users, hosts, IPs, domains, hashes, processes) - Query SIEM for correlated events and timelines - Query EDR for endpoint context and process activity - Query IdP for sign-in and privilege anomalies - Query firewall/NDR for network anomalies and beaconing - Enrich IOCs with threat intelligence - Produce a final disposition and ranked action plan ## What Makes This Exercise Different | Exercise A | Exercise B | | --- | --- | | Multi-step prompts for fixed workflow stages | Single-step runbook prompt | | Agent follows explicit sequence | Agent decides which tool to call and when | | Demonstrates structured procedural automation | Demonstrates agentic reasoning | | Primary tool: Knowledge Store | Primary tools: live integrations | ## Integrations Used (Demo Mode) | Integration | Role | What the Agent Queries | | --- | --- | --- | | Splunk | SIEM | Alerts, correlated events, timeline | | CrowdStrike | EDR | Endpoint detections, device context, process trees | | Okta | IdP | Sign-in events, MFA status, privilege changes | | Palo Alto | Firewall/NDR | Network traffic, beaconing, lateral movement | | VirusTotal | Threat Intel | IOC reputation, file analysis, domain/IP enrichment | :::note Demo Mode uses synthetic but coherent cross-tool data so correlations are realistic while avoiding production credentials. ::: ## Prerequisites - Kindo training account - Five integrations pre-enabled by session facilitator - Demo Mode enabled in training environment ## Step 1 - Create Agent and Enable Integrations (5 min) {/* Screenshot placeholder: Integrations panel showing all five tools toggled on */} :::tip This setup is the key differentiator from a standalone LLM: the model can actually call security tools instead of only analyzing pasted text. ::: ## Step 2 - Add the Triage Runbook Prompt (10 min) Add one LLM step named `Triage Runbook` and use this prompt: ```plaintext Run an end-to-end triage on the triggering alert/ticket if present. If no trigger exists, triage the highest-severity SIEM alerts from the last 4 hours. 1) Extract entities from the triggering object (users, hosts, IPs, domains, hashes, processes, email IDs, cloud resources). 2) SIEM: pull the rule details, raw events, correlations, first/last seen timestamps, and related alerts. 3) EDR/XDR: check device posture, detections, process tree, network connections, persistence mechanisms, and lateral movement signs. 4) IdP/IAM: review sign-in timeline, MFA outcomes, risk events, session/token anomalies, and privilege changes. 5) Firewall/NDR: check DNS + outbound anomalies, beaconing patterns, and east-west traffic spikes. 6) Threat Intel: enrich all IOCs (hashes, IPs, domains) and link to known campaigns or threat groups. Conclude with: - Disposition: TRUE POSITIVE / FALSE POSITIVE / NEEDS MORE DATA - Severity: CRITICAL / HIGH / MEDIUM / LOW - Confidence: HIGH / MEDIUM / LOW (with explanation) - Scope: contained to single host | multiple hosts | org-wide | unknown - Evidence summary: one line per data source showing what it found and whether it supports TP or FP - Attack narrative (if TP): initial access -> current stage -> blast radius - Ranked next steps: containment -> evidence preservation -> further investigation -> notification -> remediation ``` :::note Why one step: in this pattern, you define goal + tool access + output contract. The model determines call order dynamically from findings. ::: ## Step 3 - Run, Observe, and Review (15 min) Typical execution path you may observe: - Splunk queried for alert baseline and entities - CrowdStrike queried for host/process context - Okta queried for user authentication timeline - Palo Alto queried for network behavior linked to entities - VirusTotal queried for IOC enrichment - Final synthesis into disposition and actions ### Review Checklist - Disposition quality: verdict is evidence-backed and coherent - Tool sequence quality: calls are relevant and follow-up queries are logical - Cross-correlation quality: findings are connected across identity, endpoint, and network data - Action quality: next steps are prioritized and operationally useful :::caution Flag low-quality output when confidence is high but evidence is missing, contradictory findings are ignored, or recommendations are generic. ::: ## Step 4 (Optional) - Add One Real Integration (5 min) If credentials are available, connect one real integration and rerun. This demonstrates production connection flow while preserving demo data for the remaining tools. ## Discussion Points - Agentic versus procedural design: when should each be used? - Did the agent make unnecessary calls or miss useful ones? - How did multi-source correlation improve triage quality? - If this saves 30-60 minutes per alert, where should that analyst time be reinvested? ## Production Extensions - Trigger triage automatically for high-severity SIEM alerts - Auto-create Jira or ServiceNow tickets for true positives - Notify Slack on CRITICAL findings - Re-triage open incidents on a recurring schedule as new evidence arrives ## Troubleshooting | Issue | Likely Cause | How To Fix | | --- | --- | --- | | Agent does not call all integrations | Integration toggles are disabled or unavailable | Re-check integration enablement in agent settings | | Output contains unsupported claims | Prompt missing evidence accountability | Keep required evidence summary lines per data source | | Agent loops or over-queries one source | Runbook lacks prioritization guardrails | Add guidance: stop querying once confidence threshold is met | | Disposition stays NEEDS MORE DATA | Entities not extracted or source data sparse | Add explicit entity extraction checks and broaden time window | | Contradictory evidence not reconciled | Prompt lacks conflict-handling instruction | Add requirement to explain contradictions before verdict | ## Continue - Return to [Session Overview](/learning/customer-training-session/) - Open [Prompt Engineering Quick Reference](/reference/prompt-engineering/) - Open [Agent Construction Best Practices](/reference/agent-best-practices/) --- ## Authentication Source: https://docs.kindo.ai/overview/authentication/ A single Kindo API key authenticates every API on `api.kindo.ai` — inference (`/v1/chat/completions`, `/v1/responses`, `/v1/messages`, `/v1/mcp`) and control plane (`/v1/agents/*`, `/v1/runs/*`, `/v1/conversations/*`, `/v1/models`, `/v1/integrations/*`). There are no per-API credentials. `/v1/mcp` also accepts a [Cross App Access](/admin/cross-app-access/) token in place of an API key — a beta credential, enabled per organization, that lets an Okta-approved app call the MCP gateway on a user's behalf without a Kindo API key. It isn't governed by **Personal API Key Access**. ## Get your API key 1. Sign in to the Kindo Terminal. 2. Open [**Settings** (gear icon) > **API**](https://app.kindo.ai/?settings=api/api-keys). 3. Copy your API key. If you do not see the API tab, ask your organization admin to enable **Personal API Key Access** for at least one user group you belong to. Self-managed deployments expose the same key in the same place; only the base URL differs. A key **acts as you, its creator**: every request it makes runs with your identity, and all activity — including the audit trail — is attributed to you and carries your permissions. ## Send the key Both header forms work on every Kindo API: | Header | Example | Notes | | ----------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------- | | `Authorization: Bearer` | `Authorization: Bearer YOUR_API_KEY` | Preferred for raw HTTP clients and for the OpenAI SDKs (`OPENAI_API_KEY` populates this). | | `x-api-key` | `x-api-key: YOUR_API_KEY` | Preferred for Anthropic SDKs and Claude Code (`ANTHROPIC_API_KEY` populates this). | Pick whichever is idiomatic for the client you're using — both authenticate the same way and resolve to the same caller identity. ### Precedence If both headers are present on a single request, **`Authorization: Bearer` takes precedence**. If the `Authorization` header is present but malformed (for example, an empty token, or a non-Bearer scheme like `Basic`), the request is rejected outright instead of falling back to `x-api-key`. ## Key format Kindo API keys are opaque strings. Treat them as secrets: - Store them in your secret manager (1Password, Doppler, AWS Secrets Manager, Vault). - Inject them at runtime via environment variables. - Never commit them to source control. - Rotate them through the Kindo Terminal if you suspect exposure. ## Identity and permissions An API key acts as its creating user — that user is the key's **principal**. Every request authenticated with the key runs as that user, and every audit entry the request produces is attributed to that user. The key is not a separate service identity; it carries the identity and the permissions of the person who created it. Because the key inherits the permissions of the user who created it: - **Model and provider access** restrictions configured on the user's user groups apply to API requests. - **Personal API Key Access** must be enabled on at least one user group the user belongs to. - **Ask Mode Only** is enforced on `/v1/responses`. - DLP filters that protect the Terminal UI also apply to API requests. - All API calls are recorded in your organization's audit trail. ## Self-managed deployments For self-managed Kindo installations, replace `api.kindo.ai` everywhere with your deployment's API base URL. Header semantics, key format, and identity rules are unchanged. ## Errors | Status | Meaning | | ------ | -------------------------------------------------------------------------------------------- | | `401` | The key was missing, malformed, or revoked. Check your `Authorization` / `x-api-key` header. | | `403` | The key authenticated, but lacks permission for the model. | Each protocol formats the error envelope differently — see the per-API errors page (`/apis//errors/`) for shapes. ## See also - [Getting started](/overview/getting-started/) — pick your client. - [Model catalog](/overview/model-catalog/) — discover available model IDs once authenticated. --- ## Agents API runs Source: https://docs.kindo.ai/apis/agents/runs/ `POST /v1/agents/runs` is asynchronous. The trigger returns immediately with a `runId`; the result lands on `GET /v1/runs/{runId}` when the run finishes. ## `GET /v1/runs/{runId}` ```bash curl https://api.kindo.ai/v1/runs/7a7ced7d-95a8-416f-95bb-5c0ff3599f53 \ -H "Authorization: Bearer $KINDO_API_KEY" ``` Response: ```json { "runId": "7a7ced7d-95a8-416f-95bb-5c0ff3599f53", "agentId": "18d20df4-d9b3-4cb0-a009-9f11ec9c5d3d", "conversationId": "clx9a2b1c0000d4e5f6g7h8i9", "createdAtUtc": "2026-03-12T15:00:00.000Z", "endedAtUtc": "2026-03-12T15:00:08.000Z", "result": "{\"role\":\"assistant\",\"parts\":[{\"type\":\"text\",\"text\":\"Security scan complete. No critical findings.\"}]}", "status": "success" } ``` | Field | Type | Notes | | ---------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `runId` | string | Matches the `runId` returned by the trigger. | | `agentId` | string \| null | The agent that produced this run. `null` when the underlying agent has been soft-deleted; the run record remains visible but no longer resolves to a live agent. | | `conversationId` | string \| null | The conversation backing this run. Pass it to [`GET /v1/conversations/{conversation_id}/items`](/apis/conversations/quickstart/) to read the full message history — useful for multi-turn continuity. Always present in practice today; typed as nullable for forward compatibility, so defensive clients should tolerate `null`. | | `createdAtUtc` | string | ISO-8601 timestamp when the trigger was accepted. | | `endedAtUtc` | string \| null | ISO-8601 timestamp when the run reached a terminal status, or `null` while `status` is `in_progress`. | | `result` | string \| null | A **JSON-encoded string** of the final assistant message payload — parse with `JSON.parse` to recover `{ role, parts, ... }`. `null` while `in_progress`, always `null` on `failure` or `cancelled`, and also `null` on `success` when the run produced no assistant payload. | | `status` | string | One of `in_progress`, `success`, `failure`, `cancelled`. | ## Status lifecycle ```text in_progress ──▶ success ──▶ failure ──▶ in_progress (re-run failed step) ──▶ cancelled ``` `failure` is not a permanent sink: re-running the failed step from the Kindo app returns the run to `in_progress` (see the `failure` note below). - `in_progress` — the only non-terminal status. Continue polling. - `success` — the run completed; `result` holds the JSON-encoded final assistant payload, or is `null` if the run produced no assistant payload. - `failure` — the run errored. `result` is `null`; inspect the Kindo Terminal or the underlying conversation for diagnostics. A `failure` run is not permanently terminal: a user can re-run the failed step from the Kindo app, which returns the same run to `in_progress` (and resets `endedAtUtc` to `null`) until it reaches a terminal status again. - `cancelled` — the run was cancelled (manually or by a timeout). `result` is `null`. ## `POST /v1/runs/{runId}/restart` Re-runs a previous run by its `runId` — handy for retrying a run that `failed` — without re-sending the original inputs. Like the trigger, the call is asynchronous and returns immediately with a **new** `runId`; poll `GET /v1/runs/{newRunId}` for its result. ```bash curl -X POST https://api.kindo.ai/v1/runs/7a7ced7d-95a8-416f-95bb-5c0ff3599f53/restart \ -H "Authorization: Bearer $KINDO_API_KEY" ``` Response (`202 Accepted`): ```json { "runId": "b2f1c0a9-6e3d-4f28-9a17-2d4e8c1b7f60", "conversationId": "clx9a2b1c0000d4e5f6g7h8i9" } ``` | Field | Type | Notes | | ---------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | `runId` | string | The **new** run's id — distinct from the run you restarted. Track and poll it the same way you would a freshly triggered run. | | `conversationId` | string | The conversation backing the new run. Pass it to [`GET /v1/conversations/{conversation_id}/items`](/apis/conversations/quickstart/) to read its history. | Notes: - No request body is required, and none is read — the new run re-uses the source run's original inputs as they were first submitted. - The source run is left untouched; the returned `runId` is a separate run with its own status, result, and conversation. - The new run uses the agent's **latest** version, so its behavior can differ from the source run if the agent was edited in between. - Returns `404` when the run does not exist or the caller lacks access — the same collapse-to-404 behavior as `GET /v1/runs/{runId}`. ## `GET /v1/runs/{runId}/evals` Returns the aggregate evaluation result and per-step verdicts for a run — including runs that failed or were cancelled partway, which surface a `partial` result for the steps that were evaluated. Each agent step can have an objective; this endpoint reports whether those objectives were met. ```bash curl https://api.kindo.ai/v1/runs/7a7ced7d-95a8-416f-95bb-5c0ff3599f53/evals \ -H "Authorization: Bearer $KINDO_API_KEY" ``` Response: ```json { "runId": "7a7ced7d-95a8-416f-95bb-5c0ff3599f53", "result": "fail", "steps": [ { "stepNumber": 1, "name": "Perform Web Search for Trending Stock", "result": "pass", "explanation": "Kindo performed web searches and synthesized findings into a clear summary." }, { "stepNumber": 2, "name": "Generate Stock Forecast", "result": "pass", "explanation": "Kindo delivered a comprehensive forecast with analyst targets and risk factors." }, { "stepNumber": 3, "name": "Execute Trade on Robinhood", "result": "fail", "explanation": "Kindo cannot execute trades on Robinhood — it lacks brokerage integration." } ] } ``` | Field | Type | Notes | | -------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `runId` | string | Matches the `runId` from the trigger. | | `result` | string | Aggregate eval result. One of `pass`, `fail`, `partial`, `pending`, `skipped`, `unknown`. `pass` = all objectives satisfied; `fail` = at least one failed; `partial` = run failed/cancelled but some completed steps were evaluated (inspect `steps[]`); `pending` = run in progress or verdicts not yet written; `skipped` = run failed/cancelled with no evaluated steps; `unknown` = no definitive result. | | `steps` | array | One entry per step in the run, ordered by `stepNumber`. | Step shape: | Field | Type | Notes | | ------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `stepNumber` | integer | 1-indexed position in the run. | | `name` | string \| null | Display name of the step, or `null` if unnamed. | | `result` | string \| null | One of `pass`, `fail`, `not_evaluated`, `unknown`, or `null`. `pass` = objective satisfied; `fail` = objective not satisfied; `not_evaluated` = evaluation did not run; `unknown` = verdict could not be determined. `null` when no verdict has been recorded yet. | | `explanation` | string \| null | Free-form explanation of the verdict; `null` when not evaluated. | The evals endpoint returns `404` when the run does not exist or the caller lacks access — the same collapse-to-404 behavior as `GET /v1/runs/{runId}`. ### Result values and what produces them | `result` (aggregate) | Meaning | When it arises | | -------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `pass` | All evaluated objectives satisfied. | Run reached `success`, every evaluated step verdict is satisfied. | | `fail` | At least one objective was not satisfied. | Any step verdict is "not satisfied". | | `pending` | Evaluation not finished yet. | Run still `in_progress`, or at least one step verdict has not been written. | | `partial` | The run did not complete, but some steps that ran were evaluated. | Run ended in `failure` or `cancelled` **and** at least one completed step has a verdict. Inspect `steps[]` for the per-step results; the run never reached `success`, so no clean pass/fail aggregate is claimed. | | `skipped` | Evaluation did not run for any step. | Run ended in `failure` or `cancelled` with no evaluated steps, or no step had an objective to evaluate. | | `unknown` | Verdicts exist but no definitive aggregate could be determined. | A verdict could not be determined for a step (and no step failed). | The step-level `result` distinguishes two superficially similar cases: - `not_evaluated` — the step had no objective evaluation performed (the step itself recorded a "not evaluated" verdict). - `unknown` — an evaluation was attempted but the verdict could not be determined. - `null` — no verdict has been recorded for the step yet (see the async section above); this is transient. ### Error responses | Status | Condition | Body shape | | ------ | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | | `400` | `runId` is malformed (not a UUID). | `{ "error": "Bad Request", "message": "..." }` | | `401` | Missing or invalid API key. | `{ "error": "Unauthorized", "message": "..." }` | | `404` | Run does not exist **or** the caller lacks access (deliberately not distinguished). | `{ "error": "Not found", "message": "Run not found." }` | | `500` | Unexpected server-side error. Transient — retry with backoff. | `{ "error": "Internal Server Error", "message": "Something went wrong." }` | Example `404`: ```json { "error": "Not found", "message": "Run 7a7ced7d-95a8-416f-95bb-5c0ff3599f53 not found." } ``` Example `500`: ```json { "error": "Internal Server Error", "message": "Something went wrong." } ``` ### What to expect on failure | Condition | Transient? | Client action | | ----------------------------------------------- | ---------- | ----------------------------------------------------------------------------- | | `result: "pending"` / step `result: null` | Yes | Keep polling with backoff. | | `result: "partial"` (run `failure`/`cancelled`) | No | `cancelled` is terminal; a `failure` run can be re-run from the Kindo app (returns to `in_progress`). Read `steps[]` for the verdicts of the steps that completed. | | `500` | Yes | Retry with backoff; evals are idempotent. | | `400` (malformed `runId`) | No | Fix the request; do not retry as-is. | | `401` | No | Provide a valid API key; do not retry as-is. | | `404` | No | The run is missing or inaccessible; do not retry as-is. | There are no webhooks for evaluation completion — polling is the only mechanism. Re-fetching evals is always safe. ## Polling cadence Runs are typically long-running (seconds to minutes), so a short poll-then-back-off works well in practice: - Wait 1–2 seconds before the first poll. - Back off to 5–10 seconds for subsequent polls. - Treat any non-`in_progress` `status` as terminal and stop. If you re-read a `failure` run later, it may have returned to `in_progress` because a user re-ran the failed step from the Kindo app. Kindo does not currently push webhooks for run completion; polling is the only delivery mechanism for the result. ## Runs and conversations Every run is backed by its own server-side `conversation`. The `runId` itself is not a `conversation_id`, but the `conversationId` is returned directly on both the `POST /v1/agents/runs` trigger response and the `GET /v1/runs/{runId}` run-result response. Pass it to the [Conversations API](/apis/conversations/quickstart/) (`GET /v1/conversations/{conversation_id}/items`) to read the full message history — the basis for multi-turn continuity across runs. For most use cases, `result` on `GET /v1/runs/{runId}` is the only field you need. ## See also - [Quickstart](/apis/agents/quickstart/) — end-to-end discover → trigger → poll flow. - [Request shape](/apis/agents/request-shape/) — the trigger and discovery bodies. - [Errors](/apis/agents/errors/) — `404 Not found`, `422 Unprocessable Entity`, etc. --- ## Chat Completions API streaming Source: https://docs.kindo.ai/apis/chat-completions/streaming/ Set `"stream": true` on `POST /v1/chat/completions` to receive a standard OpenAI Server-Sent Events stream. Your existing parser works unchanged. ## Request ```bash curl -N https://api.kindo.ai/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -d '{ "model": "claude-sonnet-4-5-20250929", "stream": true, "messages": [ { "role": "user", "content": "Write a haiku about observability." } ] }' ``` Kindo returns `Content-Type: text/event-stream` and emits OpenAI's chunk format. Each event is a JSON payload prefixed with `data: `: ``` data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","choices":[{"delta":{"role":"assistant","content":""}}]} data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","choices":[{"delta":{"content":"Logs flow through"}}]} data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","choices":[{"delta":{"content":" the gate;"}}]} data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","choices":[{"delta":{},"finish_reason":"stop"}]} data: [DONE] ``` The terminal `data: [DONE]` line is the canonical end-of-stream marker. ## Event types The stream emits unnamed `data:` lines. Each line is either an incremental chunk or the terminal marker: | Event | Meaning | | -------- | ------------------------------------------------- | | `data:` | Incremental `chat.completion.chunk` JSON payload. | | `[DONE]` | End-of-stream marker. | ## Example stream Each `data:` JSON object follows OpenAI's `chat.completion.chunk` schema: ```json { "id": "chatcmpl-abc123", "object": "chat.completion.chunk", "created": 1710000000, "model": "claude-sonnet-4-5-20250929", "choices": [ { "index": 0, "delta": { "content": " the gate;" }, "finish_reason": null } ] } ``` `usage` is not included in intermediate chunks. In the OpenAI spec, `stream_options.include_usage` requests usage data in the final chunk; Kindo currently does not populate this field. If you need usage stats, make a non-streaming request or rely on Kindo's metering. ## SDK consumption ```python from openai import OpenAI client = OpenAI(base_url="https://api.kindo.ai/v1", api_key=os.environ["KINDO_API_KEY"]) stream = client.chat.completions.create( model="claude-sonnet-4-5-20250929", messages=[{"role": "user", "content": "Write a haiku."}], stream=True, ) for chunk in stream: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) ``` ```ts const client = new OpenAI({ baseURL: 'https://api.kindo.ai/v1', apiKey: process.env.KINDO_API_KEY }); const stream = await client.chat.completions.create({ model: 'claude-sonnet-4-5-20250929', messages: [{ role: 'user', content: 'Write a haiku.' }], stream: true }); for await (const chunk of stream) { process.stdout.write(chunk.choices[0].delta.content ?? ''); } ``` ## Pre-stream errors If the upstream request fails before streaming begins, Kindo returns a normal HTTP error response with the OpenAI-compatible JSON envelope — no SSE switch. See [Errors](/apis/chat-completions/errors/). ## Mid-stream errors Once SSE has started, the outer HTTP status is committed at `200`. Errors that arise after that point arrive as an inline `event: error` SSE frame followed by `data: [DONE]`: ``` event: error data: {"error":{"message":"upstream stream interrupted","type":"server_error","code":"stream_error"}} data: [DONE] ``` Treat any `event: error` payload with `code: "stream_error"` as terminal — the response will not resume. ## See also - [Quickstart](/apis/chat-completions/quickstart/) — non-streaming round trip. - [Request shape](/apis/chat-completions/request-shape/) — every field Kindo honors. - [Tool use](/apis/chat-completions/tool-use/) — streaming tool-call events. - [Errors](/apis/chat-completions/errors/) — pre-stream error envelopes. --- ## Conversations API errors Source: https://docs.kindo.ai/apis/conversations/errors/ The Conversations endpoints return errors in OpenAI's **nested** error envelope — the same shape as the OpenAI Conversations and Responses APIs. This is intentionally different from the [Agents API](/apis/agents/errors/), which uses a flat `{ "error": string, "message": string }` envelope. ## Envelope ```json { "error": { "message": "Conversation not found.", "type": "not_found_error", "code": "not_found" } } ``` ## Status / type / code reference | Status | `type` | `code` | Cause | | ------ | ----------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `400` | `invalid_request_error` | `invalid_cursor` | `?after=` references an item that does not exist in this conversation. | | `401` | — | — | Missing, malformed, or revoked API key. Emitted by the auth middleware **before** the route handler runs, so the body is the flat `{"error": "Unauthorized", "message": "..."}` shape — not the nested envelope shown above. | | `403` | `permission_error` | `forbidden` | The key authenticated, but the caller does not have access to this conversation. | | `404` | `not_found_error` | `not_found` | `conversation_id` or `item_id` does not exist, or the conversation has been pruned by retention. | | `413` | — | — | JSON request body exceeds the 50 MB (52,428,800-byte) limit. Returned before route handling as `{"message":"Request body too large. Maximum size is 50 MB."}`. | ## See also - [Quickstart](/apis/conversations/quickstart/) — list + fetch flow. - [Request shape](/apis/conversations/request-shape/) — query parameters and item types. - [Agents API errors](/apis/agents/errors/) — Kindo's other agent surface, which uses a different (flat) envelope. --- ## MCP API request shape Source: https://docs.kindo.ai/apis/mcp/request-shape/ `POST /v1/mcp` is a Streamable HTTP transport for JSON-RPC 2.0. Every request is a single JSON-RPC envelope, and the response comes back over the same HTTP connection. The gateway implements the [MCP 2024-11-05 spec](https://modelcontextprotocol.io/specification/2024-11-05). ## Required headers | Header | Value | Notes | | --------------- | -------------------------------------- | ------------------------------------------------------------------------------------------- | | `Authorization` | `Bearer YOUR_API_KEY` | Preferred. `x-api-key: YOUR_API_KEY` also works for Anthropic-compatible clients. | | `Content-Type` | `application/json` | JSON-RPC body. | | `Accept` | `application/json, text/event-stream` | Required by the MCP Streamable HTTP transport. Missing `text/event-stream` returns **406**. | When both `Authorization` and `x-api-key` are present, `Authorization: Bearer` takes precedence. A malformed `Authorization` header is rejected outright — the gateway does not fall back to `x-api-key`. `Authorization: Bearer` also accepts a short-lived Cross App Access token — one an Okta-approved app redeemed on behalf of a signed-in user, for an organization with [Cross App Access](/admin/cross-app-access/) enabled. The gateway tells the two apart by shape, so no separate header or flag is needed; a static API key remains the default for everything else. MCP SDKs and the `mcp-remote` bridge set the `Accept` header for you. Raw `curl` or `httpx` callers must include it explicitly. ## Supported methods | Method | Description | | -------------- | ---------------------------------------------------------------------------------------- | | `initialize` | Returns server capabilities and identity. Most clients call this once at connection. | | `ping` | No-op heartbeat. | | `tools/list` | List tools your account is entitled to. See [Scoping](/apis/mcp/scoping/). | | `tools/call` | Execute a tool on the resolved upstream integration. | `prompts/*` and `resources/*` methods are not supported. Calls to them return the protocol's unknown-method error. ## `tools/list` example ```bash curl -X POST https://api.kindo.ai/v1/mcp \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -d '{ "jsonrpc": "2.0", "method": "tools/list", "id": 1 }' ``` Response: ```json { "jsonrpc": "2.0", "id": 1, "result": { "tools": [ { "name": "linear_list_issues", "description": "List issues in a Linear team", "inputSchema": { "type": "object", "properties": { "teamId": { "type": "string" } }, "required": ["teamId"] } }, { "name": "github_create_issue", "description": "Create a GitHub issue", "inputSchema": { "type": "object", "properties": { "repo": { "type": "string" }, "title": { "type": "string" } }, "required": ["repo", "title"] } } ] } } ``` ## `tools/call` example ```bash curl -X POST https://api.kindo.ai/v1/mcp \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "id": 2, "params": { "name": "linear_list_issues", "arguments": { "teamId": "TEAM_ID" } } }' ``` Response: ```json { "jsonrpc": "2.0", "id": 2, "result": { "content": [{ "type": "text", "text": "..." }] } } ``` ## Tool naming convention Tool names are prefixed with their integration identifier to avoid collisions across providers. The format is: ```text {server}_{name} ``` For example: - `linear_list_issues` — the `list_issues` tool from the Linear integration. - `github_create_issue` — the `create_issue` tool from the GitHub integration. - `slack_post_message` — the `post_message` tool from the Slack integration. The gateway splits on the **first underscore only**. Kindo's integration IDs use hyphens (e.g., `sap-s4hana-rfc`), so names like `sap-s4hana-rfc_invoke_bapi` resolve cleanly to server `sap-s4hana-rfc` and tool `invoke_bapi`. You only need to know the integration ID to address its tools. ## See also - [Quickstart](/apis/mcp/quickstart/) — the smallest working call. - [Scoping](/apis/mcp/scoping/) — what `tools/list` returns and how to narrow it. - [Errors](/apis/mcp/errors/) — HTTP and protocol error envelopes. --- ## Messages API streaming Source: https://docs.kindo.ai/apis/messages/streaming/ Set `"stream": true` on `POST /v1/messages` to receive a standard Anthropic Server-Sent Events stream. Existing Anthropic SDK consumers work unchanged. ## Request ```bash curl -N https://api.kindo.ai/v1/messages \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -d '{ "model": "claude-sonnet-4-5-20250929", "max_tokens": 512, "stream": true, "messages": [ { "role": "user", "content": "Write a haiku about observability." } ] }' ``` Kindo returns `Content-Type: text/event-stream` and emits Anthropic's event sequence verbatim. ## Event types | Event | Meaning | | --------------------- | ------------------------------------------------- | | `message_start` | Initial message metadata. | | `content_block_start` | Start of a streamed content block. | | `content_block_delta` | Incremental content chunk. | | `content_block_stop` | End of a content block. | | `message_delta` | Updated usage or stop-reason data. | | `message_stop` | End of the stream. | | `error` | Stream interruption after the stream has started. | | `ping` | Keepalive heartbeat; safe to ignore. | ## Example stream ``` event: message_start data: {"type":"message_start","message":{"id":"msg_01","type":"message","role":"assistant","content":[],"model":"claude-sonnet-4-5-20250929","stop_reason":null,"usage":{"input_tokens":12,"output_tokens":1}}} event: content_block_start data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Logs flow through"}} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" the gate;"}} event: content_block_stop data: {"type":"content_block_stop","index":0} event: message_delta data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":32}} event: message_stop data: {"type":"message_stop"} ``` ## SDK consumption ```python from anthropic import Anthropic client = Anthropic(base_url="https://api.kindo.ai", api_key=os.environ["KINDO_API_KEY"]) with client.messages.stream( model="claude-sonnet-4-5-20250929", max_tokens=512, messages=[{"role": "user", "content": "Write a haiku."}], ) as stream: for text in stream.text_stream: print(text, end="", flush=True) ``` ```ts const client = new Anthropic({ baseURL: 'https://api.kindo.ai', apiKey: process.env.KINDO_API_KEY }); const stream = await client.messages.stream({ model: 'claude-sonnet-4-5-20250929', max_tokens: 512, messages: [{ role: 'user', content: 'Write a haiku.' }] }); for await (const event of stream) { if ( event.type === 'content_block_delta' && event.delta.type === 'text_delta' ) { process.stdout.write(event.delta.text); } } ``` ## Pre-stream errors If the upstream request fails before streaming begins, Kindo returns a normal HTTP error response with Anthropic's JSON envelope — no SSE switch. See [Errors](/apis/messages/errors/). ## Mid-stream errors Once the SSE has started, the outer HTTP status is committed at `200`. Stream errors arrive as an `event: error` SSE frame **in place of `message_stop`**: ``` event: error data: {"type":"error","error":{"type":"overloaded_error","message":"upstream overloaded"}} ``` Treat any `error` SSE event as terminal — no `message_stop` follows and the response will not resume. ## See also - [Quickstart](/apis/messages/quickstart/) — non-streaming round trip. - [Request shape](/apis/messages/request-shape/) — every field Kindo honors. - [Tool use](/apis/messages/tool-use/) — streaming tool-call events. - [Errors](/apis/messages/errors/) — pre-stream error envelopes. --- ## Responses API streaming Source: https://docs.kindo.ai/apis/responses/streaming/ Set `"stream": true` on `POST /v1/responses` to receive Server-Sent Events. Kindo emits the standard OpenAI Responses stream — your existing parser works without changes. ## Request ```bash curl -N https://api.kindo.ai/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -d '{ "model": "claude-sonnet-4-5-20250929", "stream": true, "input": "Write a haiku about observability." }' ``` `stream` is consumed by the Kindo route handler to flip the response into `Content-Type: text/event-stream` mode. It is not forwarded as a model parameter. ## Event types Each event has a named `event:` line and a JSON `data:` payload. The event names match OpenAI's Responses streaming spec: | Event | Meaning | | ---------------------------------------- | ----------------------------------------------------------- | | `response.created` | Initial response object is allocated. | | `response.in_progress` | Generation has started. | | `response.output_item.added` | A new output item (message, function call, etc.) has begun. | | `response.output_item.done` | An output item has completed. | | `response.content_part.added` | A new content part within a message has begun. | | `response.content_part.done` | A content part has completed. | | `response.output_text.delta` | Incremental text chunk for an `output_text` content part. | | `response.output_text.done` | The full text for a content part is now stable. | | `response.function_call_arguments.delta` | Incremental JSON for a function-call's `arguments`. | | `response.function_call_arguments.done` | Function-call arguments are complete. | | `response.completed` | The whole response is finished. | | `response.failed` | The response terminated in an error state. | | `error` | Mid-stream error. See "Mid-stream errors" below. | When the response produces reasoning or server-side tool activity (Kindo-hosted tools, MCP tools, agent runs), the same protocol carries those items too: | Event | Meaning | | ------------------------------------------------------------------------ | ----------------------------------------------------------- | | `response.reasoning_summary_part.added` / `.done` | A reasoning summary part within a `reasoning` item. | | `response.reasoning_summary_text.delta` / `.done` | Incremental reasoning-summary text. | | `response.mcp_call_arguments.delta` / `.done` | Incremental JSON for an `mcp_call` item's `arguments`. | | `response.kindo_call_arguments.delta` / `.done` | Incremental JSON for a `kindo_call` item's `arguments`. | The corresponding output items (`reasoning`, `mcp_call`, `kindo_call`, `shell_call`, `web_search_call`) open and close with the same `response.output_item.added` / `response.output_item.done` pairs as messages and function calls. Agent-run invocations (`model: "agent/"`) stream this full vocabulary live as the run executes — see [Invoke Kindo agents](/guides/invoke-agents-responses-api/#stream-the-run-live). ## Example stream ``` event: response.created data: {"id":"resp_abc123","status":"in_progress","object":"response"} event: response.output_text.delta data: {"item_id":"msg_xyz","delta":"Logs flow through"} event: response.output_text.delta data: {"item_id":"msg_xyz","delta":" the gate;\nMetrics rise like steam"} event: response.output_text.done data: {"item_id":"msg_xyz","text":"Logs flow through the gate;\nMetrics rise like steam,\ntraces find their way."} event: response.completed data: {"id":"resp_abc123","status":"completed","object":"response"} ``` ## SDK consumption Standard OpenAI SDK `stream=True` works as expected: ```python from openai import OpenAI client = OpenAI(base_url="https://api.kindo.ai/v1", api_key=os.environ["KINDO_API_KEY"]) stream = client.responses.create( model="claude-sonnet-4-5-20250929", input="Write a haiku about observability.", stream=True, ) for event in stream: if event.type == "response.output_text.delta": print(event.delta, end="", flush=True) ``` ```ts const client = new OpenAI({ baseURL: 'https://api.kindo.ai/v1', apiKey: process.env.KINDO_API_KEY }); const stream = await client.responses.create({ model: 'claude-sonnet-4-5-20250929', input: 'Write a haiku about observability.', stream: true }); for await (const event of stream) { if (event.type === 'response.output_text.delta') { process.stdout.write(event.delta); } } ``` ## Pre-stream errors If the upstream request fails before streaming begins (for example, auth fails, the model isn't found, the body is invalid), Kindo returns a normal HTTP error response with a JSON envelope — no SSE switch. See [Errors](/apis/responses/errors/) for shapes. ## Mid-stream errors Once the first SSE byte has been written, the outer HTTP status is already committed at `200`. Errors that arise after that point arrive as an `error` event followed by the appropriate terminal event: ``` event: error data: {"error":{"message":"upstream stream interrupted","type":"server_error","code":"stream_error"}} event: response.failed data: {"id":"resp_abc123","status":"failed","object":"response"} ``` Treat any `error` SSE event as terminal — the response will not resume. ## Resuming a dropped stream If the SSE connection drops mid-stream (a flaky network, a proxy timeout), you can reconnect to the **same** response and replay the events you missed instead of issuing a new `POST` — which would start a new response. Every streamed event carries its `sequence_number` both in the `data:` payload and as the SSE `id:` line: ``` id: 4 event: response.output_text.delta data: {"item_id":"msg_xyz","delta":" the gate","sequence_number":4} ``` Reconnect with `GET` on the response id, passing `stream=true` and the last `sequence_number` you received as `starting_after`: ```bash curl -N "https://api.kindo.ai/v1/responses/resp_abc123?stream=true&starting_after=4" \ -H "Authorization: Bearer $KINDO_API_KEY" ``` Kindo replays every event **after** `sequence_number` 4 with continuous numbering, then continues live until the response completes (or emits `response.completed` immediately if it already finished). No new response is created. Omit `starting_after` to replay from the beginning. A negative or non-integer `starting_after` returns HTTP 400. Browser `EventSource` clients resume automatically: the runtime resends the last `id:` it saw as the `Last-Event-ID` request header, which Kindo honors when `starting_after` is absent. The resume window is **time-bounded** (the same window over which a response is retained for polling). After it elapses, a streamed resume falls back to the response's final items followed by `response.completed` — the per-token deltas are no longer available. Retrieve the final object with a plain `GET` (no `stream`) instead. ### Agent-run responses replay from the start only Responses backed by an agent run (ids of the form `resp_run_`) support **full replay only** — not incremental resume. Reconnect with `stream=true` and `starting_after=0` (or omit `starting_after`) to replay the run's events from the beginning and continue live: ```bash curl -N "https://api.kindo.ai/v1/responses/resp_run_abc123?stream=true&starting_after=0" \ -H "Authorization: Bearer $KINDO_API_KEY" ``` A **positive** `starting_after` on a `resp_run_*` id returns HTTP 400. An agent run streams many steps, and a step that is still live emits per-token deltas while a step already finished replays only its completed items, so the event numbering is not stable enough to splice a partial reconnect onto reliably. Replaying from the start avoids any gap or duplication. ## See also - [Quickstart](/apis/responses/quickstart/) — non-streaming round trip. - [Request shape](/apis/responses/request-shape/) — every field Kindo honors. - [Tool use](/apis/responses/tool-use/) — streaming tool-call events. - [Errors](/apis/responses/errors/) — pre-stream error envelopes. --- ## Model catalog Source: https://docs.kindo.ai/overview/model-catalog/ The same model registry backs every inference API on `api.kindo.ai`. Whichever model ID you discover via `/v1/models` works as the `model` parameter on `/v1/chat/completions`, `/v1/responses`, and `/v1/messages` — Kindo routes to the correct upstream provider for you. ## Discover available models ```bash curl https://api.kindo.ai/v1/models \ -H "Authorization: Bearer $KINDO_API_KEY" ``` The response uses the standard OpenAI models-list format, enriched with the human-readable `display_name`: ```json { "object": "list", "data": [ { "id": "claude-sonnet-4-5-20250929", "object": "model", "created": 0, "owned_by": "kindo", "display_name": "Claude Sonnet 4.5" }, { "id": "gpt-4o-2024-11-20", "object": "model", "created": 0, "owned_by": "kindo", "display_name": "GPT-4o" } ] } ``` You only see models your organization has access to — model visibility is governed by your user-group configuration. ### Model display name Beyond the four OpenAI fields (`id`, `object`, `created`, `owned_by`), each proxied-model entry carries one additive Kindo field: | Field | Type | Description | | -------------- | ------ | ---------------------------------------------------------- | | `display_name` | string | Human-readable model name, as shown in the Kindo Terminal. | This field is **additive** — `id` is still the model ID you pass back as the `model` parameter, and OpenAI SDK clients ignore the extra field, so existing integrations are unaffected. Agent entries (`include_agents=true`, below) do not carry it. You can also inspect the same list under [**Settings** > **Kindo API** > **Available Models**](https://app.kindo.ai/?settings=api/available-models) in the Kindo Terminal. ## Listing agents as models Your stored Kindo agents are invocable as models on `/v1/responses` (see [Invoke Kindo agents with the Responses API](/guides/invoke-agents-responses-api/)). Add `include_agents=true` to list them alongside the model catalog: ```bash curl "https://api.kindo.ai/v1/models?include_agents=true" \ -H "Authorization: Bearer $KINDO_API_KEY" ``` Agent entries use the same models-list shape: ```json { "id": "agent/8e051857-978c-4207-8916-28a3d4c4d605", "object": "model", "created": 1748736000, "owned_by": "Acme Inc" } ``` - `id` is `agent/` — pass it directly as the `model` parameter on `POST /v1/responses`. - `owned_by` is your organization's name (or its ID when the org has no display name), distinguishing agents from `"kindo"`-owned models. - `created` is the agent's actual creation timestamp. - Only agents you can access are listed, scoped the same way as [`GET /v1/agents/list`](/apis/agents/quickstart/). Agents with triggers (scheduled, event, or webhook) are included — a trigger is an additional way the agent starts and does not prevent invoking it directly. The agents portion is paginated. Pass `limit` (1–1000, default 500) and `after` (an offset, default 0) to page through them; the response carries `has_more` and a `next_after` offset to request the next page: ```bash # First page curl "https://api.kindo.ai/v1/models?include_agents=true&limit=2" \ -H "Authorization: Bearer $KINDO_API_KEY" # → "has_more": true, "next_after": 2 # Next page curl "https://api.kindo.ai/v1/models?include_agents=true&limit=2&after=2" \ -H "Authorization: Bearer $KINDO_API_KEY" ``` Repeat with `after = next_after` until `has_more` is `false` to walk the complete set. Proxied (`"kindo"`-owned) models are not paginated — they are returned in full on the first page (`after=0`) only and omitted from later pages, so concatenating pages yields exactly one copy of each. Offsets are stable as long as the agent set is unchanged between page requests; an agent created or deleted mid-walk shifts the window and can skip or duplicate an entry across page boundaries (inherent to offset pagination). Without the parameter (the default), the response is unchanged except for an added `"has_more": false` — existing `/v1/models` consumers are unaffected. Agent IDs are only valid on `/v1/responses`; the other inference endpoints treat them as an unknown model. ## Naming conventions Model IDs follow the upstream provider's published name with the date suffix the provider uses. A few representative examples: | Family | Example IDs | | ---------------- | ------------------------------------------------------ | | Anthropic Claude | `claude-sonnet-4-5-20250929`, `claude-opus-4-20251125` | | OpenAI GPT | `gpt-4o-2024-11-20`, `gpt-4o-mini-2024-07-18` | | OpenAI o-series | `o1-2024-12-17`, `o3-mini-2025-01-31` | Kindo does not rename models. If you've used these IDs against the upstream provider directly, the same string works against `api.kindo.ai`. The exact set evolves — `GET /v1/models` is the source of truth. Hard-coding a model ID into your client is fine; assuming a model will exist forever is not. Inference endpoints return `400` for an unknown model. ## Cross-API model use You can use the same model ID on whichever inference protocol your client speaks: - A `claude-*` model works on `/v1/chat/completions`, `/v1/responses`, and `/v1/messages` — Kindo translates the wire format for you. - A `gpt-*` model works on all of them — same translation. In practice, pick the protocol that matches your SDK; pick the model based on capability, latency, and cost. ## Errors | Status | Meaning | | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `403` `model_access_denied` | The model exists but your organization is not entitled to call it. | | `400` `model_not_found` | The model ID is not registered on inference endpoints. Check `GET /v1/models`. Emitted by `/v1/chat/completions` and `/v1/responses`. `/v1/messages` returns `400 invalid_request_error` without a code field — see [Messages errors](/apis/messages/errors/). | If your client returns a model error, hit `/v1/models` first. Most "unknown model" reports trace back to a stale model ID. ## See also - [Authentication](/overview/authentication/) — get a key and call `/v1/models`. - [Responses API quickstart](/apis/responses/quickstart/) / [Chat Completions quickstart](/apis/chat-completions/quickstart/) / [Messages quickstart](/apis/messages/quickstart/) — use a model ID on each surface. --- ## Agents API errors Source: https://docs.kindo.ai/apis/agents/errors/ The Agents endpoints return errors using a **flat** envelope: a short `error` label plus a `message` field with the specific cause. This differs from the OpenAI-compatible nested envelope used by `/v1/responses` and `/v1/conversations/*`. ## Envelope ```json { "error": "Not found", "message": "Agent 18d20df4-d9b3-4cb0-a009-9f11ec9c5d3d not found." } ``` ## Status reference | Status | `error` | Cause | | ------ | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `400` | `Bad Request` | Malformed request body — e.g. missing `agentId` on `POST /v1/agents/runs`, or a body that fails Zod schema validation. | | `401` | `Unauthorized` | Missing, malformed, or revoked API key. Emitted by the auth middleware; the body is `{"error": "Unauthorized", "message": "..."}`. | | `404` | `Not found` | `agentId` does not exist or is not visible to the caller; `runId` does not exist or the caller cannot read the underlying conversation. Per-agent permission denials also collapse to `404` — Kindo does not distinguish "missing" from "forbidden" on this surface. | | `413` | — | JSON request body exceeds the 50 MB (52,428,800-byte) limit. This check runs before the endpoint and returns `{"message":"Request body too large. Maximum size is 50 MB."}`. | | `422` | `Unprocessable Entity` | Body parsed, but the request cannot be executed — required `inputs[].name` entries declared by the agent are missing from the request. | | `429` | `Too Many Requests` | Org or user rate limit. Honors `Retry-After` when present. | | `500` | `Internal Server Error` | Unexpected Kindo-side failure. | Unknown `inputs[].name` entries on `POST /v1/agents/runs` are **silently dropped** rather than rejected — only the missing-required case produces a `422`. Re-fetch `GET /v1/agents/{agentId}` to confirm the canonical label list if a run isn't picking up the values you expected. ## Common causes by endpoint ### `POST /v1/agents/runs` A `422 Unprocessable Entity` here means the request omitted a required `inputs[].name` declared on the agent. The labels are case-sensitive and must match exactly. Fetch `GET /v1/agents/{agentId}` to confirm the canonical list before retrying. (Triggers do not block this endpoint — an agent with `hasTriggers: true` can still be run directly.) A `404 Not found` here means the `agentId` is unknown to the caller — either it does not exist, or the user group does not grant access. Permission failures intentionally collapse to `404` and are not distinguished from "missing" in the response. ### `GET /v1/runs/{runId}` A `404 Not found` here means either the `runId` is bogus, the caller cannot read the underlying conversation, or the run has been pruned by retention. The three cases are not distinguished in the response envelope. A run that finished with `status: "failure"` is **not** an HTTP error — it returns `200` with a status payload. Inspect the `status` field, not the HTTP code, to detect agent-level failures. ### `POST /v1/runs/{runId}/restart` A `404 Not found` means the run does not exist or the caller lacks access — the same collapse-to-`404` behavior as `GET /v1/runs/{runId}`. The two cases are intentionally indistinguishable. A `400 Bad Request` indicates a malformed `runId` (not a valid UUID). ### `GET /v1/runs/{runId}/evals` Error behavior mirrors `GET /v1/runs/{runId}` — a `404 Not found` means the run does not exist or the caller lacks access. The two cases are intentionally indistinguishable. A `400 Bad Request` indicates a malformed `runId` (not a valid UUID). ## See also - [Authentication](/overview/authentication/) — `401` causes. - [Quickstart](/apis/agents/quickstart/) — input-label discovery flow that prevents `422` errors. --- ## Chat Completions API tool use Source: https://docs.kindo.ai/apis/chat-completions/tool-use/ `POST /v1/chat/completions` accepts OpenAI's standard function-tool shape. Definitions and `tool_choice` flow through verbatim — no Kindo-specific wrapping. ## Define a function tool ```json { "model": "claude-sonnet-4-5-20250929", "messages": [ { "role": "user", "content": "What is the weather in San Francisco?" } ], "tools": [ { "type": "function", "function": { "name": "get_weather", "description": "Get the current weather for a city.", "parameters": { "type": "object", "properties": { "location": { "type": "string" } }, "required": ["location"] } } } ], "tool_choice": "auto" } ``` Note the nested `function` object — that's the Chat Completions tool shape (different from the Responses API, where `name` / `description` / `parameters` sit at the top level of the tool entry). ## Round trip ### Step 1 — initial request Submit the request above. ### Step 2 — assistant message with `tool_calls` ```json { "id": "chatcmpl-abc123", "object": "chat.completion", "choices": [ { "index": 0, "message": { "role": "assistant", "content": null, "tool_calls": [ { "id": "call_abc", "type": "function", "function": { "name": "get_weather", "arguments": "{\"location\":\"San Francisco, CA\"}" } } ] }, "finish_reason": "tool_calls" } ] } ``` `finish_reason: "tool_calls"` is the canonical signal that the model wants you to execute a tool. ### Step 3 — execute the tool client-side Run `get_weather("San Francisco, CA")` in your application. ### Step 4 — submit the result Send a follow-up request with the assistant message preserved and a new `tool` message carrying the result: ```bash curl -X POST https://api.kindo.ai/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -d '{ "model": "claude-sonnet-4-5-20250929", "messages": [ { "role": "user", "content": "What is the weather in San Francisco?" }, { "role": "assistant", "content": null, "tool_calls": [ { "id": "call_abc", "type": "function", "function": { "name": "get_weather", "arguments": "{\"location\":\"San Francisco, CA\"}" } } ] }, { "role": "tool", "tool_call_id": "call_abc", "content": "{\"temperature\":68,\"unit\":\"fahrenheit\"}" } ], "tools": [ { "type": "function", "function": { "name": "get_weather", "description": "Get the current weather for a city.", "parameters": { "type": "object", "properties": { "location": { "type": "string" } }, "required": ["location"] } } } ] }' ``` The `tool_call_id` must match the one from Step 2. Re-include `tools` so the model still sees the schema. ### Step 5 — final answer ```json { "choices": [ { "index": 0, "message": { "role": "assistant", "content": "It's 68 °F in San Francisco." }, "finish_reason": "stop" } ] } ``` ## `tool_choice` | Value | Behavior | | --------------------------------------------- | ---------------------------------------------------- | | `"auto"` (default) | The model decides whether to call a tool. | | `"none"` | The model must produce a message; never a tool call. | | `"required"` | The model must call at least one tool. | | `{type: "function", function: {name: "..."}}` | The model must call exactly that function. | ## See also - [Request shape](/apis/chat-completions/request-shape/) — full field reference. - [Streaming](/apis/chat-completions/streaming/) — `delta.tool_calls` events. - [Errors](/apis/chat-completions/errors/) — error envelopes for tool-call failures and provider passthrough. --- ## MCP API scoping Source: https://docs.kindo.ai/apis/mcp/scoping/ When you call `tools/list`, the gateway builds the list from your authorized integration connections and available first-party Platform tools, then applies your organization's server and tool permissions. Platform tools do not require an integration connection. Discovery does not grant access to every resource a tool can act on; each call checks access to the requested resource. ## What you see in `tools/list` A tool needs **at least one** of these access paths to appear: 1. **You personally have a connection** to that integration — e.g., you connected your own Linear or GitHub account in Kindo. 2. **Someone in your org has shared a connection** with you — a teammate's connection that your account is allowed to use. 3. **The integration is public** — no credentials required, like the built-in `browser` integration. 4. **It is an available first-party Platform tool** — Platform MCP must be configured, and the tool must meet its discovery requirements. Directory and department tools are omitted unless you meet their [role and department-access requirements](/reference/tool-catalog/#platform-tools). These tools do not use an integration connection. If none of these is true for a given integration, none of its tools appear in `tools/list`, and a direct `tools/call` on one of those tools returns the protocol's invalid-params error. The tool must also pass your organization's server and tool permissions. The [organization's approval setting](/admin/rbac-tool-actions/#organization-approval-setting) applies to external MCP: **Run automatically** permits otherwise-authorized tools, while **Ask before running** and **Block** omit them from `tools/list` and prevent execution. Your personal tool approval settings apply to Kindo chats and agents. They do not restrict external MCP access or override organization restrictions. Configure which permitted tools to use and when to request approval in your external client. Every `tools/call` checks the current organization settings again, including calls using a previously discovered tool name. Platform directory and department calls also recheck their role and department-access requirements, so a tool discovered before access was revoked cannot bypass those checks. An organization requirement for manual approval returns [`manual_approval_required`](/apis/mcp/errors/#manual_approval_required-envelope); use Kindo chat if manual approvals are enabled for your organization, or contact your administrator. An organization **Block** setting returns the invalid-params error (`-32602`). You don't need to pass any filter or flag to get this behavior. The bare `POST /v1/mcp` endpoint returns the right list for your account by default. ## Narrowing the list For agents that only need a subset of your tools — for example, a Linear-only agent — the gateway accepts an allow-list as **either** a query parameter or a header: ```bash # Query parameter curl -X POST "https://api.kindo.ai/v1/mcp?integrations=linear,github" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -d '{ "jsonrpc": "2.0", "method": "tools/list", "id": 1 }' # Header (equivalent) curl -X POST https://api.kindo.ai/v1/mcp \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -H "x-kindo-integrations: linear,github" \ -d '{ "jsonrpc": "2.0", "method": "tools/list", "id": 1 }' ``` The filter narrows the set you would otherwise see — it never grants access. If your account doesn't have a path to GitHub and you pass `?integrations=linear,github`, you get only Linear tools back (no error). Names in the filter must match the integration ID exactly (e.g., `linear`, `github`, `platform`, `sap-s4hana-rfc`). Both forms accept comma-separated values. The query parameter also accepts array repetition (`?integrations=linear&integrations=github`). If both query and header are present, the query parameter wins. A missing or empty filter value is ignored. ### Why narrow? The bare list includes eligible tools across the integrations your API key can reach. For accounts with broad shared connections that can be 1000+ tools — enough to exceed most chat clients' context windows. A narrow filter keeps the tool surface small: - **Context-budget reasons.** Many MCP clients send the full tool catalog to the model on every turn. Fewer tools means a smaller system prompt and more room for actual work. - **Per-agent focus.** Pin a single MCP client to one or two integrations even though your API key has access to more. - **Stable surface.** Decouple your client from new integrations added to your org later. ## Default-connection resolution When your account has more than one connection for the same integration — say, two Linear accounts — the gateway has to pick one for every `tools/call` to that connected integration. First-party Platform tools do not use a connection or a default. The resolution chain for connected integrations is: 1. **Only one connection.** Use it. 2. **You selected active connections.** Kindo uses the first usable connection in your active set. You can also replace that set with one preferred connection via `POST /v1/integrations/connections/{integration_id}/default` (see [Kindo extensions](/apis/mcp/kindo-extensions/)). 3. **You own at least one connection.** Use your most recently created owned connection. 4. **You don't own any, but a shared one is available.** Use the most recently created shared connection. If the chosen connection's credentials have been revoked or expired, the call returns a `credentials_unavailable` error with a hint to reconnect. See [Errors](/apis/mcp/errors/) for the exact envelope. Kindo chats and agents can choose an account for each tool call from your active connections or their pinned connections. External MCP clients use one connection per integration, selected by the resolution chain above; their tools do not expose a per-call account selector. Platform tools remain connectionless. ## See also - [Quickstart](/apis/mcp/quickstart/) — first call. - [Request shape](/apis/mcp/request-shape/) — supported methods and headers. - [Kindo extensions](/apis/mcp/kindo-extensions/) — the connection- management REST endpoints used by step 2. --- ## Messages API tool use Source: https://docs.kindo.ai/apis/messages/tool-use/ `POST /v1/messages` accepts Anthropic's standard tool definitions. `tool_use` and `tool_result` content blocks pass through to the upstream provider with no Kindo-specific wrapping. ## Define a function tool ```json { "model": "claude-sonnet-4-5-20250929", "max_tokens": 512, "tools": [ { "name": "get_weather", "description": "Get the current weather for a city.", "input_schema": { "type": "object", "properties": { "location": { "type": "string" } }, "required": ["location"] } } ], "messages": [ { "role": "user", "content": "What is the weather in San Francisco?" } ] } ``` Anthropic tool definitions use `name` / `description` / `input_schema` (different from Chat Completions' nested `function` object). ## Round trip ### Step 1 — initial request Submit the request above. ### Step 2 — assistant message with `tool_use` ```json { "id": "msg_01", "role": "assistant", "content": [ { "type": "tool_use", "id": "toolu_01", "name": "get_weather", "input": { "location": "San Francisco, CA" } } ], "stop_reason": "tool_use" } ``` `stop_reason: "tool_use"` is the canonical signal that the model wants you to run a tool. ### Step 3 — execute the tool client-side Run `get_weather({ location: "San Francisco, CA" })` in your application. ### Step 4 — submit the result Return the result as a `tool_result` content block in a `user` message. Re-include the prior assistant message and re-include `tools` so the model has the schema: ```bash curl -X POST https://api.kindo.ai/v1/messages \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -d '{ "model": "claude-sonnet-4-5-20250929", "max_tokens": 512, "tools": [ { "name": "get_weather", "description": "Get the current weather for a city.", "input_schema": { "type": "object", "properties": { "location": { "type": "string" } }, "required": ["location"] } } ], "messages": [ { "role": "user", "content": "What is the weather in San Francisco?" }, { "role": "assistant", "content": [ { "type": "tool_use", "id": "toolu_01", "name": "get_weather", "input": { "location": "San Francisco, CA" } } ] }, { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_01", "content": "{\"temperature\":68,\"unit\":\"fahrenheit\"}" } ] } ] }' ``` The `tool_use_id` on the `tool_result` block must match the `id` on the originating `tool_use` block. ### Step 5 — final answer ```json { "id": "msg_02", "role": "assistant", "content": [{ "type": "text", "text": "It's 68 °F in San Francisco." }], "stop_reason": "end_turn" } ``` ## `tool_choice` Anthropic's tool-choice format: | Value | Behavior | | ----------------------------------- | ----------------------------------------- | | `{ "type": "auto" }` (default) | The model decides whether to call a tool. | | `{ "type": "any" }` | The model must call at least one tool. | | `{ "type": "tool", "name": "..." }` | The model must call exactly that tool. | ## See also - [Request shape](/apis/messages/request-shape/) — full field reference. - [Streaming](/apis/messages/streaming/) — `tool_use` content-block streaming. - [Errors](/apis/messages/errors/) — error envelopes for tool-call failures and provider passthrough. --- ## Responses API tool use Source: https://docs.kindo.ai/apis/responses/tool-use/ The Responses API uses OpenAI's typed-output tool-call shape. The flow is: 1. You send a request with `tools` defined. 2. The model decides to call a tool. The response status is `incomplete` and `output` contains a `function_call` item. 3. You execute the tool client-side and send the result back as a `function_call_output` input item, referencing the same `call_id`. 4. The model continues with the result and emits a final `message`. ## Define a function tool Function tools are defined in the Responses-API tool format (top-level `name`, `description`, `parameters`): ```json { "model": "claude-sonnet-4-5-20250929", "input": "What is the weather in San Francisco?", "tools": [ { "type": "function", "name": "get_weather", "description": "Get the current weather for a city.", "parameters": { "type": "object", "properties": { "location": { "type": "string" } }, "required": ["location"] } } ], "tool_choice": "auto" } ``` This is the shape `codex` and `openai-python`'s `client.responses` produce. ## Round trip > Set `store: true` to persist the response server-side. The response > includes `conversation.id` when `store: true`, which you can use for > stateful continuation, or ignore it for a one-shot call. ### Step 1 — initial request ```bash curl -X POST https://api.kindo.ai/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -d '{ "model": "claude-sonnet-4-5-20250929", "store": true, "input": "What is the weather in San Francisco?", "tools": [ { "type": "function", "name": "get_weather", "description": "Get the current weather for a city.", "parameters": { "type": "object", "properties": { "location": { "type": "string" } }, "required": ["location"] } } ] }' ``` ### Step 2 — tool-call response ```json { "id": "resp_abc123", "status": "incomplete", "incomplete_details": { "reason": "tool_use" }, "conversation": { "id": "conv_abc123" }, "output": [ { "type": "function_call", "call_id": "call_abc", "name": "get_weather", "arguments": "{\"location\":\"San Francisco, CA\"}" } ] } ``` `status: "incomplete"` with `incomplete_details.reason: "tool_use"` is the canonical signal that the model wants you to run a tool. > **Kindo divergence from OpenAI spec:** OpenAI's published Responses API > uses `status: "completed"` with a `function_call` output item when the > model calls a tool. Kindo returns `status: "incomplete"` with > `incomplete_details.reason: "tool_use"` instead. Stock OpenAI SDKs > will see this divergence when consuming raw HTTP responses. ### Step 3 — execute the tool client-side Run `get_weather("San Francisco, CA")` in your application. This is your code, not Kindo's — function tools are by definition client-executed. ### Step 4 — submit the result ```bash curl -X POST https://api.kindo.ai/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -d '{ "model": "claude-sonnet-4-5-20250929", "conversation": "conv_abc123", "input": [ { "type": "message", "role": "user", "content": "What is the weather in San Francisco?" }, { "type": "function_call", "call_id": "call_abc", "name": "get_weather", "arguments": "{\"location\":\"San Francisco, CA\"}" }, { "type": "function_call_output", "call_id": "call_abc", "output": "{\"temperature\":68,\"unit\":\"fahrenheit\"}" } ], "tools": [ { "type": "function", "name": "get_weather", "description": "Get the current weather for a city.", "parameters": { "type": "object", "properties": { "location": { "type": "string" } }, "required": ["location"] } } ] }' ``` The `call_id` must match the one from Step 2. Re-include the `tools` definition so the model sees the schema again. Re-include the prior input messages so the model has the context of the tool call. Alternatively, continue statefully — pass `conversation` or chain via `previous_response_id` (the prior turns are then loaded server-side). See [Chat Actions extensions](/apis/responses/kindo-extensions/) for the stateful continuation recipe. ### Step 5 — final answer ```json { "id": "resp_def456", "status": "completed", "output": [ { "type": "message", "role": "assistant", "content": [ { "type": "output_text", "text": "It's 68 °F in San Francisco." } ] } ], "output_text": "It's 68 °F in San Francisco." } ``` ## `tool_choice` | Value | Behavior | | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | | `"auto"` (default) | The model decides whether to call a tool. | | `"none"` | The model must produce a message; never a tool call. | | `"required"` | The model must call at least one tool. | | `{type: "function", name: "..."}` | The model must call exactly that function. | | `{type: "allowed_tools", mode: "auto", tools: [...]}` | Restricts the model to a subset of declared tools. Works for `function`, `mcp`, and `kindo_*` tools. | ## Other tool types `POST /v1/responses` also accepts: - `{type: "mcp", server_label: ""}` — MCP tools registered against your organization. **Stateful path only**: the `server_label` is resolved server-side against your organization's integrations, so the request must set `store: true` or pass a `conversation` ID. A stateless request that includes an `mcp` tool is rejected with `400 unsupported_tool_type`. - `{type: "kindo_"}` — individual Kindo-hosted tools (opt-in). - `{type: "kindo_tools"}` — sugar that expands to the full Kindo hosted-tool catalog. Stock OpenAI built-in types (`web_search`, `file_search`, `computer`, `shell`, etc.) are forwarded verbatim and dispatched to the upstream model when supported. See [Chat Actions extensions](/apis/responses/kindo-extensions/) for the full hosted-tool catalog and stateful flow patterns. ## See also - [Request shape](/apis/responses/request-shape/) — full field reference. - [Streaming](/apis/responses/streaming/) — `function_call_arguments.delta` events. - [Chat Actions extensions](/apis/responses/kindo-extensions/) — Kindo-hosted tools. - [Errors](/apis/responses/errors/) — error envelopes for tool-call failures and provider passthrough. --- ## Chat Completions API errors Source: https://docs.kindo.ai/apis/chat-completions/errors/ `POST /v1/chat/completions` returns errors in OpenAI's Chat Completions error envelope. Errors that originate at Kindo's edge use the table below; errors that originate upstream are forwarded verbatim with the upstream body and content-type. ## Envelope ```json { "error": { "message": "Model 'unknown-model' not found.", "type": "invalid_request_error", "code": "model_not_found" } } ``` ## Status / type / code reference | Status | Type | Common codes | Cause | | ------ | ----------------------- | --------------------------------- | --------------------------------------------------------------------------- | | `400` | `invalid_request_error` | `invalid_body`, `model_not_found` | Body fails schema validation or references a missing model. | | `401` | — | — | Missing/malformed/revoked key. Plain envelope: `{"error": "Unauthorized"}`. | | `403` | `permission_error` | `model_access_denied` | The key authenticated, but the user group does not grant access. | | `413` | — | — | JSON request body exceeds the 50 MB (52,428,800-byte) limit. Returned before route handling as `{"message":"Request body too large. Maximum size is 50 MB."}`. | | `429` | `rate_limit_error` | — | Org or user rate limit. Honors `Retry-After` when present. | | `500` | `server_error` | `internal_error` | Unexpected Kindo-side failure. | | `502` | `server_error` | `upstream_empty_body` | Upstream returned an empty body. | Chat Completions does not emit `404` from Kindo's edge — any `404` the client sees is upstream provider passthrough. ### `401` exception `401` uses the plain-string envelope `{"error": "Unauthorized"}` rather than the typed `{"error": {"message": "...", ...}}` shape. This matches Kindo's API-key middleware on `/v1/chat/completions` and `/v1/responses`. ## Mid-stream errors Once `stream: true` has flipped the response into SSE, errors arrive as an inline `event: error` SSE frame followed by `data: [DONE]`: ``` event: error data: {"error":{"message":"upstream stream interrupted","type":"server_error","code":"stream_error"}} data: [DONE] ``` Treat any `event: error` payload with `code: "stream_error"` as terminal. The outer HTTP status stays `200`. ## Provider passthrough When LiteLLM or the upstream provider returns a 4xx or 5xx with its own body, Kindo forwards that body verbatim (preserving the upstream `Content-Type`) so your client sees whatever shape the upstream produced. Kindo does not rewrap. The exception is `upstream_empty_body`, which Kindo emits when the upstream returned an HTTP error with no body so consumers always have a parseable JSON envelope. If you want a single consistent error shape across providers, parse on the outer HTTP status first; only fall back to body inspection when you need provider-specific detail. ## See also - [Authentication](/overview/authentication/) — `401` causes. - [Model catalog](/overview/model-catalog/) — `403` causes. - [Streaming](/apis/chat-completions/streaming/) — mid-stream error events. --- ## MCP API errors Source: https://docs.kindo.ai/apis/mcp/errors/ `POST /v1/mcp` can report failures at three levels. HTTP failures (auth, transport, content negotiation) come back as standard HTTP responses. JSON-RPC protocol failures come back in a JSON-RPC 2.0 error envelope with HTTP `200`. A valid tool call can instead return an MCP tool error result. ## HTTP-level errors | Status | Cause | | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `401` | Missing, malformed, or revoked Bearer token — an API key or a [Cross App Access](/admin/cross-app-access/) token. Response includes a `WWW-Authenticate` header; for an API key it points at the RFC9728 metadata document, for a Cross App Access token see [XAA envelopes](#xaa-envelopes) below. | | `403` | Either the API key is valid but the holder lacks the **Personal API Key Access** entitlement (no `WWW-Authenticate` header; ask your admin to enable it in the user's [group settings](https://app.kindo.ai/?settings=user-groups)), or a Cross App Access token is missing the `kindo.mcp` scope — see [XAA envelopes](#xaa-envelopes). | | `404` | Path not handled by the MCP gateway. Check the URL — the gateway accepts `POST /v1/mcp` and `GET /.well-known/oauth-protected-resource[/v1/mcp]`. | | `406` | `Accept` header missing `text/event-stream`. Add `Accept: application/json, text/event-stream`. | | `413` | JSON request body exceeds the 50 MB (52,428,800-byte) limit. Returned before MCP handling as `{"message":"Request body too large. Maximum size is 50 MB."}`. | | `5xx` | Unexpected gateway failure. Retries are safe; the gateway is idempotent for `tools/list` and `ping`. | ### `401` envelope ```http HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer resource_metadata="https://api.kindo.ai/.well-known/oauth-protected-resource" Content-Type: application/json { "error": "Unauthorized" } ``` The `WWW-Authenticate` header lets spec-conformant MCP clients auto-discover that the resource accepts bearer tokens in the `Authorization` header. Follow the `resource_metadata` URL for the RFC9728 document — see [Kindo extensions](/apis/mcp/kindo-extensions/) for the payload shape. ### XAA envelopes A [Cross App Access](/admin/cross-app-access/) token that fails verification — expired, the wrong audience or issuer, a bad signature — gets its own `401` with a `resource_metadata` pointer and the required scope: ```http HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer resource_metadata="https://api.kindo.ai/.well-known/oauth-protected-resource/v1/mcp", scope="kindo.mcp" Content-Type: application/json { "message": "unexpected \"aud\" claim value" } ``` A token missing the `kindo.mcp` scope gets a `403` instead: ```http HTTP/1.1 403 Forbidden WWW-Authenticate: Bearer error="insufficient_scope", scope="kindo.mcp", resource_metadata="https://api.kindo.ai/.well-known/oauth-protected-resource/v1/mcp", error_description="access token is missing required scope: kindo.mcp" Content-Type: application/json { "message": "access token is missing required scope: kindo.mcp" } ``` The gateway also rechecks a Cross App Access token's grant on every call, not just at issuance — the org's Cross App Access configuration was disabled, the client is no longer approved, the user was deactivated, or the feature is off for the organization. A recheck failure returns the same `401` status and the same `WWW-Authenticate` challenge as a verification failure above, so a caller can't distinguish the two from status or header alone. The body does differ in content: a recheck failure always reads `{ "message": "invalid_token" }`, while a verification failure carries the verifier's own message (as in the `aud` example above). ## JSON-RPC errors Protocol-level failures come back with HTTP `200` and a JSON-RPC 2.0 error envelope: | Code | Meaning | Typical cause | | -------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | `-32000` | Server error | Custom Kindo errors: `data.reason = "manual_approval_required"` when your organization requires human approval, `"credentials_unavailable"` for revoked/expired credentials, or `"dlp_uninspectable_content"` and `"dlp_unavailable"` — see [data loss prevention](/apis/mcp/dlp/). | | `-32601` | Method not found | Unsupported method (`resources/list`, `resources/read`) or a disabled integration's tools. | | `-32602` | Invalid params | Unknown tool name, gateway-level invalid parameters, or a tool your account is not authorized to call, including an organization approval setting of **Block**. | | `-32603` | Internal error | Unexpected gateway or tool execution failure (`Internal error` or `Tool execution failed`), or organization tool policy could not be loaded or validated (`Unable to load organization tool policy`, refused before execution). | ### `manual_approval_required` envelope ```json { "jsonrpc": "2.0", "id": 2, "error": { "code": -32000, "message": "This tool requires human approval and cannot run through external MCP. Use Kindo chat if manual approvals are enabled, or contact your administrator.", "data": { "reason": "manual_approval_required" } } } ``` Your [organization's approval setting](/admin/rbac-tool-actions/#organization-approval-setting) requires human approval, so the call is refused before execution. It creates no pending approval request, and retrying while that setting remains in force produces the same error. Personal tool settings cannot override this organization requirement. Use Kindo chat if manual approvals are enabled for your organization; otherwise, contact your administrator. These tools are also omitted from `tools/list`; see [Scoping](/apis/mcp/scoping/). The gateway checks approval before contacting the tool server. A misspelled tool name on an authorized server can therefore return `manual_approval_required`; this error does not confirm that the tool exists. ### `credentials_unavailable` envelope ```json { "jsonrpc": "2.0", "id": 2, "error": { "code": -32000, "message": "Credentials unavailable for integration linear", "data": { "reason": "credentials_unavailable", "integrationId": "linear", "hint": "Reconnect the integration in Settings > Integrations at https://app.kindo.ai/" } } } ``` The `hint` URL reflects your deployment's frontend URL — self-hosted customers see the URL configured for their deployment, not `app.kindo.ai`. ### DLP envelopes A result the organization's DLP policy will not let out comes back as a deliberate `-32000`, never a masked internal error: ```json { "jsonrpc": "2.0", "id": 3, "error": { "code": -32000, "message": "Result contains content that cannot be DLP-inspected; blocked by organization policy (fail-closed).", "data": { "reason": "dlp_uninspectable_content", "contentKind": "embedded binary resource" } } } ``` `data.reason` is `"dlp_unavailable"` instead when the policy itself could not be determined or applied. Both are terminal — retrying produces the same result. See [data loss prevention](/apis/mcp/dlp/) for which content is redacted and which is blocked. ## MCP tool errors A call to a valid `platform_*` tool with arguments that do not match the tool's schema returns a successful JSON-RPC response whose result is a `CallToolResult` with `isError: true`. The text content is a JSON error body: ```json { "content": [ { "type": "text", "text": "{\"code\":\"invalid_input\",\"message\":\"agent_id must be a valid UUID\",\"retryable\":false}" } ], "isError": true, "_meta": { "kindo.dev/platform-tool-error": 1 } } ``` Read the text value as JSON to inspect the error. The versioned `_meta` marker identifies this Platform tool error format. Directory and department tools also use this format when a tool-specific access check fails: `code` is `not_found`, and `message` names the missing admin role, department access, or plan requirement. This can happen when calling a previously discovered tool after access is revoked; it does not necessarily mean the requested user or department is missing. ## Common failure modes - **`406 Not Acceptable: Client must accept both application/json and text/event-stream`** — Your client is missing `Accept: application/json, text/event-stream`. Add it. MCP SDKs and `mcp-remote` set it for you; only raw HTTP callers hit this. - **`-32000 credentials_unavailable`** — Your connection's credentials have been revoked or have expired. Reconnect the integration in **Settings > Integrations** at [https://app.kindo.ai/](https://app.kindo.ai/) (or your self-hosted deployment's equivalent). - **`-32602 Invalid params` with "tool not found"** — Check the tool name prefix. Tools are named `{integration}_{name}` (underscore, not colon). For example, `linear_get_issue`, not `linear:get_issue`. Integration IDs containing hyphens still use one underscore as the separator: `sap-s4hana-rfc_invoke_bapi`. - **`401` with `WWW-Authenticate: Bearer`** — The API key is missing, malformed, or revoked. Verify the key is present and well-formed. The gateway does **not** fall back from a malformed `Authorization` header to `x-api-key`. - **`401` with `{ "message": "invalid_token" }` from a Cross App Access token** — The token verified, but its grant is no longer valid: the org's configuration was disabled, the client is no longer approved, the user was deactivated, or the feature is off for the organization. The response doesn't say which — see [XAA envelopes](#xaa-envelopes). - **Empty `tools/list`** — Your account has no integrations connected, or you passed an `?integrations=` filter that matches nothing your account can reach, or your organization's approval setting is **Ask before running** or **Block**. See [Scoping](/apis/mcp/scoping/). ## See also - [Quickstart](/apis/mcp/quickstart/) — first successful call. - [Request shape](/apis/mcp/request-shape/) — supported methods and required headers. - [Scoping](/apis/mcp/scoping/) — why a tool may not appear for your account. --- ## Messages API errors Source: https://docs.kindo.ai/apis/messages/errors/ `POST /v1/messages` returns errors using Anthropic's standard error envelope. Errors that originate at Kindo's edge use the table below; errors that originate upstream are forwarded verbatim with the upstream body and content-type. ## Envelope ```json { "type": "error", "error": { "type": "invalid_request_error", "message": "max_tokens is required" } } ``` ## Status / type / code reference | Status | Type | Common codes | Cause | | ------ | ----------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------- | | `400` | `invalid_request_error` | — | Body fails schema validation (missing `max_tokens`, malformed `messages`, etc.), references a missing model, or is blocked by org DLP policy (e.g. binary document attachments that cannot be redacted). | | `401` | `authentication_error` | — | Missing, malformed, or revoked API key. | | `403` | `permission_error` | — | The key authenticated, but the user group does not grant access. | | `413` | — | — | JSON request body exceeds the 50 MB (52,428,800-byte) limit. Returned before route handling as `{"message":"Request body too large. Maximum size is 50 MB."}`. | | `429` | `rate_limit_error` | — | Org or user rate limit. Honors `Retry-After` when present. | | `500` | `api_error` | — | Unexpected Kindo-side failure. | | `502` | `upstream_empty_body` | — | Upstream returned an empty body. Example: `{"type":"error","error":{"type":"upstream_empty_body","message":"..."}}` | ### `401` note On `/v1/messages`, `401` returns Anthropic's typed `authentication_error` envelope rather than the plain-string `{"error":"Unauthorized"}` shape used on `/v1/chat/completions` and `/v1/responses`. ```json { "type": "error", "error": { "type": "authentication_error", "message": "Unauthorized" } } ``` The Messages API uses Anthropic's typed envelope, which includes `type` but not a separate `code` field. (This differs from the Chat Completions and Responses APIs, where errors include both `type` and `code`.) ## Mid-stream errors If `stream: true` has flipped the response into SSE, errors arrive as an `event: error` frame before the stream terminates: ``` event: error data: {"type":"error","error":{"type":"overloaded_error","message":"upstream overloaded"}} ``` The outer HTTP status stays `200` because headers were already flushed when streaming began. SDK consumers should treat any mid-stream `error` event as terminal. ## Provider passthrough When an upstream provider (Anthropic, etc.) returns a 4xx or 5xx with its own body, Kindo forwards that body verbatim, preserving the upstream `Content-Type`. Your client sees whatever shape the upstream produced. The exception is `upstream_empty_body`, which Kindo emits (as the value of `error.type`) when the upstream returned an HTTP error with no body so consumers always have a parseable JSON envelope. If you want a single consistent error shape across providers, parse on the outer HTTP status first; only fall back to body inspection when you need provider-specific detail. ## See also - [Authentication](/overview/authentication/) — `401` causes. - [Model catalog](/overview/model-catalog/) — `403` causes. - [Streaming](/apis/messages/streaming/) — mid-stream error events. --- ## Responses API errors Source: https://docs.kindo.ai/apis/responses/errors/ `POST /v1/responses` returns errors in OpenAI's Responses error envelope. Errors that originate at Kindo's edge use the table below; errors that originate upstream are forwarded verbatim with the upstream body and content-type. ## Envelope ```json { "error": { "message": "Model 'unknown-model' not found.", "type": "invalid_request_error", "code": "model_not_found" } } ``` ## Status / type / code reference | Status | Type | Common codes | Cause | | ------ | ----------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | `400` | `invalid_request_error` | `invalid_body`, `model_not_found`, `unsupported_parameter`, `unsupported_tool_type`, `mutually_exclusive_parameters` | Body fails schema validation, references a missing model, asks for a feature not yet implemented (e.g. `background: true` on a Kindo-managed conversation), includes an `mcp` tool on a stateless request (set `store: true` or pass `conversation`), or sets both `previous_response_id` and `conversation`. | | `400` | `invalid_request_error` | `agent_version_pinning_unsupported`, `agent_inputs_unmappable` | Agent invocations (`model: "agent/"`): version pinning (`agent/@`) is not available yet; or the agent's declared inputs cannot be bound to a freeform `input` — it declares two or more named inputs, or a single non-text input (use [`POST /v1/agents/runs`](/apis/agents/runs/) with named inputs instead). `model_not_found` also covers unknown **and inaccessible** agent IDs — indistinguishable from an unknown model. See [the agent invocation guide](/guides/invoke-agents-responses-api/#errors). | | `401` | — | — | Missing, malformed, or revoked API key. The body is a plain string envelope: `{"error": "Unauthorized"}`. | | `403` | `permission_error` | `model_access_denied` | The key authenticated, but the user group does not grant access to the model. | | `404` | `not_found` | — | E.g. `GET /v1/responses/{id}` for a response that does not exist or has been pruned. | | `404` | `invalid_request_error` | `previous_response_not_found` | `previous_response_id` references a response that is unknown, inaccessible, or was not stored (stateless responses cannot be chained). Matches OpenAI's wire shape for this error. | | `413` | — | — | JSON request body exceeds the 50 MB (52,428,800-byte) limit. Returned before route handling as `{"message":"Request body too large. Maximum size is 50 MB."}`. | | `429` | `rate_limit_error` | — | Org or user rate limit exceeded. Honors the `Retry-After` header when present. | | `500` | `server_error` | `internal_error` | Unexpected Kindo-side failure. | | `502` | `server_error` | `upstream_empty_body` | Upstream LiteLLM / provider returned an empty response. | ### `401` exception The `401` envelope is the plain-string form `{"error": "Unauthorized"}` rather than the typed `{"error": {"message": "...", "type": "...", "code": "..."}}` shape. This matches how Kindo's API-key middleware reports auth failures across the Chat Completions and Responses APIs. ## Mid-stream errors If `stream: true` is set and an error occurs after the SSE has started, Kindo emits an `error` event followed by `response.failed`: ``` event: error data: {"error":{"message":"upstream stream interrupted","type":"server_error","code":"stream_error"}} event: response.failed data: {"id":"resp_abc123","status":"failed","object":"response"} ``` The outer HTTP status stays `200` because headers were already flushed when streaming began. SDK consumers should treat any mid-stream `error` event as terminal. See [Streaming](/apis/responses/streaming/) for the full SSE protocol. ## Provider passthrough When an upstream provider (Anthropic, OpenAI, etc.) returns a 4xx or 5xx with its own body, Kindo forwards that body verbatim, preserving the upstream `Content-Type`. Your client sees whatever shape the upstream produced — Kindo does not rewrap. The exception is `upstream_empty_body`, which Kindo emits when the upstream returned an HTTP error with no body so consumers always have a parseable JSON envelope to log. If you want a single consistent error shape across providers, parse on the outer HTTP status first; only fall back to body inspection when you need provider-specific detail. ## See also - [Authentication](/overview/authentication/) — `401` causes. - [Model catalog](/overview/model-catalog/) — `403` causes. - [Streaming](/apis/responses/streaming/) — mid-stream error events. - [Response store pruning](/apis/responses/request-shape/#response-store-pruning) — `404` causes. --- ## Child Agent Output Size Limits Source: https://docs.kindo.ai/best-practices/child-agent-output-limits/ When one Kindo agent invokes another as a child agent, the child's result is summarized for the parent agent and the verbatim transcript is parked alongside the run — fetched on demand rather than pushed back inline. Kindo bounds the size of any transcript the parent reads so multi-agent setups stay reliable even when the child produces large amounts of evidence — log dumps, Splunk result sets, verbose API responses, or any other bulky output. ## What the parent receives on completion When a child agent finishes, the parent's tool call result contains a single structured object: - A short **summary** of what the child did and concluded. - A list of **key findings** — the most important facts, data points, or decisions. - A **confidence** signal — high, medium, or low. The summary, key findings, and confidence are the distilled, high-signal channel and the parent's primary input. The full conversation transcript is **not** included in this result. ## Reading a child's transcript When the parent agent needs to inspect what a child actually did — to spot-check a finding, quote evidence, or debug a confusing result — it calls the **`get_child_agent_transcript`** tool, passing the child's `workflowRunId`. The tool works in two steps: 1. **Outline first.** The default response is a compact map of the transcript: how many turns it has, its total size, and one line per turn — who spoke and a short preview of what was said. This costs the parent agent very little context, however large the child's transcript is. 2. **Read what matters.** The parent agent then requests the full text of just the turns it needs — typically the child's closing turns, or the span around a finding it wants to verify. The text comes back as role-tagged turns (`[USER]`, `[ASSISTANT]`, etc.) in conversation order. Children that are still running return their transcript so far. A parent can only read transcripts of children it spawned. ## Access control for child-agent tools `list_child_agents` and `get_child_agent_transcript` are scoped to the child agents spawned by the parent. A parent sees and can read transcripts of only the agents it spawned itself; children spawned elsewhere in your workspace are not visible to it. ## What "bounded" means Both steps are bounded, so a parent agent's context window is protected no matter what the child produced: - The **outline** is compact by construction — previews are one short line per turn, and extremely long transcripts list their opening and closing turns with the middle elided (those turns remain readable on request). - Each **read** returns at most **48 KiB** (49,152 bytes, measured as UTF-8 byte length) of transcript text. Reads are assembled from **whole turns**: when the requested span doesn't fit in one response, the read stops at a turn boundary and tells the parent agent where to continue. No content is ever lost to the cap — larger spans just take more than one read. (The per-read text limit sits below the parent's overall inline tool-result budget so the read, once wrapped in its JSON response envelope, is always delivered in full rather than parked in external storage.) The one exception is a single turn that is by itself larger than one read — for example a turn into which a child pasted an entire log dump. In that case Kindo keeps the **beginning and the end** of that turn, drops the middle (cutting on safe character boundaries), and signals the cut explicitly — both inline, with a marker at the cut, and as a structured `transcript_overflow` field: ```json { "originalBytes": 196608, "keptBytes": 49152 } ``` Both counts are UTF-8 byte lengths: `originalBytes` is the turn's full size before truncation, `keptBytes` is the size of the returned text. The field is present **only when a turn was cut**, so a parent agent can branch on it to detect truncation programmatically rather than parsing the inline marker. ## Designing child agents that surface findings reliably ### Make the final step produce a tight, decision-ready summary The summarizer that builds the parent-visible result works on the child's whole conversation. The more compact and structured the child's final messages are, the more useful the parent-visible summary and key findings will be. A child agent whose last step is "produce a short structured conclusion" is far more useful to a parent than one whose last step dumps a thousand lines of evidence. ### Treat the transcript as evidence, not as context Design parent prompts to read the summary and key findings first, and reach for `get_child_agent_transcript` only when a specific detail is needed. The outline-then-read flow supports this naturally: the parent agent can check the outline cheaply, then pull only the turns that matter, instead of reading the whole transcript front to back. ### Externalize bulk evidence If a child agent's job is to produce a large artifact — a report, an export, a long log bundle — write it to an external system (Notion, Google Drive, a ticketing system, an S3 bucket via an API Action step) and have the child return a pointer plus a short summary. The parent agent can fetch the full artifact via an integration when needed, rather than carrying it in conversation context. ### Read further instead of reasoning from partial data A transcript larger than one read is not lost — the parent agent can keep reading until it has what it needs. The `transcript_overflow` field appears only when one giant turn had to be cut; in that case the summary, key findings, and confidence value remain the high-signal channel for that turn's content. If a child routinely produces single turns too large to read whole, narrow the child's prompt or split the work across more focused child runs. ## When child agents are the wrong tool Child agents are designed for delegating a bounded task and getting a summarized answer back. If an agent's natural shape is "produce a large dataset and hand it to the next step verbatim," consider keeping the work inside one agent — using multiple steps in a single run so intermediate data never has to cross the parent/child boundary — or coordinating via an external system so bulk data is passed through storage rather than conversation context. ## Related - [Multi-Agent Coordination](/best-practices/multi-agent-coordination/) — patterns for splitting work across parent and child agents. - [Working with Large Context](/best-practices/working-with-large-context/) — managing context window pressure in any agent. --- ## MCP API — Kindo extensions Source: https://docs.kindo.ai/apis/mcp/kindo-extensions/ By default, `POST /v1/mcp` is a stock spec-conformant MCP server. Kindo adds two opt-in extensions on top of the spec: 1. **Integration filter** — narrow `tools/list` to a specified subset of your integrations. 2. **Connection management REST endpoints** — list connections and select a preferred connection for each integration. Use these when you need them; ignore them and the gateway still works. ## 1. Integration filter Pass an explicit allow-list as either a query parameter or a header, and `tools/list` returns only those integrations' tools: ```bash # Query parameter curl -X POST "https://api.kindo.ai/v1/mcp?integrations=linear,github" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -d '{ "jsonrpc": "2.0", "method": "tools/list", "id": 1 }' # Header (equivalent) curl -X POST https://api.kindo.ai/v1/mcp \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -H "x-kindo-integrations: linear,github" \ -d '{ "jsonrpc": "2.0", "method": "tools/list", "id": 1 }' ``` The filter narrows the set you would otherwise see — it never grants access. If your account can't reach `github` and you ask for `?integrations=linear,github`, you get only Linear tools back (no error). Both forms accept comma-separated values; the query parameter also accepts the array repetition form (`?integrations=linear&integrations=github`). If both query and header are present, the query parameter wins. For the underlying rules — which integrations your account can reach in the first place — see [Scoping](/apis/mcp/scoping/). ## 2. Connection management REST endpoints These are regular Kindo REST endpoints, not MCP methods. Use them to list your connections and select a single preferred connection for an integration. Your active connections also serve as personal defaults for Kindo chats and agents. ### `GET /v1/integrations/connections` Returns all integration connections accessible to the API-key holder, including shared group connections. Each entry's `is_preferred` field indicates whether it belongs to your active connection set for that integration. Several connections can have `is_preferred: true`. ```bash curl https://api.kindo.ai/v1/integrations/connections \ -H "Authorization: Bearer $KINDO_API_KEY" ``` ```json { "data": [ { "id": "conn_abc123", "display_name": "Charlie's Linear", "integration_config_id": "linear", "nango_connection_id": "nango_conn_1", "is_preferred": true, "created_at": "2026-03-10T18:45:23.000Z" } ] } ``` ### `POST /v1/integrations/connections/{integration_id}/default` Sets the specified connection as your only active connection for an integration. `{integration_id}` is the same value as `integration_config_id` returned by the list endpoint (e.g., `linear`, `github`). This replaces your entire active connection set for that integration with the specified connection. It also changes the personal defaults used by Kindo chats and agents that do not have pinned connections. ```bash curl -X POST https://api.kindo.ai/v1/integrations/connections/linear/default \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -d '{ "connection_id": "conn_abc123" }' ``` ```json { "integration_config_id": "linear", "connection_id": "conn_abc123" } ``` | Code | Meaning | | ----- | ---------------------------------------------------------------------------------------------------------- | | `400` | Invalid request body — `connection_id` is required, or `connection_id` does not belong to `integration_id`. | | `403` | Forbidden — you are not authorized to use this connection. | | `404` | Integration connection not found (or you do not have access to view it). | Once set, the default flows through to `tools/call` resolution. See [Scoping → Default-connection resolution](/apis/mcp/scoping/#default-connection-resolution) for where your preferred connection sits in the resolution chain. ## RFC9728 discovery Kindo serves an [RFC 9728](https://www.rfc-editor.org/rfc/rfc9728) Protected Resource Metadata document so MCP clients can auto-discover the bearer-token authentication mode: - `GET https://api.kindo.ai/.well-known/oauth-protected-resource` - `GET https://api.kindo.ai/.well-known/oauth-protected-resource/v1/mcp` Both return the same document, with the second path provided for clients that follow RFC9728's path-scoped lookup rule. The body confirms bearer-header mode: ```json { "resource": "https://api.kindo.ai/v1/mcp", "bearer_methods_supported": ["header"], "authorization_servers": ["https://api.kindo.ai/oauth"], "scopes_supported": ["kindo.mcp"] } ``` Tokens are plain bearer tokens — Kindo does not support DPoP-bound tokens on this resource. The `authorization_servers` entry is a real issuer, not a placeholder — but a narrow one. It supports only the ID-JAG `urn:ietf:params:oauth:grant-type:jwt-bearer` grant, and only for an organization with [Cross App Access](/admin/cross-app-access/) enabled: an admin approves an Okta app to redeem an Okta-issued ID-JAG for a short-lived Kindo MCP access token scoped to `kindo.mcp`, on behalf of a signed-in user. It does not support `authorization_code` or dynamic client registration, and static Kindo API keys remain the default way to call `/v1/mcp` — most integrations never need this issuer. ## See also - [Scoping](/apis/mcp/scoping/) — the deeper read on what tools your account can reach. - [Errors](/apis/mcp/errors/) — the `WWW-Authenticate` header tied to RFC9728 discovery. --- ## Responses API — Chat Actions extensions Source: https://docs.kindo.ai/apis/responses/kindo-extensions/ By default, `POST /v1/responses` behaves like stock OpenAI Responses — no hidden system prompt, no auto-injected tools, no server-side state. **Chat Actions** are three orthogonal opt-ins layered on top: 1. **Curated system prompt** — let Kindo prepend its operational prompt to your `instructions`. 2. **Kindo-hosted tools** — call `kindo_shell`, `kindo_web_search`, and the rest of the Kindo tool catalog. 3. **Stateful conversations** — let Kindo persist responses server-side so subsequent turns chain off them. Each is independent. Mix and match. (If you find yourself combining all three to recreate one of your stored agents, invoke the agent itself instead: `model: "agent/"` — see [Invoke Kindo agents](/guides/invoke-agents-responses-api/).) ## 1. Curated system prompt ```json { "model": "claude-sonnet-4-5-20250929", "input": "Investigate the crashloop on api-prod-2.", "kindo": { "system_prompt": "agent_default" } } ``` | `kindo.system_prompt` value | Behavior | | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `"agent_default"` | Kindo prepends its curated operational prompt to your `instructions`. On stateful requests (`store: true`, `conversation`, or `previous_response_id`), Kindo includes service-managed context with the composed prompt and supplies the current date and timezone with the triggering input. | | `"off"` (default) | Default `"off"` does not prepend Kindo's curated prompt or add service-managed prompt or input context; client instructions and input remain unchanged. | On compacted stateful turns, a service-managed compaction hint may be appended; it follows composed instructions and other service-managed prompt guidance. With `off`, caller instructions are preserved; the separate compaction hint may still be added. If you also pass `instructions`, Kindo prepends its prompt and your `instructions` flow after it. On stateful requests, Kindo also includes service-managed context with the managed prompt and triggering input. Use this when you want Kindo's curated agent behavior on top of your own task guidance. ## 2. Kindo-hosted tools You have two ways to opt in to Kindo's hosted tools. ### 2a. The full bundle (`kindo_tools` sugar) ```json { "model": "claude-sonnet-4-5-20250929", "input": "...", "tools": [{ "type": "kindo_tools" }] } ``` `{type: "kindo_tools"}` is documented sugar. At request time Kindo expands it into the current set of individual `kindo_` entries the model can call. ### 2b. Individual tools Pick the ones you actually want: ```json { "model": "claude-sonnet-4-5-20250929", "input": "...", "tools": [{ "type": "kindo_shell" }, { "type": "kindo_web_search" }] } ``` The full hosted-tool catalog: | Tool type | Purpose | | -------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | `kindo_shell` | Run a sandboxed shell command on a Kindo-managed agent workstation. | | `kindo_web_search` | Search the web. | | `kindo_browser_control` | Drive a headless browser session. | | `kindo_browser_screenshot` | Capture a screenshot of the current page. | | `kindo_browser_list_network_requests` | List network requests captured by the browser tool. | | `kindo_browser_export_request_as_curl` | Export a captured request as a runnable curl command. | | `kindo_file_read` | Read a file the agent has access to. | | `kindo_file_write` | Write to a file the agent has access to. | | `kindo_file_list` | List files in an accessible directory. | | `kindo_web_download` | Download a URL to the agent workspace. | | `kindo_files_download_original` | Fetch the original binary of a previously stored file. | | `kindo_files_download_as_text` | Fetch a previously stored file as decoded text. | | `kindo_file_share_with_user` | Share a file with a Kindo user. The download URL is in the `kindo_call` item's `output`, not in assistant text. | | `kindo_vpn_connect` | Connect the agent's network egress to an organization VPN. | | `kindo_vpn_disconnect` | Disconnect from the organization VPN. | | `kindo_dashboard_generate` | Generate a Kindo dashboard. | | `kindo_dashboard_update` | Update a Kindo dashboard. | | `kindo_knowledge_store_search` | Search a Kindo knowledge store. | Tool types not in this list are forwarded verbatim to LiteLLM (passthrough) — useful when a new upstream tool type ships before our docs do. You can mix Kindo-hosted tools with stock OpenAI types (`function`, `mcp`, `web_search`, `file_search`, `computer`, …) in the same `tools` array. `tool_choice` (including `allowed_tools`) applies uniformly. Note that `mcp` tools require the stateful path (`store: true` or `conversation` — see [Stateful conversations](#3-stateful-conversations)); stateless requests including them are rejected with `400 unsupported_tool_type`. ## 3. Stateful conversations Stock OpenAI Responses on `/v1/responses` is stateless by default. Kindo offers two forms of stateful continuation: `conversation` (the Kindo-managed conversation ID) and `previous_response_id` (chain from a previous stored response, as the OpenAI SDK and Codex do by default). They are mutually exclusive — two spellings of the same continuation intent. ### `conversation` ```json { "model": "claude-sonnet-4-5-20250929", "input": "Continue the investigation.", "store": true, "conversation": "conv_xyz789" } ``` `conversation` is the Kindo-managed equivalent and is what the existing `/v1/responses` flow returns in `response.conversation.id`. ### `previous_response_id` ```json { "model": "claude-sonnet-4-5-20250929", "input": "Continue the investigation.", "previous_response_id": "resp_xyz789_1" } ``` Pass the `id` of a previous **stored** response (one created with `store: true` or `conversation`) to continue the conversation that produced it — this is how the OpenAI SDK and Codex chain turns by default. It is equivalent to passing that conversation's ID via `conversation`, and the two parameters are mutually exclusive (`400 mutually_exclusive_parameters` if both are set). Unknown, inaccessible, or unstored response IDs return `404 previous_response_not_found` — stateless responses are never persisted, so their IDs cannot be chained. Kindo conversations are linear: chaining from an older response continues the full conversation history rather than forking at that response. With either continuation mechanism the full prior history is loaded server-side and sent to the model — send only the new turn in `input`. Use `conversation` when you have a Kindo-managed conversation ID; use `previous_response_id` when you have the last response's ID (the OpenAI-native pattern); otherwise start a new request with `store: true` and use the returned `conversation.id` or response `id` for subsequent turns. Long stored conversations are compacted automatically. When the accumulated history approaches the model's context window, Kindo summarizes earlier turns server-side before sending the conversation to the model, so a long-running stored conversation keeps working without manual truncation on your side. Compaction is applied transparently — it does not change the response shape or emit a separate streaming event. ## Putting it together If you previously relied on `tools: [{type: "kindo_tools"}]` as a mode switch that implicitly turned on the curated prompt and statefulness, this is the equivalent explicit recipe: ```json { "model": "claude-sonnet-4-5-20250929", "input": "...", "store": true, "kindo": { "system_prompt": "agent_default" }, "tools": [{ "type": "kindo_tools" }] } ``` Three opt-ins, all visible in the request body. None are implicit. ## See also - [Chat Actions overview](/guides/chat-actions/) — conceptual overview of the three opt-ins. `/v1/chat/completions` and `/v1/messages` are stock OpenAI / stock Anthropic and do not accept these extensions. - [Quickstart](/apis/responses/quickstart/) — stock-OpenAI baseline, no extensions. - [Request shape](/apis/responses/request-shape/) — every standard field, plus the `kindo` block. - [Tool use](/apis/responses/tool-use/) — function-tool round trip; mixes with hosted tools in the same `tools` array. --- ## Kindo Best Practices Source: https://docs.kindo.ai/best-practices/ This section collects practical guidance for running Kindo well in production. It focuses on two areas that become increasingly important as usage scales: how you manage agent configuration over time, and how you keep large-context agents reliable. ## In This Section ## Security Scenarios Step-by-step guides for common security automation scenarios: ## When to Use These Guides - **Rolling out production agents** that multiple teams will depend on - **Creating shared standards** for prompt reviews, change management, and ownership - **Improving reliability** for agents that process large documents, logs, or tool outputs - **Preparing self-managed deployments** where documentation and governance need to be explicit --- ## MCP API data loss prevention Source: https://docs.kindo.ai/apis/mcp/dlp/ When your organization has gateway DLP enabled, tool data your MCP client fetches through `POST /v1/mcp` is subject to your organization's data loss prevention (DLP) policy before it leaves Kindo: the gateway redacts what the policy says to redact, and refuses to return content it cannot inspect. For organizations without gateway DLP enabled, results pass through unchanged. This is the same DLP engine and the same redaction representation you see on the Kindo chat and model APIs. What differs is how the policy is selected. ## The policy is provider-neutral On the chat and model APIs, DLP resolves against the model and provider that will serve the request. An external MCP client calls its own model directly, so the gateway has no downstream model — and it will not take one from the client, because a client-declared model could be used to select a weaker policy. Instead, the gateway resolves the policy from what it can verify about the caller: - your organization's DLP settings for the **integration** the tool belongs to; - the DLP settings of every **user group** you belong to; - and the DLP settings of the **individual tool** being called, which take precedence over the integration's when both configure filters. The result is the **strictest** combination: if any of those levels redacts an entity type, it is redacted. Because the policy is resolved on every call, an administrator's change takes effect on your next request — there is nothing to refresh or reconnect. Administrators configure this under [**Settings > Integrations**](https://app.kindo.ai/?settings=security/integrations-and-tools). Organizations with no integration-level DLP configured see results returned exactly as the upstream server produced them. ## What gets redacted | Part of the result | Treatment | | ------------------ | --------- | | `text` content items | Redacted. | | `structuredContent` | Every string value is redacted, at any depth. The JSON structure is preserved. | | Embedded resources with inline `text` | The text is redacted. | | `resource_link` items | `name`, `title`, and `description` are redacted. The `uri` is returned unchanged so the link keeps working. | | Results with `isError: true` | Redacted like any other result — error text is tool output and often quotes the record that failed. | | `image` content items | Returned unchanged. Images carry no text the DLP engine reads. | Redactions use the DLP engine's entity placeholders — an email address becomes ``, a US social security number becomes ``, and so on. Custom phrase and pattern filters your administrator has configured are replaced with `REDACTED`. ## What gets blocked Some content cannot be inspected: Kindo cannot read it, so it cannot redact it, and returning it would make an opaque payload the way around the policy. When DLP is enabled for the integration, a result containing any of the following is **blocked in full** rather than partially returned: - embedded resources carrying a binary `blob`; - `audio` content items; - any content type Kindo does not recognize. The call fails with a JSON-RPC `-32000` error and `data.reason = "dlp_uninspectable_content"`. If DLP is not enabled for the integration, this content is returned normally. A DLP outage is treated the same way: if the policy cannot be determined or applied, the call fails with `-32000` and `data.reason = "dlp_unavailable"`. Kindo does not fall back to returning unredacted data. Both envelopes are documented in [MCP API errors](/apis/mcp/errors/). ## What this means for your client - **Handle `-32000` as terminal, not retryable.** A block is a policy decision. Retrying returns the same error; the fix is a policy change or a tool that returns inspectable content. - **Expect placeholders in tool output.** A tool that returns an email address may return `` instead. Do not treat a placeholder as a literal value to write back. - **Prefer tools that return text or structured content** over tools that return binary attachments, if your organization enables gateway DLP — text and structured content are redactable, attachments are not. --- ## 'Memory and Persistence Patterns' Source: https://docs.kindo.ai/best-practices/memory-and-persistence/ Kindo agents use run-scoped memory by default, and agent owners can enable a per-user, per-agent persistent sandbox when files and state should carry across that user's runs. This guide covers the built-in persistence options and patterns for extending memory with external systems. :::note[Looking for the Memory feature?] This page is about **file and state persistence** — sandboxes, knowledge stores, and external systems that carry an agent's working data across runs. Kindo's [Memory feature](/features/memory/) (the Memory Engine) is a separate capability: it captures durable *facts* from conversations and recalls them in later ones. See the [glossary](/reference/glossary/) for how the memory-related terms differ. ::: ## Overview Kindo scopes agent memory to individual runs by default. The platform also offers an opt-in **per-user, per-agent persistent sandbox** for agents whose owners turn it on, and the patterns below extend memory further using integrations and external systems: | Pattern | Scope | Read | Write | Durability | Complexity | | ------------------------------ | ---------------------- | ---------------- | ------------------ | ------------------ | ---------- | | **Knowledge Store** | Org-wide | ✅ | ❌ (manual upload) | High | Low | | **Sandbox** | Single run | ✅ | ✅ | None (ephemeral) | Low | | **Persistent Sandbox** | Per-user, per-agent | ✅ | ✅ | Across runs | Low | | **External System** | Cross-workflow | ✅ via API/MCP | ✅ via API/MCP | High | Medium | | **Git Repository** | Team-wide | ✅ (manual sync) | ✅ (commits) | Very high | Medium | ## Pattern 1: Knowledge Store — Static Reference Data ### When to Use - Agent needs access to policies, runbooks, compliance frameworks, threat intel feeds - Data changes infrequently (updated by humans, not agents) ### How It Works 1. Upload files to Knowledge Store (PDF, markdown, CSV, etc.) 2. Attach Knowledge Store to agent(s) — multiple agents can share the same files 3. Agent references files via Library Search or direct attachment ### Limitations - **Read-only:** Agents cannot update Knowledge Store files - **Manual upload:** No API for programmatic updates - **File size limits:** Apply to uploads ### Example Upload your organization's incident response playbook as a PDF. Attach it to all SOC agents — they reference it when classifying incidents. ## Pattern 2: Sandbox — Ephemeral Per-Run Memory ### When to Use - Intermediate results within a multi-step workflow - Large data that needs filtering before LLM processing - Temporary files (scripts, extracted data, reports) ### How It Works 1. API Action steps and tool outputs write to the sandbox automatically when large 2. LLM steps can read/write sandbox files using shell commands 3. Data persists for the duration of the workflow run, then is discarded ### Cross-Reference → See [Working with Large Context](/best-practices/working-with-large-context/) for detailed sandbox techniques ### Limitations - **Ephemeral:** Gone after the run ends - **Not shareable:** Between agents or runs ## Pattern 3: Persistent Sandbox — Per-User, Per-Agent Memory Across Runs ### When to Use - Agent needs to keep files, scripts, or working state across runs (e.g., a long-running research agent that builds up notes, or a code agent that maintains a working checkout) - You want the durability of a sandbox without writing to an external system ### How It Works 1. The agent owner opens the agent's settings and turns on **Persistent Workspace**. 2. Each user who runs that agent gets their own isolated persistent sandbox — files and state created during a run are still there on that user's next run of the same agent. 3. When the toggle is off, behavior is unchanged from Pattern 2 (single-run, ephemeral). ### Per-User Isolation — Important Persistent sandboxes are scoped to **(user, agent)**, not just to the agent. If you and a teammate both run the same shared agent with persistent workspace enabled, you each get your own private sandbox. Files written during your runs are **not** visible to your teammate, and vice-versa. This is intentional. It means: - **Sharing the agent does not share its filesystem state.** A user who runs a shared agent does not see anything other users wrote into that agent's sandbox. - **Concurrent runs by the same user share the same sandbox.** Two simultaneous runs by you against the same persistent-sandbox agent will see and write to the same filesystem. Plan for that the same way you'd plan for two processes sharing a directory. - **Team-level shared persistent sandboxes are not supported.** If multiple users genuinely need to share state across runs, use Pattern 4 (External Systems) so the read/write path is auditable and consent-based. ### Limitations - **Per-user, not per-team:** see above. - **Sharing an agent does not share its sandbox state.** Recipients of a shared persistent-sandbox agent get a fresh sandbox keyed to their user identity on first run. - **Availability:** the feature requires a sandbox provider that supports persistence, such as OpenShell or Daytona. On other providers, the Persistent Workspace toggle does not appear. ### Cross-Reference → See [Working with Large Context](/best-practices/working-with-large-context/) for sandbox techniques that apply to both ephemeral and persistent sandboxes. ## Pattern 4: External Systems — Persistent Cross-Workflow Memory ### When to Use - Agent needs to remember findings from previous runs - Multiple agents need to share state (e.g., investigate → remediate pipeline) - Audit trail required for agent decisions ### How It Works **Write:** Use API Action steps to create/update records in external systems: - Create Jira tickets with structured findings - Post to Slack channels as a persistent log - Write to Notion/Confluence pages - Push to Google Sheets for tabular data **Read:** Use API Action steps at the start of a workflow to load previous state: - Query Jira for tickets created by previous agent runs - Read from a designated Notion "agent memory" page - Fetch latest entries from a Google Sheet ### Architecture ``` Agent run N writes structured JSON to Notion page ↓ Agent run N+1 reads that page as first step ↓ Processes new data ↓ Writes updated state back ``` ### Example: Vulnerability Tracking Over Time 1. Weekly scan agent writes top 10 findings to a Notion database 2. Each row: CVE, severity, first seen date, current status, remediation owner 3. Next week's run reads the database → compares with new scan → updates statuses → adds new findings → flags regressions ### Design Considerations - **Choose familiar systems:** Use tools your team already uses (don't add new tools just for agent memory) - **Use structured formats:** JSON in Jira custom fields, database rows in Notion — so agents can parse reliably - **Include provenance:** Timestamps and run IDs so you can trace which agent run wrote what - **Consider rate limits:** Especially for high-frequency scheduled agents hitting external APIs ## Pattern 5: Git Repository — Versioned Agent Configuration ### When to Use - Managing production agents across a team - Need change history, rollback capability, peer review for prompt changes - Regulatory/compliance requirement for audit trail of agent behavior changes ### How It Works Maintain a Git repository with agent configurations: ``` agents/ alert-triage/ README.md # Purpose, owner, version system-prompt.md # The agent's system prompt steps.yaml # Step definitions and configuration CHANGELOG.md # Version history vuln-reporter/ ... knowledge-store/ playbooks/ compliance-frameworks/ ``` When making changes: update in Git → review via PR → apply to Kindo manually. → See [Prompt and Agent Configuration Management](/best-practices/prompt-and-agent-configuration/) for full versioning and governance guidance. ### Limitations - **Manual sync:** No Git → Kindo auto-deploy pipeline - **Recreation required:** Agent configuration must be recreated in the Kindo UI after Git changes ### Customer Evidence A customer managing 6 production agents maintains a GitHub repository with versioned prompts, changelogs, and a meta-agent that updates the repo README — proving this pattern works at scale. ## Choosing a Pattern | Question | Pattern | | -------------------------------------------------------------- | -------------------------------- | | Agent needs static reference data? | Knowledge Store | | Need intermediate results within one run? | Sandbox | | Same user runs the agent repeatedly and needs files to stick? | Persistent Sandbox | | Multiple users need to share state across runs? | External System | | Need team-wide agent config management? | Git Repository | | Multiple agents share state? | External System | | Compliance requires audit trail? | Git Repository + External System | ## Limitations Kindo's built-in persistent memory is the per-user, per-agent Persistent Sandbox described above. It does not provide a team-shared persistent memory store that agents can write to and read from across users; the patterns above (External Systems, Git Repository) cover that case. Kindo does not support programmatic Knowledge Store updates via API. File uploads are manual through the UI. --- ## 'Multi-Agent Coordination Patterns' Source: https://docs.kindo.ai/best-practices/multi-agent-coordination/ Complex security operations often require multiple specialized agents working together. Kindo supports two broad styles of coordination: direct subagent invocation (a parent agent calls another agent as a tool, in-process) and indirect coordination through external systems and triggers. This guide covers proven patterns for both, with guidance on when each fits. ## Why Multiple Agents? Not every task needs multiple agents. Use multiple agents when: - **Different permission levels** — Investigation vs. remediation require different access - **Human approval gates** — Separate analysis from action with human review - **Team boundaries** — Different teams own different parts of the process - **Schedule differences** — Continuous monitoring vs. weekly reporting - **Separation of concerns** — Simpler, more testable individual agents A single multi-step agent is simpler and preferred when all steps can run with the same permissions, at the same time, without human review. ## Pattern 1: Trigger Chaining via Ticketing System ### How It Works Agent A writes a result to a ticketing system (Jira, ServiceNow) → Agent B triggers on that ticket event. ### Architecture ``` [Agent A: Investigate] [External System] [Agent B: Remediate] Trigger: SIEM alert → Creates Jira ticket → Triggers on ticket LLM: Analyze finding with structured findings assignment to "AutoRemediate" API Action: Create ticket in a JSON custom field LLM: Parse findings API Action: Execute fix API Action: Update ticket ``` ### Step-by-Step Setup **Agent A (Investigate):** 1. Create a triggered agent monitoring SIEM integration 2. Add LLM step: analyze the alert, classify severity, identify affected systems 3. Add API Action step: create Jira ticket with findings as structured JSON 4. Set ticket assignee to a sentinel value (e.g., "auto-remediate-queue") **Agent B (Remediate):** 1. Create a triggered agent monitoring Jira 2. Set trigger condition: Assignee = "auto-remediate-queue" AND Priority = "Critical" 3. Add LLM step: parse the JSON findings from Agent A, determine remediation steps 4. Add API Action steps: execute remediation (isolate host, rotate credential, etc.) 5. Add API Action step: update the Jira ticket with remediation report **Optional: Human Approval Gate:** 1. Agent A creates ticket assigned to a human reviewer 2. Human reviews findings, changes assignee to "auto-remediate-queue" 3. Agent B triggers only after human approval ### Pros - Full audit trail in the ticketing system - Human approval naturally fits between agents - Each agent is independently testable - Works with any ticketing integration ### Cons - Latency: depends on ticketing system webhook delivery - Coupling: agents are coupled through ticket field conventions ## Pattern 2: Direct Webhook Chaining ### How It Works Agent A makes an API Action call to Agent B's direct webhook URL. ### Step-by-Step Setup 1. **Create Agent B** as a Triggered agent with a [Direct Webhook URL trigger](/features/creating-agents/#direct-webhook-url-triggers) - Save the trigger to mint a webhook URL, then copy it - Optionally set a natural-language Run Condition to gate when Agent B actually fires 2. **Create Agent A** as any agent type - Final API Action step: POST to Agent B's webhook URL - Include the structured output from Agent A's analysis in the POST body — Agent B receives it as the trigger payload 3. Agent B evaluates the Run Condition (if set) and fires when satisfied ### Pros - Fastest coordination pattern (no external system delay) - No external system dependency - Simple to set up ### Cons - No built-in approval gate (fully automated) - No persistent record of the handoff (unless agents write logs) - Webhook tokens must be managed securely ### When to Use - Fully automated pipelines where speed matters - Internal agent coordination that doesn't need human oversight - Chaining lightweight processing stages ## Pattern 3: Shared External State ### How It Works Multiple agents read from and write to the same external system (Notion database, Google Sheet, Confluence page) on independent schedules. ### Example: Security Posture Dashboard - **Agent 1 (Vuln Scanner):** Scheduled daily, writes vulnerability counts to Notion - **Agent 2 (Compliance Checker):** Scheduled weekly, writes compliance status to same Notion DB - **Agent 3 (Posture Report):** Scheduled monthly, reads all data, generates executive summary ### Setup 1. Create a shared Notion database (or Google Sheet) with defined columns 2. Each agent uses API Actions to read/write its designated columns 3. Use timestamps and agent IDs to track provenance ### Pros - Agents are fully decoupled (don't know about each other) - Easy to add new agents to the ecosystem - Persistent, human-readable state ### Cons - No real-time coordination (schedule-dependent) - Potential race conditions if agents write concurrently - Requires consistent data format conventions ## Pattern 4: Manual Handoff with Structured Output ### How It Works Agent A produces structured output → human copies it → human provides it as input to Agent B. ### When This Is The Right Choice - Regulatory requirement for human review before action - Low-volume processes (incident-level, not alert-level) - Initial deployment before automating the handoff ### How to Set It Up 1. Agent A: ensure final LLM step outputs well-structured JSON or markdown 2. Agent B: define an agent input that accepts the output format from Agent A 3. Human: run Agent A → review output → run Agent B with output as input ### Transitioning to Automation - Start with manual handoff to validate the process - Once confident in Agent A's output quality, switch to Pattern 1 or 2 ## Pattern 5: Subagent Invocation ### How It Works The parent agent calls another agent as a tool from within its own run. The subagent executes as a separate agent run linked back to the parent — no external trigger, ticket, or webhook is involved, and the parent decides at run time which subagent (if any) to invoke and what to pass it. To make an agent invokable this way, open its agent builder and turn on **Available as subagent** in the configuration panel. ### Who Can Toggle or Invoke a Subagent One access model gates both flipping the toggle on an agent and invoking it as a subagent from a parent agent. You need **Owner** access on the agent. You have Owner access if any of the following is true: - You created the agent - You have been granted **Owner** access on the agent (directly, or via a user group) - The agent's organization-level role is set to **Owner** and you are in the same organization - You are an organization admin in the agent's organization This means an agent built by one teammate can be exposed and invoked as a subagent by another teammate as long as the second teammate has Owner access. Authorship and ownership are tracked separately — being granted ownership is sufficient. There is one additional restriction on the agent itself: the toggle cannot be enabled on an agent that has an event or direct-webhook trigger. A scheduled/cron trigger is fine, as is no trigger at all. Agents that react to external events should be exposed via their trigger, as in Pattern 2. ### Architecture ``` [Parent Agent] [Subagent: Investigate] LLM: triage SIEM alert → Triggered as a tool call → calls investigate_alert( Runs as a separate agent run hostname: "web-04") linked to the parent run LLM: deep-dive investigation API Actions: query Splunk, etc. Returns findings to the parent ``` ### Passing Data to a Subagent - **Declared inputs** — Inputs defined on the subagent become typed parameters the parent agent fills in when invoking it. - **Freeform context** — The parent agent can also forward a block of freeform text (up to 32,000 characters) alongside the declared inputs, to share any context or inputs it deems appropriate. ### Step-by-Step Setup **Subagent (callee):** 1. Build the agent as you would any single-agent setup. 2. Open the configuration panel and enable **Available as subagent**. 3. (Optional) Declare typed inputs for data the parent should pass as structured arguments. **Parent Agent (caller):** 1. Build the agent as you would any single-agent setup. 2. Eligible subagents — agents marked **Available as subagent**, with at least one step, that the user running the parent has **Owner** access to — are automatically discovered and exposed as tools. No manual tool configuration is needed. 3. For better performance, include explicit instructions in the parent's system prompt telling the LLM when to call the target subagent and what to pass. ### Audit and Visibility - Each subagent invocation creates its own run, linked back to the parent run. - Both the parent and subagent appear in the agent run history with cross-links. - When the parent forwards freeform context, it is shown in a collapsible card above the child run's first message. ### Pros - Lowest latency — no external system in the loop - No ticketing/webhook configuration required - Audit trail in Kindo (parent run ↔ subagent run linkage) ### Cons - Both agents run inside the same org and permission scope — not suitable for cross-team boundaries - No built-in human approval gate (parent decides; subagent runs) ### When to Use - Investigation/triage handoffs inside a single team - Setups where you want subagent execution audited in Kindo, not in an external ticketing system ## Choosing a Pattern | Need | Pattern | | ---------------------------------------- | ---------------------------------------------------------- | | Human approval between agents | Trigger Chaining (with approval gate) or Manual Handoff | | Fastest automated handoff | Subagent Invocation or Direct Webhook | | Agents run on different schedules | Shared External State | | Full audit trail | Trigger Chaining (ticketing system) or Subagent Invocation | | Simplest setup | Manual Handoff or Subagent Invocation | | Maximum decoupling / cross-team boundary | Shared External State | ## Design Principles for Multi-Agent Systems 1. **Start with one agent, split when needed.** Don't over-architect. Begin with a single multi-step agent and only split into multiple agents when you have clear separation requirements (permissions, schedules, teams). 2. **Use structured output formats.** JSON with a defined schema is the lingua franca between agents. Include version fields so you can evolve the format over time. 3. **Make each agent independently testable.** You should be able to run Agent B with mock input without needing Agent A. This makes debugging and iteration much faster. 4. **Log everything.** Each agent should record what it received, what it decided, and what it did — in the ticketing system or external store, not just in Kindo Terminal. 5. **Plan for failure.** What happens if Agent B fails? Design agents to be idempotent when possible (running twice produces the same result as running once). ## Limitations Kindo does not provide a built-in orchestration layer for fan-out, retries, or scheduling across multiple agents. Patterns 1–4 use external systems for that coordination; Pattern 5 keeps the handoff in-process but still leaves orchestration to the parent agent's LLM. There is no built-in human-approval gate for Pattern 5 — the parent agent's LLM decides when to invoke a subagent. If you need a human in the loop between agents, use Pattern 1 with an approval-queue assignee or Pattern 4 (Manual Handoff). These are architectural constraints of the current platform, not fundamental design decisions. --- ## 'Security Workflow: Scheduled Vulnerability Report' Source: https://docs.kindo.ai/best-practices/scheduled-vulnerability-report/ This walkthrough shows you how to build a scheduled agent that generates weekly vulnerability prioritization reports. By the end, you'll have an agent that automatically fetches vulnerability data, prioritizes findings, and delivers executive reports. ## Overview **What this workflow does:** - Runs automatically on a schedule (weekly cron) - Fetches vulnerability findings from your scanner - Prioritizes by CVSS, exploit availability, and asset criticality - Generates an executive summary with top 10 priorities - Posts the report to Slack or email **Who it's for:** Vulnerability management teams, DevSecOps engineers **What it produces:** Prioritized vulnerability report posted to Slack or email ``` Cron trigger (weekly) -> Fetch vuln data -> Prioritize with LLM -> Generate report -> Post to Slack ``` ## What You'll Need - Kindo account with a vulnerability scanner integration (Tenable, CrowdStrike, or similar) - Slack integration for report delivery - ~45 minutes ## The Workflow ### Step 1: Create a Scheduled Agent :::tip Cron expressions use the standard format: `minute hour day-of-month month day-of-week`. Use [crontab.guru](https://crontab.guru) to validate your expressions. ::: ### Step 2: Add an API Action Step — Fetch Vulnerability Data :::note Large API responses are automatically written to the sandbox. Use sandbox commands to filter and process the data before passing to LLM steps. See [Working with Large Context](/best-practices/working-with-large-context/) for techniques. ::: ### Step 3: Add an LLM Step — Prioritize and Summarize ### Working with Large Scan Results For scans with 10,000+ findings, use the sandbox: 1. **First LLM pass** — Group findings by severity in the sandbox 2. **Second LLM pass** — Analyze only Critical/High findings 3. **Filter at source** — Use scanner API query parameters to limit results → See [Working with Large Context](/best-practices/working-with-large-context/) for detailed sandbox techniques. ### Step 4: Add an API Action Step — Deliver the Report ### Step 5: Test and Enable ## Adapting This Workflow ### Change Scanner | From | To | Changes Needed | | ----------- | ----------- | ------------------------------------------------------------- | | Tenable | CrowdStrike | Update API endpoint, authentication headers, query parameters | | Tenable | Qualys | Change to Qualys API format, adjust severity mappings | | CrowdStrike | Rapid7 | Update to InsightVM API, adjust response parsing | Same pattern, different API. The LLM prioritization step remains unchanged. ### Change Cadence | Frequency | Use Case | Cron Expression | | --------- | ------------------------------ | ------------------------ | | Daily | Critical asset monitoring | `0 9 * * *` | | Weekly | Standard vulnerability reports | `0 9 * * 1` | | Monthly | Executive reviews | `0 9 1 * *` | | Custom | Compliance deadlines | Adjust to specific dates | ### Add Compliance Overlay 1. Upload your compliance framework (PCI-DSS, GDPR, SOC 2) to Knowledge Store 2. Add a step that cross-references findings against framework requirements 3. Include compliance mapping in the LLM prompt: ``` Cross-reference each finding with the attached compliance framework. Identify which requirements are violated and the potential impact. ``` 4. Output includes compliance-specific recommendations ## Troubleshooting ### Scanner API Returns No Data **Symptoms:** API Action step succeeds but returns empty results **Check:** - Date range parameters (are you looking at the right time period?) - Severity filters (are you excluding the findings you want?) - Authentication (is the API key valid and has permissions?) - API quotas (have you hit rate limits?) **Fix:** - Expand date range to "last 30 days" for testing - Remove severity filters temporarily to see all findings - Regenerate API keys if authentication fails - Add delays between requests if hitting rate limits ### LLM Produces Inconsistent Prioritization **Symptoms:** Same vulnerability gets different severity ratings on different runs **Check:** - Prompt clarity (are criteria well-defined?) - Model capability (is it a reasoning model?) - Context completeness (is all necessary data provided?) **Fix:** - Add explicit weighting: "Prioritize in this order: 1) CVSS, 2) Exploit availability..." - Include examples in the prompt: "Critical: CVSS 9.0+ with public exploit..." - Switch to a more capable model (GPT-4, Claude 3 Opus) - Add a validation step that checks consistency ### Report Not Delivered **Symptoms:** Agent runs successfully but report doesn't appear in Slack/email **Check:** - Delivery step configuration (correct channel/email?) - Integration permissions (can the integration post to that channel?) - Message formatting (is it too long? malformed?) - Rate limits (has the integration hit API limits?) **Fix:** - Verify channel name/email address is correct - Test the integration manually in the integration settings - Shorten the message or split into multiple posts - Add error handling to catch delivery failures - Check integration logs for specific error messages ## Next Steps - **[Memory and Persistence Patterns](/best-practices/memory-and-persistence/)** — Learn how to maintain state across agent runs - **[Multi-Agent Coordination](/best-practices/multi-agent-coordination/)** — Chain multiple agents for investigate → remediate pipelines - **[On-Demand Threat Hunting](/best-practices/threat-hunting/)** — Build interactive workflows for threat hunting - **[Working with Large Context](/best-practices/working-with-large-context/)** — Handle large scan results efficiently --- ## 'Security Agent: Automated Alert Triage' Source: https://docs.kindo.ai/best-practices/security-alert-triage/ This walkthrough shows you how to build a triggered agent that automatically enriches and triages security alerts from your ticketing system. By the end, you'll have an agent that fires when new alerts arrive, analyzes them, and updates tickets with triage reports. ## Overview **What this agent does:** - Monitors your ticketing system (Jira/ServiceNow) for new security alerts - Automatically extracts IOCs and enriches with threat intelligence - Classifies severity and recommends actions - Updates the ticket with a structured triage report **Who it's for:** SOC analysts, security engineers, incident responders **What it produces:** Enriched tickets with triage reports that help prioritize response ``` Jira ticket created -> Trigger fires -> LLM enriches + classifies -> Comment added with triage report ``` ## What You'll Need - Kindo account with Jira (or ServiceNow) integration configured - A Jira project where security alerts are filed - ~30 minutes ## The Agent ### Step 1: Create a Triggered Agent ### Step 2: Add an LLM Step — Enrich and Classify ### Step 3: Add an API Action Step — Update the Ticket ### Step 4: Test with a Sample Alert ## Verification ### Expected Results | Check | Expected Result | | ------------------------------ | ---------------------------------------------------------- | | Trigger fires on new ticket | Agent executes within seconds of ticket creation | | LLM produces structured output | JSON with IOCs, classification, severity, recommendation | | Ticket updated | New comment with triage report visible in Jira | | Priority set correctly | Matches severity classification (Critical/High/Medium/Low) | ### Verification Steps 1. **Check Kindo Terminal** — Review the full execution trace for errors 2. **Check Jira ticket** — Verify comment was added and priority was updated 3. **Review LLM output** — Ensure structured JSON is well-formed and complete 4. **Test edge cases** — Try tickets without CVEs, with multiple CVEs, with different alert types ## Adapting This Agent ### Replace Jira with ServiceNow 1. Change the integration from Jira to ServiceNow 2. Update API Action steps to use ServiceNow MCP tools (`servicenow.add_comment`, etc.) 3. Same pattern, different integration ### Add a Second Agent for Automated Remediation 1. Create Agent B (Remediate) as a triggered agent 2. Set trigger condition: Jira ticket assigned to "auto-remediate" queue 3. Agent B executes fixes and updates ticket 4. See [Multi-Agent Coordination](/best-practices/multi-agent-coordination/) for details ### Add Slack Notification for Critical Alerts 1. Add an API Action step after the LLM classification 2. Use Slack integration to post to a channel 3. Condition: Only post if severity is Critical or High ### Use Different Classification Framework 1. Update the LLM prompt to use MITRE ATT&CK tactics instead of generic categories 2. Map MITRE techniques to your organization's response procedures 3. Include MITRE mapping in the structured output ## Troubleshooting ### Trigger Not Firing **Symptoms:** Agent doesn't execute when tickets are created **Check:** - Integration connection status and active connection (in the Command Center under **Manage Your Integrations**) - Trigger conditions match ticket fields exactly (case-sensitive) - Ticket is created in the correct project with correct type - Webhook delivery in the integration's admin panel (Jira/ServiceNow) **Fix:** - Reconnect the integration if connection shows errors - Adjust trigger conditions to match actual ticket field values - Test with a manually created ticket matching all conditions ### LLM Output Not Structured **Symptoms:** Output is narrative text instead of JSON **Check:** - Prompt explicitly requests JSON format - JSON schema is included in the prompt - Model is capable of structured output (GPT-4, Claude 3, etc.) **Fix:** - Add explicit instruction: "Output ONLY valid JSON, no markdown, no explanation" - Include example JSON structure in the prompt - Switch to a more capable model if using a lightweight one ### API Action Failing **Symptoms:** Jira/ServiceNow actions return errors **Check:** - MCP server is running (for self-hosted deployments) - Integration permissions allow the actions (create comment, update issue) - Issue key format is correct - API rate limits haven't been exceeded **Fix:** - Check MCP server status in deployment logs - Verify integration OAuth scopes include write permissions - Test actions manually in the integration first - Add retry logic for transient failures --- ## 'Security Workflow: On-Demand Threat Hunting' Source: https://docs.kindo.ai/best-practices/threat-hunting/ This walkthrough shows you how to build a manual workflow for interactive threat hunting. By the end, you'll have a reusable workflow that accepts a hunt hypothesis, queries one or more data sources, pivots on suspicious indicators, and produces a structured hunt report. ## Overview **What this workflow does:** - Accepts a hunt hypothesis and search scope as workflow inputs - Queries a primary data source such as Splunk, Datadog, or another log system - Uses an LLM to identify suspicious patterns, IOCs, and follow-up pivots - Runs a second query to validate or expand on those findings - Produces a structured hunt report with timeline, affected systems, and recommended actions **Who it's for:** Threat hunters, senior SOC analysts, incident responders **What it produces:** A repeatable hunt report with findings, IOCs, confidence level, and next actions ``` Manual inputs -> Query logs -> Correlate with LLM -> Pivot on IOCs -> Summarize findings ``` ## What You'll Need - Kindo account with at least one logging or SIEM integration configured - Access to a dataset worth hunting in (authentication logs, endpoint telemetry, DNS, proxy logs, cloud audit logs, etc.) - A clear hypothesis to test - ~45 minutes :::tip This pattern works best as a **Workflow** agent, not a triggered agent. Threat hunting is exploratory: you want to enter a hypothesis, inspect the results, adjust the scope, and run again. ::: ## The Workflow ### Step 1: Create a Manual Workflow Agent ### Step 2: Define Workflow Inputs Workflow inputs make the hunt reusable. Each run can use a different hypothesis and scope without editing the agent. :::tip Workflow inputs are the easiest way to turn an investigative playbook into a reusable hunt template. You can keep the workflow logic stable while changing the question you are asking. ::: ### Step 3: Add an API Action Step — Query the Primary Data Source Start with the system most likely to contain the signal you need. :::note For large log datasets, avoid trying to push everything directly into the LLM context. Let the API step retrieve the data, then use the sandbox or a filtering step to narrow it down before final analysis. ::: ### Step 4: Add an LLM Step — Correlate and Extract Leads Now turn raw log output into a set of concrete leads to investigate. ### Step 5: Add an API Action Step — Pivot on the Most Suspicious Indicators A good hunt rarely ends with one query. Use the extracted leads to search another source or another log slice. :::tip This is where workflow chaining becomes powerful: each step can refine the next one. The first query finds suspicious leads; the second query validates whether those leads actually matter. ::: ### Step 6: Add an LLM Step — Produce the Hunt Report Use a second LLM step to synthesize the full investigation. ## Running a Hunt ## Working with Large Hunt Data Threat hunting often produces more data than an LLM should ingest directly. ### Useful Sandbox Patterns - Use `rg`, `grep`, or lightweight scripts to isolate only the relevant records - Build small timelines from raw logs before sending them to an LLM step - Deduplicate repeated events so the model focuses on unique signals - Split analysis by source (identity, endpoint, network) before writing the final summary For more techniques, see **[Working with Large Context](/best-practices/working-with-large-context/)**. ## Adapting This Workflow ### Add More Data Sources Start with one log source, then expand: | First source | Possible pivot source | | --- | --- | | Identity logs | Endpoint telemetry | | SIEM alerts | DNS or proxy logs | | Cloud audit logs | IAM change history | | EDR telemetry | Network flow logs | ### Save Reusable Hunt Templates Keep a library of hypotheses such as: - Suspicious OAuth consent grants - Impossible travel or unusual geo-authentication - Lateral movement after VPN login - New admin role assignments in cloud environments - Beaconing to suspicious external infrastructure You can store these in internal documentation or another system your team already uses. ### Chain into Follow-On Workflows After the hunt completes, you can: - Trigger a triage or remediation workflow - Create a ticket with the findings - Send the final report to a security operations channel - Hand off to another agent for containment or evidence collection See **[Multi-Agent Coordination](/best-practices/multi-agent-coordination/)** for patterns that connect investigations to downstream automation. ## Troubleshooting ### Query Returns Too Much Data **Symptoms:** The first API step succeeds, but the output is too large or noisy to use. **Fix:** - Narrow `time_window` - Limit `scope` to a smaller set of users, hosts, or services - Filter for event types relevant to the hypothesis - Use sandbox tools to reduce the dataset before LLM analysis ### LLM Suggests Weak or Obvious Pivots **Symptoms:** The correlation step produces generic recommendations instead of useful next queries. **Fix:** - Make the hypothesis more specific - Include clearer instructions about the types of signals you care about - Ask for prioritized pivots, not a long brainstorm list - Provide a small example of the output shape you want ### Final Report Sounds More Certain Than the Evidence Supports **Symptoms:** The report states conclusions as facts even though the data is ambiguous. **Fix:** - Instruct the LLM to separate confirmed findings from inference - Require an explicit confidence level with reasoning - Ask it to list missing evidence or validation steps - Review the intermediate outputs before sharing the final report ## Next Steps - **[Security Workflow: Automated Alert Triage](/best-practices/security-alert-triage/)** — Convert recurring alerts into structured triage reports - **[Security Workflow: Scheduled Vulnerability Report](/best-practices/scheduled-vulnerability-report/)** — Build a recurring reporting workflow for vulnerability data - **[Working with Large Context](/best-practices/working-with-large-context/)** — Learn how to handle large log volumes safely - **[Multi-Agent Coordination](/best-practices/multi-agent-coordination/)** — Chain hunts into response and remediation workflows --- ## Start Here Source: https://docs.kindo.ai/start/ Kindo is an AI-powered platform for enterprise security and governance. Whether you're evaluating, deploying, or administering Kindo, this section helps you get oriented quickly. ## What You'll Find Here ## Choose Your Path - **Evaluating Kindo?** Start with [What is Kindo?](/start/what-is-kindo/) then try a [Quickstart](/quickstarts/). - **Installing on-prem?** Jump to [Deploy and Operate](/deploy/) for infrastructure requirements and installation guides. - **Administering?** Head to [Admin and Security](/admin/) for SSO, RBAC, and governance setup. - **Integrating via API?** See [API Overview](/overview/getting-started/) for authentication, models, and per-API quickstarts. --- ## What is Kindo? Source: https://docs.kindo.ai/start/what-is-kindo/ Kindo is the AI-native automation platform built for technical operations. We equip SecOps, DevOps, and ITOps teams with intelligent agents that move beyond alerts and summaries into real, autonomous execution. With Kindo, operators don't just detect problems — they resolve them through policy-aware, human-in-the-loop agents that can remediate vulnerabilities, enforce IAM policies, trace incidents, and harden cloud environments. At the core is **Deep Hat**, Kindo's proprietary LLM tuned for real infrastructure and threat behavior. Agents execute tasks based on operator intent, not rigid playbooks, and adapt in real time as conditions change. ## Key Capabilities - **AI Chat** — Secure conversational AI with enterprise controls - **Agents** — Build and deploy autonomous AI agents for SecOps, DevOps, and ITOps workflows - **Integrations** — Universal MCP connectors let agents read and write across your stack - **Governance** — Fine-grained policies, DLP filters, and compliance controls - **Observability** — Full visibility into AI usage, audit logs, and metrics ## Deployment Models Kindo is available in two deployment models: ### SaaS (Cloud-Hosted) The fastest way to get started. Kindo manages infrastructure, updates, and availability. ### Self-Hosted (On-Prem) Deploy Kindo in your own infrastructure for maximum control over data residency and network isolation. ## Feature Comparison | Feature | SaaS | Self-Managed | |---------|------|--------------| | Chat | ✅ | ✅ | | Agents | ✅ | ✅ | | Integrations | ✅ | ✅ | | Model RBAC | ✅ | ✅ | | User Groups | ✅ | ✅ | | Self-Managed Models | ✅ | ✅ | | Enterprise IDP Support | ✅ | ✅ | | DLP Filters | ✅ | ✅ | | Audit Log | ✅ | ✅ | | Secrets Manager Integration | ✅ | ✅ | | Metrics Integration | — | ✅ | | Audit Log Isolation | — | ✅ | | Customizable Deployment | — | ✅ | | Customizable Secrets Manager | — | ✅ | ## Who Uses Kindo? **Kindo Users** are technical professionals in DevOps, SecOps, and ITOps who communicate with LLMs, create and manage agents that automate workflows, and integrate via APIs. **Kindo Administrators** manage governance, security, and compliance — controlling user and group access to models, integrations, and agents, enforcing DLP filters, and managing secrets managers. | Role | Primary Use | |------|-------------| | **End Users** | AI Chat, Agents | | **Admins** | SSO, RBAC, Policy management, DLP | | **Operators** | Deployment, Monitoring, Upgrades | | **Developers** | API integration, Custom agents | ## Next Steps --- ## Concepts and Terminology Source: https://docs.kindo.ai/start/concepts/ This glossary covers the core concepts and terminology used when working with the Kindo platform. ## AI Models ### Generative AI Model A broad term for any ML model that the Kindo platform supports. While Kindo functionality is primarily driven by LLMs, the platform also supports other model types. ### Embeddings Models Models that tokenize content into numeric vectors representing semantic meaning. Used for knowledge retrieval across files and large contexts. ### Multi-modal Models Models that work with multiple content types such as text, audio, images, and video. Models are typically constrained to a few content types. ### Large Language Model (LLM) The fundamental AI model used in requests. Examples include OpenAI o1, Anthropic Claude, and DeepSeek R1. LLMs used within Kindo are one of two types: - **Worker LLM** -- The LLM used within Kindo for internal, non-user operations. - **User LLM** -- The LLM that users or agents make requests to. Within Kindo's user interface these are referred to as "Models." ### Self-Managed Model A generative AI model that an administrator manages and configures for Kindo's use. This may include self-hosted models or cloud deployments such as Anthropic models via AWS Bedrock in GovCloud. ## Platform Concepts ### Kindo Terminal The primary user interface within Kindo. At the Terminal, a user makes requests to a User LLM, either directly through a Chat or through an Agent Run. ### Chat A collection of requests from a user to User LLMs through the Kindo Terminal *without* an Agent. Chats are divided by Memory, enabling the user to communicate with different models across different historical contexts. ### Agent A Kindo-managed automation that executes a series of steps or sequential, atomic operations. There are three types: - **Chatbots** -- Agents designed to respond to users with information gathered from an AI model and a knowledge base. - **Workflow Agents** -- Agents that conduct a set of operations within a system of record, including API calls and external tool invocations. - **Triggered Agents** -- Workflow agents that lie dormant until a specified event occurs (such as a new ticketing system entry), then launch a response. ### Memory The historical collection of LLM responses from user requests. Memory enables users to mix and match User LLM responses and leverage output from previous interactions. Chats and all types of Agents hold memory. ### Run An instance of interaction between a user and a User LLM brokered by Kindo. Runs result from communications through Chats, Agents, Triggered Agents, and Public API calls. - **Agent Run** -- The final output of the last LLM Step. - **Step Run** -- The individual response of a single LLM Step within an Agent. ### Kindo API The set of REST API endpoints exposed by Kindo: - **Inference API** -- An individual request to a User LLM with no memory. - **Agent API** -- A call that produces the output of an Agent Run. ### Target The output location of an Agent Run or Step Run. Targets typically refer to the Kindo Terminal but can also refer to external systems like ticketing platforms or APIs. ### Resource A structured data entity provided by an Integration. Examples include Tickets, Users, Emails, Logs, and Metrics. ### File A file within the Kindo Platform. Files can be uploaded by users, provided by Integrations, or generated by AI Models. ### Secret A sensitive text blob consumed on the Kindo Platform, typically representing API keys, access tokens, or environment variables. Secrets are stored in the **Secrets Vault**. ## Infrastructure Terminology ### Integration A system external to Kindo that connects to enable data ingestion and outbound actions. For example, the Linear ticketing system provides Resources to Kindo and enables actions like adding comments to tickets. ## Product Offerings ### Kindo SaaS The cloud-hosted version of Kindo, managed by the Kindo team. ### Self-Managed Kindo The version deployed by administrators into an environment they control, such as a public cloud, hybrid cloud, or on-premises infrastructure. --- ## Use Cases Source: https://docs.kindo.ai/start/use-cases/ Kindo is an enterprise platform that professionals in DevOps, SecOps, and ITOps use to create autonomous infrastructure -- infrastructure that helps manage and secure itself using natural language instructions. Kindo agents are typically deployed across three categories of use cases. ## Getting Started by Role Choose your role to see relevant workflows and guides: | I am a... | Start here | | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **SOC Analyst** | [Build Your First Workflow](/quickstarts/build-first-workflow/) → [Alert Triage](/best-practices/security-alert-triage/) → [Threat Hunting](/best-practices/threat-hunting/) | | **Vulnerability Manager** | [Build Your First Workflow](/quickstarts/build-first-workflow/) → [Vulnerability Reporting](/best-practices/scheduled-vulnerability-report/) → [Memory Patterns](/best-practices/memory-and-persistence/) | | **DevSecOps Engineer** | [Build Your First Workflow](/quickstarts/build-first-workflow/) → [Multi-Agent Coordination](/best-practices/multi-agent-coordination/) → [Integrations](/features/integrations/) | | **Platform Engineer** | [Build Your First Workflow](/quickstarts/build-first-workflow/) → [API Overview](/overview/getting-started/) → [Deploy Guides](/deploy/) | | **IT Operations** | [First Chat](/quickstarts/first-chat/) → [Create Your First Agent](/quickstarts/first-agent/) → [Integrations](/features/integrations/) | ## Incident Response Incident Response refers to use cases where Kindo agents serve as first responders to changes in the security, reliability, or uptime of infrastructure. ### Security Incident Response For Enterprise Security teams, these use cases typically fall under Managed Detection and Response (MDR) or Incident Detection and Response (IDR). Examples include: - Analyzing threat intelligence feeds against failed login alerts in a SIEM to identify potential attacker C2 infrastructure. - Querying logging infrastructure to determine exposure from compromised credentials across databases and secrets management systems. - Reviewing indicators of compromise against threat intelligence and correlating with root cause analysis to determine evidence of malicious activity. ### DevOps / Platform Engineering Incident Response For DevOps and Platform Engineering teams, Incident Response typically involves Root Cause Analysis (RCA) of outages, service interruptions, or quality of service changes. Examples include: - Analyzing logging infrastructure and observability suites for activity preceding an outage, then performing RCA using system configuration context. - Analyzing firewall configs, load balancer settings, and routing logs to determine causes of network performance degradation. - Cross-referencing internal analysis with outage maps from telcos and cloud providers to identify macro system failures. ## Vulnerability and Compliance Enforcement Kindo agents proactively review systems for faults, vulnerabilities, and change management issues to prevent incidents before they occur. ### Security Vulnerability and Compliance For Enterprise Security teams, this involves continually reviewing system changes and IAM infrastructure for exploitable vulnerabilities or compliance violations. Examples include: - Reviewing IAM policies against GDPR guidelines to verify least privilege is maintained per GRC requirements. - Scanning public-facing infrastructure for exposed services, cross-referencing versions with known CVEs, and generating proof-of-concept exploits for critical findings. ### DevOps / Platform Engineering Compliance For DevOps and Platform Engineering teams, this involves reviewing change management procedures and infrastructure changes for potential faults. Examples include: - Reviewing system or IAM profile changes to determine if they may cause instability, and reporting results via email. - Cross-referencing Infrastructure as Code changes with PCI-DSS compliance requirements. ## ITOps Automation ITOps Automation use cases focus on removing time-consuming service offering tasks from ITOps professionals. Examples include: - Providing a helpdesk chatbot for answering questions about IT policies and filing tickets. - Automatically provisioning development environments for new users when tickets are created. --- ## Quickstarts Source: https://docs.kindo.ai/quickstarts/ Get hands-on with Kindo through guided, end-to-end walkthroughs. Each quickstart is designed to get you from zero to "it worked" as fast as possible. ## Available Quickstarts --- ## Getting Started with Kindo Source: https://docs.kindo.ai/quickstarts/getting-started/ Kindo enables professionals on SecOps, DevOps, and platform engineering teams to automate challenging and tedious tasks from their playbooks and runbooks using AI agents. This guide walks you through signing in and navigating the interface. ## Signing In ## The Terminal After signing in, you arrive at the Terminal window with the query box at the bottom. This is the default screen and the primary workspace for interacting with AI models. ![The Kindo Terminal showing the main work area, Chat/Agents selector, model selector, query bar, and icon bar](../../../assets/docs/getting-started/terminal-overview.png) From here you can: - Select an AI model and run queries as a **Chat** - Navigate to the **Agents** tab to manage and execute agents ## Chat Window ### Selecting a Model Above the query bar is a model selector where you can choose from available LLMs. Models are organized into categories: - **General Purpose** -- Versatile models for most tasks - **Longer Context** -- Models optimized for large inputs - **Reasoning** -- Models specialized in complex analysis - **Cybersecurity Focused** -- Models tuned for security operations ### Running a Query Type a question in natural language in the query bar and press **Enter**. The response appears in the main work area. ### Attaching Files Click the **+** button to attach files to your query. Once files are attached, you can: ![Attaching files to a chat query](../../../assets/docs/getting-started/attach-files.png) - Remove files using the **Files** panel - When multiple files are attached, use **For All Files** to apply a condition collectively or to each file independently ### Tools Tools are how you connect to your various sources of information. Kindo can connect to virtually any system of record that you need — from security platforms like CrowdStrike and SentinelOne to collaboration tools like Jira, Slack, and GitHub. ![The Tool Policies panel showing available tool servers and their connection status](../../../assets/docs/getting-started/tools-selection.png) Use the **Tool Policies** panel to browse, search, and manage your connected tool servers. Each tool server can be set to **Active**, **Off**, or **Connect** for first-time setup. ### Work History Use the history panel to: - **History** -- Review queries from the last 5 days, filtered by your own or shared queries - **Agents Run** -- Review agent executions, filtered by type (Chatbot, Workflow, Triggered) ## Agents Window The Agents screen lets you create, edit, and deploy AI agents that automate repeatable operations. ![The Agents screen showing available agents with edit, run, share, and delete options](../../../assets/docs/getting-started/agents-screen.png) ### My Agents View all your agents filtered by type: - **All Agents** -- Every available agent - **Chatbots** -- Query-based agents - **Workflow Agents** -- Process automation agents - **Triggered Agents** -- Event-driven agents Each agent can be shared, deleted, edited, or run directly from this screen. ### Creating an Agent Select **Create an Agent** to build a new agent. Choose from three types: ![Agent type selection showing Triggered, Workflow, and Chatbot options with Build Agent buttons](../../../assets/docs/getting-started/agent-type-selection.png) - **Triggered** -- Reacts to events in integrated systems - **Workflow** -- Executes a defined sequence of operations - **Chatbot** -- Responds to user queries using knowledge bases The Agent Configuration panel lets you describe what you want using natural language: ![The Workflow Agent Configuration panel with Name, Description, Instructions, and Knowledge Store fields](../../../assets/docs/getting-started/workflow-agent-config.png) For detailed agent creation instructions, see [Creating Agents](/features/creating-agents/). ## Settings Access settings via the gear icon in the top right: - [**Models**](https://app.kindo.ai/?settings=models) -- View and manage available LLMs - **Integrations** -- Connect to third-party applications - [**Model Access & DLP**](https://app.kindo.ai/?settings=model-access-and-dlp) -- Manage access controls and DLP - [**API**](https://app.kindo.ai/?settings=api) -- View API keys and available models - [**Secrets Vault**](https://app.kindo.ai/?settings=secrets-vault) -- Manage stored secrets ## Next Steps - [Create your first agent](/features/creating-agents/) to automate a workflow - [Configure integrations](/features/integrations/) to connect your existing tools - [Set up security controls](/admin/security-controls/) to manage access and DLP --- ## 'First Chat Experience' Source: https://docs.kindo.ai/quickstarts/first-chat/ This quickstart walks you through your first AI-powered conversation in Kindo. By the end, you will have selected a model, sent a query, and used tools to enhance your results. ## Prerequisites - A Kindo account (SaaS or self-hosted) - Signed in to the Kindo Terminal (see [Getting Started](/quickstarts/getting-started/)) ## Start a Conversation ## Attach Files You can provide additional context by attaching files to your query: File attachments are useful for tasks like analyzing log files, reviewing configurations, or summarizing documents. ## Use Tools Tools connect Kindo to your systems of record, extending what the model can access beyond its training data. Kindo supports a wide range of tool servers — from security platforms like CrowdStrike and SentinelOne to collaboration tools like Jira, Slack, and GitHub. Use the **Tool Policies** panel to browse and manage available tool servers. Each tool server can be toggled to **Active**, **Off**, or **Connect** for first-time setup. Active tools are automatically available to the model during your conversation. Some integrations also let you select a connection. The active connection is used in chats and in agents configured to use the runner's active connection. Turning the integration **Off** removes its tools from the chat. To change an active connection, open **Command Center** > **Manage Your Integrations**, expand the integration, and select a connection in the expanded connections table. The table shows each connection's read-only connection handle beside its connection label, which helps distinguish connections with similar names. ## Tips - **Be specific** -- The more context you provide, the better the response. Include relevant details about your environment, constraints, and desired format. - **Iterate** -- Follow up with clarifying questions. Kindo maintains conversation context across messages. - **Try different models** -- Switch between General Purpose, Reasoning, and Cybersecurity models to compare results for different tasks. ## Next Steps - [Create Your First Agent](/quickstarts/first-agent/) to automate a recurring task - [Get Started with the Kindo API](/overview/getting-started/) to interact with Kindo programmatically - [Learn about Agents](/features/agents/) to understand Kindo's automation framework --- ## 'Create Your First Agent' Source: https://docs.kindo.ai/quickstarts/first-agent/ This quickstart walks you through creating a simple workflow agent in Kindo. By the end, you will have a working agent that can be run on demand. ## Prerequisites - A Kindo account (SaaS or self-hosted) - Signed in to the Kindo Terminal (see [Getting Started](/quickstarts/getting-started/)) ## Create a Workflow Agent ## Run the Agent ## Agent Types Kindo supports four agent types: | Type | Trigger | Use Case | | ------------------- | -------------- | ------------------------------------ | | **Chatbot** | User message | Knowledge base Q&A, helpdesk | | **Manual Workflow** | Manual run | Runbook execution, report generation | | **Scheduled** | Cron schedule | Recurring reports, periodic checks | | **Triggered** | External event | Incident response, alert triage | **Note:** Scheduled agents are workflow agents with a cron trigger. The platform distinguishes them because they have different configuration requirements (schedule expression, timezone) and run patterns. For this quickstart we used a Manual Workflow agent. To learn about all agent types and advanced configuration, see [Agents](/features/agents/) and [Creating Agents](/features/creating-agents/). ## Next Steps - **[Build Your First Multi-Step Workflow](/quickstarts/build-first-workflow/)** — Create a realistic workflow with API calls, LLM analysis, and dynamic inputs - [Get Started with the Kindo API](/overview/getting-started/) to interact with Kindo programmatically - [Configure Integrations](/features/integrations/) to connect agents to external tools - [Learn about Creating Agents](/features/creating-agents/) for advanced agent configuration --- ## 'Build Your First Workflow' Source: https://docs.kindo.ai/quickstarts/build-first-workflow/ This tutorial walks you through building a realistic multi-step workflow that solves a real security problem. By the end, you'll have a working agent that enriches security alerts with threat intelligence and produces structured triage reports. **What you'll build:** A manual workflow agent that takes a security alert description, enriches it with CVE data from the National Vulnerability Database (NVD), classifies severity, and outputs a triage report. **Expected time:** ~45 minutes ## Prerequisites - Kindo account (SaaS or self-hosted) - Completed [Create Your First Agent](/quickstarts/first-agent/) quickstart - No external accounts needed (uses public NVD API) ## What You'll Build ``` Input (alert) -> API Action (NVD lookup) -> LLM (classify + triage) -> Output (report) ``` ## Step 1: Create a Manual Workflow Agent ## Step 2: Define a Workflow Input Workflow inputs let you pass dynamic values into your agent when it runs. These become template variables you can reference in prompts. :::tip For triggered agents, the trigger event data (like ticket fields) automatically becomes available as template variables. You don't need to define workflow inputs manually — the integration provides them. ::: ## Step 3: Add an API Action Step — Fetch CVE Data Now add a step that enriches the alert with threat intelligence from the National Vulnerability Database (NVD). :::note The NVD API is free, public, and requires no authentication. It's rate-limited to 5 requests per 30 seconds. For production use with high volumes, consider caching or using a commercial threat intelligence feed. ::: ## Step 4: Add an LLM Step — Analyze and Classify Now add an LLM step that analyzes the enriched alert and produces a triage report. :::tip Template variables like `{{alert_description}}` are resolved at runtime from workflow inputs (for manual workflows) or trigger event data (for triggered workflows). You can also reference previous step outputs using `{{step_name.output}}` syntax. ::: ## Step 5: Run and Verify Test your agent with a realistic security alert. ### Verification Checklist | Check | Expected Result | | ---------------------------- | -------------------------------------- | | Agent runs without errors | No red error messages in Terminal | | API step executes | NVD API call returns CVE data | | LLM step produces output | Structured triage report generated | | Output contains all sections | IOCs, Classification, Severity, Action | | Severity makes sense | Critical/High for CVE-2024-3094 | ## Step 6: Iterate and Improve Now that you have a working agent, refine it based on the output quality. ## What You Learned In this tutorial, you built a realistic multi-step workflow and learned: - **Workflow inputs** — How to define dynamic inputs that become template variables - **API Action steps** — How to fetch external data (threat intelligence) via REST APIs - **LLM steps** — How to analyze data and produce structured outputs - **Multi-step data flow** — How output from one step becomes input to the next - **Testing and iteration** — How to verify your agent works and refine it ## Next Steps - **[Add a Trigger](/features/creating-agents/)** — Convert this to a triggered agent that fires automatically when alerts arrive - **[Configure Integrations](/features/integrations/)** — Connect Jira or ServiceNow to create tickets from triage results - **[Explore Security Workflows](/best-practices/security-alert-triage/)** — See advanced patterns for alert triage, vulnerability reporting, and threat hunting - **[Learn Multi-Agent Coordination](/best-practices/multi-agent-coordination/)** — Chain multiple agents together for investigate -> remediate pipelines --- ## 'Use Memory' Source: https://docs.kindo.ai/quickstarts/memory/ [Memory](/features/memory/) lets Kindo carry useful context across conversations, so you do not have to repeat yourself in every chat. This quickstart walks through the key tasks. ## Prerequisites - A Kindo account, signed in. - Memory turned on for your organization (it is on by default). If the **Memory** toggle does not appear in your chat settings menu, an administrator has turned it off — see [Troubleshooting](/features/memory/#troubleshooting). ## Turn Memory On or Off Memory in chats is controlled by a per-user toggle. It applies to all of your chats — direct conversations with a model. ![Chat settings menu — the Memory toggle under User Memory](../../../assets/docs/memory/chat-memory-toggle.png) The setting persists across sessions and affects only you. Turning Memory off does not delete anything already stored. ## Ask Kindo to Remember Something {/* SCREENSHOT SLOT: memory-store-tool-call — A chat conversation in which the user asked Kindo to remember a fact and the assistant's memory_store tool call is visible in the conversation. Caption: "Saving a fact — the memory_store tool call is visible in the conversation." */} :::note[Duplicate detection] If a near-identical memory already exists, the assistant reports the duplicate instead of saving a second copy. Review the existing memory on the User Memory page and edit it there if needed. ::: There is no manual "create memory" form. Everything stored comes from your conversations — to add a fact, ask the assistant to remember it. ## Ask Kindo to Forget Something :::note If several stored memories could match, delete the right one yourself from the User Memory page instead. A conversation that already recalled the memory keeps its existing context — the deletion takes effect for later conversations. ::: ## Review, Edit, or Delete Your Memories Every memory Kindo stores for you is visible on the User Memory page — no other user can see or search it. ![User Memory page — review, edit, or delete stored memories](../../../assets/docs/memory/user-memory-page.png) ## Verify Recall Works Recall is relevance-based: Kindo searches your stored memories and provides the ones related to the current topic, not all of them. ## Why the Toggle Is Greyed Out with an Agent In an agent conversation, the Memory toggle in the chat settings menu is disabled. This is expected: your User Memory applies only in chats. Each conversation uses exactly one memory space — a chat uses your User Memory; an agent conversation uses that agent's own [Agent Memory](/features/memory/#agent-memory) (when enabled), never yours. Nothing from your private User Memory is read or written while you work with an agent. ## What to Expect from Agent Memory Agent Memory is scoped to an agent rather than to you, and it is shared: a fact captured while one person works with the agent can be recalled when a different person uses the agent later. Because that crosses the usual boundary between users' sessions, it is consent-gated — it stays off unless both the organization administrator and the agent's author explicitly opt the agent in. When it is on, the chat settings menu shows an added read-only Agent Memory row (your per-user Memory toggle, when available, still appears alongside it — disabled, because agent conversations never use your User Memory), and the menu's trigger reads "MEMORY ON" so you can tell at a glance that the conversation uses the agent's shared memory. :::note See the [Memory](/features/memory/#agent-memory) reference section for the full model. ::: ## Next Steps - [Memory](/features/memory/) — the full feature reference: how memory is scoped, the memory tools, configuration, and troubleshooting. - [Glossary](/reference/glossary/) — Memory Engine, User Memory, Agent Memory, capture, and recall. - [Memory and Persistence Patterns](/best-practices/memory-and-persistence/) — file and state persistence for agents, a separate concern from memory. --- ## Features Source: https://docs.kindo.ai/features/ Kindo's feature modules cover AI-powered chat, autonomous agents, reusable skills, integrations with your existing tools, enterprise governance controls, and durable memory across conversations. ## Feature Hubs --- ## Agents Source: https://docs.kindo.ai/features/agents/ Agents perform routines and processes on behalf of users, either autonomously or semi-autonomously. These routines correspond to Kindo's use cases and frequently take the form of DevSecOps playbooks, runbooks, ITOps routines, or standard operating procedures. ![A workflow agent configured for red team analysis on a SonicWall firewall, showing API request and LLM steps](../../../assets/docs/agents/red-team-agent-example.png) Kindo agents are unique in that they can understand and utilize natural language when executing tasks. An agent can read a human-written document and use that information to inform its behavior, and can translate natural language intentions into programmatic actions such as API calls. ## Types of Agents Kindo supports four agent types: | Type | Trigger | Use Case | | ------------------- | -------------- | ------------------------------------ | | **Chatbot** | User message | Knowledge base Q&A, helpdesk | | **Manual Workflow** | Manual run | Runbook execution, report generation | | **Scheduled** | Cron schedule | Recurring reports, periodic checks | | **Triggered** | External event | Incident response, alert triage | ### Chatbots A chatbot is an agent designed to respond to users with information gathered from its AI model and a set of knowledge base data. **Example:** A helpdesk agent that answers questions about the setup process for a new company laptop or management of a development environment. ### Manual Workflow Agents A manual workflow agent conducts a set of operations within a system of record. Like chatbots, workflow agents can utilize natural language resources such as knowledge bases, files, and articles. Additionally, workflow agents can source data from external sources via API calls and employ external tools to perform actions. **Example:** An agent designed to manage and deploy a development infrastructure on a cloud platform. ### Scheduled Agents A scheduled agent is a workflow agent that runs automatically on a cron schedule. These agents are ideal for recurring tasks like daily reports, weekly compliance checks, or periodic data synchronization. Scheduled agents have [organization-level quotas and scaling limits](/features/scheduled-agents/#scaling-limits-and-quotas). **Example:** An agent that generates a weekly vulnerability report every Monday at 9 AM. ### Triggered Agents A triggered agent is a special type of workflow agent that lies dormant while waiting for a specified event to occur -- such as a new entry in a ticketing system -- before launching a response. When the trigger event is detected, data from the event can be used by LLMs within the agent for analysis and decision-making. Kindo supports two trigger flavors: - **Integration Event triggers** activate on events from a connected integration (Jira, ServiceNow, Linear, etc.) and match against typed fields with structured conditions. - **Direct Webhook URL triggers** expose a unique, token-protected URL that any external system can `POST` to. The trigger fires on every request, with optional natural-language gating against the request payload -- useful when no native integration trigger exists or when an arbitrary system needs to invoke the agent. See [Creating and Managing Agents](/features/creating-agents/#creating-an-agent) for setup details. **Example:** An agent that performs first-order root cause analysis for a platform engineering incident. ## Agent Steps Agents are collections of **Agent Steps**: atomic operations conducted by Kindo on behalf of the user. ![Agent steps showing API requests and an LLM step for sorting vulnerabilities by severity](../../../assets/docs/agents/agent-steps-detail.png) Kindo supports two step types: ### LLM Step A step that requests a response from a permitted LLM. LLM steps are typically used for analysis, sorting information, and processing data gathered by previous steps. ### API Action Step An operation where the agent makes a REST API call to request information or change state in an external system. API requests can be manually configured or generated from a natural language prompt using the Magic button. API Action endpoints must be fully qualified hostnames. IP addresses, single-label hostnames such as `jira`, and cloud metadata hostnames are rejected when the step is saved, with an "Endpoint is invalid or includes a blocked domain" error. Endpoints generated dynamically by the agent are checked the same way when they run. At run time, Kindo also refuses hostnames that resolve to private, loopback, or link-local addresses, and redirects to any of these. A step refused this way fails with an "API request blocked" error. **Note on Integration Actions:** When working with integrated systems like Jira or ServiceNow, you may see pre-built actions in the UI (like "add comment" or "change priority"). These are convenience shortcuts that use the integration's MCP tools behind the scenes — they are not a separate step type in the Kindo engine. Under the hood, they are API Action steps configured for that specific integration. ## Agent Memory and Coordination As you scale from single agents to production systems, you'll need patterns for agent memory and multi-agent coordination: - **[Memory](/features/memory/)** — Agent Memory gives an agent durable, consent-gated memory shared across all of its runs and users - **[Memory and Persistence Patterns](/best-practices/memory-and-persistence/)** — Give agents persistent memory across runs using external systems and platform features - **[Multi-Agent Coordination](/best-practices/multi-agent-coordination/)** — Orchestrate multiple agents to work together on complex workflows ## Next Steps - [Create and manage agents](/features/creating-agents/) for step-by-step instructions - [Create and manage skills](/features/skills/) to build reusable instruction packages for chats and agents - [Version History](/features/creating-agents/#version-history) to inspect and restore previous versions of an agent - [Configure integrations](/features/integrations/) to connect agents to external systems - [Build Your First Workflow](/quickstarts/build-first-workflow/) for a hands-on multi-step workflow tutorial --- ## Creating and Managing Agents Source: https://docs.kindo.ai/features/creating-agents/ Agents enable Kindo users to automate workflows including security runbooks, SRE playbooks, DevOps procedures, and ITOps response workflows. This guide covers how to create and manage agents. ## My Agents After signing in, select the **Agents** tab to see your recently used agents and all agents available to you (both your own and shared). ![The Agents list view showing recently used agents with options to filter by type](../../../assets/docs/creating-agents/agents-list-view.png) Filter agents by type using the tabs: **All Agents**, **Chatbots**, **Workflow Agents**, or **Triggered Agents**. Each agent supports the following actions: - **Share** -- Make the agent available to other users in your organization - **Delete** -- Permanently remove the agent - **Edit** -- Open the agent for modification - **Run** -- Launch the agent ## Creating an Agent Select **Create an Agent** at the top of the Agents window, then choose the agent type: **Triggered**, **Workflow**, or **Chatbot**. ![The agent type selection screen with options for Triggered, Workflow, and Chatbot agents](../../../assets/docs/creating-agents/agent-type-selection.png) :::note Not all AI Models support all file types. The model selected when you begin creating the agent is used by default, but can be changed after generation. Each step can use a different model. ::: ## Agent Configuration Panel Both Triggered and Workflow agents support the Agent Configuration panel for natural language construction: - **Name** -- Give the agent a name (auto-generated if blank) - **Description** -- Summarize the agent's purpose (auto-generated if blank) - **Instructions** -- Provide context about the agent's role, response format, and behavior - **Tools** -- Choose the tool servers that every step and chat of the agent starts from. A step can turn a server on or off for itself. Select **Platform** to use [Kindo's own tools](/features/ai-chat/#tools), which need no integration connection. - **Knowledge Store** -- Attach files, URLs, or websites as reference material Integration tools use the runner's [active connections](/admin/integration-trust-domains/#default-behavior-no-pinned-connection) by default. When your organization allows pinning, choose one or more fixed connections for a server in the agent or step's Tools settings. Everyone running the agent must have access to the connections they use; see [pinned connection rules](/admin/integration-trust-domains/#pinned-connection-agent-authoring). ## Managing Steps - To **delete** a step, open it and click the trash icon. - To **reorder** steps, drag them into the desired sequence. - Each step can be assigned a different AI model. ## Version History Kindo automatically captures a version of an agent's configuration each time it is saved. Versions let you review prior states and roll back to a previous version if a change broke something. The audit log records every restore as an `Agent Restored to Previous Version` event. ### Opening the Version History Panel From the agent builder, open the **Version History** panel via the version-history button in the builder header. The panel lists every captured version, newest first, grouped by day — a `TODAY` group at the top, followed by groups labeled with the date (e.g. `5/12/2026`). Each entry shows the time the version was saved and the email of the user who saved it. The version currently being viewed is marked with a check icon. The most recent saved version represents the agent's current state. ### Read-Only Version Viewer Selecting a version from the Version History panel opens it in a read-only viewer at: ``` /agents/builder//version/ ``` The viewer renders the agent in a read-only layout with up to four top-level sections: **Agent Name**, **Agent Steps**, and **Agent Configuration** (description, instructions, tool servers, knowledge store, flags). If the agent has any triggers, an **Agent Triggers** section also appears. Each step shows its per-step configuration — model, interaction mode, prompt template, tool servers, and static content for LLM Steps; method, endpoint, headers, parameters, body, and abort-on-failure for API Action Steps. Every field is non-editable, and run controls are hidden. A banner at the top identifies the version and offers a **Restore this version** action. Opening the version viewer and listing version history both require **`EDIT_WORKFLOW`** permission on the agent. Users with `VIEWER`-only access cannot open the Version History panel or load a historical version. ### Restoring a Previous Version Restore replaces the agent's current configuration with the selected version's configuration. After restore: - The agent's live state matches what the version captured. - A new version row is written to history with a marker that it was restored from an earlier version. The source-version reference is stored structurally and surfaced in the audit log event, so the restore itself is auditable. - The pre-restore state is captured as its own version too (if it wasn't already the latest), so nothing is lost — a second restore can roll forward again. Like the version viewer itself, restore requires **`EDIT_WORKFLOW`** permission on the agent. ### What a Version Captures (and What It Does Not) A version snapshots the **content** of an agent — its definition and the resources it depends on by reference. It deliberately does **not** snapshot sharing, secrets, or live external state. **Captured:** - Display name, description, system prompt - Tool servers enabled for the agent as a whole, including any pinned connections - Steps with all per-step configuration: - **LLM Steps** — prompt template, model selection, interaction mode, tool server configs, static content - **API Action Steps** — method, endpoint, headers, query/body parameters, secret references, abort-on-failure flag - Inputs (template variables and their settings) - Triggers (Integration Event, Cron, Direct Webhook) and their conditions - Knowledge Store attachments - The **Available as subagent** flag (see [Multi-Agent Coordination Patterns](/best-practices/multi-agent-coordination/#pattern-5-subagent-invocation)), and the demo-mode flag **Intentionally not captured:** - **Sharing and access controls** — org role, public role, user permissions, and user-group permissions are not part of version state. Restoring an old version does **not** change who can see or edit the agent (Google Docs / Notion model). - **Direct Webhook secret tokens** — restore preserves the token on the live trigger row when the row was never deleted, but never re-issues a token. - **Cron schedules' live registrations** — schedule and timezone are restored, but the underlying scheduler job is de-registered, so a restored cron trigger does not resume firing on its own. **All triggers land inactive after restore.** Integration Event, Direct Webhook, and Cron triggers all come back disabled and must be explicitly re-enabled. This always-disable-on-restore policy is intentional: restoring a months-old agent should not silently resume a webhook listener, integration subscription, or cron job whose downstream side effects may no longer be desired. ## Exporting and Importing Agent Definitions You can export an agent's definition to a portable JSON file and import it as a new agent — in another account, another organization, or another Kindo deployment. This is useful for sharing agents, keeping backups, and promoting an agent between environments. ### Exporting an Agent Export is available in two places: - **From the agent builder** -- Click **Download** in the builder header. - **From the Agents list** -- Open an agent's row menu and select **Download**. Either action downloads a `.json` file describing the agent. The export is a snapshot of the agent's definition only. It does **not** include sharing or access settings, secret values, or live integration credentials. ### Importing an Agent 1. On the **Create an Agent** screen, click **Import from file**. 2. Select one or more `.json` files that were exported from Kindo. You can import up to **20 files at once**. Each file is imported as a **new** agent that you own — importing never overwrites an existing agent, and sharing settings are not carried over. When you import a single file, Kindo opens the new agent in the builder. When you import several, Kindo shows a summary of what succeeded and what failed. ### What an Import Carries Over An imported definition contains the same content that a version captures — see [What a Version Captures (and What It Does Not)](#what-a-version-captures-and-what-it-does-not). In particular: - **Reconnect your integrations.** Integration connections are not transferred. After importing, the builder flags any tool that needs a connection so you can connect your own. - **Direct Webhook triggers are imported disabled.** The secret webhook URL is never exported, so you re-create the webhook after import. - **Unavailable AI models fall back to your default.** If a step references an AI model you can't use — it doesn't exist in your organization, is restricted, or belongs to another organization — the step is set to your default model instead. The import still succeeds; switch the model in the builder if needed. - **Secrets and Knowledge Store / static content must exist in your organization.** These are matched by reference. If one isn't available to you, the import of that file fails (see below). ### When an Import Can Fail A file is rejected — and no agent is created from it — when: - The file is not valid JSON, or is not a Kindo agent definition. - The definition references a **secret** or **Knowledge Store / static content** item that does not exist in your organization or that you do not have access to. Unavailable **AI models** and **integration connections** never fail an import — an unavailable model is swapped for your default, and an unavailable connection is left unset for you to reconnect. When you import multiple files, each is processed independently. Valid files still import even if others in the same batch fail, and the failures are reported by file name so you know which ones to fix. ## File Library Add files to Kindo's library from two locations: 1. **From Chat** -- Click the **+** button in the query bar 2. **When Creating an Agent** -- Click **Add Knowledge Store** Both methods open the File Library where you can select existing files or upload new ones. ![The File Library showing uploaded files with checkboxes for selection](../../../assets/docs/creating-agents/file-library.png) ## Agent Types Reference Kindo supports four agent types, each designed for different automation patterns: | Type | Trigger | Best For | | ------------------- | -------------- | -------------------------------------------------------------- | | **Chatbot** | User message | Knowledge base Q&A, helpdesk, conversational interfaces | | **Manual Workflow** | Manual run | Runbook execution, one-off reports, on-demand analysis | | **Scheduled** | Cron schedule | Recurring reports, periodic compliance checks, daily summaries | | **Triggered** | External event | Incident response, alert triage, ticket automation | ### Choosing an Agent Type **Start with a Chatbot** when you need conversational interaction or knowledge base access. **Use a Manual Workflow** when you need to perform a sequence of operations on demand, especially when those operations involve API calls or data processing. **Add a Schedule** when the same workflow needs to run repeatedly (daily security reports, weekly compliance scans). **Add a Trigger** when the workflow should respond to external events. Use an **Integration Event** trigger for events from a connected integration (new tickets, security alerts), or a **Direct Webhook URL** trigger when no native integration exists or when an arbitrary system needs to invoke the agent. ## Best Practices As you move from simple agents to production workflows, consult these guides: - **[Prompt and Agent Configuration Management](/best-practices/prompt-and-agent-configuration/)** — Version control, testing, and governance for production agents - **[Working with Large Context](/best-practices/working-with-large-context/)** — Techniques for handling large documents, logs, and tool outputs - **[Memory and Persistence Patterns](/best-practices/memory-and-persistence/)** — Give agents persistent memory across runs using external systems - **[Multi-Agent Coordination](/best-practices/multi-agent-coordination/)** — Orchestrate multiple agents to work together on complex workflows --- ## AI Chat Source: https://docs.kindo.ai/features/ai-chat/ Kindo AI Chat provides a secure, enterprise-grade interface for interacting with AI models. Unlike consumer chatbots, every conversation runs through Kindo's governance layer with DLP filtering, audit logging, and role-based access controls. ## Key Capabilities - **Multi-model access** -- Switch between 26+ LLMs from the same interface. Choose General Purpose, Reasoning, Long Context, or Cybersecurity-focused models depending on your task. - **File attachments** -- Upload documents, logs, configurations, images, and other files for the model to analyze. Supported direct and tool-assisted formats include text, PDF, CSV, JSON, code files, images for vision models, and audio/video conversion when available. - **Tool integration** -- Enable Library Search, URL Search, and Web Search to give the model access to your knowledge store, specific URLs, or live internet results. - **Conversation history** -- Review past conversations filtered by date, type (your own or shared), and topic. Conversations persist across sessions. - **Sharing** -- Share conversations with team members for review or collaboration. ## Enterprise Controls Every chat interaction is protected by Kindo's security framework: | Control | What It Does | | ---------------------- | --------------------------------------------------------------------------------------------------------- | | **DLP Filters** | Automatically detect and redact sensitive data (PII, credentials, secrets) before it reaches the AI model | | **Audit Logging** | Every message, model selection, and tool use is logged to your organization's audit trail | | **RBAC** | Administrators control which models and tools are available to each user or group | | **Model Restrictions** | Disable specific models organization-wide or restrict them to certain roles | ## Model Categories Models are organized by capability: | Category | Best For | Examples | | ------------------- | ------------------------------------------------------ | ------------------------------- | | **General Purpose** | Most tasks -- writing, analysis, summarization | Claude, GPT-4 | | **Reasoning** | Complex analysis, multi-step problem solving | OpenAI o-series | | **Long Context** | Large documents, codebases, lengthy analysis | Models with 100k+ token windows | | **Cybersecurity** | Threat analysis, vulnerability assessment, red teaming | DeepHat | ## Working with Files Attach files to provide context for your queries: ![Attaching files to a chat query using the + button](../../../assets/docs/getting-started/attach-files.png) 1. Click the **+** button next to the query bar. 2. Select files from your machine. 3. Choose how the model should process them: - **For All Files** -- Apply your query to the entire collection - **Per File** -- Apply your query to each file independently Image attachments can be provided directly to supported vision models. Audio and video files are processed through file conversion or transcription tools rather than being sent directly to chat models as raw media. Kindo shows an unsupported file type message when a provider rejects a file part that reached the model request. ## Tools Toggle tools to extend the model's capabilities: ![The Tools panel with Library Search, URL Search, and Web Search toggles](../../../assets/docs/getting-started/tools-selection.png) - **Library Search** -- Searches your organization's uploaded knowledge base using semantic search (embeddings). - **URL Search** -- Fetches and includes content from a specific URL you provide. - **Web Search** -- Searches the live internet for current information. - **Platform** -- Creates and edits agents, enables or disables their tool servers, and attaches or removes existing skills and knowledge. It also lists agents, reads their configuration, starts standalone runs, and inspects runs and conversations. Organization admins can also [look up members](/reference/tool-catalog/#platform-tools). Successful creation and edits show an agent preview card with Open Agent and Start chat actions. All actions use your existing access permissions; no integration account is required for Platform itself. Skills and Knowledge Store must be enabled to manage those attachments. - **Integrations** -- Uses your connected accounts to work with external services. Tools are enabled per-message and can be combined. For integrations, choose your [active accounts](/admin/integration-trust-domains/#default-behavior-no-pinned-connection) or [pin connections](/admin/integration-trust-domains/#pinned-connection-agent-authoring) to a chat. External clients can use integration and Platform tools through the [MCP API](/apis/mcp/quickstart/). ## Context Usage Once the assistant has responded in Actions mode, a circular indicator next to the model selector shows how much of the selected model's context window has been used. (In Ask mode or before the first response, the model selector shows a bolt icon instead.) The ring fills and changes color as the conversation grows: green under 60%, yellow at 60-79%, red at 80% and above. Hover the ring for a tooltip with the current token count and -- when available -- the context window total, tokens remaining, and conversation credits consumed. The ring pulses while the model is responding and briefly afterward while the latest counts are refreshed. When a long conversation approaches the context limit, Kindo automatically compacts it: earlier messages are summarized so the most relevant context is kept while the session continues. A **Context summarized** marker appears in the conversation when this happens, so the change is visible rather than silent. You do not need to start over or manage the context yourself -- your conversation keeps working as it grows. ## Next Steps - [Create an Agent](/features/creating-agents/) to automate recurring chat-based tasks - [Configure Security Controls](/admin/security-controls/) to manage DLP and model access - [Learn about Integrations](/features/integrations/) to connect external data sources --- ## Scheduled Agents Source: https://docs.kindo.ai/features/scheduled-agents/ Scheduled agents are workflow agents that run automatically on a cron schedule. They are ideal for recurring tasks like daily reports, weekly compliance checks, or periodic data synchronization. ## How Scheduling Works Each scheduled agent is backed by a **cron trigger** that determines when it runs. Kindo uses standard 5-field cron expressions (minute, hour, day-of-month, month, day-of-week) and supports timezone-aware scheduling. When you create a scheduled agent, you define: - A **cron expression** or natural-language schedule (e.g., "every Monday at 9 AM") - A **timezone** for the schedule to run in - The **workflow steps** the agent executes on each run ## Scaling Limits and Quotas Scheduled agents have organization-level quotas to ensure platform stability. These limits apply to the total number of **enabled** scheduled agents across all users in your organization. ### Default Limits | Limit | Default Value | Description | | --- | --- | --- | | **Max scheduled agents** | 5 per organization | Total number of enabled scheduled agents allowed | | **Minimum run interval** | 60 minutes | Shortest allowed interval between consecutive runs | ### How Quotas Work - Quotas are counted at the **organization level**, not per user. - Only **enabled** scheduled agents count toward the quota. Disabling a scheduled agent frees up a slot. - The quota is checked when **creating** a new scheduled agent or **re-enabling** a disabled one. - If your organization has reached its quota, the "Build Agent" button for scheduled agents will be disabled with an explanatory message. - Your current usage is displayed on the agent creation screen (e.g., "3 / 5 scheduled agents used"). ### Requesting Higher Limits If your organization needs more scheduled agents or shorter run intervals, contact your Kindo account representative or reach out to [support](mailto:support@kindo.ai). Limits can be adjusted per organization based on your plan and usage patterns. ## Best Practices ### Choose Appropriate Intervals - **Hourly or less frequent** schedules are recommended for most use cases. - Consider whether your data sources update frequently enough to justify shorter intervals. - Longer intervals reduce credit consumption and system load. ### Design for Idempotency Scheduled agents should produce consistent results when run multiple times. If a run fails or is retried, the agent should handle duplicate data gracefully. ### Monitor Agent Runs Review your scheduled agent run history regularly in the **Agent Monitoring** tab. Look for: - Failed runs that may need investigation - Runs that take unexpectedly long to complete - Agents that are no longer needed and can be disabled to free quota ### Manage Your Quota - **Disable unused agents** rather than deleting them if you may need them again. Disabled agents don't count toward your quota. - **Consolidate similar agents** where possible. A single agent with multiple steps is often more efficient than several single-step agents. ## Creating a Scheduled Agent For a detailed walkthrough, see the [Scheduled Vulnerability Report](/best-practices/scheduled-vulnerability-report/) tutorial. ## Next Steps - [Creating and Managing Agents](/features/creating-agents/) for general agent setup - [Agents Overview](/features/agents/) for all agent types - [Scheduled Vulnerability Report](/best-practices/scheduled-vulnerability-report/) for a hands-on tutorial --- ## Model Configuration Source: https://docs.kindo.ai/features/models/ This guide explains how Self-Managed Kindo installations define and maintain **global models** -- the system-wide LLMs (and related models like embeddings or transcription) that power workflows, chat, ingestion, and other core experiences. In a self-managed environment, you add models to Kindo, map them to Unleash feature variants, and keep those mappings up to date when models are replaced. :::tip[Deploying model infrastructure?] Before configuring models in Kindo, you need working inference endpoints. See [Prepare AI Models](/deploy/prepare-ai-models/) for how to deploy and verify self-hosted models (vLLM, context window sizing, tool-call parsers) or connect cloud API providers. ::: ## Unleash Features and Strategy Variants Each key below is an Unleash feature whose **variant payload** contains one or more global model IDs. The backend and frontend read these variants to decide which models to use. ### Required Features | Feature Key | Description | |---|---| | `AUDIO_TRANSCRIPTION` | Converts audio to text | | `CRON_EXPRESSION_GENERATION` | Generates cron expressions from natural language schedule descriptions | | `DEFAULT_WORKFLOW_STEP_MODEL` | Default model for workflow step execution | | `EMBEDDING_MODELS` | Models for generating text embeddings | | `INGESTION_WORKERS` | Models for data ingestion and extraction | | `INTERNAL_AUTO_GENERATION` | Internal model for automatic content generation and internal worker tasks | | `LOCAL_STORAGE_CHAT_ACTIONS_MODELS` | Fallback default chat model for the model dropdown and chat action selection. Applies when an org admin has not chosen a default chat model in Settings → Models (or the chosen model is unavailable) | | `TOOL_CALLING_MODELS` | Models that support tool/function calling (used to filter model selection in chat UI) | ### Optional Features | Feature Key | Description | Notes | |---|---|---| | `API_STEP_GENERATION` | Models for generating API steps in workflows | | | `DYNAMIC_API_REQUEST_PARSER` | Parses dynamic API requests | | | `SLACK_MESSAGE_GENERATION` | Generates Slack messages | Only if Slack integration is enabled | | `MULTIMODAL_MODELS` | Models with image/multimodal input support | Only if multimodal chat is enabled | ## Minimum Model Requirements Self-Managed Kindo needs baseline global models to function. Without them, core flows like chat, workflow execution, ingestion, and indexing will fail. ### Absolute Minimum Set | Model Type | Purpose | Recommendations | |---|---|---| | **Embedding model** | Semantic search, retrieval, and indexing | Open source: BAAI/bge-m3. Hosted: Gemini embedding | | **Strong/large model** | Complex reasoning, workflow execution, long-form generation | Open source: gpt-oss 120B. Hosted: Gemini 2.5 Pro, Claude 4.5, GPT 5.2 | | **Audio transcription** | Audio file processing, voice notes, transcription workflows | Open source: Faster Whisper. Hosted: Deepgram | ### Recommended Additional Models - **Small model** (title generation, quick summaries, low-latency steps): Gemma 3 or Llama 3.2 7B - **Security-focused**: DeepHat V3 - **Multimodal**: Gemini 2.5 Pro (image + text) ## Managing Models ### Overview of Steps 1. Add the new model in **Settings → Models → Add model**. 2. Update Unleash variants to reference the new model IDs. 3. Delete the old model with replacement. :::caution If the model you are deleting is still referenced by Unleash, the response will list the feature flags that need updates. Update those variants first, then retry the delete. ::: ### Add a Self-Managed Model in the UI A self-managed model is a model you already run or pay for somewhere else (a self-hosted vLLM box, a serverless endpoint such as Together, Fireworks, or Groq, an internal inference gateway, or your own OpenAI account) that you want to use inside Kindo for chat and agents. The endpoint must be **OpenAI-compatible** (it accepts OpenAI-style chat completion requests), or an **Anthropic Messages** endpoint whose URL ends in `/anthropic`, such as Claude on Microsoft Foundry. The Add Model form asks only for the handful of facts any such endpoint needs, in plain English. | Field | Required? | What to enter | |---|---|---| | Display name | Yes | The name you'll see in Kindo when choosing this model, for example `Llama 3.3 70B (self-managed)`. | | Model ID | Yes | The model's name exactly as your endpoint expects it in a request, for example `Qwen/Qwen3.7-Max`, `llama-3.3-70b`, or `gpt-4o`. Use exactly what your provider or server calls it. For Azure OpenAI or Microsoft Foundry, use your deployment name. | | Endpoint URL | Yes | The base URL of your model's API, for example `https://api.together.xyz/v1`, `http://my-vllm:8000/v1`, or `https://your-resource.openai.azure.com`. Paste exactly what your provider gives you — Kindo tidies it up (trims it, drops a trailing slash or a mis-pasted `/chat/completions`, and adds the version segment if your base URL doesn't already include one). | | API version | No | Leave blank for most providers. Only needed when your endpoint requires an `api-version` query parameter — for example an Azure OpenAI deployment pinned to a dated version such as `2024-10-21`. | | API key | Yes | The key your endpoint uses to authenticate. Stored encrypted; never shown again. | | Context window | Yes | Your model's maximum context length in tokens, from your provider's docs, for example `128000`. Kindo uses this to keep long conversations within the model's limit. | | Max output tokens | Yes | The maximum number of tokens the model should generate per response, from your provider's docs, for example `4096`. | #### Endpoint URL examples Paste the base URL your provider hands you. Common shapes already work as-is: - OpenAI: `https://api.openai.com/v1` - Together: `https://api.together.xyz/v1` - Groq: `https://api.groq.com/openai/v1` - Fireworks: `https://api.fireworks.ai/inference/v1` - Self-hosted vLLM: `http://:8000` (Kindo appends `/v1` for you) - Azure OpenAI: `https://.openai.azure.com` (Kindo routes to Azure's OpenAI-compatible `/openai/v1` surface for you) - Claude on Microsoft Foundry: `https://.services.ai.azure.com/anthropic` (see [Claude on Microsoft Foundry](#claude-on-microsoft-foundry)) #### Azure OpenAI Azure OpenAI endpoints work like any other OpenAI-compatible endpoint: - **Most deployments:** paste your resource URL (for example `https://my-resource.openai.azure.com`), set Model ID to your **deployment name**, and leave API version blank. Kindo talks to Azure's OpenAI-compatible `v1` surface, which needs no `api-version` parameter. - **Deployments pinned to a dated API version:** fill in the API version field (for example `2024-10-21`) and Kindo dispatches through Azure's versioned API instead. Pasting the full target URI from the Azure portal (`https://…/openai/deployments//chat/completions?api-version=…`) also works — Kindo picks up the `api-version` from the URL automatically. #### Claude on Microsoft Foundry Microsoft Foundry serves Claude only through the Anthropic Messages API, not an OpenAI-compatible route. An Endpoint URL whose path ends in `/anthropic`, `/anthropic/v1`, or `/anthropic/v1/messages` tells Kindo to use that API: - Paste `https://.services.ai.azure.com/anthropic` (the full `/anthropic/v1/messages` URL from the Foundry portal also works). - Set Model ID to your **deployment name**. Any deployment name works. - Leave API version blank. The Messages API does not use one, so Kindo ignores it for these endpoints. - Set Max output tokens within the model's limit. The Messages API requires it on every request. Kindo authenticates with your key in the `x-api-key` header and translates chat and tool calls between Kindo and the Messages API for you. :::note The URL must point at your chat completions or Anthropic Messages endpoint. URLs for other APIs (for example ones ending in `/embeddings` or `/responses`) are rejected so a wrong paste can't silently mis-configure the model. ::: #### Test & Save **Test & Save** (the primary button) sends a real request to your endpoint (a chat completion, or a Messages request for an `/anthropic` endpoint) before anything is saved, and tries again in the shapes described below when the endpoint asks for one. If the test fails, nothing is created: the form stays filled in and the error points at the field to fix: a rejected key highlights **API key**, an unknown model name highlights **Model ID**, an unreachable or timed-out endpoint highlights **Endpoint URL**, a too-high **Max output tokens** highlights that field and names the model's maximum when the provider reports it, and a rejected request parameter is called out by name. From the failure banner you can either **Edit** the form and test again, or **Save anyway** to store the config without a passing test. Some models reject the `max_tokens` request parameter and accept only `max_completion_tokens`, for example some newer Azure OpenAI deployments. When your endpoint rejects `max_tokens` by name, **Test & Save** sends the test once more with `max_completion_tokens`. If that passes, Kindo saves the model so its requests carry your **Max output tokens** as `max_completion_tokens`. **Save without testing** can't detect this, so save such a model with **Test & Save**. Kindo chat always offers the model tools. Some Azure OpenAI reasoning models reject tools on chat completions and point to the Responses API instead. For an Azure OpenAI endpoint, the test includes a tool, as a real chat does. When the endpoint rejects it that way, **Test & Save** sends the test once more to the Responses API. If that passes, Kindo saves the model so its requests use the Responses API, where chat with tools works. **Save without testing** can't detect this either. On an endpoint with an API version, the Responses API needs version `2025-03-01-preview` or later. **Save without testing** skips the test entirely — useful when the endpoint is temporarily down, a self-hosted model is cold-starting, or you don't want to spend a request on a quota-limited endpoint. The model list shows the outcome as a chip next to each self-managed model: **Verified** (last test passed), **Untested** (saved without a passing test), or **Failed** (a later re-test failed — hover the chip for the reason). Kindo's built-in global models are managed for you and carry no chip. ### Models with Multiple Providers (Model Groups) A model in Kindo is not tied to one endpoint. You can attach several provider endpoints to the same model — for example the same model served from two regions of your cloud provider, or a self-hosted model behind two vLLM endpoints. In the Add Model form, pick an existing model in the **Model** field instead of typing a new name, and the endpoint you are configuring is added as another provider for it. Attaching and reordering a model's providers is an organization-admin action on the Enterprise tier, in installations where multi-provider models are enabled; the endpoints you attach belong to the model's own provider. People always pick the **model**, never a specific provider. Which of the model's providers serves a given request is not decided until the request is already on its way, by the model's [fallback chain](#fallback-chain) and its [session affinity](#session-affinity). Because of that, **a model group behaves as the lowest common denominator of its providers**: | Control | How the group resolves it | Effect | |---|---|---| | Context window | **Smallest** across the model's enabled providers | A 32k provider under a 200k model makes the whole model 32k. | | Tool limit | **Smallest** across the model's enabled providers | An OpenAI or Azure OpenAI endpoint accepts at most 128 tools per request, so one of them makes 128 tools the model's effective limit. | | DLP filters | **Strictest** across the model's providers | A filter that redacts for any provider redacts for the whole model. | | Provider enablement | **Strictest** across the model's providers | A provider your organization has disabled in its security controls makes the whole model unavailable, even where that provider is attached to the model only through a disabled endpoint. (Turning off one endpoint in the model's provider list is a different thing — see [Fallback chain](#fallback-chain).) | #### Why the group uses the minimum Two reasons, both about the request being decided before the provider is: - **Routing is blind and failover is silent.** Kindo has to fix the capacity budget for a request *before* it knows which provider will serve it — and a request that fails over mid-flight can land on a different provider than it started with. The only budget that is valid for every provider the request could reach is the smallest one. - **Runs have to replay identically.** The budget is recorded when the request starts so that a resumed or retried run reproduces exactly the same conversation. If the budget tracked whichever provider happened to answer, a retry that fell back to a smaller provider would overflow it. #### Group providers with like-for-like capacity Pick providers that are close to each other in capability, so nothing is stranded at the floor: - **Match context windows.** Attaching a 32k endpoint to a 200k model does not add 32k of capacity — it removes 168k from the model. If a provider is much smaller, give it its own model instead, so people can choose it deliberately. - **Match tool support.** If tool-heavy agents use the model, an OpenAI or Azure OpenAI endpoint accepts at most 128 tools per request — a request carrying more can fail whenever that endpoint serves it, so the model's effective tool limit becomes 128. - **Match governance.** A provider your organization has disabled, or one with stricter DLP filters, applies its rules to the whole model, not just to the requests it serves. The Add Model form tells you about this before you save: once you pick a model to attach to, it shows the context window the new endpoint should clear and warns you when the endpoint would newly limit the model's tool use. {/* The `#fallback-chain` and `#session-affinity` slugs are linked from the product UI and from announcements. Renaming either heading breaks those deep links — change the links in the same commit if you must. */} #### Fallback chain The providers attached to a model form a **fallback chain**: an ordered list, lowest priority number first, and that is the order the provider list shows in **Settings → Models**. Move a provider up or down to change the chain. Turning a provider off takes its endpoint out of the chain — the model's other endpoints keep serving — and leaves its configuration in place so you can turn it back on. Two things move a request off the provider that was chosen for it: - **Retries.** A transient failure — a timeout, a rate limit, or a provider-side error — is retried inside the model, up to twice. Failures a retry cannot fix, such as a malformed request, are not retried. - **Descent to the next priority level.** When the retries are used up, the request is re-aimed at the next priority level down the chain. This is also how an interrupted streamed response continues: it resumes on the next level, and text already streamed to the reader is not repeated. Most errors take this path once, not only transient ones — a request whose content the model rejected outright, or one that overflowed the context window, is returned to the caller instead. Descending the chain needs at least two distinct priority levels. An endpoint you attach starts at the top priority, which ties it with the provider the model already had, and providers that share a priority number share traffic rather than one preferring the other — a retry can still move a failed request to the other, but there is no lower level to descend to. So if you want a deliberate primary with a backup behind it, move the rows up or down once: the list renumbers into distinct levels and the chain becomes strict. A provider that fails persistently is put in **cooldown**: it is dropped from the chain for about half a minute and then becomes eligible again on its own, so a hard-down endpoint is not re-tried by every request in the meantime and recovery needs no administrator action. Cooldown only ever removes a provider that has an alternative — a model with a single provider is never cooled out of its own chain, because there would be nothing left to serve it. #### Session affinity Providers cache the part of a prompt they have already seen, and that cache lives on the endpoint that served the request — it is not shared between two endpoints of the same model. Because a conversation resends its whole history on every turn, a cache hit is the difference between the provider re-reading the entire conversation and re-reading only the newest turn. That drives both **latency** (how fast a long conversation starts responding) and **cost** (cached input is billed at a fraction of fresh input). A long conversation bounced between two providers would pay full price for the same history again on every switch. So once a conversation is under way, Kindo keeps it on the provider that served its earlier turns where it can — **session affinity**: - **The pin is per conversation, and it is best effort.** It exists to keep a cache warm; it is not a promise about which provider answers you. It covers the conversations Kindo drives — chat and agent runs. Requests sent straight to the Kindo APIs are not pinned. - **The pin expires.** It is upper-bounded by a time-to-live (one hour by default), so a conversation picked back up much later is routed fresh. The lifetime is deliberately longer than the provider cache windows it protects: a pin whose cache has already lapsed simply re-warms the same endpoint. - **Health beats affinity.** If the pinned provider is in cooldown or otherwise out of the healthy set, the conversation falls through to a surviving provider and pins to whichever one serves it. Affinity never holds a conversation on a dead endpoint. - **Until it is cooled out, though, the pinned provider is still preferred** — including over a healthy provider at a lower priority level. A conversation can therefore keep attempting a failing endpoint for the turn or two it takes cooldown to remove it, which is what recovery looks like from the inside rather than a stuck conversation. - **A single-provider model is unaffected** — with one endpoint there is only one choice. What this means when you plan a model's providers: adding a second provider to a busy model does not cool the caches you already have, because conversations in flight stay where they are and only later ones spread out. It also means the providers worth grouping are the ones that are close in capability (see above) — a chain whose members are interchangeable is the chain that keeps caches warm, since every fallback lands on a cold cache. #### One provider is a complete configuration A model has exactly one provider configured unless an administrator attaches another, and that is the state every model starts in. A single-provider model has no chain to descend and no pin worth observing, so everything above describes behavior you opt into per model, one provider at a time — there is nothing to migrate, nothing to re-configure, and nothing required of an administrator who never attaches a second provider. --- ## Integrations Setup Source: https://docs.kindo.ai/features/integrations/ Kindo supports integrations using Nango for authentication and the MCP protocol for agent access to external systems. Most MCP servers are built in-house and run by the customer as part of a self-managed installation, though a growing number of official hosted MCPs can be accessed directly. :::tip This page covers infrastructure setup. For governance of who can use which integration and which tools (per-user-group scoping, shared connections, etc.), see [Tool Action Access Controls](/admin/rbac-tool-actions/). For the enterprise pattern of isolating an integration's identity to a single team -- so other teams cannot see or use the connection -- see [Integration Identity Isolation](/admin/integration-trust-domains/). ::: ## Nango Setup ### Connecting Nango to Your Installation ![Architecture diagram showing the connection between Nango and a Kindo installation](../../../assets/docs/integrations/nango-kindo-connection.png) #### 1. Set Environment Variables in Kindo | Variable | Description | Required Services | | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | | `NANGO_SECRET_KEY` | Shared secret key for Nango integrations | api, task-worker-ts | | `NANGO_URL` | Host on the Ingress pointing to the `nango` service (must match your Nango dashboard URL) | api, task-worker-ts | | `API_INTEGRATION_WEBHOOK_BASE_URL` | The URL of your API service with `/webhook/integration-connection` appended (e.g., `https:// For self-managed Kindo installs, this secret is managed by the installer and applied to both Nango and the Kindo services that use it. ### Configuring New Integrations The set of integrations available in a Self-Managed install is driven by the Kindo **integration catalog** that ships with each release. On deploy — and on every `kindo integrations apply` — the reconciler creates the corresponding Nango integration instances for the integrations you have enabled, so for a production install you do **not** hand-create them in the Nango dashboard. The Nango dashboard is still where you supply the provider-side configuration the reconciler cannot generate: the OAuth Client ID and Client Secret for `OAUTH2` integrations (see [OAUTH2 Integrations](#oauth2-integrations)) and any webhook secrets (see [Integration Webhooks](#integration-webhooks)). For local or ad-hoc experimentation you can still configure an integration directly in the Nango dashboard (**Integrations** > **Configure New Integration** > select the integration and auth method): ![Nango dashboard showing integration configuration](../../../assets/docs/integrations/nango-integration-config.png) ### Supported Integrations The integrations below have their authentication method and setup notes on this page. For the full inventory of integrations and the tools each one exposes to an agent, see the [Tool Catalog](/reference/tool-catalog/). | Integration | Auth Method | Notes | | -------------------------- | ----------- | ----------------------------------------------------- | | Azure DevOps | BASIC | | | Box | OAUTH2 | | | Cisco Catalyst | BASIC | | | Cisco FMC | BASIC | | | Cofense Triage | OAUTH2_CC | | | CrowdStrike Falcon | OAUTH2_CC | | | Datadog | API_KEY | | | Dropbox | OAUTH2 | Requires file and account scopes | | GitHub | OAUTH2 | Scopes: repo, read:org, user | | GitLab | OAUTH2 | Scopes: api | | Google Docs | OAUTH2 | Requires documents and drive scopes | | Google Drive | OAUTH2 | Requires userinfo and drive scopes | | Google SecOps | OAUTH2 | Configured per connection | | Grafana | API_KEY | | | Graylog | BASIC | | | Jira | OAUTH2 | Requires Jira API and User Identity API scopes | | Linear | OAUTH2 | Scopes: admin, read, write | | Microsoft Entra ID | OAUTH2 | | | Microsoft OneDrive | OAUTH2 | Requires Files scopes | | Microsoft Outlook Calendar | OAUTH2 | Requires Microsoft Graph calendar and contacts scopes | | Microsoft Teams | OAUTH2 | Requires Teams and Chat scopes | | Neo4j | BASIC | | | NVD | API_KEY | | | SailPoint | OAUTH2_CC | | | SAP S/4HANA | BASIC | OData and RFC variants | | ServiceNow | OAUTH2_CC | | | Slack | OAUTH2 | Requires channels, chat, and users scopes | | Smartsheet | OAUTH2 | Requires sheets, folders, and workspaces scopes | | Sourcegraph | API_KEY | | | Splunk | API_KEY | | | Sumo Logic | BASIC | | | Tenable | API_KEY | | :::tip For Microsoft integrations (Entra ID, Defender, Teams, Purview, Intune, etc.), see [Enterprise Application Permissions](/admin/enterprise-app-permissions/) for the full list of API permissions each app registration requires. ::: ### OAUTH2 Integrations For OAUTH2 integrations, first create an OAuth app within the target integration. The callback URL should be ` :::note Connections created before configuring webhooks must be reauthenticated (deleted and reconnected in the Kindo UI). ::: ### Jira Webhooks No additional setup is needed for Jira webhooks. ## Enabling Integrations Kindo keeps each installation's `IntegrationConfig` rows in sync with the **integration catalog** that ships with the release — there is no manual sync step to run. Whenever the platform is deployed or upgraded, the reconciler registers the selected integrations with Nango and reconciles their metadata against the catalog automatically. Which integrations are active is driven by `install-contract.yaml` and applied with the `kindo` CLI: ```bash # See what is available and which are enabled kindo integrations list # Enable one or more (prompts for OAuth credentials where needed) kindo integrations enable github slack # Register the enabled integrations with Nango and reconcile their metadata kindo integrations apply ``` `kindo integrations apply` is idempotent and can be re-run any time the contract's integrations change. It deploys the `integrations-reconciler` release, whose reconcile job registers each selected integration with Nango and reconciles its `IntegrationConfig` metadata against the catalog, marking each selected integration verified. For the full operator reference — including `disable`, `bootstrap`, and `status` — see [Operate](/deploy/operate/) and [Install with kindo-cli](/deploy/install-with-kindo-cli/). Then confirm in the Kindo app under **Command Center** > **Manage Your Integrations**: ![The Kindo integrations settings showing available API providers](../../../assets/docs/integrations/kindo-available-integrations.png) ## MCP Server Setup Each integration's MCP routing — the `mcpServerUrl` and `supportsWebhooks` fields on its `IntegrationConfig` row — is owned by the integration catalog and applied by the reconciler. Do **not** set these by hand: any direct edit to the `IntegrationConfig` table is overwritten the next time the platform is deployed or `kindo integrations apply` runs. The catalog provides each integration's MCP server URL and the reconciler writes it to the row. The stored values look like: ``` http://mcp-grafana.mcp:80/mcp http://mcp-box.mcp:80/mcp http://mcp-unified.mcp:5050/linear/ ``` The stored URL is called exactly as written and includes the full path: `/mcp` for standalone MCP servers, or the integration's mount path (e.g. `/linear/`) for servers hosted by the unified MCP service. Integrations that support event webhooks (for example Jira and Linear) have `supportsWebhooks` set from the catalog as well; see [Integration Webhooks](#integration-webhooks) for the provider-side configuration those require. --- ## Governance and Controls Source: https://docs.kindo.ai/features/governance/ Kindo provides a comprehensive governance framework that ensures AI usage across your organization remains secure, compliant, and auditable. Every interaction -- whether through chat, agents, or the API -- passes through these controls. ## Core Governance Capabilities ### Data Loss Prevention (DLP) DLP filters run on every request and response, scanning for sensitive data before it reaches AI models: - **PII Detection** -- Automatically identifies and redacts personal information (names, emails, phone numbers, SSNs) - **Credential Detection** -- Catches API keys, passwords, tokens, and connection strings - **Custom Patterns** -- Define organization-specific patterns to protect proprietary data - **Per-Model Controls** -- Enable or disable DLP filters for specific models DLP uses Presidio for entity recognition and can be configured by administrators under [**Settings > Organization > Model Access & DLP**](https://app.kindo.ai/?settings=model-access-and-dlp). When your organization has gateway DLP enabled, tool data fetched by external MCP clients through the [MCP API](/apis/mcp/quickstart/) is covered too. Those clients call their own model, so there is no model or provider to resolve a policy against — the gateway instead applies the strictest combination of your organization's, your user groups', and the individual tool's DLP settings, and refuses to return content it cannot inspect. See [MCP API data loss prevention](/apis/mcp/dlp/). ### Models Served by Several Providers A model can be served by more than one provider endpoint (see [Models with Multiple Providers](/features/models/#models-with-multiple-providers-model-groups)). Any of those providers can serve a given request, so **security controls resolve as the strictest across the whole model, not per provider**: a DLP filter that redacts for one of the model's providers redacts on every request to that model, and disabling any one of its providers makes the whole model unavailable to your organization. There is no configuration in which a request slips through under the more permissive provider's rules. The same intersection rule governs capacity — the model runs at the smallest context window and tool limit across its providers. Capacity and security are two halves of one idea: a model group is only ever as capable and as permissive as its most restrictive provider. ### Role-Based Access Control (RBAC) Control who can do what across the platform: | Role | Capabilities | |------|-------------| | **User** | Chat, run shared agents, use approved integrations | | **Agent Creator** | Create and share agents, configure workflows | | **Administrator** | Manage models, integrations, security settings, users | Administrators assign roles and manage permissions through the **Organization** tabs in Settings. Beyond role-based access, Kindo provides **tool action access controls** that govern which integrations and specific tool actions are available to users and AI agents. Administrators configure these at the organization level and refine them per user group. See [Tool Action Access Controls](/admin/rbac-tool-actions/) for the full configuration guide. ### Audit Logging Every action in Kindo is logged: - Chat messages and model responses - Agent executions and step-by-step results - API calls and authentication events - Configuration changes and administrative actions - Integration connections and data access Audit logs can be exported as CSV from the admin UI. ### Model Access Controls Administrators manage which AI models are available: - **Enable/Disable models** -- Toggle model availability organization-wide - **New provider policy** -- Control whether new model providers are automatically enabled - **New model policy** -- Control whether new models from enabled providers are automatically activated ## Policy Enforcement Kindo enforces policies at the platform level, not at the model level. This means: - Policies apply uniformly regardless of which model a user selects - Switching models does not bypass DLP, RBAC, or audit requirements - Agent actions are governed by the same rules as interactive chat ## Compliance Support Kindo's governance features support common compliance frameworks: | Framework | Relevant Controls | |-----------|------------------| | **SOC 2** | Audit logging, access controls, encryption | | **GDPR** | DLP, data residency (on-prem), right to erasure | | **ISO 27001** | RBAC, audit trails, security policies | | **NIST CSF** | Monitoring, access management, incident response | For FedRAMP requirements, deploy Kindo on-premises in an approved cloud environment (e.g., AWS GovCloud) under your own authorization boundary. ## Next Steps - [Configure Security Controls](/admin/security-controls/) to set up DLP and model access policies - [Configure Tool Action Access Controls](/admin/rbac-tool-actions/) to manage which integrations and tools are available per user group - [Set up SSO](/admin/sso/) to integrate with your identity provider - [Set agent and department budgets](/admin/budgets/) to get an email when an agent's or a department's monthly spend reaches an amount you set - [Learn about Agents](/features/agents/) to understand how governance applies to automated workflows --- ## Sandbox Tool Channel Source: https://docs.kindo.ai/features/sandbox-tool-channel/ The sandbox tool channel lets a shell command running in an agent's [sandbox](/admin/faq/#what-is-a-secure-sandbox) call the agent's integration tools directly, without a model turn for each call. A script can look up a record, branch on the answer, and update another system in one command. The agent already knows this from its shell tool, so you do not have to explain the mechanics: describe the read-then-branch job and it writes the script. :::note The sandbox tool channel is being rolled out gradually and is available only to organizations enrolled in the rollout. Contact your Kindo representative if you want it enabled for your organization. ::: ## Two Ways to Call a Tool From the shell, `kindo-tool` prints the tool's result as JSON on stdout, so it pipes into `jq`: ```sh kindo-tool list kindo-tool call linear_list_issues '{"teamId": "ENG"}' | jq . ``` From Python, `kindo_tool` is importable in any command: ```python tools = kindo_tool.list_tools() issues = kindo_tool.call_tool("linear_list_issues", {"teamId": "ENG"}) ``` A failed call raises `kindo_tool.KindoToolError`. The CLI prints the error and exits non-zero. The script decides whether to stop or continue. ## What a Command Can Call - The integration tools available to the conversation that are set to run automatically. Tools set to **Ask before running** are neither listed nor callable, because a running command cannot wait for approval. See [Tool Actions and Permissions](/admin/rbac-tool-actions/). - Not the sandbox's own tools, not child-agent tools, and not Platform tools. - `kindo-tool list` shows exactly what the current command may call. ## Limits - The channel is open only while the command that started it runs. When the command exits, calls fail instead of hanging, and a background process that outlives the command cannot call tools. - Each command may make up to 100 tool calls. Further calls fail. - The command's timeout bounds every call it makes. The default is 60 seconds and the maximum is 300. The agent can raise the timeout on a command that needs longer. - Requests travel on the command's stderr. A command that redirects stderr, for example with `2>/dev/null`, disables the channel. Redirect stdout instead. ## Permissions and Auditing No credential enters the sandbox. Kindo executes each call on the agent's behalf, under the same permissions as a tool call the model makes itself, and records it in the audit log the same way. A command cannot reach a tool the agent could not call directly. ## What the Conversation Records The shell command's result reports how many tool calls it made. The individual calls appear in the audit log, not as separate entries in the conversation. For [Script steps](/features/script-steps/#call-tools-from-a-script), each call is also recorded in the run. ## Next Steps - [Script Steps](/features/script-steps/) - [Working with Large Context](/best-practices/working-with-large-context/) - [Tool Actions and Permissions](/admin/rbac-tool-actions/) --- ## Skills Source: https://docs.kindo.ai/features/skills/ :::note[Beta] Skills are in beta and appear only for organizations enrolled in the rollout. Contact your Kindo representative if you want them enabled for your organization. ::: Skills are reusable instruction packages that help Kindo handle a specific task consistently. A skill contains a required `SKILL.md` file with a name, a description, and instructions. It can also include supporting files such as reference material, templates, or scripts. This guide covers skills you create and manage yourself. ## Create a Skill in Chat Ask Kindo to turn a repeatable set of instructions into a skill. For example: ```text Create a skill that turns meeting notes into a concise summary with decisions, action items, and open questions. Do not invent details that are not in the notes. ``` Review the drafted `SKILL.md`, make any changes you need, then ask Kindo to save it. You can find the saved skill in **Skills**. To write the package yourself, follow the steps below. ## Create a Skill Package Create a folder for each skill. Put one `SKILL.md` file at the root of that folder. File paths inside the package must be relative to the folder. ### Write `SKILL.md` Start with YAML frontmatter between `---` lines. The `name` and `description` fields are required. Use a lowercase, hyphen-separated name with up to 64 characters. ```md --- name: meeting-notes description: Turn meeting notes into a concise summary with decisions and next steps. --- # Meeting notes Summarize the supplied notes in this order: 1. Key decisions 2. Action items, including an owner when one is named 3. Open questions Do not invent decisions, owners, or dates that are not in the notes. ``` Write instructions that state the task, the expected output, and important boundaries. Specific instructions are easier to reuse than broad requests such as “help with meetings.” ### Add Supporting Files When Useful Keep optional files in the same folder, for example: ```text meeting-notes/ ├── SKILL.md └── references/ └── team-terms.md ``` Reference supporting files by their relative paths in `SKILL.md` when the instructions need them. A package can contain up to 64 files. `SKILL.md` can be up to 64 KiB, each other file up to 256 KiB, and the complete package up to 2 MiB. ## Upload a Skill Each single-skill package must contain exactly one root `SKILL.md`. Kindo rejects unsafe paths, duplicate paths that differ only by letter case, packages that exceed the limits, and frontmatter that does not meet the required format. ## Inspect and Use a Skill Select a skill in the Skills library to view its description, instructions, and files. Select a file to preview it when it is text. Markdown files render as Markdown; a binary file or a file that is too large to preview can still be downloaded with **Export**. Use skills in AI Chat by asking about the task the skill describes. You can also attach a skill you own to an agent you can edit: For more on creating an agent, see [Creating and Managing Agents](/features/creating-agents/). ## Update a Skill To publish a new version, open the skill and select **Upload new version**. Choose a single folder whose root `SKILL.md` has the same `name` as the existing skill. The skill's creator may view its version history, restore an earlier version, or delete it. Uploading a new version replaces the complete package. Include every file that the new version needs; files omitted from the uploaded folder are not part of the new version. The skill keeps its name and version history, and the library opens the new version after upload. If a normal upload uses a name that already exists, Kindo reports the conflict and offers **Replace existing skill?**. Review the package name before confirming, because replacement uses the name in `SKILL.md`. ## Version History and Restore Select **Version history** on a skill to view its versions. Open a prior version to inspect its instructions and file list. To return to an earlier version, select **Restore this version** and confirm. Kindo creates a new current version with the earlier version’s content; it does not remove the versions already in history. ## Export or Delete a Skill Select **Export** on a skill to download its package as a ZIP archive. Export before making a significant change or deleting a skill if you want a local copy. Select **Delete** and confirm to remove a skill from your library. You cannot undo deletion from the Skills library. ## Troubleshooting | Problem | What to check | | --- | --- | | Upload cannot find a skill | Select the package folder itself, and make sure one root `SKILL.md` is inside it. | | Upload rejects the name or description | Check that frontmatter is valid YAML, `name` is lowercase and hyphen-separated, and both required fields have values. | | Upload exceeds a limit | Reduce the number of files or their sizes. The package limit includes `SKILL.md` and all supporting files. | | A replacement is blocked | Upload a single folder and make sure its `SKILL.md` name matches the skill you opened. | | A file will not preview | Use **Export** to download the package; binary files and large files are not previewed in the browser. | ## Next Steps - [Create and manage agents](/features/creating-agents/) - [Learn about AI Chat](/features/ai-chat/) - [Run a skill's script as an agent step](/features/script-steps/) --- ## Agent Script Steps Source: https://docs.kindo.ai/features/script-steps/ :::note[Beta] Script steps are in beta and appear only for organizations enrolled in the rollout. Contact your Kindo representative if you want them enabled for your organization. ::: A Script step runs a Python script from one of the agent's attached [skills](/features/skills/). No model is involved unless the script asks for one: the step runs the script in the agent's sandbox, reads the result the script declares, and moves the run on. Use it for work that should happen the same way every time, such as syncing records, transforming a webhook payload, or calling an integration in a loop. ## Add a Script Step | Setting | What it does | | --- | --- | | **Skill** | The attached skill the script comes from. The step runs the skill's latest version, and the run records the exact content that ran. | | **Script** | A relative `.py` path inside the skill package. Python is the only supported language. | | **Tools** | Read-only. Scripts run with the tool servers enabled in the agent's **Tools** section. | | **Timeout (seconds)** | Wall-clock limit for the script, from 1 to 900 seconds. Defaults to 120. | | **Agent follow-up** | Lets the script hand its result to the agent for a model turn. Off by default. See [Agent follow-up](#agent-follow-up). | A skill that a Script step runs cannot be detached from the agent until the step is removed or pointed at another skill. The agent's organization needs a default chat model even with **Agent follow-up** off; without one the step fails before the script starts. A Script step never retries: scripts can call tools with side effects, so a step that fails is reported, not run again. ## How a Script Runs - Kindo runs `python3