# XonaMind > XonaMind runs AI agents. An agent is a prompt + a model + tools + memory + skills, created in the XonaMind panel. Any backend, script or product can talk to an agent over HTTP with an API key. Base URL: https://xonamind-api.xonastudio.app Every JSON response of the Product API is `{ "status": "ok", "data": … }` or `{ "status": "error", "code": "…", "message": "…", "details"?: … }`. Dates are ISO 8601 and amounts are USD numbers. Errors carry a stable `code`: branch on it, not on the message. ## Authentication - Product API and OpenAI-compatible API: `Authorization: Bearer xm_live_…` (an API key of a client, created in the panel). Each key has permissions (`agents:read`, `agents:run`, `conversations:read`, `runs:control`, `files:write`, `memory:read`, `memory:write`, `usage:read`, `connections:manage`, `end_users:manage`, `widget:sessions`, `knowledge:read`), an optional list of allowed agents, an optional expiry and a rate limit per minute (headers `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`; 429 `RATE_LIMITED` with `Retry-After`). A missing permission is 403 `PERMISSION_DENIED`; a bad key is 401 `INVALID_API_KEY`. - Widget routes: `Authorization: Widget ` (a short-lived token minted by your backend). - MCP server and dashboard API (`https://xonamind-api.xonastudio.app/v1/admin`): `Authorization: Bearer xm_pat_…` (a personal token of an account, bound to one workspace, scope `read` or `write`) or `xm_oat_…` (the OAuth access token of an MCP client the account authorized in the browser; see "MCP server"). - Call these APIs from your backend. Never put an API key in a browser: use a widget session instead. ## Product API (https://xonamind-api.xonastudio.app/v1) - `GET https://xonamind-api.xonastudio.app/v1/agents` → the agents the key can use: `{ slug, name, description, avatarUrl }[]`. - `POST https://xonamind-api.xonastudio.app/v1/agents/{slug}/messages` with `{ externalUserId, content, conversationId?, attachments?: string[], userContext?: object, toolAuth?: { [mcpServerSlug]: token }, maxCostUsd?, async?: boolean }`. `externalUserId` is YOUR id for the end user (1-200 characters); conversations and memory belong to it. Without `conversationId` a conversation is created. - Default response: Server-Sent Events (`id`, `event`, `data` per event). Events: `run_started`, `text_delta` `{ text }`, `reasoning_delta`, `tool_call_started`, `tool_call_finished`, `image`, `citation`, `confirmation_required` `{ runId, toolCalls: [{ callId, name, arguments }] }`, `usage`, `done` `{ runId, messageId, status, stopReason, usage, output }`, `error` `{ code, message }`. A `ping` event every 15 s is to be ignored. The stream ends after `done`, `error` or `confirmation_required`. - `Accept: application/json` → waits for the end and returns `{ runId, conversationId, status, stopReason, message, pendingConfirmation, usage, error }`. - `"async": true` → `202 { runId, conversationId, status }` right away; read the result with `GET https://xonamind-api.xonastudio.app/v1/runs/{id}`. - `POST https://xonamind-api.xonastudio.app/v1/conversations/{id}/messages` with `{ content, attachments?, userContext?, toolAuth?, maxCostUsd?, async? }`. Only one run can be alive per conversation (409 `RUN_IN_PROGRESS`). - `POST https://xonamind-api.xonastudio.app/v1/conversations` `{ agent, externalUserId, title?, metadata? }`, `GET https://xonamind-api.xonastudio.app/v1/conversations?externalUserId=&agent=&cursor=&limit=`, `GET https://xonamind-api.xonastudio.app/v1/conversations/{id}` → `{ conversation, messages }`. - `GET https://xonamind-api.xonastudio.app/v1/runs/{id}` → status, usage (`promptTokens`, `completionTokens`, `reasoningTokens`, `costUsd`), tool calls and `output` `{ text, data?, error? }`. `GET https://xonamind-api.xonastudio.app/v1/runs/{id}/events?after={seq}` re-attaches to the SSE of a run. - `POST https://xonamind-api.xonastudio.app/v1/runs/{id}/confirm` `{ decision: "approve" | "reject", callIds: string[] }`: a tool that needs approval stops the run in `waiting_confirmation` and emits `confirmation_required`. `callIds` must be exactly the pending calls you saw (otherwise 409 `CONFIRMATION_MISMATCH`). `POST https://xonamind-api.xonastudio.app/v1/runs/{id}/cancel` cancels a run. - `POST https://xonamind-api.xonastudio.app/v1/files` (multipart, field `file`, up to 20 MB) → `{ id, name, mime, sizeBytes, kind, url }`; send `id` in `attachments`. - `GET https://xonamind-api.xonastudio.app/v1/memory?externalUserId=`, `DELETE https://xonamind-api.xonastudio.app/v1/memory/{id}`: what the agents remember about an end user. - `GET https://xonamind-api.xonastudio.app/v1/knowledge/{kbSlug}/search?q=&topK=`: hybrid search in a knowledge base. - `GET https://xonamind-api.xonastudio.app/v1/usage?from=&to=&externalUserId=`: tokens and cost (USD) of the client, by agent and by kind. Bill your own users with the `usage.costUsd` of each run. - Common errors: 401 `INVALID_API_KEY`, 402 `BUDGET_EXCEEDED` / `INSUFFICIENT_CREDITS`, 404 `AGENT_NOT_FOUND`, 409 `RUN_IN_PROGRESS`, 422 `VALIDATION_ERROR` (`details.fields`), 429 `RATE_LIMITED`. ## OpenAI-compatible API (https://xonamind-api.xonastudio.app/openai/v1) Use any OpenAI SDK with `baseURL = https://xonamind-api.xonastudio.app/openai/v1` and an XonaMind API key (permission `agents:run`). The "model" is an agent slug. - `GET https://xonamind-api.xonastudio.app/openai/v1/models` → the agents the key can use, as `{ object: "list", data: [{ id: "", object: "model", created, owned_by }] }`. - `POST https://xonamind-api.xonastudio.app/openai/v1/chat/completions` with `{ model: "", messages, stream?, user?, metadata?, response_format? }`. - `user` is the end user id (default `openai:anon`). - Continue a conversation with the header `X-Conversation-Id` (or `metadata.conversation_id`); every response returns `X-Conversation-Id`. Without it a new conversation is created and the earlier `user` / `assistant` messages of the request become its history; `system` messages are added as instructions of the session. The last `user` message is the one answered. - `stream: true` → `chat.completion.chunk` events and `data: [DONE]`; the last chunk carries `finish_reason` and `usage`. - The tools of the agent run inside XonaMind and are never exposed; `tools`, `temperature` and `max_tokens` of the request are ignored (the agent config rules). A run that needs the approval of a tool ends with `finish_reason: "tool_calls"` and a message that says so; approve it with the Product API. - `response_format` (`json_object` or `json_schema`) is honored when the agent does not set its own output. - Errors use the OpenAI format `{ "error": { "message", "type", "code", "param" } }`: 401 `invalid_api_key`, 402 `insufficient_quota`, 404 `model_not_found`, 429 `rate_limit_exceeded`. ## Chat widget 1. Your backend asks for a session: `POST https://xonamind-api.xonastudio.app/v1/widget/sessions` (API key with `widget:sessions`) with `{ agent, externalUserId, userContext?, subject?: { type, id }, ttlSeconds? }` (60-86400, default 3600; `userContext` up to 4 KB) → `{ token, expiresAt }`. 2. Your page loads the script and mounts it: `` or `XonaMindWidget.mount({ token, container?, theme?, locale?, title?, onTokenExpired })`; `onTokenExpired` must return a new token. 3. The widget talks to `https://xonamind-api.xonastudio.app/v1/widget/*` with `Authorization: Widget `: `GET /conversation`, `POST /messages` (SSE, same events as above), `POST /runs/{id}/confirm`, `POST /conversation/reset`, `POST /files`. CORS is limited to the client's allowed origins (set them in the panel). Errors: 401 `WIDGET_TOKEN_INVALID`, 401 `WIDGET_TOKEN_EXPIRED`, 403 `ORIGIN_NOT_ALLOWED`. ## Outgoing webhooks Create endpoints in the panel (Webhooks). XonaMind sends `POST ` with `Content-Type: application/json` and the headers `XonaMind-Event: `, `XonaMind-Delivery: ` and `XonaMind-Signature: t=,v1=`. The body is `{ "id": "", "type": "run.succeeded", "createdAt": "…", "workspaceId": "…", "data": { … } }`. Verify the signature: `v1 = HMAC-SHA256(secret, ".")` in hex, with the `whsec_…` secret of the endpoint, comparing in constant time and rejecting an old `t`. Any 2xx within 10 s is a success; anything else is retried (1 min, 5 min, 30 min, 2 h, 12 h). Run events carry `{ runId, conversationId, agent, clientId, externalUserId, status, stopReason, output, usage }`. ## Connections (accounts of the end user) Agents can use tools that act on a third-party account of the end user (Google, GitHub…). Your backend starts the connection with `POST https://xonamind-api.xonastudio.app/v1/connections/{provider}/authorize` `{ externalUserId, redirectUrl }` (permission `connections:manage`; `redirectUrl` must be inside the client's allowed redirect origins) → `{ url, expiresAt }`: send the user to `url`; they come back to `redirectUrl` with `?connection=ok|error&provider=`. `GET https://xonamind-api.xonastudio.app/v1/connections?externalUserId=` lists them and `DELETE https://xonamind-api.xonastudio.app/v1/connections/{id}` removes one. When a tool needs an account that is not connected the stream emits `connection_required`. ## MCP server (the whole workspace) Manage everything the dashboard manages from Claude, ChatGPT, Cursor or any MCP client: `https://xonamind-api.xonastudio.app/mcp` (Streamable HTTP, stateless), as a custom connector with just the URL (OAuth) or with `Authorization: Bearer xm_pat_…`. - OAuth (no token to paste): in Claude.ai / Claude Desktop (Settings → Connectors → Add custom connector), ChatGPT (connectors / developer mode), Cursor or VS Code, add a remote MCP server with ONLY the URL `https://xonamind-api.xonastudio.app/mcp`. The client finds the login by itself (the 401 carries `WWW-Authenticate: Bearer resource_metadata="https://xonamind-api.xonastudio.app/.well-known/oauth-protected-resource/mcp"`), you sign in to the dashboard, choose the workspace and the access (`mcp:read` reads, `mcp:write` also changes things with your role, `mcp:platform` only for superadmins) and approve. The client gets an access token `xm_oat_…` (1 hour) and a rotating refresh token `xm_ort_…` (30 days) and behaves EXACTLY like a personal token of that scope: same tools, roles, denylist, per-minute cap and audit log (`via: "oauth"` with the client name). See and disconnect your clients with `GET`/`DELETE https://xonamind-api.xonastudio.app/v1/admin/account/connections`. Discovery: `https://xonamind-api.xonastudio.app/.well-known/oauth-protected-resource` and `https://xonamind-api.xonastudio.app/.well-known/oauth-authorization-server` (PKCE S256, dynamic client registration at `https://xonamind-api.xonastudio.app/oauth/register`). - Personal token: create it in the dashboard (Account → Tokens) for ONE workspace with a scope: `write` (default; everything your role allows) or `read` (only reads; the write tools are not even listed). It acts with the role your account has TODAY in that workspace (a superadmin acts as owner). The workspace is the token one: there is no workspace argument. A token never signs in, changes the password, the email or the 2FA, lists or closes sessions, creates or revokes tokens, accepts an invitation, transfers the ownership or archives the workspace (403 `SESSION_REQUIRED`; those need the signed-in dashboard). Platform operations (`superadmin_*`, models, providers) need a token created with `platformAdmin` by a superadmin account (403 `PLATFORM_TOKEN_REQUIRED` otherwise); they are listed only for such tokens. JSON-RPC batches are rejected (-32600): send one request per call. - Tools: one per operation of the dashboard API, named `__` (for example `agents_list`, `agents_revisions_create`, `skills_create`, `knowledge_documents_add`, `tools_http_create`, `automation_schedules_create`, `quality_evals_runs_start`, `workspace_members_list`, `workspace_usage_get`). They run the same operation as `https://xonamind-api.xonastudio.app/v1/admin/*`: same validation, roles, audit log and errors. The input comes from the route (path ids, query and body fields); an error comes back as `CODE (HTTP status): message {details}` (for example `ROLE_FORBIDDEN (HTTP 403): … {"required":"admin"}`, `VALIDATION_ERROR (HTTP 422)` with the fields, `TOKEN_SCOPE_FORBIDDEN`). Superadmin tools are listed only for superadmin accounts. - Curated tools (by slug, shorter): `list_agents`, `get_agent`, `create_agent`, `update_agent`, `run_agent`, `search_memory`, `add_memory`, `search_knowledge`, `list_conversations`, `get_conversation`, `get_usage`. Writes need the member role. - Toolsets: there are 200+ tools, so connect to `https://xonamind-api.xonastudio.app/mcp?toolsets=agents,knowledge` to list only some areas. Areas: agents, skills, tools, knowledge, memory, conversations, clients, automation, quality, workspace, billing, models, templates, account, files, superadmin. Names that do not exist are ignored; without the parameter every tool you can use is listed. The tool `list_toolsets` returns the areas with a description and how many tools you can use in each. - Files: `files_upload_from_url` { url, name?, conversationId? } downloads an https file into the workspace (public hosts only, up to 20 MB; png, jpg, jpeg, webp, gif, pdf, txt, md, csv, json) and returns `{ id, name, mime, sizeBytes, kind, url }`: use the id in `attachments` of `conversations_chat_send`. To add a document to a knowledge base pass the URL to `knowledge_documents_add`. - Limits: the token shares the per-minute cap of API keys (`X-RateLimit-*`, 429 `RATE_LIMITED`); every write is in the audit log with the token prefix as the actor. - The same token also works as `Authorization: Bearer xm_pat_…` on `https://xonamind-api.xonastudio.app/v1/admin/*` (GET only for a `read` token; `X-Workspace-Id`, if sent, has to be the token workspace: 403 `WORKSPACE_FORBIDDEN`). ## Optional - [llms.txt](https://xonamind-api.xonastudio.app/llms.txt): this document.