
# Bot platform troubleshooting

If the bot does not reply to messages, start with the sections below. Each scenario is a separate set of checks with ready-to-use curl commands and reference responses from Vibecode.

## Diagnostic scenarios

- [Events do not arrive](#events-do-not-arrive) — a user writes to the bot, but `GET /v1/bots/:botId/events` returns an empty array
- [TOKEN_MISSING with an authorization key](#token_missing-with-an-authorization-key) — `vibe_app_…` sent without `Authorization: Bearer`
- [Empty events in a row](#empty-events-in-a-row) — `success: true`, but `nextOffset` does not move
- [BITRIX_ERROR: User is not subscribed](#bitrix_error-user-is-not-subscribed) — `withUserEvents=true` without a prior subscription
- [INTERNAL_ERROR while polling events](#internal_error-while-polling-events) — a transient proxy error and a retry scheme with a growing pause
- [Bot disabled (BOT_DISABLED)](#bot-disabled-bot_disabled) — automatic disabling on `AUTH_FAILURES` or `PORTAL_DELETED`
- [fetch vs webhook differences](#fetch-vs-webhook-differences) — the difference in data formats and behavior during event delivery
- [Stickers](#stickers) — a platform limitation
- [Where to reach out](#where-to-reach-out) — what to attach to a support ticket

---

## Events do not arrive

Symptom: a user writes to the bot in a Bitrix24 chat, the bot does not reply, `GET /v1/bots/:botId/events` returns `events: []`.

### Checklist in order

1. **The bot is registered and active.** `GET /v1/bots/:botId` returns `success: true` with the fields `bot.id`, `bot.code`, `bot.eventMode`. If 404 — the bot is not registered, run `POST /v1/bots`.
2. **`eventMode: "fetch"`.** Checked in the response of step 1. If `webhook` — events do not arrive via polling, the bot pushes them to `webhookUrl`.
3. **The bot type matches the scenario.** A bot of type `bot` only receives messages with an `@mention` and direct messages. To receive all messages in a chat you need the type `personal` or `supervisor` — specified at registration in the `type` field and not changed afterwards. If you expect all messages but the type is `bot`, events will arrive only on an `@mention`.
4. **The bot is added to the chat.** The bot receives events only from chats it is a member of. For a direct dialog this happens on the user's first interaction with the bot. For a group chat the bot must be added explicitly via `POST /v1/bots/:botId/chats/:dialogId/users`.
5. **The user is writing to this exact bot.** If your Bitrix24 account has several bots, check the `code` of the bot being addressed in the chat and compare it with the `code` in the `GET /v1/bots/:botId` response. Messages to another bot will not land in this bot's queue.
6. **After 5+ empty polls, check the `hint` field** in the `GET /v1/bots/:botId/events` response. The platform adds a diagnostic hint if the queue has been empty for several polls in a row.
7. **Re-bind the event subscription.** If the bot is active (messages are reaching the chats) but the queue is empty and the `hint` field appeared, call [`POST /v1/bots/:botId/resubscribe`](/docs/bots/management/resubscribe). Re-binding restores delivery and preserves the bindings of Open Channels and the welcome bot, unlike re-registering via `POST /v1/bots`.

If all 7 items pass but the queue is still empty — it means the Bitrix24 account is not routing events to the bot. Collect the data from the [Where to reach out](#where-to-reach-out) section and submit a ticket.

---

## TOKEN_MISSING with an authorization key

Symptom:

```json
{ "success": false, "error": { "code": "TOKEN_MISSING", "message": "API key has no tokens configured." } }
```

### Cause

The `vibe_app_…` key was sent without the `Authorization: Bearer <session_token>` header. An authorization key works in tandem with a user session token — without Bearer the request has no context on whose behalf to address Bitrix24.

### Solution

A personal key `vibe_api_…` — no Bearer needed:

```bash
curl https://vibecode.bitrix24.com/v1/bots/42/events \
  -H "X-Api-Key: YOUR_API_KEY"
```

An authorization key `vibe_app_…` — Bearer is mandatory:

```bash
curl https://vibecode.bitrix24.com/v1/bots/42/events \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN"
```

Obtaining the `USER_SESSION_TOKEN` for an OAuth app — see [Keys and authorization](/docs/keys-auth).

A common source of this error is an application-side event handler that calls the V1 API in response to a delivered event. The handler has no user session, so a `vibe_app_` key does not work there — use a personal `vibe_api_` key. See [Application-side handler](/docs/infra/event-subscriptions/handler).

---

## Empty events in a row

Symptom: the response is correct, there are no errors, but `events: []`, `nextOffset === storedOffset`, `persisted: false`.

### What the response fields mean

- `persisted: false` — the `lastOffset` cursor in the Vibecode database did not move, because Bitrix24 did not return a single event.
- `nextOffset === storedOffset` — nothing to process.
- `hint` appears after 5+ empty polls in a row — a diagnostic hint.

### This is normal if

- the bot was just registered — nobody has written yet
- the queue is empty — clients have read all messages
- there is no activity in chats with the bot between polls.

### When to move on to diagnostics

If at least one condition holds — go back to the [Events do not arrive](#events-do-not-arrive) section:

- there are unread messages to the bot in the chat
- more than a minute has passed since the message was sent
- the `hint` field appeared.

---

## BITRIX_ERROR: User is not subscribed

Symptom: `GET /v1/bots/:botId/events?withUserEvents=true` returns 502 with this message.

### Cause

User events (types `ONIMV2*` — chat changes, user reactions) require a separate subscription. Without a subscription the `withUserEvents=true` parameter does not enable delivery.

### Solution

Subscribe once before using `withUserEvents=true`. The full setup order and the event list — in the [User events](/docs/bots/events/user-events) section.

After subscribing, `GET /v1/bots/:botId/events?withUserEvents=true` starts returning user events together with bot events in the same array.

---

## INTERNAL_ERROR while polling events

Symptom:

```json
{ "success": false, "error": { "code": "INTERNAL_ERROR", "message": "Internal server error" } }
```

### What to do

1. **Do not repeat the request right away.** Frequent retries without a pause can prolong the error state and themselves become a cause of overload.
2. **Use a growing pause between attempts** — start at 5 seconds, double on each next failure, cap at 60 seconds. After a successful response the interval resets to the working value (2-5 seconds between polls).
3. **Do not reset `offset`.** The stored cursor was not affected — continue from the same value. A reset leads to reprocessing already delivered events.
4. **If the error does not go away after several delay cycles** — submit a support ticket with the `botId`, the time of first occurrence, the last successful `nextOffset`, and the interval between attempts. Do not increase the request frequency "just in case" — it will make things worse.

### Ready-to-use template with a growing pause

```javascript
const BOT_ID = 42
const API_KEY = 'YOUR_API_KEY'
const BASE = 'https://vibecode.bitrix24.com/v1'

let backoffMs = 5000

while (true) {
  try {
    const res = await fetch(`${BASE}/bots/${BOT_ID}/events`, {
      headers: { 'X-Api-Key': API_KEY },
    })
    const json = await res.json()

    if (!json.success && json.error?.code === 'INTERNAL_ERROR') {
      console.warn(`INTERNAL_ERROR — pause ${backoffMs} ms`)
      await new Promise(r => setTimeout(r, backoffMs))
      backoffMs = Math.min(backoffMs * 2, 60000)
      continue
    }

    backoffMs = 5000
    for (const event of json.data?.events ?? []) {
      // handle the event
    }
  } catch {
    await new Promise(r => setTimeout(r, backoffMs))
    backoffMs = Math.min(backoffMs * 2, 60000)
  }

  await new Promise(r => setTimeout(r, 3000))
}
```

---

## Bot disabled (BOT_DISABLED)

Symptom:

```json
{ "success": false, "error": { "code": "BOT_DISABLED", "message": "Bot is disabled (reason: …)" } }
```

HTTP code — `410 Gone`. All bot endpoints are affected: events, messages, chats, commands, update, delete.

### Possible causes (the `reason` field)

- `AUTH_FAILURES` — 10 consecutive `401 INVALID_CREDENTIALS` from Bitrix24. The platform protects the Bitrix24 account from noisy requests and automatically disables a bot with broken authorization.
- `PORTAL_DELETED` — the Bitrix24 account was deleted or confirmed unavailable.

### Solution

`AUTH_FAILURES` — call [`POST /v1/bots/:botId/reauth`](/docs/bots/management/reauth). The check confirms the bot's access, refreshes the token if needed, and lifts the disabling. If the response is `410 REAUTH_REQUIRED` — access cannot be restored automatically, the key must be re-authorized via OAuth or the personal key re-created.

To reset only the authorization error counter, without an access check, use `PATCH /v1/bots/:botId` with the body `{"disabled": false}`:

```bash
curl -X PATCH https://vibecode.bitrix24.com/v1/bots/42 \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"disabled": false}'
```

`PORTAL_DELETED` — the bot is restored automatically when the Bitrix24 account returns to the `ACTIVE` status. Forced enabling via `PATCH` is not required for this reason.

`PATCH … {"disabled": true}` is forbidden — it returns `400 DISABLE_NOT_ALLOWED`. Disabling is system-imposed; to delete, use `DELETE /v1/bots/:botId`.

---

## fetch vs webhook differences

Vibecode supports two event delivery modes: `fetch` (default) and `webhook`. The mode is set by the `eventMode` field at bot registration and does not change afterwards.

| | `fetch` | `webhook` |
|---|---|---|
| How it works | The client requests events itself via `GET /v1/bots/:botId/events` | Bitrix24 pushes events as HTTP POST to `webhookUrl` |
| Value types | Native: `number`, `boolean`, `null` | Everything arrives as strings (`"1"`, `"true"`, `""`) — a consequence of body serialization on the Bitrix24 side |
| The `bot` object in an event | Without the `auth` field | Contains `auth` with the Bitrix24 account's OAuth tokens |
| Timeout | None (long-poll) | 10 seconds — Bitrix24 waits for a 200 from your server and closes the connection |
| Redelivery | Server-side offset storage — an event cannot be missed | No automatic retries on timeout or a 5xx response |
| Body format | JSON in the `GET /v1/bots/:botId/events` response | `application/x-www-form-urlencoded` in Bitrix24's own format (`event=…&data[bot][id]=…&auth[member_id]=…`) |
| When to choose | Most scenarios, especially AI agents | When you need minimal reaction time and have a publicly accessible server |

**With `eventMode: "webhook"`, `GET /v1/bots/:botId/events` always returns an empty array** — events are delivered directly to `webhookUrl` and do not land in polling.

### webhook requires a publicly reachable URL

In `webhook` mode, Bitrix24 sends events as a POST request directly to `webhookUrl` — **without Vibecode authorization**. So `webhookUrl` must be publicly reachable from the internet.

A Black Hole server with the default `OWNER_ONLY` access policy does not accept such a request: without a Vibecode session it stops at the tunnel splash page, and the event never reaches the app. For the Black Hole server to accept incoming events, switch it to the `PUBLIC` policy via [`PATCH /v1/infra/servers/:id/access-policy`](/docs/infra/access/access-policy). The `PUBLIC` policy opens the subdomain to everyone without authorization — enable it deliberately. Alternatively, specify an external publicly reachable `webhookUrl`, not on a Black Hole subdomain.

A server created for an AI agent always stays on the `OWNER_ONLY` policy — it cannot be changed. For a webhook bot on such a server, use an external publicly reachable `webhookUrl`.

The server must be running when the event arrives. A sleeping Black Hole server will not receive the event, and there are no delivery retries — for a webhook bot, disable automatic sleep so the server stays available.

The reachability of `webhookUrl` is not verified at registration — the bot will register even with an unreachable address.

---

## Stickers

Bots **cannot send** stickers to users. This is a limitation of the Bitrix24 Bot API v2.

Bots can **receive** stickers: if a user sent a sticker, the `ONIMBOTV2MESSAGEADD` event arrives, but the `message.text` field will be empty. You can react to stickers by sending a text message or an attachment via `POST /v1/bots/:botId/messages`.

---

## Where to reach out

If none of the sections above helped — leave a ticket in the [Feedback](/docs/feedback) section. Attach to the ticket:

- the `botId` (a number)
- the full `GET /v1/bots/:botId` response
- the last 3 `GET /v1/bots/:botId/events` responses with the fields `nextOffset`, `storedOffset`, `persisted`, `hint`
- the time of the symptom's first occurrence (UTC)
- the key type (`vibe_api_…` or `vibe_app_…`) — without the key itself
- a screenshot of the chat with the unread message, if any.

---

## See also

- [Bot platform](/docs/bots)
- [Get events (polling)](/docs/bots/events/polling)
- [User events](/docs/bots/events/user-events)
- [Register a bot](/docs/bots/management/create)
- [Application-side handler](/docs/infra/event-subscriptions/handler)
- [Keys and authorization](/docs/keys-auth)
- [Limits and optimization](/docs/optimization)
