QR-подключение бота к вашему сервису

QR connection bot → your service

Покажите QR на странице «Подключить X2Chat». Юзер сканит его в приложении — бот настраивается на ваш webhook одним подтверждением. Ни bot_token, ни secret в QR не попадают.

Show a QR on your "Connect X2Chat" page. The user scans it in the app — the bot is configured to call your webhook with one tap. No bot_token, no secret in the QR.

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

How it works

Ваш сайт генерит QR с публичной информацией: webhook_url + site_name + expires_at. Никакого секрета.
Юзер открывает X2Chat → Settings → «Мои боты» → выбирает бота → «Подключить webhook» → сканит QR.
X2Chat показывает confirmation: домен крупно + предупреждение «бот будет отправлять копии сообщений на my-site.com». Юзер вводит «Да» (typed confirmation, не tap).
X2Chat backend генерит криптостойкий secret_token (32 байта random) и делает первый ping на ваш webhook_url с заголовком X-X2Chat-Secret-Token: <secret> и body {"setup_confirm": true, "bot": {...}}.
Ваш сайт отвечает 200 OK в течение 5 сек → X2Chat применяет setWebhook(url, secret) и записывает в БД. Если timeout/non-2xx — откат без сохранения (нет «висящих» неработающих интеграций).
Готово. С этого момента все сообщения из X2Chat-чатов где есть этот бот будут POST'иться на webhook_url с тем же header'ом для проверки подписи. Сайт сравнивает header с сохранённым secret.
Your site generates a QR with public info: webhook_url + site_name + expires_at. No secrets.
User opens X2Chat → Settings → "My Bots" → picks a bot → "Connect webhook" → scans the QR.
X2Chat shows confirmation: domain in big text + warning "bot will send chat messages to my-site.com". User types "Yes" (typed confirmation, not a button tap).
X2Chat backend generates a cryptographically strong secret_token (32 random bytes) and makes the first ping to your webhook_url with header X-X2Chat-Secret-Token: <secret> and body {"setup_confirm": true, "bot": {...}}.
Your site answers 200 OK within 5 sec → X2Chat applies setWebhook(url, secret) and persists. If timeout/non-2xx — rollback without saving (no dangling broken integrations).
Done. From now on, every message from X2Chat chats where this bot is added will be POSTed to webhook_url with the same header for signature verification. Site compares the header with the stored secret.

Формат QR payload

QR payload format

В QR кодируется plain-JSON. Только 5 полей:

QR encodes plain JSON. Only 5 fields:

{
  "v": 1,
  "type": "x2chat-webhook-setup",
  "webhook_url": "https://my-site.com/x2chat-webhook",
  "site_name": "MyService",
  "expires_at": "2026-05-22T12:05:00Z"
}
FieldTypeОписаниеDescription
vintegerВерсия формата. Сейчас 1Format version. Currently 1
typestring"x2chat-webhook-setup"
webhook_urlstringHTTPS URL вашего endpoint'а. Не LAN, не raw IP, не localhostHTTPS URL of your endpoint. No LAN, no raw IP, no localhost
site_namestringКороткое название сервиса для UI (≤ 32 символов, printable ASCII)Short service name for UI (≤ 32 chars, printable ASCII)
expires_atstring ISO-8601Когда QR протухнет. Рекомендуем now+5minWhen the QR expires. Recommended: now+5min

Что НЕ должно быть в payload: логотипы, длинные описания, ссылки на политику, lock'и, hashes, secret_token. Лишние поля игнорируются X2Chat'ом, но создают phishing-вектор для других сервисов.

What MUST NOT be in payload: logos, long descriptions, policy links, locks, hashes, secret_token. Extra fields are ignored by X2Chat but create phishing surface for other parsers.

Setup confirmation ping

Setup confirmation ping

После confirmation X2Chat делает один HTTP POST на ваш webhook_url:

After confirmation X2Chat makes one HTTP POST to your webhook_url:

POST /your/x2chat-webhook HTTP/1.1
Host: my-site.com
Content-Type: application/json
X-X2Chat-Secret-Token: 9f4e7a2bd9c6f1...  // 32-байт hex
X-X2Chat-Setup-Confirm: true

{
  "setup_confirm": true,
  "bot": {
    "id": "d03bff3b-...uuid",
    "username": "my_info_bot",
    "first_name": "My Info Bot"
  },
  "owner_user_slot": "XI9W4A",
  "issued_at": "2026-05-22T12:00:00Z"
}

Что должен сделать ваш сайт:

What your site must do:

  1. Считать X-X2Chat-Secret-Token из header'а
  2. Сохранить его в БД, привязав к bot.id и (если есть) к вашему пользователю
  3. Ответить 200 OK в течение 5 секунд (любой 2xx без body)
  4. На последующих ping'ах сравнивать header с сохранённым значением — если не совпадает, отбрасывать запрос как поддельный
  1. Read X-X2Chat-Secret-Token from header
  2. Store it in DB, linked to bot.id and (if applicable) your user
  3. Respond 200 OK within 5 seconds (any 2xx without body)
  4. On subsequent webhook calls, compare the header with the stored value — reject mismatches as spoofed

Live генератор QR

Live QR generator

Заполните форму — QR обновляется автоматически. Скопируйте код в свой сайт.

Fill in the form — QR updates automatically. Copy the code to your site.

Готовый payload:

Resulting payload:

Пример: Node.js / Express

Example: Node.js / Express

// 1) Render page with QR
import express from 'express';
import QRCode from 'qrcode';

const app = express();
app.use(express.json());

app.get('/connect-x2chat', async (req, res) => {
  const payload = {
    v: 1,
    type: 'x2chat-webhook-setup',
    webhook_url: 'https://my-site.com/x2chat-webhook',
    site_name: 'MyService',
    expires_at: new Date(Date.now() + 5*60*1000).toISOString(),
  };
  const qrDataUrl = await QRCode.toDataURL(JSON.stringify(payload), { width: 320 });
  res.send(`<h2>Сканируйте в X2Chat:</h2><img src="${qrDataUrl}">`);
});

// 2) Setup confirmation ping
app.post('/x2chat-webhook', (req, res) => {
  const secret = req.headers['x-x2chat-secret-token'];
  if (req.body?.setup_confirm) {
    // First ping — save secret + bot info
    saveX2ChatBot({
      bot_id: req.body.bot.id,
      bot_username: req.body.bot.username,
      secret_token: secret,
    });
    return res.sendStatus(200);
  }
  // Regular update — verify secret
  const stored = lookupSecretByBotId(req.body?.bot?.id);
  if (secret !== stored) return res.sendStatus(401);
  // process update (message/callback_query/...)
  console.log('Update from', req.body);
  res.sendStatus(200);
});

app.listen(3000);

Пример: Python / FastAPI

Example: Python / FastAPI

# requirements: fastapi, qrcode[pil], pydantic
import qrcode, json, io, base64
from datetime import datetime, timedelta, timezone
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import HTMLResponse

app = FastAPI()

@app.get('/connect-x2chat', response_class=HTMLResponse)
def connect_page():
    payload = {
        'v': 1,
        'type': 'x2chat-webhook-setup',
        'webhook_url': 'https://my-site.com/x2chat-webhook',
        'site_name': 'MyService',
        'expires_at': (datetime.now(timezone.utc) + timedelta(minutes=5)).isoformat(),
    }
    img = qrcode.make(json.dumps(payload))
    buf = io.BytesIO(); img.save(buf, format='PNG')
    b64 = base64.b64encode(buf.getvalue()).decode()
    return f'<h2>Сканируйте в X2Chat:</h2><img src="data:image/png;base64,{b64}">'

bot_secrets: dict = {}  # bot_id -> secret

@app.post('/x2chat-webhook')
async def webhook(req: Request):
    body = await req.json()
    secret = req.headers.get('x-x2chat-secret-token')

    if body.get('setup_confirm'):
        bot_secrets[body['bot']['id']] = secret
        return {'ok': True}

    bot_id = body.get('message', {}).get('bot', {}).get('id')
    if secret != bot_secrets.get(bot_id):
        raise HTTPException(401)

    print('Update:', body)
    return {'ok': True}

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

Security

Что защищено

Что НЕ защищено (вне нашей зоны)

🚨 Никогда не запрашивайте secret_token напрямую у пользователя. Если ваш сайт показывает форму «введите secret из X2Chat» — это phishing. X2Chat НЕ показывает secret юзеру, он доходит до вашего сайта только через ping.

🚨 Never ask the user for secret_token directly. If your site shows a "paste secret from X2Chat" form — that's phishing. X2Chat does NOT show the secret to the user; it reaches your site only via the ping.

Возможные ошибки

Possible errors

CodeReasonЧто делатьWhat to do
400Invalid payload schemaПроверьте поля QR. Все 5 обязательны, типы строгиеVerify QR fields. All 5 are required, types strict
400webhook_url not https / LAN / raw IPИспользуйте публичный HTTPS URL с домен-именемUse public HTTPS URL with domain name
410QR expiredRefresh QR на сайтеRefresh QR on the site
504Setup ping timed out (5s)Ваш endpoint должен отвечать быстро. Не делайте тяжёлую логику в setup_confirm — только save+200Your endpoint must reply fast. Don't run heavy logic on setup_confirm — just save+200
502Setup ping returned non-2xxПроверьте логи сайта. Endpoint должен возвращать любой 2xxCheck site logs. Endpoint must return any 2xx
429Rate limit (5 setup attempts/hour)Подождите час или revoke старые webhook'иWait an hour or revoke old webhooks

© X2Chat · Bot API docs · bots@x2chat.com

© X2Chat · Bot API docs · bots@x2chat.com