
## Обработчик на стороне приложения

Приём доставленного события в приложении на Black Hole-сервере. Платформа доставляет каждое событие POST-запросом на субдомен приложения по пути `appPath` в формате `application/x-www-form-urlencoded` — это стандартное тело события Битрикс24. Обработчик читает `event` и `data`, сверяет `auth[application_token]` со своим `application_token` и отвечает `2xx`. Платформа повторяет доставку при ответе не `2xx` и будит спящий сервер.

```javascript
// Express-обработчик: поля формы Битрикс24 приходят в req.body
import express from 'express'

const APP_TOKEN = process.env.APPLICATION_TOKEN // application_token приложения
const app = express()
app.use(express.urlencoded({ extended: true }))

app.post('/api/webhooks/b24', (req, res) => {
  // Сверка токена приложения — события без верного токена отклоняются
  if (req.body.auth?.application_token !== APP_TOKEN) {
    return res.status(403).json({ ok: false })
  }

  const event = req.body.event              // например "ONTASKADD"
  const taskId = req.body.data?.FIELDS?.ID  // ID сущности из события

  // Повторное событие не должно сработать дважды — платформа может доставить его снова
  console.log(`Событие ${event}, задача #${taskId}`)

  // Ответить 2xx, иначе платформа повторит доставку
  res.status(200).json({ ok: true })
})
```

## Тело события

Пример тела, которое приходит в обработчик:

```
event=ONTASKADD&data[FIELDS][ID]=20152&ts=1780843200&auth[access_token]=<токен Битрикс24>&auth[refresh_token]=<токен обновления Битрикс24>&auth[application_token]=<application_token приложения>&auth[domain]=your-portal.bitrix24.ru&auth[member_id]=...
```

Тело несёт токены пользователя Битрикс24. `auth[application_token]` обработчик сверяет, чтобы отклонить чужие события. `auth[access_token]` — токен Битрикс24 для прямых вызовов REST Битрикс24. Как ключ V1 API напрямую он не работает, но его можно обменять на токен сессии — это второй способ авторизации ниже.

## Авторизация вызовов V1 API из обработчика

Обработчик часто вызывает V1 API в ответ на событие — читает связанную сущность или создаёт новую. Платформа доставляет событие сама и **не добавляет заголовков авторизации**: в запросе приходит только `Content-Type` и тело события. Авторизацию вызова обработчик задаёт сам. Есть два рабочих способа — выбор зависит от того, под чьим ключом должны идти вызовы.

### Способ 1. Персональный ключ `vibe_api_`

Самый короткий путь: персональный ключ в заголовке `X-Api-Key`, сессия не нужна. Вызовы идут от лица владельца ключа. Подходит, когда их не обязательно делать под тем же приложением, что и подписка.

```javascript
app.post('/api/webhooks/b24', async (req, res) => {
  if (req.body.auth?.application_token !== APP_TOKEN) {
    return res.status(403).json({ ok: false })
  }

  const taskId = req.body.data?.FIELDS?.ID

  // Вызов V1 API — персональный ключ в заголовке X-Api-Key
  const task = await fetch(
    `https://vibecode.bitrix24.tech/v1/tasks/${taskId}`,
    { headers: { 'X-Api-Key': process.env.VIBE_API_KEY } },
  ).then((r) => r.json())

  console.log('Задача из события:', task.data)

  res.status(200).json({ ok: true })
})
```

Персональный ключ `vibe_api_` создают в личном кабинете — порядок создания описан в [Ключах и авторизации](/docs/keys-auth). Скоупы Битрикс24 закрепляются за ключом при выпуске: отметьте те группы данных, к которым обращается обработчик (для примера с задачами — `tasks`).

### Способ 2. Ключ авторизации `vibe_app_` под тем же приложением

Если вызовы должны идти под тем же ключом авторизации, что и подписка, то есть от лица пользователя портала — обработчику нужна сессия. Тело события несёт токены этого пользователя: `auth[access_token]`, `auth[member_id]`, `auth[domain]` и `auth[refresh_token]`. Их обменивают на токен сессии `vibe_session_` запросом `POST /v1/oauth/placement-session` с телом `{ app_key, access_token, member_id, domain, refresh_token }`. Полученную сессию передают в `Authorization: Bearer` рядом с `X-Api-Key`:

```bash
curl https://vibecode.bitrix24.tech/v1/tasks/20152 \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN"
```

Как получить сессию из токенов события — раздел [«Приложение на своём сервере»](/docs/keys-auth#приложение-на-своём-сервере) в «Ключах и авторизации». Сессия живёт 24 часа, продления нет.

### Частые ошибки авторизации

Три сочетания заголовков, которые не сработают, и код ответа для каждого:

| Что передали | Ответ | Причина |
|---|---|---|
| Ключ авторизации `vibe_app_` в `X-Api-Key` без `Bearer` | `401 TOKEN_MISSING` | На самом ключе портальных токенов нет — нужна сессия в `Authorization: Bearer` рядом с `X-Api-Key` (Способ 2) |
| Ключ авторизации `vibe_app_` в `Authorization: Bearer` | `401 WRONG_AUTH_SCHEME` | В `Bearer` идёт токен сессии `vibe_session_`, а не ключ приложения |
| Токен `auth[access_token]` из события в `Authorization: Bearer` | `401 MISSING_API_KEY` | Это токен Битрикс24, а не ключ V1. В `Bearer` V1 API принимает только токены `vibe_` — токен Битрикс24 сначала обменивают на сессию (Способ 2) |

Подробный разбор `TOKEN_MISSING` — [Диагностика проблем бот-платформы](/docs/bots/troubleshooting#token_missing-при-ключе-авторизации).

## Известные особенности

- **Доставка проходит через туннель — публичный URL приложению не нужен.** Платформа регистрирует обработчик на своей стороне, Битрикс24 шлёт событие туда, а платформа доставляет его в приложение через туннель Black Hole с проверкой `auth[application_token]`. Своего публичного адреса приложению не требуется.
- **Спящий сервер просыпается под доставку.** Если на момент события сервер спит, платформа будит его и доставляет событие после подключения туннеля. Поэтому первое событие после простоя приходит с задержкой на пробуждение.
- **Доставка надёжная, но не бесконечная.** Неуспешные доставки повторяются с нарастающей задержкой. После исчерпания попыток доставка переходит в `FAILED`, подписка помечается `DEGRADED`, владелец получает уведомление. Статусы доставок видны в [`GET /v1/infra/servers/:id/event-subscriptions`](./list.md).
- **Повторная доставка не должна дублировать эффект.** Одно и то же событие может прийти повторно. Обрабатывайте его так, чтобы второй приём с тем же `data[FIELDS][ID]` не сделал работу дважды.

## Смотрите также

- [Подписки на события портала](/docs/infra/event-subscriptions)
- [Создать подписку](/docs/infra/event-subscriptions/create)
- [Список подписок](/docs/infra/event-subscriptions/list)
- [Ключи и авторизация](/docs/keys-auth)
- [Диагностика проблем бот-платформы](/docs/bots/troubleshooting)
