Messages
Сообщения принадлежат тикету. sender_type — кто написал: contact (клиент), operator, bot (системные сообщения и сообщения, отправленные через API), ai. is_internal: true — внутренняя заметка, клиент её не видит.
sender_id— id контакта для сообщений клиента, id пользователя для сообщений оператора. Уbot-сообщений, отправленных через API, это id проекта (так они отличаются от системных, у которыхnull).edited_at— когда сообщение правили, иначеnull.media— вложения.urlв них относительный (/api/v1/media/…), скачивание — на странице Медиа.
/api/v1/tickets/{ticket_id}/messagesСообщения тикета
По порядку, старые сверху. Внутренние заметки входят в список. Для несуществующего тикета — пустой список.
Query-параметры:
since— ISO 8601, сообщения, созданные в этот момент или позже. Время со смещением пояса переводится в UTC (13:30+02:00— это 11:30 UTC), время без смещения считается UTCpage(по умолчанию 1),page_size(по умолчанию 50, максимум 200)
{
"items": [
{
"id": "f1...",
"ticket_id": "8a3f...",
"workspace_id": "1c2b...",
"sender_type": "contact",
"sender_id": "44e1...",
"content": "Здравствуйте!",
"is_internal": false,
"media": [
{
"id": "m1...",
"url": "/api/v1/media/m1...",
"mime_type": "image/png",
"original_name": "screenshot.png",
"file_type": "png",
"width": 1280,
"height": 720,
"size": 184523
}
],
"created_at": "2026-04-06T10:12:33.123456",
"edited_at": null
}
],
"total": 4,
"page": 1,
"page_size": 50
}/api/v1/tickets/{ticket_id}/messagesНаписать в тикет от имени бота
Сообщение сохраняется с sender_type: bot и id проекта в sender_id. Поля тела:
content— текст, до 10 000 символов. Может быть пустым, если естьmedia_ids: тогда сообщение сохраняется с текстом[Attachment]. Пустой текст без файлов — 422MESSAGE_EMPTYis_internal—trueдля внутренней заметки, по умолчаниюfalsemedia_ids— id файлов этого проекта, загруженных черезPOST /api/v1/media/upload. Id не в формате UUID — 422VALIDATION_ERROR; несуществующий файл или файл другого проекта — тоже 422VALIDATION_ERROR, такие id перечислены вdetail.media_ids. В обоих случаях ничего не сохраняется. Файл, который уже прикреплён к другому сообщению, копируется: в ответе у него новый id, исходное сообщение его не теряет
{
"content": "Спасибо! Прикладываю инструкцию.",
"is_internal": false,
"media_ids": ["m1...", "m2..."]
}Ответ — 201 и сообщение с прикреплёнными файлами.
message.created уже содержит их в media./api/v1/messages/{message_id}Изменить текст сообщения
Меняет content сообщения клиента, оператора или сообщения, которое отправлено через этот API, и ставит edited_at. Если сообщение оператора было отправлено в Telegram, текст меняется и там. Ограничения:
- Из
bot-сообщений редактируются только отправленные через API проекта (с id проекта вsender_id); операторы в дашборде их не правят. Системныеbot-сообщения и ответыai— 422VALIDATION_ERRORс причинойSystem messages cannot be edited - Окно редактирования проекта:
message_edit_window_minutesминут с момента отправки (по умолчанию 15,0— без ограничения) - В тикетах
closedиresolved— только если в проекте включеноmessage_edit_after_close - Пустой текст — 422
MESSAGE_EMPTY
{
"content": "Обновлённый текст сообщения"
}В ответ приходит сообщение целиком. Шлёт webhook message.edited. Текущие значения окна и политики — в GET /api/v1/workspace/settings:
{
"id": "1c2b...",
"name": "Мой проект",
"slug": "my-project",
"timezone": "Europe/Warsaw",
"message_edit_window_minutes": 15,
"message_edit_after_close": false,
"language": "ru"
}/api/v1/messagesВходящее сообщение от внешнего контакта
Для случая, когда внешняя система (CRM, бот, форма на сайте) передаёт сообщение от клиента. Контакт ищется по telegram_id, затем по email и создаётся, если не найден. Сообщение добавляется в последний тикет контакта, который не закрыт и не решён; если такого нет, создаётся новый (тема — первые 120 символов текста, тикет учитывается в месячном лимите тарифа).
contact— обязательно:telegram_id(число) и/илиemail, плюсname. Без обоих идентификаторов — 422VALIDATION_ERRORmessage— текст, 1–10 000 символов, обязательноsession_id— принимается, но не используется
Вложений у этого эндпоинта нет.
{
"contact": {
"email": "ivan@example.com",
"name": "Иван Петров"
},
"message": "Здравствуйте, не приходит код"
}curl -X POST https://api.support.forestsnet.com/api/v1/messages \
-H "Authorization: Bearer sk_xxx" \
-H "Content-Type: application/json" \
-d '{"contact": {"email": "ivan@example.com"}, "message": "Здравствуйте"}'{
"ok": true,
"message": { "id": "f2...", "ticket_id": "8a3f...", "sender_type": "contact", ... }
}Если API-канал выключен на уровне платформы, приходит 200 с {"ok": false, "message": "API channel is disabled"}. Если контакт заблокирован, сообщение не сохраняется и приходит 200 с {"ok": false, "message": null, "reason": "contact_blocked"}.
contact.extra_data.last_ip (время — в last_ip_at). Когда запрос шлёт ваш сервер, это адрес вашего сервера, а не клиента.
