Поддержка и обращения
Обращения посетителей ассистента можно вести целиком через API: заводить из своей CRM, читать переписку, отвечать от лица поддержки, закрывать и переводить. Там же — как поддержка выглядит для посетителя.
Как устроена поддержка
Посетитель пишет в окно поддержки внутри ассистента, письмом на его почтовый адрес или через Telegram — каждое сообщение становится обращением. Владелец видит обращения в интерфейсе и через API: это один и тот же набор данных, изменения видны сразу в обе стороны.
Окно поддержки у посетителя включается флагом tickets_enabled в возможностях ассистента. API обращений работает и при выключенном флаге: обращение заведётся, но посетитель его у себя не увидит. Поэтому ответы несут поле enabled.
Авторизация — ключ user_…. Доступ к ассистенту у владельца и пользователя с расшаренным доступом. Чужой или несуществующий ассистент — 404 {"success": false, "error": "Project not found or access denied"}, чужое или несуществующее обращение — 404 {"success": false, "error": "Ticket not found"}. Нет ключа — 401 {"success": false, "error": "API token is required"}.
Настройки поддержки
Как поддержка выглядит для посетителя: чем подписаны ответы владельца, аватар, и что показывает пустое окно обращений на каждом языке ассистента.
Получить настройки
GET /assistants/{assistantId}/support
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
assistantId | integer | да | Путь. Id ассистента. |
curl -s "$BASE/assistants/$ASSISTANT/support" \
-H "Authorization: Bearer $KEY"
{
"success": true,
"tickets_enabled": true,
"support_name": "",
"support_avatar": "",
"support_email": "my-bot-ab12cd34ef56@framesuite.app",
"locales": ["ru", "en", "de"],
"support_locales": {},
"effective": {
"ru": {
"support": { "name": "Поддержка", "avatar": null, "initial": "П" },
"empty_state": {
"title": "У вас пока нет обращений",
"description": "Напишите нам о любой проблеме — оплата, возврат, ошибка. Мы читаем каждое сообщение и отвечаем здесь.",
"cta": "Новое обращение",
"image": null,
"resolved_locale": null
}
},
"en": {
"support": { "name": "Support", "avatar": null, "initial": "S" },
"empty_state": {
"title": "You have no tickets yet",
"description": "Tell us about any problem — a payment, a refund, a bug. We read every message and reply here.",
"cta": "New ticket",
"image": null,
"resolved_locale": null
}
}
}
}
| Свойство | Тип | Описание |
|---|---|---|
tickets_enabled | boolean | Включено ли окно поддержки у посетителя. Меняется через возможности. |
support_name | string | Подпись ответов владельца. Пусто — на каждом языке берётся словарное «Поддержка» или «Support». До 80 символов. |
support_avatar | string | Адрес аватара поддержки. Пусто — у посетителя кружок с первой буквой подписи. До 500 символов. |
support_email | string или null | Почтовый адрес ассистента: с него уходят ответы на почтовые обращения, письмо на него заводит обращение. null — почта для ассистента не настроена. |
locales | array | Языки ассистента, на которых настраивается поддержка. |
support_locales | object | Что задал владелец: язык → empty_title, empty_description, empty_cta, empty_image. Только непустые поля. |
effective | object | Что реально увидит посетитель на каждом языке, с подстановкой словаря вместо пустых полей. |
effective.*.support | object | name, avatar, initial — подпись, аватар и буква для кружка. |
effective.*.empty_state | object | title, description, cta (текст кнопки), image, resolved_locale — с какого языка взяты тексты владельца (null — везде словарь). |
Если тексты на языке не заданы, берутся тексты владельца на английском, затем на языке ассистента по умолчанию, а незаполненное поле — из словаря на языке посетителя.
Изменить настройки
Меняет только присланные поля. Ответ — те же данные, что у GET.
PUT /assistants/{assistantId}/support
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
assistantId | integer | да | Путь. Id ассистента. |
support_name | string или null | нет | Тело. До 80 символов. Пустая строка или null — вернуть словарную подпись. |
support_avatar | string или null | нет | Тело. Адрес картинки, до 500 символов. Пусто — убрать аватар. |
support_locales | object | нет | Тело. Язык → объект с полями ниже. Сливается с сохранённой картой по языкам и полям. "<код>": null удаляет язык. |
support_locales.*.empty_title | string | нет | Заголовок пустого окна, до 160 символов. |
support_locales.*.empty_description | string | нет | Текст под заголовком, до 1000 символов. |
support_locales.*.empty_cta | string | нет | Текст кнопки «Новое обращение», до 80 символов. |
support_locales.*.empty_image | string | нет | Картинка пустого окна, до 500 символов. Загрузить — POST …/support/avatar с context=empty. |
curl -s -X PUT "$BASE/assistants/$ASSISTANT/support" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{
"support_name": "Команда поддержки",
"support_locales": {
"ru": { "empty_title": "Здесь пока пусто", "empty_cta": "Написать нам" },
"en": { "empty_title": "Nothing here yet", "empty_cta": "Contact us" }
}
}'
support_locales сливается с сохранённой картой: неприсланный язык и неприсланное поле остаются как были, пустая строка в поле снимает его (действует текст словаря), "<код>": null удаляет язык целиком. Язык, у которого не осталось ни одного заполненного поля, убирается. Языки не из списка ассистента отбрасываются молча. Например, запрос ниже поменяет только кнопку на русском и удалит английский:
curl -s -X PUT "$BASE/assistants/$ASSISTANT/support" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"support_locales": {"ru": {"empty_cta": "Задать вопрос"}, "en": null}}'
| HTTP | Когда |
|---|---|
| 404 | Ассистент не найден или нет доступа |
| 422 | Поле длиннее допустимого, support_locales не объект: {"success": false, "errors": {…}} |
Загрузить аватар или картинку пустого окна
Принимает картинку, кладёт в публичное хранилище и отдаёт адрес. Без context или с context=avatar адрес сразу записывается в support_avatar. С context=empty картинка крупнее и никуда не записывается — адрес нужно самому передать в support_locales.<язык>.empty_image.
POST /assistants/{assistantId}/support/avatar
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
assistantId | integer | да | Путь. Id ассистента. |
avatar | file | да | Multipart. jpeg, jpg, png, gif, webp, до 5 МБ. |
context | string | нет | Multipart. avatar (по умолчанию) или empty. |
curl -s -X POST "$BASE/assistants/$ASSISTANT/support/avatar" \
-H "Authorization: Bearer $KEY" \
-F "avatar=@support.png"
Пример ответа собран по коду:
{
"success": true,
"url": "https://framesuite.app/storage/welcome-cards/2f6c1e0a-8b7d-4c1e-9a55-3f1d2b7c9e10.png",
"path": "welcome-cards/2f6c1e0a-8b7d-4c1e-9a55-3f1d2b7c9e10.png",
"support_avatar": "https://framesuite.app/storage/welcome-cards/2f6c1e0a-8b7d-4c1e-9a55-3f1d2b7c9e10.png"
}
support_avatar — значение после вызова; при context=empty там прежний аватар.
| HTTP | Когда |
|---|---|
| 404 | Ассистент не найден или нет доступа |
| 422 | Нет файла, не картинка, больше 5 МБ, неизвестный context: {"success": false, "error": "The avatar field is required."} |
Обращения
| Метод | Путь | Что делает |
|---|---|---|
GET | /assistants/{assistantId}/tickets | Список с фильтрами, поиском, сортировкой и страницами |
POST | /assistants/{assistantId}/tickets | Завести обращение от имени человека |
GET | /assistants/{assistantId}/tickets/unread-count | Счётчики для бейджа |
POST | /assistants/{assistantId}/tickets/read-all | Отметить все прочитанными |
POST | /assistants/{assistantId}/tickets/attachments | Загрузить файлы для вложений |
GET | /assistants/{assistantId}/tickets/{ticketId} | Обращение с перепиской |
PATCH | /assistants/{assistantId}/tickets/{ticketId} | Статус и язык |
POST | /assistants/{assistantId}/tickets/{ticketId}/reply | Ответить от лица поддержки |
POST | /assistants/{assistantId}/tickets/{ticketId}/read | Отметить прочитанным или непрочитанным |
POST | /assistants/{assistantId}/tickets/{ticketId}/needs-dev | Пометка «требует доработки» |
GET | /assistants/{assistantId}/tickets/{ticketId}/translations | Сохранённые переводы переписки |
POST | /assistants/{assistantId}/tickets/{ticketId}/translate | Перевести переписку и сохранить |
Свойства обращения
Один и тот же объект приходит в списке, в треде и в ответах на изменения.
| Свойство | Тип | Описание |
|---|---|---|
id | integer | Id обращения. |
project | object | id, name, frame_slug ассистента (поле называется project по историческим причинам). |
subject | string | Тема. |
status | string | open — в работе, closed — закрыто, archived — убрано из рабочих списков (посетитель видит его закрытым). |
was_anonymous | boolean | Автор был гостем без аккаунта на момент обращения. |
owner_unread | boolean | Не прочитано владельцем. Снимается только явной отметкой или ответом владельца, а не открытием треда. |
user_unread | boolean | Посетитель ещё не видел ответ. |
locale | string или null | Язык обращения: собственный, при пустом — язык автора, при пустом — язык ассистента. |
locale_own | string или null | Только собственный язык обращения. |
source | string | Откуда пришло: widget (окно поддержки), mail, telegram, panel, api, feedback или своё имя службы, до 64 символов. |
external_id | string или null | Id обращения во внешней системе, если передан при создании. |
messages_count | integer | Сколько сообщений в треде. |
preview | string или null | Первые 160 знаков первого сообщения (или имена файлов). В ответах на изменения — null. |
author, user | object или null | Один и тот же автор под двумя именами: id, public_id, name, email, is_anonymous, locale. null — автор не определён (например, письмо с неизвестного адреса). |
summary | string или null | Короткая сводка по автору на момент обращения: платит ли, сколько диалогов, упирался ли в пейволл. |
has_dossier | boolean | Сводка по автору собрана. |
has_ai_reply | boolean | Есть черновик ответа от модели. |
needs_dev | boolean | Помечено «требует доработки». |
needs_dev_at, needs_dev_by, needs_dev_note | string, object, string | Когда, кем ({id, name}) и с какой заметкой поставлена пометка. |
last_message_at, created_at, updated_at | string | ISO 8601, UTC. |
Сообщение треда: id, author_role (user — посетитель, owner — поддержка), body, attachments, created_at, author ({id, name}), mail. Если запрошен язык перевода — ещё translation, translation_locale, translation_at.
mail — судьба письма по сообщению или null, если письма не было: status (inbound — входящее письмо; sent — ответ ушёл; failed — не ушёл; queued — в очереди), at, to, error, error_code, permanent (повтор не поможет).
Вложение: id, name, type (image, pdf, document, file), mime, size, url, path.
Список обращений
GET /assistants/{assistantId}/tickets
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
assistantId | integer | да | Путь. Id ассистента. |
status | string | нет | Строка запроса. open, closed, archived или несколько через запятую. По умолчанию open,closed — архив скрыт. |
unread | boolean | нет | Строка запроса. 1 — только непрочитанные владельцем. |
needs_dev | boolean | нет | Строка запроса. 1 — только помеченные, 0 — только непомеченные. Нет параметра — без фильтра. |
locale | string | нет | Строка запроса. Языки через запятую: ru,en. |
source | string | нет | Строка запроса. Источник или несколько через запятую. widget включает старые обращения без отметки. |
search | string | нет | Строка запроса. По теме, текстам посетителя, имени и почте автора. |
sort | string | нет | Строка запроса. last_message_at (по умолчанию), created_at, messages_count, user, project, needs_dev, id. |
dir | string | нет | Строка запроса. desc (по умолчанию) или asc. |
page | integer | нет | Строка запроса. Страница, с 1. |
per_page | integer | нет | Строка запроса. 1–200, по умолчанию 30. |
Без page и per_page отдаётся одна пачка до 500 последних обращений.
curl -s "$BASE/assistants/$ASSISTANT/tickets?status=open&source=mail&page=1&per_page=20" \
-H "Authorization: Bearer $KEY"
{
"success": true,
"enabled": true,
"unread_count": 1,
"open_count": 1,
"tickets": [
{
"id": 118,
"project": { "id": 53, "name": "bota.chat", "frame_slug": "x8OA7Azs4hBy" },
"subject": "Не приходит письмо после оплаты",
"status": "open",
"was_anonymous": false,
"owner_unread": true,
"user_unread": false,
"locale": "ru",
"locale_own": "ru",
"source": "mail",
"external_id": null,
"messages_count": 1,
"preview": "Оплатил тариф на месяц, письмо с подтверждением так и не пришло…",
"author": {
"id": 104522,
"public_id": "u_k3m9x2p7qa",
"name": "Анна",
"email": "user@example.com",
"is_anonymous": false,
"locale": "ru"
},
"user": { "…": "тот же объект, что author" },
"summary": "платит, 4 диалога, 1 обращение, открытых 1",
"has_dossier": true,
"has_ai_reply": false,
"needs_dev": false,
"needs_dev_at": null,
"needs_dev_by": null,
"needs_dev_note": null,
"last_message_at": "2026-09-25T09:41:56+00:00",
"created_at": "2026-09-25T09:41:56+00:00",
"updated_at": "2026-09-25T09:41:56+00:00"
}
],
"total": 7,
"page": 1,
"per_page": 20,
"last_page": 1
}
unread_count и open_count считаются по всем обращениям ассистента без архива и фильтрами не сужаются. total, page, per_page, last_page — по выборке.
Счётчики непрочитанных
Лёгкий метод для бейджа.
GET /assistants/{assistantId}/tickets/unread-count
curl -s "$BASE/assistants/$ASSISTANT/tickets/unread-count" \
-H "Authorization: Bearer $KEY"
{ "success": true, "enabled": true, "unread_count": 1, "open_count": 1 }
Обращение с перепиской
Обращение и все сообщения с вложениями. Прочитанным не отмечает.
GET /assistants/{assistantId}/tickets/{ticketId}
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
assistantId | integer | да | Путь. Id ассистента. |
ticketId | integer | да | Путь. Id обращения. |
locale | string | нет | Строка запроса. Подставить к каждому сообщению сохранённый перевод на этот язык. Модель не вызывается. |
curl -s "$BASE/assistants/$ASSISTANT/tickets/118?locale=ru" \
-H "Authorization: Bearer $KEY"
{
"success": true,
"ticket": {
"id": 118,
"subject": "Can't log in after payment",
"status": "closed",
"owner_unread": false,
"user_unread": true,
"locale": "en",
"source": "mail",
"messages_count": 2,
"…": "остальные свойства обращения",
"messages": [
{
"id": 362,
"author_role": "user",
"body": "Hi, I paid for the monthly plan but can't log in anymore.",
"attachments": [],
"created_at": "2026-09-23T13:55:53+00:00",
"author": { "id": 104522, "name": "Kate" },
"mail": { "status": "inbound", "at": null, "to": null, "error": null, "error_code": null, "permanent": false },
"translation": null,
"translation_locale": null,
"translation_at": null
},
{
"id": 395,
"author_role": "owner",
"body": "Hello! Please sign in with the same Google account you used to pay.",
"attachments": [],
"created_at": "2026-09-25T08:32:46+00:00",
"author": { "id": 1, "name": "Поддержка" },
"mail": { "status": "sent", "at": "2026-09-25T08:32:48+00:00", "to": "user@example.com", "error": null, "error_code": null, "permanent": false },
"translation": null,
"translation_locale": null,
"translation_at": null
}
]
}
}
| HTTP | Когда |
|---|---|
| 404 | Ассистент или обращение не найдены |
Завести обращение
Создаёт обращение от имени человека — например, из вашей CRM или формы на сайте. Вместе с обращением собирается сводка по автору, владельцу уходит уведомление. Человека нужно указать явно: не нашли — отказ, новая учётка заводится только по почте и только с create_user: true.
POST /assistants/{assistantId}/tickets
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
assistantId | integer | да | Путь. Id ассистента. |
user_id | integer | нет | Тело. Внутренний id пользователя. Проверяется первым. Только пользователь из круга ассистента, иначе 404 user_not_found. |
user_public_id | string | нет | Тело. Публичный id u_…, до 64 символов. Только из круга ассистента. |
user_email | string | нет | Тело. Почта. Ищется по всей площадке. Если учёток с ней несколько, берётся самая ранняя. |
user | string | нет | Тело. Любой идентификатор: id, u_…, почта, Telegram, отпечаток браузера. Проверяется последним. Кроме почты — только из круга ассистента. |
create_user | boolean | нет | Тело. Завести учётку, если по почте никого нет. По умолчанию false. |
user_name | string | нет | Тело. Имя для новой учётки. По умолчанию — часть почты до @. |
body | string | да* | Тело. Текст, до 20 000 символов. Обязателен, если нет attachments. |
subject | string | нет | Тело. Тема, до 255. По умолчанию — первые 120 знаков текста или имя первого файла. |
locale | string | нет | Тело. Язык обращения, до 8. По умолчанию — язык автора, затем ассистента. |
attachments | array | нет | Тело. До 5 объектов из POST …/tickets/attachments. |
status | string | нет | Тело. open (по умолчанию), closed, archived. |
owner_unread | boolean | нет | Тело. Считать непрочитанным владельцем. По умолчанию true. |
source | string | нет | Тело. Имя источника, до 64. По умолчанию api. Приводится к нижнему регистру, пробелы — в дефис. |
external_id | string | нет | Тело. Id во внешней системе, до 190. |
meta | object | нет | Тело. Произвольные данные. Служебные ключи source, created_via, created_by, created_at перезаписать нельзя. |
Нужен хотя бы один из user_id, user_public_id, user_email, user.
Круг ассистента — владелец, пользователи с доступом и люди, у которых с ассистентом есть диалог, платёж, начисленные кредиты или обращение. По id и u_… найти человека вне круга нельзя: ответ такой же, как на несуществующего, 404 user_not_found, — так по id не узнать чужую почту. Почта ищется по всей площадке, но если человек с ассистентом ещё не связан, досье к обращению не прикладывается.
curl -s -X POST "$BASE/assistants/$ASSISTANT/tickets" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{
"user_email": "user@example.com",
"subject": "Не приходит письмо",
"body": "Оплатил, письмо не пришло. Заказ 4412.",
"source": "crm",
"external_id": "CRM-4412"
}'
Ответ 201, пример собран по коду:
{
"success": true,
"enabled": true,
"user_resolution": { "resolved_by": "email", "candidates_count": 1, "created": false },
"ticket": {
"id": 231,
"subject": "Не приходит письмо",
"status": "open",
"owner_unread": true,
"source": "crm",
"external_id": "CRM-4412",
"messages_count": 1,
"…": "остальные свойства обращения",
"messages": [
{ "id": 401, "author_role": "user", "body": "Оплатил, письмо не пришло. Заказ 4412.", "attachments": [], "created_at": "2026-09-25T10:25:54+00:00", "author": { "id": 104522, "name": "Анна" }, "mail": null }
]
}
}
user_resolution.resolved_by — чем опознан человек: user_id, public_id, email или способ, которым его нашёл общий user. created: true — учётка заведена этим вызовом.
| HTTP | Когда |
|---|---|
| 404 | Ассистент не найден или нет доступа |
| 422 | Не указан человек: {"success": false, "error": "user_required", "message": "…"} |
| 404 | Человек не найден или он не из круга ассистента (ответ одинаковый): {"success": false, "error": "user_not_found", "message": "…"} |
| 422 | Ошибка полей или вложений: {"success": false, "errors": {…}} |
Вложения проверяются раньше, чем ищется или заводится автор: при кривом вложении учётка по create_user: true не создаётся. Обращение и первое сообщение пишутся одной транзакцией.
Ответить
Сообщение от лица поддержки. Посетитель видит ответ в окне поддержки, почтовому обращению ответ уходит письмом. Ответ снимает owner_unread, ставит user_unread, переоткрывает закрытое обращение и обновляет last_message_at.
POST /assistants/{assistantId}/tickets/{ticketId}/reply
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
assistantId, ticketId | integer | да | Путь. |
body | string | да* | Тело. До 20 000 символов. Обязателен, если нет attachments. |
attachments | array | нет | Тело. До 5 объектов из POST …/tickets/attachments. |
curl -s -X POST "$BASE/assistants/$ASSISTANT/tickets/118/reply" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"body": "Проверьте папку «Спам», письмо ушло в 10:02."}'
Пример ответа собран по коду:
{
"success": true,
"message": {
"id": 402,
"author_role": "owner",
"body": "Проверьте папку «Спам», письмо ушло в 10:02.",
"attachments": [],
"created_at": "2026-09-25T10:40:00+00:00",
"author": { "id": 1, "name": "Поддержка" },
"mail": null
},
"ticket": { "id": 118, "status": "open", "owner_unread": false, "user_unread": true, "…": "…" }
}
| HTTP | Когда |
|---|---|
| 404 | Ассистент или обращение не найдены |
| 422 | Нет ни текста, ни вложений; вложение чужое, не найдено, недопустимого типа или больше 50 МБ |
Сменить статус и язык
PATCH /assistants/{assistantId}/tickets/{ticketId}
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
assistantId, ticketId | integer | да | Путь. |
status | string | нет | Тело. open, closed, archived. |
locale | string | нет | Тело. До 8 символов. Пустая строка стирает собственный язык обращения. |
curl -s -X PATCH "$BASE/assistants/$ASSISTANT/tickets/118" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"status": "closed"}'
Ответ: {"success": true, "ticket": {…}}.
| HTTP | Когда |
|---|---|
| 404 | Ассистент или обращение не найдены |
| 422 | {"success": false, "error": "Invalid status"} или {"success": false, "error": "Invalid locale"} |
Отметить прочитанным
POST /assistants/{assistantId}/tickets/{ticketId}/read
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
assistantId, ticketId | integer | да | Путь. |
read | boolean | нет | Тело. true (по умолчанию) — прочитано; false — вернуть в непрочитанные. |
curl -s -X POST "$BASE/assistants/$ASSISTANT/tickets/118/read" \
-H "Authorization: Bearer $KEY"
{ "success": true, "owner_unread": false, "unread_count": 0 }
Отметить все прочитанными
Снимает owner_unread со всех обращений ассистента, кроме архивных.
POST /assistants/{assistantId}/tickets/read-all
curl -s -X POST "$BASE/assistants/$ASSISTANT/tickets/read-all" \
-H "Authorization: Bearer $KEY"
{ "success": true, "updated": 3, "unread_count": 0 }
Пометка «требует доработки»
Пометка для обращений, по которым нужна правка в продукте. Включение записывает время и владельца ключа, снятие стирает всё, включая заметку.
POST /assistants/{assistantId}/tickets/{ticketId}/needs-dev
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
assistantId, ticketId | integer | да | Путь. |
needs_dev | boolean | нет | Тело. Без поля пометка переключается. |
note | string | нет | Тело. Заметка, до 500 символов. Пустая строка стирает. Меняется, только если поле прислано. |
curl -s -X POST "$BASE/assistants/$ASSISTANT/tickets/118/needs-dev" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"needs_dev": true, "note": "письмо о подтверждении не уходит на mail.ru"}'
Ответ: {"success": true, "ticket": {…}} с заполненными needs_dev_at, needs_dev_by, needs_dev_note.
| HTTP | Когда |
|---|---|
| 404 | Ассистент или обращение не найдены |
| 422 | needs_dev не boolean, note не строка или длиннее 500 |
Сохранённые переводы
Переписка с уже сохранёнными переводами на выбранный язык. Модель не вызывается.
GET /assistants/{assistantId}/tickets/{ticketId}/translations
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
assistantId, ticketId | integer | да | Путь. |
locale | string | нет | Строка запроса. Язык перевода, по умолчанию ru. |
curl -s "$BASE/assistants/$ASSISTANT/tickets/118/translations?locale=ru" \
-H "Authorization: Bearer $KEY"
{
"success": true,
"ticket_id": 118,
"target_locale": "ru",
"source_locale": "en",
"has_translation": false,
"messages": [
{
"id": 362,
"author_role": "user",
"body": "Hi, I paid for the monthly plan but can't log in anymore.",
"attachments": [],
"created_at": "2026-09-23T13:55:53+00:00",
"author": { "id": 104522, "name": "Kate" },
"mail": { "status": "inbound", "at": null, "to": null, "error": null, "error_code": null, "permanent": false },
"translation": "",
"translation_locale": null,
"translation_at": null
}
]
}
Перевода ещё нет — translation пустой, translation_locale равен null.
Перевести переписку
Переводит сообщения, у которых ещё нет перевода на этот язык, и сохраняет. Повторный вызов ничего не переводит заново, если не передан force. Вызов служебный: кредиты посетителя не тратятся.
POST /assistants/{assistantId}/tickets/{ticketId}/translate
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
assistantId, ticketId | integer | да | Путь. |
target_locale | string | нет | Тело. До 8 символов, по умолчанию ru. |
force | boolean | нет | Тело. Перевести заново. Переводы на другие языки не трогаются. |
curl -s -X POST "$BASE/assistants/$ASSISTANT/tickets/118/translate" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"target_locale": "ru"}'
Пример ответа собран по коду:
{
"success": true,
"ticket_id": 118,
"target_locale": "ru",
"source_locale": "en",
"translated": 2,
"cached": 0,
"skipped": 0,
"ms": 1136,
"usage": { "cost": 0.0002, "prompt_tokens": 204, "completion_tokens": 35, "requests": 1 },
"messages": [
{ "id": 362, "author_role": "user", "body": "Hi, I paid for the monthly plan but can't log in…", "translation": "Здравствуйте, я оплатил месячный тариф, но не могу войти…", "translation_locale": "ru", "translation_at": "2026-09-25T10:27:10+00:00", "…": "…" }
]
}
translated — переведено сейчас, cached — взято из сохранённого, skipped — не требовало перевода (уже на целевом языке).
| HTTP | Когда |
|---|---|
| 404 | Ассистент или обращение не найдены |
| 422 | target_locale длиннее 8, force не boolean |
| 502, 503 | Сервис перевода недоступен: {"success": false, "error": "…"} |
Загрузить файлы для вложений
Файл сначала загружается этим методом, затем объект из ответа кладётся в attachments при создании обращения или ответе. Файл ложится в личную папку владельца ключа — чужой файл приложить нельзя, сервер проверяет путь.
POST /assistants/{assistantId}/tickets/attachments
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
assistantId | integer | да | Путь. |
files[] | file | да | Multipart. До 5 файлов, каждый до 50 МБ. Типы: jpg, jpeg, png, gif, webp, pdf, doc, docx, ppt, pptx, odp, txt, md, csv, json. |
curl -s -X POST "$BASE/assistants/$ASSISTANT/tickets/attachments" \
-H "Authorization: Bearer $KEY" \
-F "files[]=@scan.pdf"
Пример ответа собран по коду:
{
"success": true,
"files": [
{
"id": "e5d2a1c4-7b1f-4f0e-9a3d-2c8b6f1e0d42",
"name": "scan.pdf",
"type": "pdf",
"mime": "application/pdf",
"size": 28104,
"url": "https://framesuite.app/storage/users/1/p53/scan.pdf",
"path": "users/1/p53/scan.pdf"
}
]
}
Из объекта сервер доверяет только path: имя, тип и размер пересобираются по файлу на диске.
| HTTP | Когда |
|---|---|
| 404 | Ассистент не найден или нет доступа |
| 422 | Нет файлов, больше пяти, недопустимый тип или размер: {"success": false, "error": "…"} |
Сценарий: обращения из своей CRM
Завести обращение от клиента, приложить файл, а потом забирать ответы поддержки:
FILE=$(curl -s -X POST "$BASE/assistants/$ASSISTANT/tickets/attachments" \
-H "Authorization: Bearer $KEY" -F "files[]=@invoice.pdf" | jq -c '.files[0]')
curl -s -X POST "$BASE/assistants/$ASSISTANT/tickets" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d "{\"user_email\": \"user@example.com\", \"body\": \"Счёт во вложении\", \"source\": \"crm\", \"external_id\": \"CRM-4412\", \"attachments\": [$FILE]}"
curl -s "$BASE/assistants/$ASSISTANT/tickets?source=crm&status=open&per_page=50" \
-H "Authorization: Bearer $KEY"
Храните у себя ticket.id рядом с external_id. Непрочитанные ответы видно по user_unread: true в треде, свои обращения отделяются фильтром source.
Дальше
- Возможности, сайдбар и задержка — тумблер
tickets_enabled - Языки и переводы
- Чаты по ключу ассистента