База знаний WiFly
Подключение тикет-системы Партнёра к порталу техподдержки WiFly по REST API
Портал: support.wifly.net (MantisBT 2.21.0, REST API)
Кому: разработчикам интеграций на стороне Партнёра
Что получится: заявки создаются и ведутся из вашей тикет-системы, статусы и ответы поддержки возвращаются к вам
Партнёры WiFly могут подключить свою тикет-систему (CRM, helpdesk, ITSM) к нашему порталу техподдержки: операторы работают в одном привычном интерфейсе, а обращения автоматически заводятся на портале, обмениваются комментариями и вложениями и получают актуальные статусы. Эта статья — сокращённая версия руководства по интеграции; полный комплект (PDF на 25 страниц, OpenAPI-спецификация, готовые примеры на curl / Python / C#, docker-compose стенда) скачивается одним архивом:
- 📦 api-integration-guide.zip — полный комплект документации и примеров
- 📄 Только PDF-руководство
Модель интеграции
Портал построен на MantisBT и наружу отдаёт его штатный REST API — проприетарной обёртки нет, готовые клиентские библиотеки для MantisBT применимы. Весь обмен покрывают три вызова:
| Действие | Вызов | Направление |
|---|---|---|
| Создать обращение | POST /api/rest/issues |
ваша система → портал |
| Комментарий / вложение | POST /api/rest/issues/{id}/notes |
ваша система → портал |
| Статусы и ответы поддержки | GET /api/rest/issues?filter_id=reported (опрос раз в 5 минут) |
ваша система запрашивает у портала |
Каждая система Партнёра работает от отдельной сервисной учётной записи с доступом только к своему проекту: клиент видит и изменяет только обращения, заведённые от его имени. Портал не умеет искать по номеру вашего тикета — связку систем держит маппинг «ваш номер → id обращения» в вашей БД, а кастомное поле external_ticket_id дублирует его на портале для ручного разбора.
Подключение
При подключении мы: заводим проект (или подключаем к существующему), создаём сервисную учётку svc_<client>_integration с уровнем reporter, выпускаем API-токен и передаём его по защищённому каналу, настраиваем кастомные поля и согласуем окно проверочного прогона. Вы получаете: базовый URL https://support.wifly.net/api/rest, свой project_id, токен, список категорий, имена кастомных полей (external_ticket_id, contact_email, contact_phone) и лимит вложения (5 000 000 байт ≈ 4,8 МБ до base64). Контакт по вопросам интеграции — welcome@wifly.net: при эскалации указывайте время с часовым поясом, метод и путь, код ответа, тело ошибки, X-Mantis-Version и ваш номер тикета; токен не прикладывайте.
⚠ Тестового контура нет. Портал существует в единственном экземпляре, любое обращение через API видит дежурная смена. Отладка ведётся на вашем локальном стенде (MantisBT в Docker или мок по OpenAPI — оба в архиве), на боевом портале выполняется только короткий согласованный прогон.
Аутентификация: без префикса Bearer
API-токен передаётся в заголовке Authorization как есть:
POST /api/rest/issues HTTP/1.1
Host: support.wifly.net
Authorization: <API-TOKEN>
Content-Type: application/json
⚠ Поддержка схемы
Authorization: Bearer <токен>появилась в MantisBT позже версии портала. Портал считает токеном всё значение заголовка, поэтомуBearer abc123даст403 API token not found. Многие HTTP-клиенты добавляют Bearer сами (в .NET —AuthenticationHeaderValue, в Python —HTTPBearerAuth) — здесь заголовок задаётся вручную.
Проверка доступа: GET /api/rest/users/me должен вернуть 200 и вашу сервисную учётку. Токен храните в секрет-хранилище (не в коде и не в git), ротация — раз в 12 месяцев и немедленно при компрометации, TLS 1.2+.
Создание обращения
Обязательные поля: summary (до 128 символов), description, category, project. Вложения — внутри JSON в base64 (одной строкой, без переносов и префикса data:).
{
"project": { "id": 1 },
"category": { "name": "Инцидент" },
"summary": "Не открывается модуль отчётности",
"description": "При переходе в раздел «Отчёты» появляется ошибка 500.",
"priority": { "name": "high" },
"severity": { "name": "major" },
"custom_fields": [
{ "field": { "name": "external_ticket_id" }, "value": "CRM-2026-000123" },
{ "field": { "name": "contact_email" }, "value": "ivanov@company.example" }
],
"files": [ { "name": "error-500.png", "content": "iVBORw0KGgoAAAANSUhEUg…" } ]
}
Ответ 201 Created содержит issue.id — сохраните его у себя: это единственный надёжный способ впоследствии обратиться к обращению.
⚠ Категорию портал не проверяет. Несуществующее имя категории не даёт ошибку — обращение создаётся с
201и попадает в чужую очередь, где его никто не ждёт. Сверяйте имя со справочником проекта до отправки (GET /api/rest/projects/{id}, полеcategories[].name) и кэшируйте список у себя (обновление раз в сутки достаточно). Опечатка в категории — самая тихая из возможных ошибок интеграции.
Остальные вызовы
GET /issues/{id}— обращение целиком: поля, история, заметки (notes), вложения; отдаётETagдля условных запросов. Приватные заметки поддержки клиентской учётке не возвращаются.GET /issues?project_id=…&filter_id=reported&page=…&page_size=50— список своих обращений;filter_id=reported— заведённые данной учёткой. Параметрselectпорталом не поддерживается, ответ всегда приходит целиком.POST /issues/{id}/notes— комментарий (view_state: public), файлы — в полеfilesзаметки. Отдельного эндпойнта чтения заметок нет — они приходят при чтении обращения.POST /issues/{id}/files/GET /issues/{id}/files/{file_id}— вложения к обращению.GET /projects/{id}— справочники: категории, версии, кастомные поля.
Маппинг полей и статусы
| У вас | На портале | Обяз. |
|---|---|---|
| Тема | summary (≤128) |
да |
| Описание | description |
да |
| Тип обращения | category.name — только из списка проекта |
да |
| Номер вашего тикета | custom_fields[external_ticket_id] |
да |
| Контакт | custom_fields[contact_email] (невалидный e-mail → 400), contact_phone |
e-mail — да, по регламенту* |
| Срочность / влияние | priority.name / severity.name |
нет |
| Статус | status.name — только чтение: жизненным циклом управляет поддержка |
— |
* Портал технически не проверяет наличие contact_email (обращение создастся и без него) — обязательность здесь требование регламента поддержки, а не API. Не рассчитывайте на серверную проверку: заполняйте поле на своей стороне. Валидность формата при этом проверяется — невалидный адрес даёт 400.
Статусы: new (10) → acknowledged (30) → confirmed (40) → assigned (50) → resolved (80) → closed (90).
Отдельно стоит feedback (20) — требуется уточнение от клиента. Это не ступень между new и acknowledged: поддержка может перевести обращение в feedback в любой момент, когда ей нужен ответ. Для интеграции это самый значимый статус — он требует действия с вашей стороны: покажите вопрос пользователю и передайте его ответ комментарием (POST /issues/{id}/notes).
Рекомендуемое соответствие приоритетов: P1 → immediate/block, P2 → urgent/crash|major, P3 → high/major, P4 → normal/minor, консультация → low/feature. Зафиксируйте выбранный маппинг у себя — поддержка ориентируется на присланные значения при расчёте времени реакции.
Получение статусов: polling (и вебхуки как опция)
Базовый способ — опрос раз в 5 минут с ETag: GET /issues?project_id=…&filter_id=reported с заголовком If-None-Match: <сохранённый ETag>; ответ 304 — изменений нет. Для каждого обращения сравнивайте updated_at с сохранённым (даты приводите к UTC), импортируйте новые публичные заметки по note.id. Закрытые обращения снимайте с опроса через 14 дней.
Push-уведомления (вебхуки) — опция, включается по заявке: в ядре MantisBT их нет, они реализуются плагином на нашей стороне. Контракт: события issue.created / updated / status_changed / note_added / closed, подпись HMAC-SHA256 в X-Support-Signature, идемпотентность по X-Support-Delivery, ответ 2xx за 5 секунд, ретраи с паузами 1–625 с. Даже с вебхуками оставляйте polling раз в час как подстраховку.
Ошибки, дубли и ловушки
| Код | Значение | Ретрай |
|---|---|---|
| 400 | Некорректное тело, неизвестное кастомное поле, невалидный e-mail, превышен лимит вложения | нет — исправить данные |
| 401 / 403 | Нет заголовка / токен не найден (часто — префикс Bearer) / нет прав | нет |
| 404 | Обращение удалено или недоступно | нет — снять с синхронизации |
| 429 | Слишком много запросов | да, по Retry-After |
| 500 / 503 | Ошибка портала / режим обслуживания | да, с backoff (1→2→4→8→16 с + джиттер) |
| 200 + text/html | Отказ в доступе, замаскированный под успех | нет — трактовать как 403 |
⚠ Главная ловушка: при обращении к чужому проекту портал возвращает не 403, а
200 OKс HTML-страницей «Доступ запрещён». Код, который сразу вызываетresponse.json(), упадёт с ошибкой разбора и может увести интеграцию в бесконечные ретраи. Перед разбором тела проверяйтеContent-Type: application/jsonи наличие заголовковX-Mantis-*.
Дубли. Заголовок идемпотентности MantisBT не поддерживает — отсутствие дублей обеспечивает ваша система: ведите маппинг «ваш номер → issue.id»; при неопределённом исходе (таймаут, обрыв) не повторяйте отправку вслепую — сначала найдите своё external_ticket_id в последних обращениях filter_id=reported. Отправку ставьте в очередь pending → sent → confirmed.
Обязательные предохранители в коде: kill switch (флаг, мгновенно отключающий отправку), ограничитель частоты, жёсткий предел числа обращений за запуск. Нагрузочное тестирование без согласования недопустимо. В логи пишите метод, путь, код, issue.id, X-Mantis-Version — и никогда токен или содержимое вложений.
Отладка и вывод в прод
- Локальный стенд — MantisBT в Docker (готовый
docker-compose.ymlв архиве, порт 8989): настоящие коды, id, категории, ETag, права. Назовите категории и кастомные поля как в боевом проекте — тогда переход в прод сводится к смене URL, токена иproject_id. - Мок по OpenAPI (Prism) — для автотестов в CI: проверяет форму запросов, но не бизнес-логику.
- Согласованный прогон на портале — сначала проверки без записи (
/users/me,/projects/{id}, сверка категорий и полей), затем 3–5 тестовых обращений в согласованное окно: префикс[TEST]в теме, приоритетlow. Номера пришлите поддержке — мы их закроем (право DELETE клиентским учёткам не выдаётся). - Пилот — 3–5 рабочих дней параллельно с обычным каналом, затем полный переход; старый канал остаётся резервным, kill switch — в конфигурации навсегда.
⚠ Версия стенда. Портал работает на MantisBT 2.21.0, в стенде используется ближайший образ 2.24.1 — по ключевым для интеграции особенностям (Authorization без Bearer, нет
select) он ведёт себя так же. Образ 2.28+ начнёт принимать Bearer — и код, отлаженный на нём, получит 403 на портале. СверяйтеX-Mantis-Versionна обоих концах. Клиент, сгенерированный по свежей OpenAPI-спецификации MantisBT, будет содержать методы, которых на портале нет (move, DELETE files, управление токенами и др.).
Что в архиве
| Файл | Что показывает |
|---|---|
api-integration-guide.pdf |
полное руководство: справочник API, маппинг, сценарии, приёмочный чек-лист |
openapi/mantisbt_openapi.yaml |
спецификация для генерации клиента / импорта в Postman |
examples/curl/create-issue.sh, add-note.sh, poll-updates.sh |
создание с вложением, комментарий, опрос с ETag |
examples/python/mantis_client.py |
клиент: ретраи, polling, защита от дублей, проверка категории |
examples/csharp/MantisSupportClient.cs |
клиент на C# (.NET 8) |
examples/sandbox/docker-compose.yml |
локальный стенд MantisBT для отладки |
Хотите подключить свою тикет-систему?
Напишите на welcome@wifly.net или в поддержку — заведём проект и сервисную учётку, выдадим токен и параметры, согласуем проверочный прогон.
Частые вопросы
Почему портал отвечает «403 API token not found», хотя токен верный?
Почти всегда — префикс Bearer в заголовке Authorization. Портал принимает токен только без схемы; уберите префикс и задавайте заголовок вручную, минуя встроенные механизмы Bearer-авторизации HTTP-клиента.
Можно ли получить тестовый доступ к порталу?
Отдельного тестового контура нет. Отладка ведётся на вашем локальном стенде (Docker-compose в архиве), а на боевом портале выполняется короткий согласованный прогон с помеченными [TEST] обращениями.
Как узнать, что поддержка ответила или сменила статус?
Базово — опрос GET /issues?filter_id=reported раз в 5 минут с ETag. По заявке включаются push-уведомления (вебхуки) с подписью HMAC — но polling раз в час стоит оставить как подстраховку.
Может ли наша система менять статус или закрывать обращения?
По умолчанию нет: сервисная учётка создаётся с уровнем reporter, статус для неё — только чтение, жизненным циклом управляет служба поддержки. Если вашему сценарию нужна смена статусов, уровень updater выдаётся по запросу с обоснованием — напишите на welcome@wifly.net. Ответ пользователя в любом случае передаётся комментарием (POST /issues/{id}/notes).
Какой лимит на вложения?
5 000 000 байт на файл (~4,8 МБ до base64-кодирования; base64 добавляет ~33 % объёма). Крупные логи архивируйте. Превышение даёт 400 Bad Request.