Справочный центр / Интеграция с Support по API (для разработчиков)

Интеграция с Support по API (для разработчиков)

Для кого

Разработчики, которые встраивают TehProf Support как канал поддержки в своё приложение или сервис: чтобы сотрудники клиента подавали заявки на доработку прямо из вашего продукта, получали ответы и полноценно переписывались, не покидая приложение. Подходит и для приложений на Bitrix24 (например, наше приложение бронирования), и для внешних SaaS без Bitrix24.

Что вы получите

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

Предварительные требования

Три способа интеграции — что выбрать

Support даёт три независимых входа. Выберите по сценарию:

СпособКто вызываетАутентификацияКогда использовать
Серверный мост (ctoken)Ваш сервер → затем браузер пользователяAPI-ключ (Bearer) на сервере, потом клиентский токен у пользователяВаши сотрудники подают заявки в нашу поддержку из вашего приложения. Основной сценарий этого руководства.
Операторский API (Bearer)Ваш серверAPI-ключ (Bearer)Программное управление заявками со стороны поддержки: массовое создание, чтение, аналитика, боты.
Встраивание интерфейса (JWT)Ваш сервер + iframeJWT-токенВы хотите показать готовый интерфейс поддержки внутри своего приложения через iframe (полный кабинет, не своя вёрстка).
Входной вебхук + синхронизация компанийВаш серверAPI-ключ в заголовке X-Api-KeyВаш продукт создаёт заявки от имени клиентов и передаёт их компанию/контакт по своим стабильным ключам — заявка автоматически привязывается к нужной компании. Идемпотентно.

Дальше подробно разобран основной сценарий (серверный мост), затем кратко остальные.


Сценарий 1: серверный мост — заявки от ваших сотрудников

Это путь, по которому работает наше приложение бронирования. Логика канона Stripe Connect: платформенный ключ живёт только на сервере, пользователь получает узкий одноразовый токен.

Как это устроено

Ваш сервер (держит API-ключ)
   │  1. POST issue_client_token (Bearer = API-ключ)
   ▼
Support выдаёт персональный клиентский токен (ctoken) для одного сотрудника
   │  2. ctoken отдаётся в браузер сотрудника (или ваш сервер ходит сам)
   ▼
Браузер/сервер с ctoken вызывает client.php:
   - создать заявку
   - список своих заявок
   - ответить в переписке
   - проверить новые ответы

Шаг 1: получить персональный токен сотрудника

Ваш сервер (не браузер!) запрашивает у Support персональный токен для конкретного сотрудника. Публичный endpoint принимает запросы server-to-server по Bearer API-ключу; безопасность зависит от того, что ключ хранится только на вашем сервере и никогда не попадает в браузер.

POST https://support.tehprof.kz/api/mcp-internal.php
Authorization: Bearer sk_ваш_ключ
Content-Type: application/json

{
  "action": "issue_client_token",
  "b24_user_id": "12345",
  "portal_domain": "client-portal.bitrix24.kz",
  "name": "Иван Петров",
  "email": "ivan@client.kz"
}

Ответ:

{
  "ok": true,
  "data": {
    "ctoken": "ct_eyJ0Ijox...подпись",
    "user_id": 2890,
    "expires_in": 86400
  }
}

Поля запроса:

Важно: этот метод не принимает компанию. Серверный мост идентифицирует только человека (по паре b24_user_id + portal_domain). Поля вроде company_name, company_id, company_external_id молча игнорируются — заявка к компании по ним не привяжется. Если вам нужно, чтобы заявка попадала под нужную компанию клиента по вашему ID компании — используйте Сценарий 4 (входной вебхук) с полями company_external_id / contact_external_id. Это частая причина «компанию передаю, а она не подтягивается».
Как формируется отображаемое имя. Имя контакта берётся из поля name, которое вы прислали. Повторный вызов issue_client_token сам по себе не перезаписывает имя уже существующего контакта. Поэтому сразу передавайте реальное ФИО конкретного пользователя, а исправлять ранее сохранённое имя нужно вручную в карточке контакта.

Важно про идемпотентность: для одной и той же пары b24_user_id + portal_domain всегда возвращается один и тот же user_id. Поэтому фильтр «только мои заявки» работает стабильно при повторных выдачах токена. ctoken живёт 24 часа (expires_in) — кешируйте его и перевыпускайте за несколько минут до истечения.

Шаг 2: работать от лица сотрудника по ctoken

Полученный ctoken передаётся в заголовке X-Client-Token при вызовах client.php.

Создать заявку:

POST https://support.tehprof.kz/api/client.php
X-Client-Token: ct_eyJ0Ijox...
Content-Type: application/json

{
  "action": "create_ticket",
  "subject": "Не открывается календарь брони",
  "description": "При клике на дату ничего не происходит, консоль пустая",
  "category": "other",
  "priority": "normal"
}
Обязательно укажите валидную категорию. Если поле category не передать, заявка получит категорию по умолчанию, которая может оказаться невалидной → ошибка err_input_required_fields. Безопасное значение — other. Список доступных категорий зависит от настроек принимающей компании (встроенные + кастомные).

Поля: subject (обязательно), description (обязательно), category (обязательно, рекомендуем other), priority (low/normal/high/urgent, по умолчанию normal; устаревший алиас critical нормализуется в urgent).

Список своих заявок (изоляция по сотруднику):

POST https://support.tehprof.kz/api/client.php
X-Client-Token: ct_eyJ0Ijox...

{ "action": "list_tickets", "own_only": 1 }

Ответ: { "ok": true, "data": { "items": [...], "total": N, "page": 1, "limit": 20 } }. У каждой заявки есть unread — число непрочитанных ответов (для бейджа).

Ответить в заявку:

POST https://support.tehprof.kz/api/client.php
X-Client-Token: ct_eyJ0Ijox...

{ "action": "add_message", "ticket_id": 10094, "message": "Проблема повторяется и в другом браузере" }

Проверить новые ответы (для опроса/бейджа):

{ "action": "check_updates" }

Другие действия по X-Client-Token: get_ticket (заявка + переписка), update_ticket, reopen_ticket, cancel_ticket, sign_download_url (защищённая ссылка на файл), submit_feedback, get_changelog. Полный список — в машинной спецификации (см. «Связанные материалы»).

Шаг 3 (опционально): встроить готовый кабинет вместо своей вёрстки

Если не хотите рисовать свой список заявок, встройте готовый кабинет Support в iframe — он уже умеет всё (заявки, переписка, файлы, создание):

<iframe src="https://support.tehprof.kz/embed.html?ctoken=ct_eyJ0Ijox...&lang=ru"
        style="width:100%;height:100%;border:0"></iframe>

embed.html принимает ctoken и lang (ru/en/kk). Кабинет работает в рамках одного сотрудника, поэтому iframe можно показывать прямо в вашем интерфейсе.


Сценарий 2: операторский API (Bearer)

Если нужно управлять заявками программно со стороны поддержки (боты, массовые операции, аналитика), используйте API-ключ напрямую как Bearer — без ctoken.

POST https://support.tehprof.kz/api/mcp-internal.php
Authorization: Bearer sk_ваш_ключ
Content-Type: application/json

{ "action": "get_tickets", "status": "new", "limit": 20 }

Доступные действия: get_tickets, get_ticket, create_ticket, reply_ticket, update_ticket, analytics_dashboard, analytics_sla, analytics_operators, list_operators, list_channels, get_settings, list_automation_rules, и др.

Права (scope) важны. Каждое действие требует определённого права у ключа:
- чтение (get_tickets, analytics_, list_) → нужен scope read;
- запись (create_ticket, reply_ticket, update_ticket) → scope write;
- выдача клиентских токенов (issue_client_token) → scope write + clients.
Ключ только с write + clients (как у серверного моста) не сможет читать заявки операторским get_tickets — вернётся 403. Это сделано намеренно: ключ моста не предназначен для операторского доступа. Если нужны оба сценария — запросите ключ с правами read + write + clients или заведите два ключа.

Подключение через MCP (без программирования)

Для AI-ассистентов (Claude Desktop, Claude Code) есть MCP-сервер Support — он оборачивает операторский API в набор инструментов. Это даёт управление поддержкой на естественном языке без написания кода. Адрес и набор инструментов уточняйте у нас — MCP отдаёт tickets_create/get/reply/update/list, аналитику, базу знаний и онбординг-гайды.


Сценарий 3: встраивание интерфейса (JWT) — для внешних SaaS без Bitrix24

Если у вас собственная система идентификации (не Bitrix24), используйте Token API: ваш сервер обменивает API-ключ на JWT для конкретного пользователя, JWT передаётся во встраиваемый интерфейс.

POST https://support.tehprof.kz/api/install-generic.php
X-Api-Key: tps_ваш_ключ
Content-Type: application/json

{ "action": "issue_token", "sub": "user_42", "name": "Иван Петров",
  "email": "ivan@company.com", "role": "employee", "exp": 3600 }

Ответ: { "ok": true, "token": "eyJ...", "expires_in": 3600 }. Дальше токен передаётся в embed-sdk.js / iframe. Полное описание Token API, самоподписанного JWT (PHP/Node.js/Python), embed SDK и веб-хуков — в машинной спецификации llms-embed.txt.


Сценарий 4: входной вебхук + синхронизация компаний и контактов

Подходит, когда ваш продукт создаёт заявки от имени своих клиентов и хочет, чтобы каждая заявка попадала под нужную компанию клиента. Сопоставление идемпотентно по вашим стабильным ключам (канон Zendesk/Intercom: external_id): справочник пополняется по мере заявок. Если же нужно заранее связать весь свой справочник с нашими карточками (в том числе тех, кто писал ещё до интеграции) — есть отдельная синхронизация без создания заявок, см. Сценарий 5.

Ключ создаётся в админке: Настройки → Автоматизация → Входящие вебхуки. Передаётся только в заголовке X-Api-Key (query-параметр ?key= не принимается).

POST https://support.tehprof.kz/api/webhook-in.php
X-Api-Key: tps_ваш_ключ
Content-Type: application/json

{
  "subject": "Ошибка входа в кабинет",
  "description": "Не проходит авторизация.",
  "client_name": "Иванов Артём",
  "client_phone": "+77001234567",
  "client_email": "ivanov@company.kz",
  "company_external_id": "ORG-42",      // ваш стабильный ID компании (главный ключ)
  "company_bin": "123456789012",        // БИН/ИИН — запасной ключ для юрлиц
  "company_name": "ТОО Ромашка",        // имя при создании новой компании
  "contact_external_id": "USER-999",    // ваш стабильный ID пользователя
  "external_ref": "TICKET-2026-0042",   // ключ идемпотентности заявки
  "category": "other"
}

Порядок на нашей стороне: проверка external_ref (повтор → возврат той же заявки, deduplicated: true) → найти/создать компанию по company_external_id/company_bin → найти/создать контакт по contact_external_id/телефону/почте → связать контакт с компанией → создать заявку, уже привязанную к компании.

Ответ при создании (HTTP 201): { "ok": true, "ticket_id": 4522, "status": "new", "company_id": 1611, "contact_id": 9270 }.

Ответ при повторе (HTTP 200): { "ok": true, "ticket_id": 4522, "status": "new", "deduplicated": true }.

Категория — из списка crm · telephony · whatsapp · tasks · automation · setup · bugfix · other (или задайте категорию по умолчанию при создании вебхука). Лимит: 60 запросов в час с одного IP. Полное описание — в статье «Входящие вебхуки».


Сценарий 5: синхронизация справочника контактов и компаний (без создания заявки)

Подходит, когда у вас уже есть собственный справочник клиентов и вы хотите заранее связать его с нашими карточками по своим стабильным ID (external_id), не дожидаясь, пока человек напишет. После синхронизации, когда клиент обратится, мы сразу узнаем его и его компанию по вашему ID — и в наших карточках будет проставлен ваш идентификатор.

Авторизация — Bearer-ключ с правами clients + write (как в Сценарии 2). Тенант определяется по ключу.

Одна запись — `upsert_contact`

POST https://support.tehprof.kz/api/mcp-internal.php
Authorization: Bearer tps_ваш_ключ
Content-Type: application/json

{
  "action": "upsert_contact",
  "contact_external_id": "USER-999",     // ваш стабильный ID контакта (сильнейший ключ)
  "name": "Иванов Артём",
  "phone": "+77001234567",
  "email": "ivanov@company.kz",
  "company_external_id": "ORG-42",       // ваш стабильный ID компании
  "company_name": "ТОО Ромашка"          // имя при создании новой компании
}

Ответ: { "ok": true, "data": { "contact_id": 9270, "company_id": 1611, "status": "created" } } (status: created / updated / skipped).

Много записей за один вызов — `upsert_contacts_bulk` (рекомендуется для большого справочника)

Чтобы не слать тысячи отдельных запросов, отправляйте записи пачками до 500 за вызов:

POST https://support.tehprof.kz/api/mcp-internal.php
Authorization: Bearer tps_ваш_ключ
Content-Type: application/json

{
  "action": "upsert_contacts_bulk",
  "records": [
    { "contact_external_id": "USER-1", "name": "Иван Петров",  "phone": "+77011112233", "company_external_id": "ORG-1", "company_name": "ТОО Альфа" },
    { "contact_external_id": "USER-2", "name": "Анна Сидорова", "email": "anna@beta.kz",  "company_external_id": "ORG-2", "company_name": "ТОО Бета" }
  ]
}

Ответ — построчный отчёт:

{
  "ok": true,
  "data": {
    "processed": 2,
    "created": 2, "updated": 0, "skipped": 0,
    "errors": [],
    "results": [
      { "index": 0, "external_id": "USER-1", "status": "created", "contact_id": 9270, "company_id": 1611 },
      { "index": 1, "external_id": "USER-2", "status": "created", "contact_id": 9271, "company_id": 1612 }
    ]
  }
}

Поведение (оба метода):

Рекомендованный паттерн для большого справочника: первичная заливка — upsert_contacts_bulk пачками по 200–500 (например, 10 000 компаний = ~20–50 вызовов); дальше — точечный upsert_contact только на изменившиеся записи (не пересылать весь справочник каждый раз).


Как получить API-ключ

API-ключи выдаются только после регистрации и только в настройках принимающей компании в Support:

Если вы — внешний интегратор и не знаете, в каком тенанте создавать ключ, обратитесь к нам: support.tehprof.kz.


Безопасность (обязательно к соблюдению)

Частые вопросы

Почему `create_ticket` возвращает `err_input_required_fields`, хотя я передал тему и описание?

Скорее всего не передана валидная category. Категория по умолчанию может быть невалидной для принимающей компании. Передавайте category: "other" явно.

Сотрудник видит чужие заявки — почему?

Вы не передали own_only: 1 в list_tickets. Без этого флага возвращаются заявки всей компании сотрудника (так задуман клиентский кабинет). Для персональной изоляции всегда передавайте own_only: 1.

Мой ключ читает заявки оператора — 403. Что не так?

У ключа нет права read. Ключ серверного моста обычно имеет только write + clients. Запросите ключ с read либо отдельный ключ для операторского доступа.

Можно ли вызывать `mcp-internal.php` из браузера?

Нет. Хотя endpoint доступен по публичному HTTPS, Bearer API-ключ является серверным секретом. Все вызовы выполняйте server-to-server со своего бэкенда.

Передаю название и ID компании, но компания не привязывается к заявке

Проверьте, на какой эндпоинт вы шлёте. Серверный мост (issue_client_token) и операторский create_ticket поля компании не принимают — они идентифицируют только человека. Для привязки заявки к компании по вашему стабильному ID используйте Сценарий 4 (входной вебхук webhook-in.php) с полями company_external_id / company_bin / contact_external_id.

В заявке показывается не то имя (например, «Администратор» или «Client»)

Support показывает ровно то имя, которое прислал ваш бэкенд (name / client_name / fullName) — он ничего не выдумывает. Две частые причины:

Заранее заводить клиента как «Контакт» не нужно — контакт и компания создаются автоматически при первой заявке.

Связанные материалы

Не нашли ответ? Найдите другую статью или напишите в поддержку.