KarboAI Bot API
Build bots that interact with users in KarboAI chats via a simple REST API and WebSocket events.
Bots are created through the in-app Store. Open the Store, switch to the Development category (</> icon), tap Create bot — the new bot card shows its unique token used for authentication. Official bots (paid status, RUB 1000) can be added to any chat — to public chats by the organizer or a helper, to private chats by the organizer. Non-official bots can only be added by their owner, and only to private chats where the owner is the organizer.
This documentation covers every API endpoint and WebSocket event. You can use any language or HTTP library to build a bot — or use our official Python SDK.
KarboAI Bot API
Создавайте ботов, которые взаимодействуют с пользователями в чатах KarboAI через REST API и WebSocket.
Боты создаются во внутриигровом Магазине. Откройте магазин, перейдите в категорию Разработка (иконка </>), нажмите Создать бота — на карточке нового бота будет уникальный токен для аутентификации. Официальных ботов (платный статус, 1000 RUB) можно добавлять в любой чат — в публичные чаты добавляет организатор или помощник, в приватные — организатор. Неофициальных ботов может добавлять только их владелец и только в приватные чаты, где он является организатором.
Документация описывает все API-эндпоинты и WebSocket-события. Вы можете использовать любой язык программирования — или наш официальный Python SDK.
Authentication
All API requests must include the Bot-Token header with your bot's token.
Аутентификация
Все API-запросы должны содержать заголовок Bot-Token с токеном вашего бота.
// Every request must include this header Bot-Token: your_bot_token_here
Base URL
All REST API endpoints use the following base URL:
Базовый URL
Все REST API эндпоинты используют следующий базовый URL:
https://api.karboai.com
WebSocket connections use:
WebSocket-подключения используют:
wss://api.karboai.com/bot/ws
Rate Limits
Rate limits depend on your bot's status:
Лимиты запросов
Лимиты зависят от статуса вашего бота:
When rate limited, the server returns 429 Too Many Requests with a Retry-After header (seconds).
При превышении лимита сервер вернёт 429 Too Many Requests с заголовком Retry-After (секунды).
Errors
All errors return a JSON object with an error field:
Ошибки
Все ошибки возвращают JSON-объект с полем error:
{
"error": "not_in_chat"
}
| Code | Meaning | Значение | Common errors | Частые ошибки |
|---|---|---|---|---|
| 400 | Bad request | Неверный запрос | empty_message, content_too_long, too_many_images | |
| 401 | Unauthorized | Не авторизован | bot_token_required, invalid_bot_token | |
| 403 | Forbidden | Запрещено | bot_banned, not_in_chat, cannot_kick_organizer, cannot_kick_helper, cannot_kick_app_admin, forbidden | |
| 404 | Not found | Не найдено | user_not_found, message_not_found, user_not_in_chat | |
| 413 | File too large | Файл слишком большой | file_too_large | |
| 429 | Rate limited | Лимит превышен | — |
Endpoints
Эндпоинты
Returns information about the authenticated bot.
Возвращает информацию об аутентифицированном боте.
{
"bot_id": "uuid",
"name": "My Bot",
"status": "official" // "not_official" | "official" | "banned"
}
curl -H "Bot-Token: YOUR_TOKEN" \
https://api.karboai.com/bot/me
Send a text message and/or images to a chat. The bot must be a member of the chat.
Отправить текстовое сообщение и/или изображения в чат. Бот должен быть участником чата.
| Field | Поле | Type | Тип | Description | Описание |
|---|---|---|---|---|---|
| chat_id | string | requiredобяз. Target chat IDID целевого чата | |||
| content | string | optionalопц. Message text (max 5000 chars). Required if no images.Текст сообщения (макс. 5000 символов). Обязательно, если нет изображений. | |||
| reply_message_id | string | null | optionalопц. Message ID to reply toID сообщения для ответа | |||
| images | string[] | null | optionalопц. Array of image URLs (max 10). Get URLs from /bot/upload/imageМассив URL изображений (макс. 10). Получите URL через /bot/upload/image | |||
| inline_buttons | Button[][] | null | optionalопц. Grid of inline buttons attached to the message. Outer list is rows, inner is columns within a row. Max 10 rows, 5 per row, 30 total. See Inline buttons.Сетка inline-кнопок. Внешний список — ряды, внутренний — кнопки в ряду. Максимум 10 рядов, 5 в ряду, 30 всего. См. Inline buttons. |
{
"message_id": "uuid",
"created_time": 1712070000
}
curl -X POST https://api.karboai.com/bot/send-message \ -H "Bot-Token: YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "chat_id": "chat-uuid", "content": "Hello from bot!", "images": ["https://api.karboai.com/static/images/abc123.jpg"] }'
Upload an image file. Returns a URL you can use in send-message. Maximum file size: 20 MB. Supported formats: .jpg, .png, .webp, .gif.
Загрузите файл изображения. Возвращает URL для использования в send-message. Максимальный размер: 20 МБ. Форматы: .jpg, .png, .webp, .gif.
| Field | Поле | Type | Тип | Description | Описание |
|---|---|---|---|---|---|
| file | file | requiredобяз. Image fileФайл изображения |
{
"url": "https://api.karboai.com/static/images/abc123def.jpg"
}
curl -X POST https://api.karboai.com/bot/upload/image \ -H "Bot-Token: YOUR_TOKEN" \ -F "file=@photo.jpg"
Returns full metadata for a chat the bot is in: type (public / bot-DM / PM / private), community, title, background, pinned message, member count, last message preview, and (for DMs) the other party's profile snapshot.
Response shape is identical to the user-facing GET /chat/{chat_id} — the app's own Flutter client uses the same payload, so any field documented in the app's Chat model applies. The type field on the response encodes the chat kind (see the enum in the response block below).
Access is restricted to chats where the bot is currently a member. Same rule as every other /bot/chat/{chat_id}/* endpoint — trying to inspect a chat you're not in returns 403 not_in_chat.
Возвращает полную информацию о чате, в котором состоит бот: тип (публичный / bot-DM / PM / приватный), сообщество, заголовок, фон, закреплённое сообщение, кол-во участников, превью последнего сообщения и (для DM) снимок профиля собеседника.
Формат ответа 1-в-1 совпадает с пользовательским GET /chat/{chat_id} — то же самое ест Flutter-клиент приложения, любая документация по модели Chat в приложении применима и тут. Поле type в ответе кодирует тип чата (см. enum в блоке ответа).
Доступ ограничен чатами, где бот сейчас состоит. Правило одинаково для всех /bot/chat/{chat_id}/* — попытка глянуть чат, в котором бота нет, вернёт 403 not_in_chat.
{
"chat_id": "uuid",
"type": 0, // 0=public, 1=bot-DM, 2=PM, 3=private
"chatting_type": 3, // legacy field kept for client compat
"title": "General chat",
"background": "https://api.karboai.com/static/images/...",
"icon": "https://api.karboai.com/static/images/...",
"chat_description": "",
"community_id": 42, // 0 for DMs and global chats
"pinned_message": "",
"everyone_can_invite": true,
"voice_invite_only": false,
"is_voice_active": false,
"is_cinema_active": false,
"is_hidden": 0,
"created_time": 1712070000,
"creator_user_id": "uuid",
"creator_nickname": "Organizer",
"creator_avatar_url": "https://...",
"users_count": 12,
"is_chat_member": true,
"member_status": "joined",
"is_muted_chat": false,
"last_readed_message_id": "msg-uuid",
"helper_ids": ["helper-uuid-1"],
// For DMs only — the other party's profile snapshot:
"other_user_id": "user-uuid",
"other_user_nickname": "Alice",
"other_user_avatar_url": "https://...",
// Last message preview (null if the chat is empty):
"last_message": {
"message_id": "uuid",
"created_time": 1712070100,
"content": "Hey there!",
"type": 0,
"user_id": "user-uuid",
"has_images": false,
"audio": null,
"sticker": null,
"video_note": null,
"author": {
"user_id": "user-uuid",
"nickname": "Alice",
"avatar_url": "https://..."
}
}
}
type field (chat kind) is not the same as last_message.type (message kind). See the event table under new_message for message-type values.type (тип чата) — не то же самое, что last_message.type (тип сообщения). Значения message-type смотри в таблице под new_message.Retrieve a specific message from a chat the bot is in.
Получить конкретное сообщение из чата, в котором бот состоит.
{
"message_id": "uuid",
"chat_id": "uuid",
"user_id": "uuid",
"content": "Hello!",
"created_time": 1712070000,
"type": 0,
"reply_message_id": null,
"audio": null,
"audio_duration_ms": null,
"waveform": null,
"video_note": null,
"video_note_duration_ms": null,
"sticker": null,
"images": ["https://api.karboai.com/static/images/..."],
"transparent": false,
"bubble_id": "bubble-uuid",
"bubble_version": 1,
"reactions": [
{
"reaction": "🔥",
"is_sticker": false,
"count": 3,
"me": false
}
],
"author": {
"user_id": "uuid",
"nickname": "Username",
"avatar_url": "https://...",
"avatar_frame": {
"frame_id": "frame-uuid",
"file": "https://api.karboai.com/static/images/..."
},
"role": 0,
"app_role": 0,
"panel_color": "#1f2937",
"level": 12,
"nickname_color": "#ff7a00",
"nickname_emoji": "✨",
"is_api_bot": false
}
}
List members of a chat. Supports pagination.
Список участников чата. Поддерживает пагинацию.
| Param | Параметр | Type | Тип | Description | Описание |
|---|---|---|---|---|---|
| limit | int | optionalопц. Max results (default 100, max 200)Макс. результатов (по умолчанию 100, макс. 200) | |||
| offset | int | optionalопц. Offset for pagination (default 0)Смещение для пагинации (по умолчанию 0) | |||
| community_id | int | optionalопц. Community ID to resolve member profiles from (nickname, avatar, role, level). If omitted, the chat's own community is used automatically.ID сообщества для получения профилей участников (ник, аватар, роль, уровень). Если не указан, используется сообщество чата автоматически. |
{
"items": [
{
"user_id": "uuid",
"nickname": "Username",
"avatar_url": "https://...",
"role": 0,
"app_role": 0,
"panel_color": "#1f2937",
"member_status": "joined",
"avatar_frame": {
"frame_id": "frame-uuid",
"file": "https://api.karboai.com/static/images/..."
},
"level": 12,
"nickname_color": "#ff7a00",
"nickname_emoji": "✨",
"is_api_bot": false
}
]
}
Get the public profile of any user.
Получить публичный профиль любого пользователя.
{
"user_id": "uuid",
"nickname": "Username",
"avatar": "https://...",
"short_info": "Bio text",
"role": 0,
"app_role": 0,
"panel_color": "#1f2937",
"level": 12,
"nickname_color": "#ff7a00",
"nickname_emoji": "✨",
"avatar_frame": {
"frame_id": "frame-uuid",
"file": "https://api.karboai.com/static/images/..."
},
"bubble_id": "bubble-uuid"
}
Get a user's profile within a specific community. Returns community-specific data such as nickname, avatar, role, and level. Falls back to the global profile if the user has no community-specific data. No sensitive data (email, token) is returned.
Получить профиль пользователя в конкретном сообществе. Возвращает данные, специфичные для сообщества: никнейм, аватар, роль, уровень. Если у пользователя нет данных в этом сообществе, возвращается глобальный профиль. Конфиденциальные данные (email, токен) не возвращаются.
| Parameter | Параметр | Type | Тип | Description | Описание |
|---|---|---|---|---|---|
| user_id | string | Target user UUIDUUID пользователя | |||
| community_id | integer | Community IDID сообщества |
{
"user_id": "uuid",
"community_id": 42,
"nickname": "CommunityNick",
"avatar": "https://...",
"short_info": "Bio text",
"role": 1,
"app_role": 0,
"panel_color": "#1f2937",
"level": 5,
"nickname_color": "#ff7a00",
"nickname_emoji": "✨",
"avatar_frame": {
"frame_id": "frame-uuid",
"file": "https://api.karboai.com/static/images/..."
},
"bubble_id": "bubble-uuid"
}
Remove the bot from a chat.
Удалить бота из чата.
{ "ok": true }
Kick a user from a chat. The bot must have the helper role or be the chat organizer.
Кикнуть пользователя из чата. Бот должен иметь роль помощника или быть организатором чата.
| Field | Поле | Type | Тип | Description | Описание |
|---|---|---|---|---|---|
| user_id | string | requiredобяз. User ID to kickID пользователя для кика |
{ "ok": true }
Inline buttons
Inline buttons are interactive UI elements attached below the text content of a bot message. Users tap (or swipe) a button, and the bot receives a button_pressed WebSocket event identifying who pressed which button on which message.
Buttons are styled (color, gradient, shape), can carry visual flair (pulse / neon / glitch / outline animations + spark/confetti/heart/pixel/smoke particle bursts on tap), and support two interaction modes: regular tap and swipe-to-confirm.
This section is the deep reference. If you just want a quick example, jump to Sending buttons.
Inline-кнопки
Inline-кнопки — это интерактивные элементы, прикрепляемые под текстом сообщения бота. Пользователь тапает (или свайпает) кнопку, и бот получает WebSocket-событие button_pressed с информацией о том, кто, на каком сообщении и какую именно кнопку нажал.
Кнопки настраиваются (цвет, градиент, форма), могут иметь анимации (pulse / neon / glitch / outline) и вспышки частиц при тапе (spark / confetti / heart / pixel / smoke), а также поддерживают два режима взаимодействия: обычный тап и свайп-подтверждение.
Эта секция — подробный референс. Быстрый пример — в Отправка кнопок.
Limits at a glance
Лимиты — кратко
| Limit | Value | Значение |
|---|---|---|
| Rows per messageРядов на сообщение | 10 | |
| Buttons per rowКнопок в одном ряду | 5 | |
| Total buttons per messageВсего кнопок на сообщение | 30 | |
| Label / swipe text lengthДлина label / swipe-text | 64 chars | |
| Button ID formatФормат ID кнопки | [A-Za-z0-9_.-]{1,64} | |
| Animations per buttonАнимаций на кнопку | 4, no duplicate kinds4, без дублей по kind | |
| Particles per buttonПартиклов на кнопку | single configодна конфигурация |
Full button schema
Полная схема кнопки
One button is one JSON object. All fields except id and label are optional.
Одна кнопка — один JSON-объект. Обязательны только id и label; остальные поля опциональны.
{
"id": "unique_id", // required, [A-Za-z0-9_.-]{1,64}, unique per message
"label": "Visible text", // required, <= 64 chars
"shape": "rectangle", // rectangle | circle | capsule (default rectangle)
"corner_radius": 12, // only for rectangle, 0..64 (default 12)
"color": {
"hex": "#6C8CFF", // solid fill
"text_hex": "#FFFFFF",
"gradient": { // optional, overrides solid fill
"start_hex": "#6C8CFF",
"end_hex": "#4ECDC4",
"direction": "horizontal" // horizontal | vertical | diagonal | radial
}
},
"interaction": {
"type": "tap", // tap (default) | swipe
"swipe": { // required iff type=swipe
"text": "Release to confirm",
"fill_hex": "#22C55E"
}
},
"animations": [ // optional, 0..4 entries, unique by kind
{ "kind": "pulse", "speed_ms": 1200 },
{ "kind": "neon", "color_hex": "#6C8CFF", "blur": 12 },
{ "kind": "glitch", "intensity_px": 3, "frequency_ms": 2500 },
{ "kind": "outline", "color_hex": "#FFFFFF", "thickness_px": 2, "corner_radius": 12 }
],
"particles": { // optional, single config per button
"type": "spark", // spark | confetti | heart | pixel | smoke
"color_hex": "#FFD54F",
"intensity": 3 // 1..5 (default 3)
}
}
Sending buttons
Отправка кнопок
Attach the inline_buttons field to any normal POST /bot/send-message request. It is a list of rows, and each row is a list of buttons.
Layout rule of thumb:
[[a]]— one button, full-width.[[a, b]]— two buttons side-by-side in the same row.[[a], [b], [c]]— three buttons stacked vertically.[[a, b], [c]]— first row has two columns, second row a single full-width button.
Validation runs server-side at send time. If anything is malformed the server replies HTTP 400 with an explanatory error code — see Errors.
Поле inline_buttons добавляется в обычный запрос POST /bot/send-message. Это список рядов, и каждый ряд — это список кнопок.
Правило раскладки:
[[a]]— одна кнопка во всю ширину.[[a, b]]— две кнопки рядом в одном ряду.[[a], [b], [c]]— три кнопки одна под другой.[[a, b], [c]]— первый ряд из двух колонок, второй — одна на всю ширину.
Валидация выполняется на сервере при отправке. Любая ошибка приведёт к HTTP 400 с понятным кодом — см. Ошибки.
import karbo as k await bot.send_message( chat_id, "Confirm payment?", buttons=[ [ k.Button( "pay", "Pay", style=k.ButtonStyle(color="#22C55E", shape="capsule"), animations=[k.Pulse(speed_ms=900)], particles=k.SparkParticles(color="#FFD54F"), ), k.Button( "cancel", "Cancel", style=k.ButtonStyle(color="#1F2937", shape="capsule"), ), ], ], )
POST /bot/send-message
Bot-Token: YOUR_TOKEN
Content-Type: application/json
{
"chat_id": "chat-uuid",
"content": "Confirm payment?",
"inline_buttons": [
[
{
"id": "pay",
"label": "Pay",
"shape": "capsule",
"color": { "hex": "#22C55E", "text_hex": "#FFFFFF" },
"animations": [{ "kind": "pulse", "speed_ms": 900 }],
"particles": { "type": "spark", "color_hex": "#FFD54F", "intensity": 3 }
},
{
"id": "cancel",
"label": "Cancel",
"shape": "capsule",
"color": { "hex": "#1F2937", "text_hex": "#FFFFFF" }
}
]
]
}
edit_inline_buttons endpoint — mutable buttons break the "press = same intent forever" guarantee that bots rely on.edit_inline_buttons сознательно нет — изменяемые кнопки нарушают гарантию «нажатие = одно и то же действие всегда», на которой строятся боты.Shapes & colors
Формы и цвета
Shape
Форма
| shape | corner_radius | Effect | Эффект |
|---|---|---|---|
| rectangle | 0..64 | Rounded rectangle. Default. Standard chat-button look.Скруглённый прямоугольник. По умолчанию. Стандартный вид кнопки чата. | |
| circle | ignoredигнорируется | Full circle. Use for icon-only buttons; min size 44 px guaranteed by the client.Круг. Используй для кнопок-иконок; клиент гарантирует минимум 44 px. | |
| capsule | ignoredигнорируется | Pill / stadium shape. Best for call-to-action buttons.Капсула. Хорошо для главной кнопки (call-to-action). |
Solid color
Сплошной цвет
Use color.hex for the button's background and color.text_hex for the label color. Both expect #RRGGBB hex strings (alpha is not supported — bot buttons are always opaque).
Поле color.hex задаёт фон кнопки, color.text_hex — цвет текста. Оба — hex-строки #RRGGBB (альфа не поддерживается, кнопки всегда непрозрачные).
Gradient (two-stop)
Градиент (две точки)
If you set color.gradient, the solid hex is overridden by a two-stop gradient. Good for "premium" / featured buttons.
Если задан color.gradient, он перекрывает сплошной hex. Хорошо для premium / featured кнопок.
| direction | Effect | Эффект |
|---|---|---|
| horizontal | Left to right.Слева направо. | |
| vertical | Top to bottom.Сверху вниз. | |
| diagonal | Top-left to bottom-right.От верхнего левого к нижнему правому углу. | |
| radial | Outwards from the center.От центра наружу. |
# SDK k.Button( "premium", "✨ Get Pro", style=k.ButtonStyle( gradient=k.Gradient(start="#A78BFA", end="#FF7AB6", direction="diagonal"), text_color="#FFFFFF", shape="capsule", ), )
Interactions: tap vs swipe
Взаимодействия: тап и свайп
Tap (default)
Тап (по умолчанию)
Single tap. Fires button_pressed with interaction: "tap" immediately. If you omit the interaction field entirely, this is the default.
Один тап. Сразу же шлёт button_pressed с interaction: "tap". Если поле interaction не указано — режим по умолчанию.
Swipe (drag-to-confirm)
Свайп (drag-to-confirm)
The button renders as a slider track with a circular thumb on the left. The user drags the thumb to the right; while dragging, the track fills with swipe.fill_hex and shows swipe.text. Releasing past ~85% of the track fires button_pressed with interaction: "swipe"; releasing earlier animates the thumb back without firing.
Use it for destructive or irreversible actions — Delete, Confirm Payment, End Voice Chat — where you want to prevent fat-finger taps.
Кнопка рисуется как трек со скользящим thumb-кружком слева. Пользователь тянет thumb направо; во время drag'а трек заполняется цветом swipe.fill_hex и показывает swipe.text. Если отпустить thumb после ~85% пути — стреляет button_pressed с interaction: "swipe". Если отпустить раньше — thumb упруго возвращается, событие не уходит.
Используй для разрушительных или необратимых действий — удаление, подтверждение оплаты, завершение голос-чата — там, где не хочется ловить случайные тапы.
# SDK k.Button( "delete", "Delete", style=k.ButtonStyle(color="#1F2937"), interaction=k.SwipeInteraction( text="Swipe to delete", fill_color="#EF4444", ), )
Animations
Анимации
Up to 4 entries per button, no duplicates of the same kind. They are visual only — they do not change the press event in any way. Pick a mix that matches your button's intent.
Animations are paused automatically when the message scrolls off-screen, so even pulsing buttons in a long chat history are essentially free.
До 4 элементов на кнопку, без дублей по kind. Анимации только визуальные — на событие нажатия они не влияют. Подбирай комбинацию под смысл кнопки.
Когда сообщение скрывается за экраном, анимации автоматически паузятся — пульсирующие кнопки в длинной истории чата практически бесплатны.
| kind | Params | Параметры | Effect | Эффект |
|---|---|---|---|---|
| pulse | speed_ms (200..4000, default 1200) |
Soft scale up-down breathing. Lower speed_ms = faster pulse.Мягкое «дыхание» — масштаб туда-обратно. Меньше speed_ms — быстрее пульс. | ||
| neon | color_hex, blur (0..40, default 12) |
Coloured drop-shadow halo around the button. Static, not animated.Цветной drop-shadow ореол вокруг кнопки. Статичный, не анимирован. | ||
| glitch | intensity_px (1..8, default 3), frequency_ms (500..10000, default 2500) |
Periodic CRT-style RGB-channel shift flash. intensity_px controls how far the R/G/B channels offset; frequency_ms is the interval between flashes.Периодическая CRT-вспышка с RGB-сдвигом каналов. intensity_px задаёт силу сдвига, frequency_ms — интервал между вспышками. |
||
| outline | color_hex, thickness_px (1..8, default 2), corner_radius |
Static coloured border around the button. Lives in the animations list for layering convenience, but is intentionally non-animated.Статичная цветная рамка вокруг кнопки. Лежит в списке animations ради удобства слоёв, но сама не анимирована. |
# SDK: combo of three kinds on the same button animations=[ k.Pulse(speed_ms=900), k.Neon(color="#22D3EE", blur=16), k.Outline(color="#FFD54F", thickness_px=2, corner_radius=14), ]
Particles (burst on tap)
Партиклы (вспышка при тапе)
One optional particles config per button. Triggered by the client on a successful tap (only for type=tap; swipe doesn't fire particles). One-shot burst from the center of the button, then fades.
Doesn't affect the press event in any way — purely visual feedback.
Опциональная единственная конфигурация particles на кнопку. Срабатывает на успешный тап (только для type=tap; свайп не пускает частицы). Одноразовый «взрыв» из центра кнопки, потом затухает.
На событие нажатия не влияет — чисто визуальный feedback.
| type | Effect | Эффект |
|---|---|---|
| spark | Short line sparks shooting outwards. Good for "submit" / "send" buttons.Короткие искры-линии, разлетающиеся в стороны. Хорошо для «отправить» / «submit». | |
| confetti | Rotating rectangles with gravity. Celebrations / "you won" / "level up".Вращающиеся прямоугольники с гравитацией. Празднования / «победа» / «повышение уровня». | |
| heart | Soft hearts floating upward. Likes, friend-requests.Мягкие сердечки, поднимающиеся вверх. Лайки, заявки в друзья. | |
| pixel | Tiny squares — retro / 8-bit vibe.Мелкие квадраты — ретро / 8-битная эстетика. | |
| smoke | Soft blurred clouds dissipating slowly. Dramatic actions.Мягкие размытые облачка, медленно рассеивающиеся. Драматичные действия. |
Common parameters: color_hex — particle color; intensity (1..5, default 3) — controls how dense the burst is.
Общие параметры: color_hex — цвет частиц; intensity (1..5, default 3) — плотность взрыва.
Receiving press events
Получение нажатий
When a user taps or swipes a button, the server emits a button_pressed WebSocket event only to the bot that authored the message. It's not broadcast to other chat members.
The event tells you:
user_id— who pressed (call /bot/user/{id} or /bot/user/{id}/community/{id} if you need nickname / avatar);chat_id— in which chat (use it for the reply via/bot/send-message);community_id— in which community (0 for DMs);button_id— which button (theidfield you set when sending);interaction—"tap"or"swipe".
Когда пользователь тапает или свайпает кнопку, сервер шлёт WebSocket-событие button_pressed только тому боту, который отправил это сообщение. Другим участникам чата ничего не рассылается.
В событии приходит:
user_id— кто нажал (зови /bot/user/{id} или /bot/user/{id}/community/{id}, если нужно имя / аватар);chat_id— в каком чате (используй для ответа через/bot/send-message);community_id— в каком сообществе (0 для DM);button_id— какая кнопка (полеid, которое ты задал при отправке);interaction—"tap"или"swipe".
import karbo @ws.on_button_pressed async def handle(press: karbo.ButtonPress): if press.button_id == "pay" and press.interaction == "swipe": await bot.send_message( press.chat_id, f"Paid by {press.user_id}!", ) elif press.button_id == "cancel": await bot.send_message(press.chat_id, "Cancelled.")
Common recipes
Готовые рецепты
Yes / No prompt
Запрос «Да / Нет»
await bot.send_message( chat_id, "Ready to start?", buttons=[[ k.Button("yes", "Yes", style=k.ButtonStyle(color="#22C55E")), k.Button("no", "No", style=k.ButtonStyle(color="#EF4444")), ]], )
Destructive action with confirmation swipe
Опасное действие со свайп-подтверждением
await bot.send_message( chat_id, "Delete this post?", buttons=[[ k.Button( "del", "Delete", style=k.ButtonStyle(color="#1F2937", shape="capsule"), interaction=k.SwipeInteraction( text="Swipe to delete", fill_color="#EF4444", ), ), ]], )
Multi-row keyboard (4 quick actions)
Многорядная клавиатура (4 быстрых действия)
await bot.send_message( chat_id, "Pick an action:", buttons=[ [ k.Button("a1", "Info", style=k.ButtonStyle(color="#3B82F6")), k.Button("a2", "Stats", style=k.ButtonStyle(color="#06B6D4")), ], [ k.Button("a3", "History", style=k.ButtonStyle(color="#8B5CF6")), k.Button("a4", "Settings",style=k.ButtonStyle(color="#64748B")), ], ], )
Premium "wow" button — gradient + animations + confetti
Premium-кнопка с эффектами — градиент + анимации + конфетти
k.Button( "go_pro", "✨ Get Pro", style=k.ButtonStyle( gradient=k.Gradient(start="#A78BFA", end="#FF7AB6", direction="diagonal"), text_color="#FFFFFF", shape="capsule", ), animations=[ k.Pulse(speed_ms=1100), k.Neon(color="#A78BFA", blur=16), ], particles=k.ConfettiParticles(color="#FFD54F", intensity=5), )
[[a],[b],[c]].[[a],[b],[c]].Error codes
Коды ошибок
On a bad inline_buttons payload, /bot/send-message returns HTTP 400 with this body:
Если inline_buttons прошёл валидацию плохо, /bot/send-message отвечает HTTP 400 с таким телом:
{
"error": "inline_buttons_invalid",
"code": "field-name-or-rule",
"message": "human-readable explanation"
}
code | When | Когда |
|---|---|---|
| too_many_rows | More than 10 rows.Больше 10 рядов. | |
| too_many_buttons_in_row | More than 5 buttons in a single row.Больше 5 кнопок в одном ряду. | |
| too_many_buttons_total | More than 30 buttons across the message.Больше 30 кнопок на сообщение в сумме. | |
| missing_id / bad_id_format | No id or it doesn't match [A-Za-z0-9_.-]{1,64}.Не задан id или он не соответствует [A-Za-z0-9_.-]{1,64}. | |
| duplicate_id | Same id on two buttons of the same message.Один и тот же id у двух кнопок одного сообщения. | |
| label_too_long | Label longer than 64 chars.Label длиннее 64 символов. | |
| bad_shape | Shape is not one of rectangle / circle / capsule.Форма не одна из rectangle / circle / capsule. | |
| bad_hex | Color is not a valid #RRGGBB string.Цвет не валидная #RRGGBB строка. | |
| bad_gradient_direction | direction not one of horizontal / vertical / diagonal / radial.direction не одно из horizontal / vertical / diagonal / radial. | |
| bad_interaction_type | interaction.type is not tap / swipe.interaction.type не tap / swipe. | |
| missing_swipe_block | interaction.type=swipe but no swipe.text / swipe.fill_hex.interaction.type=swipe, но нет swipe.text / swipe.fill_hex. | |
| too_many_animations | More than 4 animations on one button.Больше 4 анимаций на одной кнопке. | |
| duplicate_animation_kind | Two animations of the same kind on one button.Две анимации одного kind на одной кнопке. | |
| bad_animation_kind | kind not one of pulse / neon / glitch / outline.kind не одно из pulse / neon / glitch / outline. | |
| bad_animation_param | An animation parameter is out of range (e.g. pulse.speed_ms < 200).Параметр анимации вне диапазона (например, pulse.speed_ms < 200). | |
| bad_particle_type | particles.type not spark / confetti / heart / pixel / smoke.particles.type не spark / confetti / heart / pixel / smoke. | |
| bad_particle_intensity | particles.intensity out of 1..5.particles.intensity вне 1..5. |
k.Button(...), k.ButtonStyle(...), etc.) validate locally before sending — most of these errors are impossible if you use the SDK. They only matter if you build the JSON by hand.k.Button(...), k.ButtonStyle(...) и т.д.) валидируют локально до отправки — большую часть этих ошибок невозможно получить, если использовать SDK. Они важны только если ты собираешь JSON руками.WebSocket — Real-time Events
Bots connect to a dedicated Socket.IO WebSocket server to receive real-time messages from chats they are in.
WebSocket — События в реальном времени
Боты подключаются к отдельному Socket.IO WebSocket-серверу для получения сообщений из чатов, в которых они состоят.
Connection
Подключение
Use a Socket.IO client in any language. Protocol: WebSocket transport only (no polling).
Используйте клиент Socket.IO на любом языке. Протокол: только WebSocket транспорт (без polling).
| Parameter | Параметр | Value | Значение |
|---|---|---|---|
| URL | https://api.karboai.com | ||
| path | /bot/ws | ||
| transports | ["websocket"] | ||
| auth | {"bot_token": "YOUR_TOKEN"} |
import { io } from "socket.io-client"; const socket = io("https://api.karboai.com", { path: "/bot/ws", transports: ["websocket"], auth: { bot_token: "YOUR_TOKEN" } }); socket.on("connect", () => console.log("Connected!")); socket.on("new_message", (data) => { console.log(`[${data.chat_id}] ${data.content}`); });
import socketio sio = socketio.AsyncClient() @sio.on("new_message") async def on_message(data): print(f"[{data['chat_id']}] {data['content']}") await sio.connect( "https://api.karboai.com", socketio_path="/bot/ws", auth={"bot_token": "YOUR_TOKEN"}, transports=["websocket"], )
Events
События
Emitted when a message is sent to any chat the bot is in (including messages from the bot itself), and also for a handful of system events (see the type field below).
This is the only event the bot receives about chat activity — there is no separate chat_created, member_joined or bot_added_to_chat event. Detect those situations by branching on type.
Событие при отправке сообщения в чат, где состоит бот (включая сообщения самого бота), а также для набора системных событий (см. поле type ниже).
Это единственное событие, которое бот получает про активность в чате — отдельных chat_created, member_joined или bot_added_to_chat нет. Различай эти ситуации по значению type.
{
"chat_id": "uuid",
"message_id": "uuid",
"user_id": "uuid",
"content": "Hello!",
"created_time": 1712070000,
"type": 0,
"community_id": 42,
"chat_type": 0, // 0=public, 1=bot-DM, 2=PM, 3=private
"reply_message_id": null,
"audio": null,
"audio_duration_ms": null,
"waveform": null,
"video_note": null,
"video_note_duration_ms": null,
"sticker": null,
"images": [],
"transparent": false,
"bubble_id": "bubble-uuid",
"bubble_version": 1,
"reactions": [],
"author": {
"user_id": "uuid",
"nickname": "Username",
"avatar_url": "https://...",
"avatar_frame": {
"frame_id": "frame-uuid",
"file": "https://api.karboai.com/static/images/..."
},
"role": 0,
"app_role": 0,
"panel_color": "#1f2937",
"level": 12,
"nickname_color": "#ff7a00",
"nickname_emoji": "✨",
"is_api_bot": false
}
}
user_id with your bot's bot_id (from /bot/me).user_id с bot_id вашего бота (из /bot/me).Message types
Типы сообщений
The type field tells you whether this is a regular chat message or a system event. Only a subset of system types is relevant for bots — the rest can be safely ignored.
Поле type говорит, обычное это сообщение или системное событие. Для ботов реально важна лишь часть системных типов — остальные можно спокойно игнорировать.
| type | Name | Название | Meaning | Значение |
|---|---|---|---|---|
0 | NORMAL_MESSAGE | Regular chat message (text / images / audio / video note / sticker). content is user-typed text; media fields are populated when applicable. | Обычное сообщение (текст / картинки / аудио / видеозаметка / стикер). В content — текст, медиа-поля заполнены по мере необходимости. | |
1 | JOIN_CHAT_MESSAGE | A user joined the chat. user_id is the joiner. | Пользователь вошёл в чат. user_id — вошедший. | |
2 | LEAVE_CHAT_MESSAGE | A user left the chat. | Пользователь покинул чат. | |
3 | KICK_CHAT_MESSAGE | A user was kicked from the chat. | Пользователя выгнали. | |
4 | CREATE_CHAT_MESSAGE | A DM was opened with the bot. Sent when a user calls /api-bots/{bot_id}/start-dm and the chat is created for the first time. user_id is the user who opened the DM, chat_id is the new PM chat. Use this to greet the user or run onboarding. | С ботом открыли личку. Приходит, когда пользователь вызывает /api-bots/{bot_id}/start-dm и чат создаётся впервые. user_id — тот, кто открыл ЛС, chat_id — новый PM-чат. Удобно для приветственного сообщения и онбординга. | |
5 | USER_DELETED_MESSAGE | Placeholder for a message the author deleted. | Заглушка на месте сообщения, удалённого автором. | |
6 | ADMIN_DELETED_MESSAGE | Placeholder for a message removed by moderation. | Заглушка сообщения, удалённого модерацией. | |
7 | VOICE_CHAT_STARTED | A voice chat was started in this chat. | В чате запустили голос-чат. | |
8 | VOICE_CHAT_ENDED | The voice chat ended. | Голос-чат завершён. | |
9 | BACKGROUND_CHANGED | Chat background was changed by the organizer. | Организатор сменил фон чата. | |
10 | INVITED_USER_TO_CHAT | A user was invited to the chat. | Пригласили пользователя. | |
11 | INVITED_BOT_TO_CHAT | This bot was added to a chat. Sent when a chat organizer / helper calls /api-bots/{bot_id}/add-to-chat. user_id is the inviter, chat_id is the chat you just joined. | Этого бота добавили в чат. Приходит, когда организатор или помощник чата вызывает /api-bots/{bot_id}/add-to-chat. В user_id — тот, кто пригласил, в chat_id — только что добавленный чат. | |
21-23 | ACTION_* | Chat action messages (beer, kick, fight roleplay effects). | Игровые чат-действия (beer / kick / fight). | |
24-27 | DM_CALL_* | DM voice call lifecycle (started, declined, missed, ended). | Жизненный цикл DM-звонка (начался, отклонён, пропущен, закончился). | |
28-29 | CINEMA_* | Cinema (shared video) started / ended. | Кинозал запустили / завершили. |
Detecting a new DM
Как отследить новый DM
When a user opens a personal chat with the bot for the first time, the bot receives new_message with type == 4 (CREATE_CHAT_MESSAGE), chat_type == 2 (PM) and community_id == 0. The content field is empty — this is a marker, not human-readable text.
Subsequent re-opens of the same DM (same user hitting start-dm again after previously declining or deleting the chat locally) do not re-emit the event — the chat already exists on the server, so no new system message is inserted.
Когда пользователь впервые открывает личный чат с ботом, боту прилетает new_message с type == 4 (CREATE_CHAT_MESSAGE), chat_type == 2 (PM) и community_id == 0. Поле content пустое — это маркер, а не человекочитаемый текст.
Повторное открытие того же DM (когда тот же юзер снова дёргает start-dm после того как раньше вышел / удалил чат локально) событие не повторяет — чат уже есть в БД, новое системное сообщение не вставляется.
import karbo # SDK-style @ws.on_new_message async def handle(msg: karbo.Message): if msg.type == 4: # CREATE_CHAT_MESSAGE — user just opened DM with the bot await bot.send_message( chat_id=msg.chat_id, content="👋 Привет! Я бот. Напиши /help чтобы узнать что я умею.", ) elif msg.type == 11: # INVITED_BOT_TO_CHAT — added to a group chat await bot.send_message( chat_id=msg.chat_id, content="Спасибо, что добавили! Пиши /help — расскажу что умею.", ) elif msg.type == 0: # normal text message if msg.user_id == bot.bot_id: return # ignore our own echoes # … handle msg.content …
/bot/me and iterate through recent chats via /bot/chat/{chat_id}/message/{message_id} on next connect to reconcile, or simply respond on first user-typed message you receive.Fields available on every event
Поля, доступные в каждом событии
Every new_message payload includes:
В каждом new_message присутствуют:
chat_id— chat the message belongs to.chat_id— чат, которому принадлежит сообщение.message_id— unique ID of this message (for follow-up API calls).message_id— уникальный ID сообщения (для последующих вызовов API).user_id— sender's user ID (the bot's ownbot_idfor its own messages, the initiator for system events likeCREATE_CHAT_MESSAGEandINVITED_BOT_TO_CHAT).user_id— ID отправителя (для сообщений самого бота — егоbot_id, для системных событий типаCREATE_CHAT_MESSAGEиINVITED_BOT_TO_CHAT— инициатор действия).type— message type (see table above).type— тип сообщения (см. таблицу выше).community_id— community the chat lives in.0for DMs and any global chats. Present only in WebSocket events (not in REST responses).community_id— сообщество, к которому относится чат.0для DM и глобальных чатов. Присутствует только в WebSocket-событиях (не в REST-ответах).chat_type— kind of chat this event came from:0= public,1= bot-DM (chat with a bot),2= user-to-user PM,3= private group. Injected by the WS layer specifically for bots (regular clients don't need it). Absent only if the chat was deleted between insert and emit — extremely rare.chat_type— тип чата, из которого пришло событие:0= публичный,1= бот-DM (чат с ботом),2= юзер-юзер PM,3= приватный групповой. Вставляется WS-слоем специально для ботов (обычным клиентам это поле не нужно). Отсутствует только если чат удалён между вставкой сообщения и эмитом — крайне редкая гонка.created_time,content,author— sender profile snapshot, plus media fields (images,audio,sticker,video_note) andreply_message_id.created_time,content,author— снимок профиля отправителя, а также медиа-поля (images,audio,sticker,video_note) иreply_message_id.
GET /bot/chat/{chat_id} — same shape as the app's own GET /chat/{chat_id}.GET /bot/chat/{chat_id} — тот же формат, что и GET /chat/{chat_id} у обычных клиентов.Emitted once per user interaction with a button attached to one of this bot's messages. The bot receives the press regardless of whether the user tapped or swiped — branch on the interaction field.
Only the authoring bot receives the event — it's not broadcast to other chat members.
Приходит на каждое нажатие пользователем кнопки под сообщением этого бота. Бот получает событие независимо от типа взаимодействия — тап или свайп — переключайся по полю interaction.
Событие получает только бот-автор — остальным участникам чата ничего не рассылается.
{
"chat_id": "chat-uuid",
"message_id": "message-uuid",
"button_id": "pay",
"user_id": "user-uuid",
"community_id": 42,
"chat_type": 0, // 0=public, 1=bot-DM, 2=PM, 3=private
"interaction": "tap" // tap | swipe
}
Models
Message
A chat message object.
Модели
Message
Объект сообщения в чате.
| Field | Поле | Type | Тип | Description | Описание |
|---|---|---|---|---|---|
| message_id | string | Unique message ID | Уникальный ID сообщения | ||
| chat_id | string | Chat this message belongs to | Чат, которому принадлежит сообщение | ||
| user_id | string | Sender's user ID | ID отправителя | ||
| content | string | Message text | Текст сообщения | ||
| created_time | int | Unix timestamp | Unix-время | ||
| type | int | 0 = normal text, other values = system events (see the table under new_message: 4 = DM opened, 11 = bot added to chat, 1/2/3 = join/leave/kick, etc.) | 0 = обычный текст, остальные значения = системные события (см. таблицу под new_message: 4 = открыли DM, 11 = бота добавили в чат, 1/2/3 = join/leave/kick и т.п.) | ||
| community_id | int | Community ID of the chat (0 = global / no community, including DMs with the bot). Only present in WebSocket events. | ID сообщества чата (0 = глобальный / без сообщества, включая DM с ботом). Присутствует только в WebSocket-событиях. | ||
| chat_type | int | Kind of the containing chat: 0 = public, 1 = bot-DM, 2 = user-to-user PM, 3 = private group. Injected into WebSocket events for bots so message handlers can branch on chat kind without an extra REST call. | Тип чата, к которому относится сообщение: 0 = публичный, 1 = бот-DM, 2 = юзер-юзер PM, 3 = приватный групповой. Добавляется в WebSocket-события специально для ботов, чтобы обработчик мог различить чат без дополнительного REST-запроса. | ||
| reply_message_id | string? | ID of the replied message | ID сообщения, на которое ответили | ||
| audio | string? | Voice message URL | URL голосового сообщения | ||
| audio_duration_ms | int? | Voice message duration in ms | Длительность голосового в мс | ||
| waveform | float[]? | Audio waveform data | Данные формы волны аудио | ||
| video_note | string? | Video note URL | URL видеозаметки | ||
| video_note_duration_ms | int? | Video note duration in ms | Длительность видеозаметки в мс | ||
| sticker | string? | Sticker identifier | Идентификатор стикера | ||
| images | string[] | Array of image URLs | Массив URL изображений | ||
| transparent | bool | Whether message has transparent background | Прозрачный ли фон сообщения | ||
| bubble_id | string? | Sender bubble style ID | ID пузыря отправителя | ||
| bubble_version | int | Bubble asset version | Версия ассета пузыря | ||
| reactions | MessageReaction[] | Aggregated reactions for the message | Агрегированные реакции на сообщение | ||
| author | Author | Sender info | Информация об отправителе |
User
| Field | Поле | Type | Тип | Description | Описание |
|---|---|---|---|---|---|
| user_id | string | Unique user ID | Уникальный ID | ||
| nickname | string | Display name | Отображаемое имя | ||
| avatar | string | Avatar URL | URL аватара | ||
| short_info | string | Bio text | Описание профиля | ||
| role | int | User role (0 = regular) | Роль пользователя (0 = обычный) | ||
| app_role | int | Global app role | Глобальная роль в приложении | ||
| panel_color | string? | Profile panel color | Цвет панели профиля | ||
| level | int | Current user level | Текущий уровень пользователя | ||
| nickname_color | string? | Premium nickname color | Премиум-цвет ника | ||
| nickname_emoji | string? | Premium nickname emoji | Премиум-эмодзи ника | ||
| avatar_frame | AvatarFrame? | Selected avatar frame | Выбранная рамка аватара | ||
| bubble_id | string? | Selected bubble ID | ID выбранного пузыря |
Member
Chat member object. Similar to User but includes member_status and is_api_bot.
Объект участника чата. Похож на User, но включает member_status и is_api_bot.
| Field | Поле | Type | Description | Описание |
|---|---|---|---|---|
| user_id | string | User ID | ID пользователя | |
| nickname | string | Display name | Отображаемое имя | |
| avatar_url | string | Avatar URL | URL аватара | |
| role | int | Community role | Роль в сообществе | |
| app_role | int | Global app role | Глобальная роль | |
| panel_color | string? | Profile panel color | Цвет панели | |
| level | int | User level | Уровень | |
| nickname_color | string? | Premium nickname color | Цвет ника | |
| nickname_emoji | string? | Premium nickname emoji | Эмодзи ника | |
| avatar_frame | AvatarFrame? | Avatar frame | Рамка аватара | |
| member_status | string | "joined" or "invited" | "joined" или "invited" | |
| is_api_bot | bool | Whether this member is an API bot | Является ли участник API-ботом |
Author
Embedded in message objects.
Вложенный объект в сообщениях.
| Field | Поле | Type | Description | Описание |
|---|---|---|---|---|
| user_id | string | Sender ID | ID отправителя | |
| nickname | string | Display name | Отображаемое имя | |
| avatar / avatar_url | string | Avatar URL | URL аватара | |
| avatar_frame | AvatarFrame? | Selected avatar frame | Выбранная рамка аватара | |
| role / app_role | int | Community and app roles | Роль в сообществе и глобальная роль | |
| panel_color | string? | Profile panel color | Цвет панели профиля | |
| level | int | Current level | Текущий уровень | |
| nickname_color | string? | Premium nickname color | Премиум-цвет ника | |
| nickname_emoji | string? | Premium nickname emoji | Премиум-эмодзи ника | |
| is_api_bot | bool | Whether the author is an API bot | Является ли автор API-ботом |
AvatarFrame
Compact avatar frame object used by users, members, and message authors.
Компактный объект рамки аватара, используется у пользователей, участников и авторов сообщений.
| Field | Поле | Type | Description | Описание |
|---|---|---|---|---|
| frame_id | string? | Avatar frame ID | ID рамки аватара | |
| file | string? | Frame image URL | URL изображения рамки |
MessageReaction
Aggregated reaction entry attached to a message.
Агрегированная реакция, прикреплённая к сообщению.
| Field | Поле | Type | Description | Описание |
|---|---|---|---|---|
| reaction | string | Emoji or sticker reaction key | Эмодзи или ключ стикер-реакции | |
| is_sticker | bool | Whether this reaction is a sticker | Является ли реакция стикером | |
| count | int | Total number of this reaction | Общее количество этой реакции | |
| me | bool | Whether the current bot reacted with it | Реагировал ли этим текущий бот |
Official Python SDK
We provide an official Python library karbo that wraps the entire Bot API. Install it from PyPI:
Официальный Python SDK
Мы предоставляем официальную Python-библиотеку karbo, которая покрывает весь Bot API. Установите из PyPI:
pip install -U karbo
import asyncio import karbo async def main(): async with karbo.KarboBot("YOUR_TOKEN") as bot: ws = karbo.KarboBotWS("YOUR_TOKEN") me = await bot.get_me() print(f"Bot: {me.name}") @ws.on_message async def on_message(msg: karbo.Message): if msg.user_id == me.bot_id: return # Echo text await bot.send_message(msg.chat_id, f"Echo: {msg.content}") # Send an image url = await bot.upload_image("photo.jpg") await bot.send_message(msg.chat_id, images=[url]) await ws.run_forever() asyncio.run(main())
import karbo as k await bot.send_message( chat_id, "Confirm payment?", buttons=[ [ k.Button( "pay", "Pay", style=k.ButtonStyle(color="#22C55E", shape="capsule"), animations=[k.Pulse(speed_ms=900)], particles=k.SparkParticles(color="#FFD54F"), ), k.Button("cancel", "Cancel"), ], ], ) @ws.on_button_pressed async def on_press(press: k.ButtonPress): if press.button_id == "pay": await bot.send_message( press.chat_id, "Paid!", reply_to=press.message_id, )
Known Libraries
If you prefer another language, here are known community packages for the KarboAI Bot API.
Известные библиотеки
Если вам нужен другой язык, вот известные пакеты сообщества для KarboAI Bot API.
Package: npmjs.com/package/karboai
Install:
Пакет: npmjs.com/package/karboai
Установка:
npm install karboai
Language/runtime: Node.js
Язык/рантайм: Node.js
KarboAI Bot API Documentation
Документация KarboAI Bot API