Tickets
Для разработчиков

Tickets

Тикет — обращение клиента. Через API тикеты можно читать, создавать, менять, закрывать и переоткрывать. Удалить тикет через API нельзя.

  • Статусы: new, open, in_progress, pending (отложен), resolved, closed.
  • Приоритеты: low, normal, high, urgent.
  • channel_type — канал, из которого пришёл тикет (telegram, widget, email, vk, whatsapp, billmgr…). У тикетов, созданных через API, channel_id и channel_type равны null.
GET/api/v1/tickets

Список тикетов

Все тикеты проекта постранично, новые сверху (по дате создания).

Query-параметры:

  • status, priority — одно из значений выше; другое значение — 422
  • contact_id — UUID контакта, только его тикеты
  • since — ISO 8601, тикеты, созданные в этот момент или позже. Время со смещением пояса переводится в UTC (13:30+02:00 — это 11:30 UTC), время без смещения считается UTC
  • include_history — true добавляет к каждому тикету last_message_preview (первые 100 символов последнего сообщения) и message_count; внутренние заметки не учитываются
  • page (по умолчанию 1), page_size (по умолчанию 20, максимум 100)
curl
curl "https://api.support.forestsnet.com/api/v1/tickets?status=open&page_size=10&include_history=true" \
  -H "Authorization: Bearer sk_xxx"
200 OK
{
  "items": [
    {
      "id": "8a3f...",
      "workspace_id": "1c2b...",
      "contact_id": "44e1...",
      "channel_id": "c7d0...",
      "channel_type": "widget",
      "assigned_to": null,
      "department_id": null,
      "subject": "Не приходит код подтверждения",
      "status": "open",
      "priority": "normal",
      "tags": ["billing"],
      "first_response_at": null,
      "resolved_at": null,
      "closed_at": null,
      "rating": null,
      "created_at": "2026-04-06T10:12:33.123456",
      "updated_at": "2026-04-06T10:12:33.123456",
      "last_message_preview": "Здравствуйте, не приходит код",
      "message_count": 4
    }
  ],
  "total": 137,
  "page": 1,
  "page_size": 10
}
GET/api/v1/tickets/{ticket_id}

Тикет с последними сообщениями

Возвращает тикет, его последние сообщения (по порядку, старые сверху) и контакт. Внутренние заметки операторов тоже входят в список — у них is_internal: true. Формат сообщения — на странице Messages.

Query-параметры:

  • last_messages — сколько последних сообщений вернуть (1–200, по умолчанию 20)
curl
curl "https://api.support.forestsnet.com/api/v1/tickets/8a3f...?last_messages=50" \
  -H "Authorization: Bearer sk_xxx"
200 OK
{
  "ticket":   { "id": "8a3f...", "status": "open", ... },
  "messages": [ { "id": "f1...", "sender_type": "contact", "content": "Здравствуйте", ... } ],
  "contact":  { "id": "44e1...", "full_name": "Иван", "email": "ivan@example.com", ... }
}
POST/api/v1/tickets

Создать тикет

Всегда создаёт новый тикет, даже если у контакта уже есть открытый. Контакт ищется по telegram_id, затем по email; если не найден — создаётся. В contact обязателен хотя бы один из них, имя можно передать в name или full_name (оно обновит имя найденного контакта).

  • contact — обязательно: telegram_id (число) и/или email, плюс name
  • message — текст первого сообщения; сохраняется как сообщение клиента
  • subject — тема; если не задана, берутся первые 120 символов message
  • priority — по умолчанию normal
  • tags — массив строк
POST /api/v1/tickets
{
  "subject": "Не работает оплата",
  "priority": "high",
  "tags": ["billing", "vip"],
  "contact": {
    "email": "ivan@example.com",
    "name": "Иван Петров"
  },
  "message": "Пытаюсь оплатить тариф, получаю ошибку 500."
}
curl
curl -X POST https://api.support.forestsnet.com/api/v1/tickets \
  -H "Authorization: Bearer sk_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "priority": "high",
    "contact": {"email": "ivan@example.com", "name": "Иван"},
    "message": "Не приходит код"
  }'

Ответ — 201 и объект тикета (как в списке, без полей истории) со статусом new. Ошибки: 400 без telegram_id и email, 422 при неизвестном priority, 403 TICKET_LIMIT_REACHED при исчерпанном месячном лимите тикетов, 403 для заблокированного контакта.

PATCH/api/v1/tickets/{ticket_id}

Изменить тикет

Меняет status, priority, tags (список заменяется целиком) и subject. Передавайте только нужные поля; null значение не меняет. Неизвестный status или priority — 422, тикет не меняется.

PATCH body
{ "priority": "urgent", "tags": ["vip", "p1"] }
PATCH записывает поля и, как изменение в дашборде, шлёт webhook ticket.updated (ticket_id, workspace_id). Он не ставит closed_at и не отправляет клиенту сообщение о закрытии. Смена статуса видна в /updates как ticket.status_change. Чтобы закрыть или переоткрыть тикет, используйте эндпоинты ниже.
POST/api/v1/tickets/{ticket_id}/close

Закрыть тикет

Ставит статус closed и closed_at (и resolved_at, если его ещё не было), фиксирует SLA решения, публикует в переписке системное сообщение о закрытии и отправляет его клиенту в его канал. Шлёт webhook ticket.closed. Уже закрытый тикет — 409 TICKET_ALREADY_CLOSED. Ответ — объект тикета.

Закрытие через API — действие вашей стороны, поэтому клиент читает, что обращение закрыла поддержка, без имени оператора: «🔒 Обращение закрыто оператором» или «🔒 Conversation closed» — на языке клиента (язык виджета, затем язык контакта, затем язык проекта по умолчанию). В тикетах из виджета используется текст «Тикет закрыт» из конструктора виджета, если он задан; если это сообщение там выключено, его нет. Если для канала включены оценки, клиенту приходит просьба оценить обращение.

POST/api/v1/tickets/{ticket_id}/reopen

Переоткрыть тикет

Работает только для тикетов в статусе closed или resolved, иначе — 422 TICKET_INVALID_STATUS_TRANSITION. Ставит статус open, очищает closed_at, resolved_at и first_response_at, перезапускает SLA. Клиент получает уведомление, если оно включено в настройках канала. Шлёт webhook ticket.reopened.

Ещё о тикетах: история обращений контакта, оценка тикета.

Была ли страница полезной?