# Botovod agent quickstart > Build, register and run a bot connected to this Botovod instance. Documentation is public; provisioning requires the owner's explicit authorization and credentials. Base URL: `https://botovod.srgkslv.ru`. API version: 1. JSON uses UTF-8. Read the [API reference](https://botovod.srgkslv.ru/agents/api.md) or [OpenAPI schema](https://botovod.srgkslv.ru/agents/openapi.json) for exact payloads. ## Decide which credentials you have - `BOT_TOKEN` or a private bot-credentials JSON: the bot already exists. Skip provisioning. Use `/api/bot/*` only. - `BOTOVOD_EMAIL` and `BOTOVOD_PASSWORD`, supplied by the owner for this task: you may provision a bot using `/api/admin/*`, then discard owner credentials from the runtime environment. - Neither: implement the bot locally and ask the owner for an existing bot token or authorized provisioning credentials. Do not guess passwords, reuse example tokens, or scrape private files. ## Register a bot once Use one cookie-preserving client for all three requests. Send `Content-Type: application/json`. 1. `POST https://botovod.srgkslv.ru/api/admin/session` ```json {"email":"","password":""} ``` The server sets a `session` cookie and returns `{"email":"","csrf_token":""}`. Retain the cookie and CSRF token privately. Login failures are limited: five wrong passwords cause a 15-minute block per source address; login also has a request rate limit. 2. `POST https://botovod.srgkslv.ru/api/admin/bots`, with the session cookie and `X-CSRF-Token: `: ```json {"name":"Server Monitor","slug":"server-monitor","description":"Reports server health","quick_commands":["status","help"]} ``` Success is HTTP **201**, for example: ```json {"id":42,"name":"Server Monitor","slug":"server-monitor","description":"Reports server health","status":"unknown","quick_commands":["status","help"],"chat_url":"/bots/server-monitor","token":""} ``` Save `token` immediately in a private file or secret store. It is returned only at creation. Never put it in source code, URLs, logs, screenshots, or a user-facing report. A slug must be 1–64 lowercase ASCII letters/digits with single hyphens between groups. Choose a stable slug for this bot. If the request returns **409**, that slug already exists; no token is issued or changed. Use `GET /api/admin/bots` to inspect bots, recover the previously saved token, or ask the owner. Do not automatically rotate tokens, create suffixed duplicates, or retry an ambiguous creation with a new slug. On a timeout, inspect the bot list before deciding whether to retry the same slug. 3. `DELETE https://botovod.srgkslv.ru/api/admin/session`, with the same cookie and CSRF header. Success is HTTP **204**. Run the worker with the bot token, never the owner password/session. Creating the database record does **not** deploy or start a worker. Run the generated code on a computer/server that can reach this instance, using an appropriate service manager for continuous operation. ## Runnable Python examples Download [register_bot.py](https://botovod.srgkslv.ru/agents/examples/register_bot.py) and [agent_bot.py](https://botovod.srgkslv.ru/agents/examples/agent_bot.py), then install `httpx`: ```sh python -m pip install httpx ``` With `BOT_API_URL`, `BOTOVOD_EMAIL` and `BOTOVOD_PASSWORD` already provided through your secure environment: ```sh python register_bot.py --name "Server Monitor" --slug server-monitor --output bot-credentials.json ``` The registrar reserves a new output file, saves only the runtime token, and signs out. It never prints credentials. Keep the JSON outside version control; on Windows, restrict its folder permissions to the account running the bot. Remove the owner credentials from the worker's environment. Set `BOT_CONFIG` to the private JSON path, then run: ```sh python agent_bot.py ``` Alternatively set `BOT_TOKEN` and `BOT_API_URL` directly. The starter supports `status`, `help`, and `echo `; replace these handlers with the requested bot's behavior. ## Worker contract 1. Send `Authorization: Bearer ` on every `/api/bot/*` request. Never use the admin session in a worker. 2. Send `POST /api/bot/heartbeat` with `{"status":"online"}` on startup and about every 60 seconds. 3. Poll `GET /api/bot/commands/pending` about every 3 seconds. **This GET claims commands**, changing their state to `taken`; it is not a harmless queue preview or health check. Run one worker per bot. 4. For every returned command, call `POST /api/bot/commands/{id}/complete` with either `{"status":"done","result":"..."}` or `{"status":"failed","error":"..."}`. 5. Post proactive chat messages through `/api/bot/messages`; create Inbox notifications through `/api/bot/events`. 6. Back off on network errors, HTTP 429 and 5xx. Stop on HTTP 401 and recover credentials; do not retry invalid credentials forever. Fix 400/422 payloads before retrying. Commands are delivered **at least once**. By default unfinished `taken` commands can be reclaimed after 300 seconds (server-configurable). There is no lease-renewal endpoint. Keep work within that window or design durable job handling. Multiple workers and long commands can cause duplicates. Persist completed command IDs before repeating irreversible side effects. Completing the same finished command ID is idempotent, but arbitrary message/event posts are not. The `Idempotency-Key` header on completion is echoed; it is not a general deduplication mechanism. ## Verify the integration - Confirm heartbeat returns `{"ok":true}`. - Open `https://botovod.srgkslv.ru/bots/`, send `status`, and confirm the worker posts its result. - Send an info event with `also_post_to_chat=true`; verify both Inbox and the chat. - Report the chat URL, where the worker runs, and how to restart it. Do not report its token. Use the existing bot only for the owner's requested purpose. Do not change Mattermost integrations or other existing services as part of onboarding a bot. --- # Botovod API reference for agents Base URL: `https://botovod.srgkslv.ru`. [OpenAPI JSON](https://botovod.srgkslv.ru/agents/openapi.json). [Onboarding](https://botovod.srgkslv.ru/agents/quickstart.md). ## Authentication Provisioning: authenticated owner `session` cookie, plus `X-CSRF-Token` on writes. Acquire both through `POST /api/admin/session` with a JSON email/password body. Keep one cookie-preserving HTTP client. `GET /api/admin/session` inspects the session and returns its CSRF token. `DELETE /api/admin/session` signs out (204). Runtime: `Authorization: Bearer ` on `/api/bot/*`. The token represents exactly one bot. It cannot list/create bots or access admin endpoints. Bots never specify their own `bot_id` in runtime payloads; the server derives it from the token. ## Provisioning `GET /api/admin/bots` returns `{"bots":[...]}` with id, name, slug, description, status, quick_commands and relative chat_url. It never returns tokens. `POST /api/admin/bots` requires JSON name (1–255 characters), slug (1–64 lowercase ASCII alphanumeric characters with hyphens between groups), optional description (up to 1000 characters) and quick_commands (up to 30 nonempty strings, each up to 255 characters; default `["status","help"]`). Returns 201 with bot data and a one-time token. Duplicate slug: 409 `{"detail":{"code":"bot_exists","slug":"..."}}`. The existing bot remains unchanged. No automatic token rotation is provided by this API. ## Chat messages `POST /api/bot/messages` ```json {"content":"Backup finished","message_type":"text","severity":"info","metadata":{}} ``` Returns `{"ok":true,"message_id":123}`. Message types: `text`, `event`, `error`, `command_result`. Severity values used by the UI: `info`, `warning`, `critical`. Store optional structured data in metadata. Chat content is plain text; do not depend on full Markdown or HTML rendering. ## Inbox events `POST /api/bot/events` ```json {"title":"Backup complete","body":"All files saved","severity":"info","also_post_to_chat":true,"metadata":{"job":"nightly-backup"}} ``` Returns `{"ok":true,"event_id":321,"message_id":123}` when posted to chat; otherwise message_id is absent. Warning/critical events always also post to chat; info events post to chat only when also_post_to_chat is true. Inbox events are what the Android client checks for system notifications. Chat-only messages do not create Inbox events. ## Commands `GET /api/bot/commands/pending` claims all currently pending commands: ```json {"commands":[{"id":15,"text":"status","created_at":"2026-10-26T12:00:00","metadata":{"user_message_id":100}}]} ``` An empty queue returns `{"commands":[]}`. Claimed commands are not returned again immediately. By default a command can be reclaimed after 300 seconds if it was not completed. Treat delivery as at least once and run one worker per bot. Do not cache this GET or use it to inspect the queue without processing. `POST /api/bot/commands/15/complete`, success: ```json {"status":"done","result":"The service is running","metadata":{}} ``` Failure: ```json {"status":"failed","error":"The upstream service timed out","metadata":{}} ``` Returns ok, command_id, status, and message_id; repeated completion includes idempotent=true. Done requires nonempty result or metadata.image_url; failed requires nonempty error. A command belonging to another bot returns 404. Retry completion by the same command ID after an ambiguous timeout, without rerunning its side effects. ## Heartbeat `POST /api/bot/heartbeat` with `{"status":"online","metadata":{}}` returns `{"ok":true}`. Status may be omitted to update only last-seen time. Recommended statuses: online, offline, error, unknown. Metadata is accepted here for compatibility but is currently not persisted. Send about every 60 seconds; send offline on graceful shutdown if possible. ## PDF commands and result images A PDF uploaded by the owner through the UI creates a command whose text is `pdf:` and metadata includes `kind=pdf`, upload_id and filename. The starter does not process PDFs; implement a handler if needed. Download it with `GET /api/bot/uploads/` and your bot's Bearer header. Success returns PDF bytes. Upload a result image via `POST /api/bot/uploads/image` using multipart field `file`. Supported filename extensions: .png, .jpg, .jpeg, .webp. The response includes `ok`, `file_id`, `url`, `path`, `kind`. Use `url`, which is a relative public image URL; `path` is a server filesystem path and should not be shown to the user. Uploaded image links are not private signed URLs; do not upload confidential material expecting link-level access control. Then complete the command, for example: ```json {"status":"done","result":"Analysis complete","metadata":{"image_url":"/uploads/images/.png"}} ``` ## Errors and retries | HTTP | Action | | --- | --- | | 400 | Fix the request semantics; read detail. | | 401 | Missing/invalid credentials or expired owner session; stop and recover access. | | 403 | Admin CSRF missing/mismatch; get the token for the same authenticated cookie. | | 404 | Unknown upload/command or command belongs to another bot. | | 409 | Bot slug exists; reuse saved credentials rather than creating duplicates. | | 413 | Request exceeds an application/proxy limit; reduce upload/payload size. | | 422 | JSON/form schema validation failed; inspect detail and fix fields. | | 429 | Back off; login lockout after five failed passwords lasts 15 minutes. | | 5xx / network | Bounded exponential backoff with jitter; do not blindly duplicate non-idempotent writes. | Use request timeouts around 30 seconds. Provisioning and runtime are separate clients/environments. Never log Authorization, session cookies, passwords, or token-bearing creation responses.