Для AI-агентов: markdown этой страницы — /docs-content/partner-connect.md индекс документации — /llms.txt

Partner Connect

Подключение внешних сервисов к Битрикс24: ваше приложение запрашивает у пользователя разрешение на доступ к его порталу и получает API-ключ Вайбкод для дальнейших вызовов. Поток построен по схеме Authorization Code — той же, что у OAuth-провайдеров Google или GitHub.

Базовый URL: https://vibecode.bitrix24.tech/v1 | Авторизация: client_id + client_secret партнёра | Скоупы ключа: запрашиваются на странице согласия

Содержание

Режим подключения

Новые клиенты используют режим PERSONAL: согласие и ключ принадлежат пользователю. Режим COMPANY подготавливает общее подключение компании к облачному аккаунту Битрикс24; его устанавливает только администратор платформы. Он требует клиента CONFIDENTIAL, секрета клиента и PKCE S256. Включённый вход по коду устройства несовместим с COMPANY; попытка включить вторую возможность отклоняется. Переключение возможно, пока клиент не выдал ни одного ключа: история попыток без выдачи его не запрещает. Первая выдача закрепляет режим навсегда, даже после отзыва ключей; смена даёт HTTP 409 CONNECT_COMPANY_MODE_LOCKED — для режима Компании зарегистрируйте нового клиента. Незавершённые попытки отменяются при разрешённом переключении. Существующее подключение Компании запрещает переход обратно в PERSONAL.

Выдача для COMPANY пока закрыта. GET /v1/connect/authorize возвращает на зарегистрированный redirect_uri ошибку access_denied, исходный state и error_description=CONNECT_COMPANY_NOT_READY независимо от доступности подготовки. Код авторизации не создаётся. POST /v1/connect/token с grant_type=authorization_code и корректными учётными данными такого клиента возвращает HTTP 403 с тем же кодом в code и error_description.

Запуск и обмен device-кода для COMPANY возвращают HTTP 400, error=unauthorized_client, code=CONNECT_COMPANY_DEVICE_FLOW_FORBIDDEN. Подтверждение согласия из сессии поддержки, действующей от имени другого пользователя, отклоняется с CONNECT_COMPANY_IMPERSONATION_FORBIDDEN, в том числе на разрешённом для поддержки аккаунте. Повторная попытка и выбор другого аккаунта эти ограничения не устраняют. Описанные ниже сценарии выдачи относятся к PERSONAL.

Для секрета COMPANY хранилище закрыто с HTTP 403 CONNECT_COMPANY_STORAGE_NOT_READY. Выпуск производных токенов и ключей, а также управление ключами внешних моделей закрыты с HTTP 403 CONNECT_COMPANY_DERIVED_TOKEN_FORBIDDEN. Неизвестные маршруты отклоняются с HTTP 403 CONNECT_COMPANY_ROUTE_FORBIDDEN. Эти отказы не снимаются выбором другого портала. Выдача в проде остаётся закрытой независимо от флага.

Опрос подключения Компании

GET /v1/connect/company/status позволяет серверу Продукта проверить своё подключение по уже выданному секрету COMPANY. Передайте его только в заголовке X-Api-Key. Секрет клиента, cookie и Bearer для этого метода не подходят. Храните секрет подключения на сервере, не передавайте в браузер и не записывайте в журналы. Пара аккаунта и Продукта определяется секретом. Метод не принимает параметры запроса, включая идентификаторы другой пары.

Запрос

Terminal
curl --max-time 20 "$VIBE_URL/v1/connect/company/status" \
  -H "X-Api-Key: $VIBE_COMPANY_SECRET"
javascript
const response = await fetch(`${process.env.VIBE_URL}/v1/connect/company/status`, {
  headers: { 'X-Api-Key': process.env.VIBE_COMPANY_SECRET },
  cache: 'no-store',
  signal: AbortSignal.timeout(20000),
})
const result = await response.json()
if (!response.ok) throw new Error(result.error.code)
console.log(result.data.status, result.data.revision, result.data.action)

Ответ и ошибки возвращаются с Cache-Control: no-store. Пример ответа для подключения, ожидающего администратора:

JSON
{
  "success": true,
  "data": {
    "connection_id": "connection-id",
    "connection_mode": "COMPANY",
    "status": "NEEDS_ADMIN",
    "generation": 0,
    "revision": 1,
    "credential_version": 1,
    "bitrix_ready": false,
    "reason_code": "CONNECTION_ADMIN_REQUIRED",
    "webhook_cleanup": "none",
    "capabilities": { "bitrix": false, "ai": false, "storage": false },
    "poll_after_seconds": 30,
    "action": "AUTHORIZE_ADMIN"
  }
}

Поля ответа

Поле Тип Значение
success boolean true для успешного опроса
data object Диагностика собственного подключения
data.connection_id string Идентификатор подключения, полученный этим методом
data.connection_mode string Всегда COMPANY
data.status string ACTIVE, NEEDS_ADMIN, SUSPENDED, DISCONNECTED, DELETION_PENDING
data.generation integer Поколение согласия подключения
data.revision integer Записанная версия состояния подключения
data.credential_version integer Версия текущего секрета подключения
data.bitrix_ready boolean Пока всегда false
data.reason_code string Причина закрытого доступа, см. таблицу ниже
data.webhook_cleanup string none — нет незавершённых попыток, pending — очистка или создание не завершены, attention_required — результат создания вебхука неизвестен
data.capabilities.bitrix boolean Пока всегда false
data.capabilities.ai boolean Пока всегда false
data.capabilities.storage boolean Пока всегда false
data.poll_after_seconds integer Рекомендуемый интервал опроса: 30 секунд
data.action string WAIT, AUTHORIZE_ADMIN, CONTACT_SUPPORT, RECONNECT, WAIT_FOR_DELETION
Состояние или условие reason_code action
ACTIVE, подтверждение администратора действительно CONNECT_COMPANY_NOT_READY WAIT
ACTIVE, подтверждение истекло, отозвано или отсутствует ADMIN_CHECK_REQUIRED AUTHORIZE_ADMIN
NEEDS_ADMIN CONNECTION_ADMIN_REQUIRED AUTHORIZE_ADMIN
SUSPENDED CONNECTION_SUSPENDED CONTACT_SUPPORT
DISCONNECTED CONNECTION_DISCONNECTED RECONNECT
DELETION_PENDING CONNECTION_DELETION_PENDING WAIT_FOR_DELETION

Опрос не продлевает подтверждение администратора. Значение ACTIVE само по себе не открывает доступ: сейчас Битрикс24 и искусственный интеллект (AI) возвращают CONNECT_COMPANY_NOT_READY, файлы — CONNECT_COMPANY_STORAGE_NOT_READY. В ответе нет персональных данных, перечня людей, секретов и URL.

Ошибки и повторение опроса

HTTP Код Что делать
401 INVALID_API_KEY Проверить текущий секрет. Неизвестный, отозванный, старый после ротации секрет и запрос чужой пары дают одинаковый отказ
403 CLIENT_BLOCKED Продукт заблокирован, обратиться в поддержку
403 PORTAL_DELETED Аккаунт удалён, остановить опрос
403 PORTAL_BLOCKED Аккаунт заблокирован, обратиться в поддержку
403 PORTAL_SUSPENDED Аккаунт приостановлен, обратиться в поддержку
429 RATE_LIMITED Достигнут лимит IP. Подождать не меньше Retry-After секунд
429 CALLER_RATE_LIMITED Достигнут общий лимит подключения или аккаунта. Подождать не меньше Retry-After секунд

Пример одинакового отказа для недействительного секрета:

JSON
{ "success": false, "error": { "code": "INVALID_API_KEY", "message": "Invalid API key" } }

Опрашивайте не чаще poll_after_seconds, при 429 увеличивайте паузу. Базовый лимит IP — 120 запросов в минуту; действующий порог указан в x-ratelimit-limit. Успешный опрос также расходует общий лимит подключения и аккаунта вместе с другими API-запросами. Ротация секрета не сбрасывает этот лимит.

Секрет в состоянии STATUS_ONLY позволяет читать диагностику, но не вызывать рабочие методы. REVOKED не позволяет даже опрос. После ротации используйте новый секрет. Уже выданная защита и опрос действуют независимо от доступности новой выдачи. Повторное подключение выполняет администратор аккаунта через отдельное согласие. Коды ошибок описаны также в справочнике ошибок.

Когда применять

Сценарий — внешний SaaS-продукт, который интегрируется с порталами Битрикс24 ваших клиентов: CRM-аналитика, синхронизация с 1С, чат-ассистент, конструктор лендингов. Кнопка «Подключить Битрикс24» на вашем сайте запускает поток: пользователь выбирает портал и подтверждает скоупы, ваш сервер получает постоянный API-ключ и работает с порталом от имени пользователя.

Если вы пишете приложение, которое ставится внутри портала Битрикс24 и работает только с ним, — используйте обычный API-ключ, см. Ключи и авторизация.

Как это работает

Ваше приложение → [1. Redirect] → Вайбкод Consent Page → [2. Согласие]
    ↑                                                          ↓
    └──── [4. API-ключ] ←── [3. Code → redirect_uri] ──────────┘
  1. Redirect — отправляете пользователя на /v1/connect/authorize с client_id, redirect_uri, state и списком скоупов.
  2. Согласие — пользователь видит страницу Вайбкод, выбирает портал и подтверждает или отклоняет запрошенные скоупы.
  3. Код — после одобрения пользователь возвращается на redirect_uri с параметрами code и state. При отказе — redirect_uri?error=access_denied&state=....
  4. Обмен — сервер партнёра отправляет POST /v1/connect/token и получает API-ключ.

Справочник эндпоинтов

Метод Путь Описание
GET /v1/connect/authorize Старт потока: редирект на страницу согласия
POST /v1/connect/token Обмен одноразового кода на постоянный API-ключ
GET /v1/connect/company/status Диагностика собственного подключения Компании

Полный сценарий

Пример Express-обработчика, который проводит пользователя через оба эндпоинта от и до. Перед запуском поместите client_id и client_secret, полученные при регистрации клиента, в переменные окружения, а redirect_uri зарегистрируйте у этого клиента.

javascript
import express from 'express'
import crypto from 'node:crypto'

const app = express()
const sessions = new Map() // в продакшене — Redis или БД

const CLIENT_ID = process.env.PARTNER_CLIENT_ID
const CLIENT_SECRET = process.env.PARTNER_CLIENT_SECRET
const REDIRECT_URI = 'https://yourapp.com/callback'

// 1. Кнопка «Подключить Битрикс24» ведёт сюда
app.get('/connect', (req, res) => {
  const state = crypto.randomBytes(16).toString('hex')
  sessions.set(state, { userId: req.user.id, createdAt: Date.now() })

  const params = new URLSearchParams({
    client_id: CLIENT_ID,
    redirect_uri: REDIRECT_URI,
    scope: 'crm task',
    state,
  })
  res.redirect(`https://vibecode.bitrix24.tech/v1/connect/authorize?${params}`)
})

// 2. Вайбкод возвращает пользователя сюда после согласия или отказа
app.get('/callback', async (req, res) => {
  const { code, state, error } = req.query

  const session = sessions.get(state)
  if (!session) return res.status(400).send('Неизвестный state')
  sessions.delete(state)

  if (error === 'access_denied') {
    return res.redirect('/dashboard?connect=denied')
  }

  // 3. Обмениваем код на API-ключ — серверный запрос с client_secret
  const tokenRes = await fetch('https://vibecode.bitrix24.tech/v1/connect/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      grant_type: 'authorization_code',
      client_id: CLIENT_ID,
      client_secret: CLIENT_SECRET,
      code,
      redirect_uri: REDIRECT_URI,
    }),
  })
  const data = await tokenRes.json()

  if (!tokenRes.ok) {
    // Ошибка приходит в RFC-форме: { error, error_description }
    return res.status(400).send(`Ошибка обмена: ${data.error}`)
  }

  // 4. Сохраняем привязку: пользователь нашего сервиса ↔ портал Битрикс24
  await db.connections.upsert({
    userId: session.userId,
    portalDomain: data.portal.domain,
    portalName: data.portal.name,
    apiKey: encrypt(data.api_key), // секрет, шифруем перед хранением
    scopes: data.scopes,
  })

  res.redirect('/dashboard?connect=ok')
})

// 5. Дальше используем сохранённый ключ для вызовов от имени пользователя
async function listDeals(userId) {
  const connection = await db.connections.findByUserId(userId)
  const apiKey = decrypt(connection.apiKey)

  const res = await fetch('https://vibecode.bitrix24.tech/v1/deals?limit=10', {
    headers: { 'X-Api-Key': apiKey },
  })
  return res.json()
}

Ключевые моменты сценария:

  • state генерируется на сервере и проверяется при возврате — без этой проверки злоумышленник может подсунуть пользователю чужой код.
  • client_secret хранится только на сервере и в браузер не попадает.
  • API-ключ шифруется перед записью в БД и расшифровывается только в момент вызова.
  • При error=access_denied пользователь видит понятное сообщение, а не страницу с ошибкой обмена.

Подключение AI из приложения внутри Битрикс24

Приложение, которое открывается во фрейме портала Битрикс24, не может показать у себя страницу согласия: страница согласия запрещает встраивание в чужие сайты заголовками X-Frame-Options: SAMEORIGIN и Content-Security-Policy: frame-ancestors 'self'. Так её нельзя подменить или спрятать под чужим интерфейсом. Поэтому кнопка «Подключить AI» открывает согласие в отдельном окне браузера, а фрейм ждёт результат от вашего сервера.

Порядок такой:

  1. Кнопка во фрейме открывает окно на адресе вашего сервера и передаёт в нём одноразовый идентификатор попытки ticket.
  2. Сервер запоминает попытку и перенаправляет окно на /v1/connect/authorize с правом vibe:ai.
  3. Пользователь выбирает портал и подтверждает доступ. Вайбкод возвращает окно на ваш redirect_uri.
  4. Сервер обменивает код на ключ, отмечает попытку завершённой и закрывает окно.
  5. Фрейм опрашивает сервер по ticket и показывает результат.

Код во фрейме приложения:

html
<button id="connect-ai" type="button">Подключить AI</button>
<p id="connect-ai-status" role="status"></p>

<script>
  const button = document.getElementById('connect-ai')
  const statusLine = document.getElementById('connect-ai-status')

  button.addEventListener('click', () => {
    // Окно открывается прямо в обработчике клика: окно, открытое после await, браузер блокирует
    const ticket = crypto.randomUUID()
    const popup = window.open(
      `/connect/ai/start?ticket=${ticket}`,
      'vibe-connect-ai',
      'popup,width=640,height=760',
    )
    if (!popup) {
      statusLine.textContent = 'Разрешите всплывающие окна для этого приложения и нажмите кнопку ещё раз'
      return
    }
    statusLine.textContent = 'Подтвердите доступ в открывшемся окне'
    waitForResult(ticket)
  })

  async function waitForResult(ticket) {
    const deadline = Date.now() + 10 * 60 * 1000 // 10 минут на согласие
    while (Date.now() < deadline) {
      await new Promise((resolve) => setTimeout(resolve, 2000))
      const res = await fetch(`/connect/ai/status?ticket=${ticket}`)
      const result = await res.json()
      if (result.status === 'ok') {
        statusLine.textContent = 'AI подключён'
        return
      }
      if (result.status === 'denied') {
        statusLine.textContent = `AI не подключён: ${result.reason}`
        return
      }
    }
    statusLine.textContent = 'Подключение не завершено. Нажмите кнопку ещё раз'
  }
</script>

Сервер приложения делает тот же обмен кода, что в полном сценарии, и хранит состояние попытки для фрейма. db и encrypt — ваши хранилище и шифрование. Адрес https://yourapp.com/connect/ai/callback зарегистрируйте у клиента как redirect_uri, а право vibe:ai должно входить в права клиента — см. Платформенные права.

javascript
import express from 'express'
import crypto from 'node:crypto'

const app = express()
const attempts = new Map() // ticket → результат попытки; в продакшене — Redis или БД
const states = new Map() // state → ticket

const CLIENT_ID = process.env.PARTNER_CLIENT_ID
const CLIENT_SECRET = process.env.PARTNER_CLIENT_SECRET
const REDIRECT_URI = 'https://yourapp.com/connect/ai/callback'
const TICKET_RE = /^[0-9a-f-]{36}$/

// 1. Окно, открытое кнопкой, приходит сюда и уходит на страницу согласия
app.get('/connect/ai/start', (req, res) => {
  const { ticket } = req.query
  if (typeof ticket !== 'string' || !TICKET_RE.test(ticket) || attempts.has(ticket)) {
    return res.status(400).send('Некорректная попытка подключения')
  }
  attempts.set(ticket, { status: 'pending' })

  const state = crypto.randomBytes(16).toString('hex')
  states.set(state, ticket)

  const params = new URLSearchParams({
    client_id: CLIENT_ID,
    redirect_uri: REDIRECT_URI,
    scope: 'vibe:ai',
    state,
  })
  res.redirect(`https://vibecode.bitrix24.tech/v1/connect/authorize?${params}`)
})

// 2. Вайбкод возвращает окно сюда после согласия или отказа
app.get('/connect/ai/callback', async (req, res) => {
  const { code, state, error, error_description: reason } = req.query
  const ticket = states.get(state)
  if (!ticket) return res.status(400).send('Неизвестный state')
  states.delete(state)

  if (error) {
    // error_description несёт машинный код причины — см. «Отказ после экрана согласия»
    attempts.set(ticket, { status: 'denied', reason: reason ?? error })
    return res.send(closeWindowPage('Доступ не выдан. Окно можно закрыть'))
  }

  const tokenRes = await fetch('https://vibecode.bitrix24.tech/v1/connect/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      grant_type: 'authorization_code',
      client_id: CLIENT_ID,
      client_secret: CLIENT_SECRET,
      code,
      redirect_uri: REDIRECT_URI,
    }),
  })
  const data = await tokenRes.json()
  if (!tokenRes.ok) {
    attempts.set(ticket, { status: 'denied', reason: data.error })
    return res.send(closeWindowPage('Ключ не получен. Окно можно закрыть'))
  }

  await db.connections.upsert({
    portalDomain: data.portal.domain,
    apiKey: encrypt(data.api_key), // секрет, шифруем перед хранением
    scopes: data.scopes,
  })
  attempts.set(ticket, { status: 'ok' })
  res.send(closeWindowPage('AI подключён. Окно можно закрыть'))
})

// 3. Фрейм спрашивает результат попытки
app.get('/connect/ai/status', (req, res) => {
  res.json(attempts.get(req.query.ticket) ?? { status: 'unknown' })
})

function closeWindowPage(text) {
  return `<!doctype html><meta charset="utf-8"><p>${text}</p><script>window.close()</script>`
}

Ключевые моменты:

  • Окно открывается синхронно в обработчике клика. Окно, открытое после await, браузер блокирует как всплывающее.
  • Результат фрейм берёт у своего сервера по ticket, а не у окна: окно уходит на другие сайты, и опрос своего сервера не зависит от того, что с окном происходит по дороге.
  • ticket — не секрет: по нему видно только состояние попытки. Ключ остаётся на сервере и во фрейм не попадает.
  • Опрос ограничен по времени: пользователь может закрыть окно, не ответив, и тогда на redirect_uri не придёт ничего.
  • Отказ приходит в error и error_description, фрейм показывает код причины как есть. Разбор кодов — Отказ после экрана согласия.

GET /v1/connect/authorize

Перенаправляет пользователя на страницу согласия Битрикс24 Вайбкод.

Query-параметры:

Параметр Тип Обяз. Описание
client_id string да Идентификатор партнёра, полученный при регистрации клиента
redirect_uri string да URL обратного вызова. Должен совпадать с одним из зарегистрированных у клиента
state string да Произвольная строка, которая вернётся вместе с кодом. Защита от CSRF
scope string нет Запрашиваемые права через пробел или запятую: crm task im. Если не передать — применяются права, зарегистрированные для клиента. Запрос права за пределами зарегистрированного набора отклоняется
code_challenge string условно Хеш SHA-256 от code_verifier в base64url без =. Обязателен для публичного клиента или при обязательном PKCE, иначе необязателен
code_challenge_method string условно S256, обязателен вместе с code_challenge. Другие методы, пустые значения и метод без хеша отклоняются

Для клиента с секретом PKCE необязателен, если не включён в настройках клиента. Если передать code_challenge, платформа сохранит его и потребует соответствующий code_verifier при обмене кода. PKCE дополняет client_secret, а не заменяет его. Для публичного клиента и при обязательном PKCE хеш нужен всегда.

Пример URL:

https://vibecode.bitrix24.tech/v1/connect/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=https://yourapp.com/callback&scope=crm%20task&state=abc123random

Успешный редирект после одобрения:

https://yourapp.com/callback?code=AUTH_CODE_HERE&state=abc123random

Редирект при отказе пользователя на странице согласия:

https://yourapp.com/callback?error=access_denied&state=abc123random

Ошибки

Ошибки этого эндпоинта приходят тремя разными путями, и обработать нужно все три.

До того как redirect_uri проверен — обычный ответ 400 с телом RFC 6749. Возвращать пользователя некуда: адрес ещё не подтверждён, редирект на непроверенный URL был бы дырой.

HTTP error Условие
400 invalid_request Не передан один из client_id, redirect_uri, state
400 invalid_client client_id неизвестен либо клиент отключён
400 invalid_request redirect_uri искажён: не разбирается как URL, содержит фрагмент #, логин или пароль. error_description — Redirect URI is malformed
400 invalid_request Схема redirect_uri не разрешена для его адреса: не https у адреса не на localhost, не http и не https у localhost. error_description — Redirect URI scheme is not allowed for this host
400 invalid_request redirect_uri не совпадает ни с одним зарегистрированным. error_description — Redirect URI not registered
JSON
{
  "error": "invalid_client",
  "error_description": "Unknown or inactive client"
}

После того как redirect_uri проверен — редирект обратно на него с параметрами error и state (RFC 6749 §4.1.2.1). Ваш обработчик redirect_uri обязан это разбирать.

error в редиректе Условие
invalid_scope Запрошено право за пределами зарегистрированного набора клиента
invalid_request Обязательный PKCE не передан, либо переданы некорректный code_challenge или code_challenge_method. Поддерживается только S256
access_denied Пользователь нажал «Отклонить» на странице согласия
access_denied Пользователь подтвердил доступ, но ключ выдать не удалось: у портала нет подписки или тарифа, открывающего доступ к Вайбкод, регион не поддерживается либо исчерпан лимит ключей. Машинный код причины приходит в error_description
temporarily_unavailable Пользователь подтвердил доступ, но выписка ключа не завершилась из-за временной недоступности Битрикс24. Попытку можно повторить
https://yourapp.com/callback?error=invalid_scope&state=abc123random

Превышение частоты — 429 до всякой проверки параметров, поэтому ни редиректа, ни state не будет. Лимит: 30 запросов в минуту, считается по паре client_id и адрес вызывающего. Точное значение — в заголовке x-ratelimit-limit (потолок делится на реплики). Тело приходит в общем конверте API — { "success": false, "error": { "code": "RATE_LIMITED", "message": "..." } }, а не в форме RFC 6749, и заголовки X-RateLimit-* присутствуют.

Обработать этот путь нужно отдельно, потому что authorize открывается переходом браузера: пользователь увидит страницу с JSON вместо экрана согласия, на redirect_uri не придёт ничего, и приложение не получит обратно свой state. Приложение обязано считать такую авторизацию незавершённой по своему таймауту, а не ждать колбэка. Значение Retry-After называет минимальную паузу.

Отказ после экрана согласия

Отказ в выдаче ключа наступает уже после того, как пользователь подтвердил доступ. Причину он видит на странице согласия Вайбкод, а в приложение возвращается по кнопке — тогда на redirect_uri приходит error, error_description и исходный state:

https://yourapp.com/callback?error=access_denied&error_description=MARKETPLACE_REQUIRED&state=abc123random

error_description несёт машинный код причины, по которому приложение может показать свой экран:

Код в error_description Что произошло
MARKETPLACE_REQUIRED На портале нет активной подписки BitrixGPT + Маркетплейс. Там, где доступ открывает тариф Битрикс24, вместо него приходят BY_PAID_ONLY, KZ_PAID_ONLY и UZ_PAID_ONLY
KEY_LIMIT_REACHED Ключ выдать нельзя: исчерпан лимит ключей пользователя или портальный потолок платформы — см. Лимит ключей
BITRIX_UNAVAILABLE Битрикс24 не ответил во время выписки ключа
STATE_EXPIRED Ссылка согласия протухла до подтверждения — авторизацию начинают заново с /v1/connect/authorize
admin_approval_requested Пользователь не администратор портала и попросил администраторов разрешить приложению AI — запрос пришёл им в чат бота. Когда администратор разрешит AI, авторизацию начинают заново с /v1/connect/authorize

Список кодов открыт: приложение должно понимать любой error_description как причину отказа и не полагаться на то, что перечень выше исчерпывающий.

При отказе в доступе по денежной причине пользователь видит встроенную карточку оплаты на странице согласия. Действия относятся к выбранному порталу Битрикс24. Для MARKETPLACE_REQUIRED карточка предлагает «Узнать подробнее» — открыть описание подписки в новой вкладке, а также активировать пробный период, если он доступен этому порталу. Для BY_PAID_ONLY, KZ_PAID_ONLY, UZ_PAID_ONLY и INT_TARIFF_REQUIRED предлагается выбрать тариф Битрикс24; для INT_VIBE_PLUS_REQUIRED — тариф Vibe+. При INT_TARIFF_REQUIRED на .com в зависимости от выбранного портала также может предлагаться Vibe+. В модели Vibe+ карточка предлагает активацию пробного периода, если он доступен; демо-тариф Битрикс24 подключают в самом Битрикс24, если он там доступен.

Пока пользователь не нажмёт кнопку возврата в приложение, на redirect_uri ничего не приходит. Возврат после такого отказа передаёт error=access_denied, машинный код причины в error_description и исходный state; code и API-ключ не выдаются. После покупки или активации пробного периода вернитесь в приложение и начните авторизацию заново с /v1/connect/authorize: одноразовое состояние прежней ссылки уже использовано.

Полный справочник общих ошибок API — Ошибки.

POST /v1/connect/token

Обменивает одноразовый код авторизации на постоянный API-ключ. Принимает application/json и application/x-www-form-urlencoded.

Поля запроса (body):

Поле Тип Обяз. Описание
grant_type string да Для этого потока — authorization_code. Без него эндпоинт отвечает unsupported_grant_type. Другие режимы той же ручки: urn:ietf:params:oauth:grant-type:device_code (см. Вход с устройства без браузера) и обмен кода входа в Коворк urn:vibecode:params:oauth:grant-type:desktop-handoff с полями grant и device_id — описан на странице «Вход в Коворк по коду» (/docs/cowork/desktop-handoff), только на .tech
client_id string да Идентификатор партнёра
client_secret string да Секрет партнёра. Передавать только с серверной стороны
code string да Код из параметра code редиректа
redirect_uri string да Тот же redirect_uri, что был передан в /v1/connect/authorize
code_verifier string условно Исходная строка PKCE. Обязательна, если при авторизации передан code_challenge, в том числе для клиента с секретом

Примеры

curl

Terminal
curl -X POST https://vibecode.bitrix24.tech/v1/connect/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d grant_type=authorization_code \
  -d client_id=YOUR_CLIENT_ID \
  -d client_secret=YOUR_CLIENT_SECRET \
  -d code=RECEIVED_CODE \
  -d redirect_uri=https://yourapp.com/callback

JavaScript

javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/connect/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    grant_type: 'authorization_code',
    client_id: 'YOUR_CLIENT_ID',
    client_secret: 'YOUR_CLIENT_SECRET',
    code: receivedCode,
    redirect_uri: 'https://yourapp.com/callback',
  }),
})

const data = await res.json()
// data.api_key — постоянный API-ключ Вайбкод
// data.portal — выбранный пользователем портал
// data.scopes — запрошенные скоупы, data.granted_scopes — права ключа
// data.user — пользователь, выдавший доступ

Поля ответа

Поле Тип Описание
api_key string API-ключ Вайбкод в формате vibe_api_.... Передавать в заголовке X-Api-Key при последующих вызовах
portal.domain string Домен портала Битрикс24, например example.bitrix24.ru
portal.name string Название портала
scopes string[] Права, запрошенные приложением. На странице согласия пользователь подтверждает набор целиком
granted_scopes string[] Права, которые реально несёт выданный ключ. У самостоятельно зарегистрированного партнёрского приложения, как правило, совпадают со scopes — разойдутся, если права ключа изменили после согласия
user.name string Имя пользователя, выдавшего доступ
user.email string Электронная почта пользователя

Два набора прав расходятся только у приложений, которым состав прав назначает сама платформа — например у настольного приложения Cowork. Такое приложение может не запрашивать ничего, тогда scopes приходит пустым, а granted_scopes перечисляет весь набор ключа. Проверяйте доступ по granted_scopes.

Поле granted_scopes может отсутствовать в ответе, если платформа не смогла прочитать права выданного ключа. Пустым оно при этом не приходит, поэтому отсутствие поля означает «набор неизвестен», а не «прав нет» — в таком ответе ориентируйтесь на scopes.

Набор в granted_scopes отражает то, что записано в ключе. Отдельное право портал Битрикс24 может не подтвердить на своей стороне, поэтому отказ разбирайте по коду ответа Битрикс24, а не по наличию права в списке.

Пример ответа

JSON
{
  "api_key": "vibe_api_...",
  "portal": {
    "domain": "example.bitrix24.ru",
    "name": "Моя компания"
  },
  "scopes": ["crm", "task"],
  "granted_scopes": ["crm", "task"],
  "user": {
    "name": "Иван Иванов",
    "email": "ivan@example.com"
  }
}

Пример ответа при ошибке

400 — не передан обязательный параметр:

JSON
{
  "error": "invalid_request",
  "error_description": "Missing client_id, code or redirect_uri"
}

Ошибки

Эндпоинт отвечает в форме RFC 6749 — { error, error_description }, без обёртки success. Это сделано ради совместимости с готовыми OAuth-библиотеками. Исключение — отказ ограничителя частоты самого эндпоинта, его форма описана после таблицы.

HTTP error Условие
400 invalid_request Не передан один из client_id, code, redirect_uri
400 invalid_grant Код не существует, истёк, уже был использован, либо redirect_uri не совпадает с переданным в /v1/connect/authorize
400 invalid_grant Проверка PKCE не прошла: code_verifier не передан либо не соответствует переданному ранее code_challenge
400 invalid_client Публичный клиент прислал client_secret — публичному клиенту секрет не выдаётся и передавать его нельзя
400 unsupported_grant_type grant_type не передан или не равен authorization_code
401 invalid_client client_id неизвестен, клиент отключён, либо client_secret не совпадает
429 slow_down Превышен лимит частоты на входе в платформу. Лимит считается по адресу вызывающего и общий для обмена кода и опроса устройства. Ответ несёт Retry-After с минимальной паузой в секундах и не несёт заголовков X-RateLimit-*

Ограничителей частоты два, и оба отвечают 429. Различать их по заголовку X-RateLimit-Limit. Отказ на входе в платформу из таблицы выше этого заголовка не несёт и приходит в форме RFC 6749. Отказ самого эндпоинта его несёт, а тело приходит в общем конверте API — { "success": false, "error": { "code": "RATE_LIMITED", "message": "..." } }. Действие в обоих случаях одно: подождать и повторить, увеличив паузу.

Полный справочник общих ошибок API — Ошибки.

Публичный клиент: поток с PKCE

Всё выше описывает конфиденциальный клиент — тот, у которого есть сервер и client_secret. Если сервера нет и секрет спрятать негде (мобильное приложение, десктопная программа, одностраничное веб-приложение), регистрируйте публичный клиент. Секрет ему не выдаётся, подлинность подтверждается механизмом PKCE.

Отличий от основного потока три, всё остальное совпадает.

1. Приложение генерирует пару на каждый запуск потока. Случайная строка (code_verifier) и её SHA-256-хеш в base64url без выравнивающих знаков = (code_challenge).

javascript
import crypto from 'node:crypto'

const b64url = (buf) => buf.toString('base64url')
const verifier  = b64url(crypto.randomBytes(32))
const challenge = b64url(crypto.createHash('sha256').update(verifier).digest())

verifier держите до конца потока, challenge уходит в первый запрос.

2. В /v1/connect/authorize добавляются два параметра.

Параметр Значение
code_challenge Хеш из шага 1
code_challenge_method Всегда S256. Другие значения отклоняются

Для публичного клиента они обязательны: без них поток завершится редиректом ?error=invalid_request.

3. В /v1/connect/token вместо client_secret уходит code_verifier.

Terminal
curl -X POST https://vibecode.bitrix24.tech/v1/connect/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "authorization_code",
    "client_id": "YOUR_CLIENT_ID",
    "code": "RECEIVED_CODE",
    "redirect_uri": "http://127.0.0.1:9999/callback",
    "code_verifier": "CODE_VERIFIER"
  }'

Ответ тот же, что у конфиденциального клиента:

JSON
{
  "api_key": "vibe_api_...",
  "scopes": ["crm"],
  "granted_scopes": ["crm"],
  "portal": { "domain": "example.bitrix24.ru", "name": "Моя компания" },
  "user": { "name": "Иван Иванов", "email": "ivan@example.com" }
}

client_secret публичному клиенту передавать нельзя — платформа ответит 400 invalid_client. Это не придирка: секрет, зашитый в раздаваемое приложение, секретом не является, и поток построен так, чтобы им нельзя было пользоваться.

Возврат на localhost

У программы без своего сайта нет публичного адреса, куда вернуть код. Для таких случаев разрешён возврат на себя: зарегистрируйте redirect_uri вида http://127.0.0.1:9999/callback, поднимите на время потока временный слушатель на этом порту и закройте его сразу после получения кода.

Годятся 127.0.0.1, localhost и [::1], для них разрешены схемы http и https. Все остальные адреса обязаны быть https. Схема и путь сверяются всегда, а порт и сам адрес localhost — в зависимости от типа клиента:

Тип клиента Как сверяется адрес на localhost
Публичный Порт не учитывается — если занят, приложение может слушать любой другой, повторно регистрировать адрес не нужно. Адреса 127.0.0.1, localhost и [::1] взаимозаменяемы: зарегистрированный http://127.0.0.1/callback принимает и http://localhost:5123/callback
Конфиденциальный Адрес сверяется целиком, включая порт. Так серверное приложение отлаживают на своём компьютере: зарегистрируйте адрес с портом, на котором слушает сервер, например http://localhost:3030/callback

Имя localhost может указывать не на этот компьютер, если его переопределили в настройках сети, поэтому надёжнее 127.0.0.1.

Вход с устройства без браузера

Телевизор, приставка, утилита в терминале — там, где неудобно вводить логин и пароль. Пользователь подтверждает вход на телефоне или компьютере, а доступ получает устройство. Поток описан стандартом RFC 8628.

Устройству нужен разрешённый device-режим: он включается платформенным администратором и только проверенным приложениям. Клиент, зарегистрированный самостоятельно, получит 400 unauthorized_client — это защита пользователей от того, чтобы код подтверждения показывало непроверенное приложение.

Шаг 1. Устройство запрашивает код.

Terminal
curl -X POST https://vibecode.bitrix24.tech/v1/connect/device/authorize \
  -H "Content-Type: application/json" \
  -d '{"client_id":"YOUR_CLIENT_ID","scope":"crm","code_challenge":"ХЕШ","code_challenge_method":"S256"}'
JSON
{
  "device_code": "MYQnuWB3H0tR...",
  "user_code": "54BV-XT8F",
  "verification_uri": "https://vibecode.bitrix24.tech/connect/device",
  "verification_uri_complete": "https://vibecode.bitrix24.tech/connect/device?user_code=54BV-XT8F",
  "expires_in": 899,
  "interval": 5
}

device_code — секрет устройства, на экран не выводится. user_code показывается пользователю вместе с адресом. expires_in — сколько секунд у пользователя есть на подтверждение.

Этот шаг тоже ограничен по частоте — 30 запросов в минуту по адресу вызывающего, client_id в ключ не входит. Сверху действует ограничитель на входе в платформу, общий на весь /v1/. Превышение любого из них даёт 429 в общем конверте API с заголовком Retry-After; какой именно сработал, видно по наличию X-RateLimit-Limit.

Шаг 2. Пользователь открывает адрес и подтверждает. На экране подтверждения он видит карточку приложения, запрошенные права, выбор портала и предупреждение о фишинге: подтверждать следует только тот вход, который начал он сам.

Шаг 3. Устройство опрашивает /v1/connect/token.

Terminal
curl -X POST https://vibecode.bitrix24.tech/v1/connect/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
    "client_id": "YOUR_CLIENT_ID",
    "device_code": "DEVICE_CODE",
    "code_verifier": "CODE_VERIFIER"
  }'

Пока пользователь не подтвердил — 400 с одним из состояний:

error Что делать
authorization_pending Ждать и опрашивать дальше с интервалом interval
slow_down Опрос идёт чаще разрешённого. Увеличить интервал на 5 секунд и продолжать
access_denied Пользователь отклонил. Прекратить опрос
access_denied с error_description=admin_approval_requested Пользователь попросил администратора портала разрешить приложению AI. Прекратить опрос, а после решения администратора запросить новый код с шага 1
expired_token Код истёк. Запросить новый с шага 1

Опрос может получить и 429 — это отказ ограничителя частоты, а не состояние авторизации, и форм у него две, потому что ограничителей на этом маршруте два. Отказ на входе в платформу приходит в форме RFC 6749 (error равен slow_down) и без заголовков X-RateLimit-*. Отказ ограничителя самого эндпоинта приходит в общем конверте API — { "success": false, "error": { "code": "RATE_LIMITED", "message": "..." } } — и заголовки X-RateLimit-* несёт. Ветку по error === "slow_down" одной проверки поэтому недостаточно: она пропустит половину отказов.

Обрабатывать обе одинаково: ждать не меньше значения Retry-After и продолжать опрос, не сбрасывая свою растущую паузу. Отличить их, если нужно, можно по наличию X-RateLimit-Limit.

После подтверждения тот же запрос отдаёт 200 с ключом и двумя наборами прав:

JSON
{ "api_key": "vibe_api_...", "scopes": ["crm"], "granted_scopes": ["crm"] }

Обратите внимание: здесь объект уже, чем в основном потоке, — без блоков portal и user.

Ключ отдаётся один раз. Повторный опрос тем же device_code вернёт expired_token, поэтому сохраняйте ключ сразу.

Что обязано уметь устройство

  • Показывать обратный отсчёт. Код живёт 15 минут; по истечении показать новый, а не мёртвый экран.
  • Соблюдать interval и реагировать на slow_down. Опрос чаще разрешённого не ускорит выдачу, а приведёт к отказам.
  • Читать Retry-After и наращивать собственную паузу. Значение заголовка — минимальная пауза, а не гарантия, что следующий запрос примут: за лимит по адресу конкурируют все запущенные копии приложения. Ждать не меньше Retry-After и при этом увеличивать свою паузу с каждым отказом.
  • Не показывать device_code. На экран идёт только user_code.

Использование API-ключа

Полученный ключ имеет тип APP и работает как стандартный API-ключ Вайбкод. Передавайте его в заголовке X-Api-Key:

Terminal
curl -H "X-Api-Key: vibe_api_..." \
  https://vibecode.bitrix24.tech/v1/deals

Один и тот же ключ можно использовать с любым эндпоинтом Вайбкод API в пределах подтверждённых скоупов: список сделок, batch-запросы, агрегация и т. д.

Партнёрский ключ несёт ровно те права, которые пользователь подтвердил на странице согласия: платформенные скоупы сами к набору не добавляются. Нужен AI-роутер (/v1/chat/completions) — запросите vibe:ai вместе с остальными правами, как это сделать. Не запросили — вызов вернёт 403. Веб-поиск (/v1/search), vibe:infra и vibe:feedback выдаёт платформенный администратор, а vibe:storage приложению Connect не выдаётся, там же.

AI приложению на портале разрешает администратор портала — на странице согласия либо по запросу сотрудника. Если приложение запрашивает vibe:ai, подтверждает не администратор и разрешения у портала нет, страница согласия отвечает отказом CONNECT_AI_ADMIN_REQUIRED и предлагает попросить администратора, а после запроса на redirect_uri приходит error=access_denied с error_description=admin_approval_requested. Пока разрешения нет или администратор его отозвал, маршруты моделей (/v1/chat/completions, /v1/embeddings, /v1/models и другие) отвечают 403 с кодом CONNECT_AI_NOT_PERMITTED. Остальные права ключа при этом работают, перевыпускать ключ не нужно: как только администратор разрешит AI, тот же ключ снова получит доступ к моделям.

Партнёрский ключ работает с одним заголовком X-Api-Key и не требует Authorization: Bearer <session_token>. По префиксу его видно сразу: партнёрский ключ начинается с vibe_api_, а ключ OAuth-приложения из каталога Вайбкод — с vibe_app_, и для второго сессия пользователя обязательна.

Доступные скоупы

При вызове /v1/connect/authorize передавайте те же скоупы, что и при создании стандартного API-ключа в портале Битрикс24. На странице согласия пользователь видит список запрошенных прав и подтверждает его целиком — снять отдельный пункт нельзя, доступны только «Разрешить» и «Отклонить». Итоговый набор приходит в поле granted_scopes ответа /v1/connect/token, а запрошенный — в поле scopes рядом.

Актуальный перечень поддерживаемых значений описан в разделе Ключи и авторизация — он пополняется по мере появления новых модулей Битрикс24, поэтому здесь не дублируется.

Платформенные права Вайбкод делятся на три группы: vibe:ai приложение запрашивает само, vibe:search, vibe:infra и vibe:feedback выдаёт платформенный администратор, а vibe:storage приложению Connect не выдаётся. Порядок — Платформенные права.

Машиночитаемый перечень того, что приложение вправе запросить само, отдаёт поле scopes_supported документа /.well-known/oauth-authorization-server (RFC 8414). Права, выданные администратором конкретному приложению, в этот анонимный документ не попадают.

Время жизни и отзыв ключа

  • Код авторизации (code) действует 5 минут и одноразовый. После обмена через /v1/connect/token повторный вызов с тем же кодом вернёт invalid_grant.

  • Стейт согласия (внутреннее состояние страницы согласия) живёт 10 минут — если пользователь не подтвердит за это время, ссылку придётся выдавать заново.

  • API-ключ не имеет срока истечения. Действует, пока его не отзовут. Отзыв происходит одним из трёх способов:

    1. Пользователь отзывает доступ сам — раздел «Подключённые приложения» в его профиле Вайбкод. Это гасит все ключи, которые он выдал вашему приложению для этого портала, включая ключи от прежних авторизаций. Доступы других сотрудников того же портала не затрагиваются.
    2. Приложение отзывает свой ключ само — POST /v1/connect/revoke, см. ниже. Гасится ровно один ключ, тот, который вы предъявили.
    3. Платформенный администратор отзывает выданные клиентом ключи.

    Деактивация клиента ключи НЕ отзывает — она только закрывает выдачу новых: с неактивным клиентом /v1/connect/authorize отклонит запрос, а уже выданные ключи продолжат работать.

При отзыве ключа все запросы с ним возвращают 401 KEY_INACTIVE. Если ключ удалён или неизвестен — 401 INVALID_API_KEY. Партнёр должен корректно обработать оба кода и предложить пользователю повторно авторизоваться.

Отзыв ключа приложением: `POST /v1/connect/revoke`

Ручка сделана по RFC 7009 и гасит один ключ — тот, который вы в ней предъявили. Вызывайте её, когда пользователь выключил интеграцию у вас на стороне: иначе прежние ключи остаются рабочими и занимают места в лимите (см. ниже).

POST /v1/connect/revoke
Content-Type: application/x-www-form-urlencoded

client_id=your-app&client_secret=your-secret&token=<api_key>

Принимается и JSON с теми же полями. Публичные клиенты (PUBLIC) client_secret не передают — его у них нет; конфиденциальные передают обязательно.

Ответы:

  • 200 {} — либо ключ погашен, либо гасить было нечего. Один и тот же ответ на неизвестный токен, на чужой ключ и на уже отозванный — так вы не сможете перебором выяснять, живы ли чужие ключи. Значит, вызов идемпотентен: повторить его безопасно.
  • 400 invalid_request — не передан client_id или token.
  • 400 invalid_client — публичный клиент передал client_secret.
  • 401 invalid_client — клиент неизвестен или секрет не подошёл.
  • 409 KEY_MANAGES_FOREIGN_SERVER — ключ управляет живым сервером другого владельца, сервером без владельца или сервером того же владельца на другом портале. Установите владельца и нужный портал сервера, перепривяжите его к ключу этого владельца в том же портале и затем повторите отзыв.
  • 409 KEY_OPERATION_IN_PROGRESS — тот же ключ сейчас меняет другая операция. Повторите запрос после её завершения. Тело обоих отказов содержит error, error_description и code.
  • 429 — превышена частота, и ограничителей здесь два. Собственный ограничитель ручки: 60 запросов в минуту. Точное значение — в заголовке x-ratelimit-limit (потолок делится на реплики). Считается только по адресу вызывающего — client_id в ключ не входит намеренно, поэтому его ротация нового бюджета не даёт, а все копии интеграции за одним адресом делят эти 60; заголовки X-RateLimit-* он ставит. Ограничитель на входе в платформу: 60 запросов в секунду на адрес (считает на входе, поэтому между репликами не делится), и это ведро общее на весь /v1/, а не на эту ручку; заголовков X-RateLimit-* он не ставит, а Retry-After у него всегда 1. Тело в обоих случаях приходит в общем конверте API — { "success": false, "error": { "code": "RATE_LIMITED", "message": "..." } }, — то есть отличается от формы RFC 7009 остальных ответов этой ручки, и различить ярусы по телу нельзя, только по наличию X-RateLimit-Limit. Вызов не терминальный: ключ не отозван, повторить после паузы из Retry-After.

Отозвать можно и ключ, выданный до деактивации вашего клиента: деактивация закрывает выдачу новых ключей, но отзыв прежних не запрещает.

Чего ручка не делает:

  • не снимает вебхук на портале Битрикс24 — его пользователь удаляет у себя, отзыв ключа этого не заменяет;
  • не возвращает потерянный ключ — чтобы отозвать ключ, его надо предъявить, восстановить его нельзя;
  • не срабатывает сама — если пользователь просто перестал пользоваться интеграцией, ключ живёт дальше; вызов ручки — ваша ответственность.

Если предъявленный ключ выпущен именно вашему клиенту и был активен, успешный отзыв прекращает его работу сразу: запросы с ним начинают отвечать 401 KEY_INACTIVE, а место в лимите освобождается. Сам по себе ответ 200 {} не доказывает отзыв: неизвестный, чужой клиенту и уже отозванный ключ дают тот же ответ.

Повторная авторизация выдаёт новый ключ

Каждое прохождение согласия выписывает новый API-ключ. Прежний ключ той же пары «пользователь и портал» при этом продолжает работать: авторизация не отзывает его. В профиле пользователя обе выдачи показаны одним подключением, и отзыв доступа гасит их вместе.

Что из этого следует для приложения:

  • храните последний полученный ключ и заменяйте им предыдущий, иначе накопите рабочие ключи, о которых не помните,
  • каждая повторная авторизация занимает место в лимите ключей — см. ниже,
  • ключ, потерявший силу, восстановлению не подлежит: получите новый через /v1/connect/authorize.

Лимит ключей

На одну пару «пользователь и портал» приходится не больше 100 действующих ключей — это и значение по умолчанию, и потолок настройки. Администратор портала может задать другое значение, от 1 до 100. В счёт идут все ключи пользователя на этом портале, а не только выданные вашим приложением.

Кроме пользовательского лимита есть портальный потолок платформы — он считается по всем живым ключам портала и не поднимается администратором портала. Когда один из пределов исчерпан, выдача отклоняется кодом KEY_LIMIT_REACHED: пользователь видит причину на странице согласия, а приложение получает только этот код в error_description при возврате на redirect_uri. Для пользовательского лимита место освобождает удаление ненужных ключей в разделе «API-ключи» кабинета Вайбкод или отзыв доступа приложению в профиле. При портальном потолке экран согласия предлагает выбрать тариф с большим лимитом ключей. Удаление ключей также освобождает место, если общее число действующих ключей портала становится ниже потолка.

Управление приложениями портала

Администратор выбранного портала видит приложения всех сотрудников в профиле на вкладке «Все приложения портала»: подключения, запросы к AI и долю лимита. В разделе «AI-модели» доступна разбивка «По приложениям».

«Разрешить ИИ» одобряет ожидающий запрос или выдаёт разрешение без запроса. Само разрешение не создаёт ключ: сотрудник повторяет подключение. «Запретить ИИ» закрывает вызовы моделей, сохраняя остальные права ключа. «Отключить для всех» отзывает все ключи приложения на этом портале и производные токены. Расход за текущий период остаётся в статистике.

Управление доступно, когда на платформе включено разрешение AI администратором портала. Ключи других порталов не затрагиваются.

Безопасность

  • State-параметр — генерируйте криптостойкую случайную строку на каждый запрос и сверяйте при возврате на redirect_uri. Защищает от CSRF.
  • client_secret — только на сервере. Никогда не передавайте секрет в браузер, мобильное приложение или клиентский код. Обмен кода на ключ выполняется только серверным запросом.
  • Хранение ключа. API-ключ — секрет, который даёт доступ к данным пользователя. Храните в зашифрованном виде, ограничивайте доступ к записи подключения, не логируйте.
  • Привязка redirect_uri. Значение в /v1/connect/authorize и /v1/connect/token должно совпадать символ в символ — несоответствие приведёт к invalid_grant.

Регистрация партнёра

Клиент регистрируется самостоятельно в кабинете Вайбкод — раздел Connect-приложения, боковое меню, группа «Доступ». Войдите под учётной записью Вайбкод и нажмите «Зарегистрировать приложение» — client_id выдаётся сразу, без заявки и предварительной проверки.

В форме заполняются:

  • название и краткое описание приложения,
  • идентификатор — строчные латинские буквы, цифры и дефис,
  • один или несколько redirect_uri — адреса https:// либо локальные 127.0.0.1, localhost и [::1] по http или https,
  • запрашиваемые права доступа.

Набор прав не может быть пустым: регистрация без единого права возвращает EMPTY_SCOPES. Приложение без прав не может запросить у пользователя ничего.

Тип клиента выбирается при создании и позже не меняется — разбор с примерами в разделе Типы клиента.

client_secret показывается один раз при создании. Прочитать его повторно нельзя, только перевыпустить действием «Обновить секрет».

В ⋮-меню карточки приложения есть два помощника: «Собрать ссылку» — конструктор ссылки авторизации (выбор redirect_uri и прав, готовый URL и curl для обмена кода, для клиента без секрета — одноразовая пара строк PKCE), и «Как увидит клиент» — превью страницы согласия ровно в том виде, в каком её увидит пользователь, включая бейдж верификации.

Число активных клиентов на одного пользователя ограничено. При достижении предела регистрация возвращает CLIENT_LIMIT_REACHED, текущее значение приходит в поле limit. Деактивация клиента освобождает место.

Новое приложение получает статус «непроверенное» — пользователь видит эту отметку на странице согласия. Повысить статус может платформенный администратор, запрос отправляется действием «Запросить верификацию» в карточке приложения. Заявку могут отклонить с причиной — она приходит владельцу приложения, и после правок заявку подают заново.

Платформенные права

Права Вайбкод открывают разделы самой платформы, а не данные Битрикс24. Для приложения Connect они делятся на три группы.

vibe:ai приложение запрашивает само. Отметьте его в правах при регистрации — там же, где права Битрикс24, — и дальше оно идёт обычным путём: передаётся в параметре scope у /v1/connect/authorize, показывается пользователю на странице согласия строкой «AI-модели» и приходит на выданный ключ. Заявки и проверки для этого не нужно. Расход покрывает месячный AI-лимит аккаунта, к которому подключается приложение.

Автоматически право не добавляется: приложение, которое его не отметило, работает без доступа к AI-роутеру, а уже выданные ключи набор прав не меняют. Чтобы открыть AI существующему подключению, отметьте право в карточке приложения и проведите пользователя через согласие заново — он получит новый ключ.

vibe:search, vibe:infra и vibe:feedback выдаёт платформенный администратор. При саморегистрации запрос с ними отклоняется кодом SCOPE_NOT_ALLOWED. Порядок — три шага, и первые два делает владелец приложения:

  1. Отправьте заявку действием «Запросить верификацию» в карточке приложения.
  2. Напишите в ленте «Обсуждение с платформой» той же карточки, какое право нужно и для какого сценария. Отдельного поля «запрашиваю право» в заявке нет, поэтому без сообщения проверяющий видит только заявку на значок.
  3. Администратор выдаёт право проверенному приложению. Верификация сама по себе прав не открывает — это два разных решения, и первое является условием второго.

После выдачи право ведёт себя как vibe:ai: передаётся в scope, показывается на странице согласия, приходит на ключ.

vibe:storage приложению Connect не выдаётся. Ключ, выданный через Connect, попадает в ту же область хранилища, что и личные ключи подключившего пользователя в этом аккаунте Битрикс24. С этим правом приложение видело бы все файлы, загруженные его личными ключами. Поэтому право не выдаёт даже платформенный администратор, а запрос с ним при саморегистрации отклоняется кодом SCOPE_NOT_ALLOWED.

Что снимает отметку «проверено»

Отметка держится на том, каким приложение прошло проверку, поэтому правка четырёх полей возвращает статус «непроверенное»:

  • название,
  • логотип,
  • адреса возврата redirect_uri,
  • набор запрашиваемых прав.

Описание и адрес сайта на отметку не влияют.

Кабинет предупреждает до сохранения: форма показывает подтверждение с перечнем полей, из-за которых отметка будет снята. Без подтверждения правка не сохраняется, ответ — VERIFICATION_RESET_NOT_CONFIRMED с тем же перечнем в теле.

Заполнение пустого набора прав отметку не снимает, когда выбранные права укладываются в набор, выданный администратором.

Сброс касается только отметки. Выданные ключи, подключённые пользователи, платформенные права и разрешённый вход по коду устройства сохраняются. Заявку на проверку подают заново.

Типы клиента

Тип отвечает на один вопрос: есть ли у приложения место, недоступное пользователю, где можно хранить секрет. От ответа зависит, выдаётся ли client_secret.

Конфиденциальный

У приложения есть серверная часть. client_secret лежит там и никогда не попадает в браузер или на устройство пользователя.

Примеры: SaaS-сервис аналитики, который ходит в портал клиента по расписанию. Синхронизация с 1С на вашем сервере. Чат-ассистент, который принимает вебхуки и отвечает от своего бэкенда.

Этот тип нужен для потока, описанного на этой странице: обмен кода на ключ выполняется серверным запросом с client_secret.

Публичный

Секрет спрятать негде — код целиком выполняется на устройстве пользователя. client_secret не выдаётся, вместо него подлинность подтверждает PKCE: приложение генерирует на каждый запуск потока случайную строку, отправляет в /v1/connect/authorize её хеш, а в /v1/connect/token — саму строку. Перехваченный код без неё не обменивается.

Примеры: мобильное приложение. Десктопная программа. Одностраничное веб-приложение без собственного бэкенда.

Как выбрать

Если у приложения есть сервер, который вы контролируете, — конфиденциальный. Если код целиком уезжает пользователю — публичный.

Тип задаётся один раз при регистрации. Чтобы сменить его, зарегистрируйте нового клиента.

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