Перейти к содержимому

Письмо

Обновлено 17 октября 2026

В подключённый ящик пришло письмо, или письмо ушло из ящика. Письмо приходит тем же уведомлением, что и сообщение мессенджера: текст — в textMessageData, вложения — файлами. Сверх этого в messageData есть emailMessageData — тема, цепочка, адресаты, папка.

Что typeWebhook Флаг настроек
новое письмо в отслеживаемых папках incomingMessageReceived incomingWebhook
своё письмо: «Отправленные», ответ из почтовой программы outgoingMessageReceived outgoingMessageWebhook
письмо, отправленное через API outgoingAPIMessageReceived outgoingAPIMessageWebhook
недоставка вашего письма outgoingMessageStatus, status: "failed" outgoingWebhook
отчёт о прочтении outgoingMessageStatus, status: "read" outgoingWebhook

Отслеживаемые папки — настройка emailFolders, по умолчанию «Входящие» и «Отправленные». См. Настройки ящика.

Пример

Мария написала на info@school.ru:

{
  "typeWebhook": "incomingMessageReceived",
  "instanceData": {"idInstance": 7100000001, "wid": "info@school.ru", "typeInstance": "email", "channel": "email"},
  "timestamp": 1791823658,
  "idMessage": "c62b698304229a98aad3",
  "eventId": "msg:maria@client.ru:c62b698304229a98aad3",
  "senderData": {
    "chatId": "maria@client.ru", "chatName": "Мария Иванова", "chatType": "user",
    "sender": "maria@client.ru", "senderName": "Мария Иванова", "senderType": "user",
    "senderContactName": "", "senderPhoneNumber": 0
  },
  "messageData": {
    "typeMessage": "textMessage",
    "textMessageData": {"textMessage": "Здравствуйте! Хочу записать сына на пробное занятие. Когда можно прийти?"},
    "emailMessageData": {
      "subject": "Запись на пробное",
      "messageId": "CAF1xq7d2Lm9VbT0@mail.gmail.com",
      "threadId": "c62b698304229a98aad3",
      "inReplyTo": null,
      "from": "maria@client.ru",
      "to": ["info@school.ru"],
      "cc": [],
      "replyTo": [],
      "auto": null,
      "folder": "inbox",
      "signature": "Мария Иванова\n+7 999 123-45-67",
      "quoteCut": false,
      "form": null,
      "truncated": false,
      "mailbox": "INBOX",
      "uid": 1532,
      "hasHtml": true
    }
  }
}

Поля

Поле Описание
idMessage id письма в канале: 20 шестнадцатеричных знаков от Message-ID, одинаковый во всех папках
senderData.chatId адрес собеседника в нижнем регистре — куда уйдёт ответ: Reply-To, если он не ваш, иначе отправитель
textMessageData.textMessage текст письма без цитаты прошлых писем и без подписи

Поля emailMessageData:

Поле Описание
subject тема
messageId заголовок Message-ID, без угловых скобок
threadId id первого письма цепочки — общий у всех писем переписки
inReplyTo Message-ID письма, на которое отвечают; null у первого письма
from, to, cc, replyTo адреса из заголовков, в нижнем регистре
auto вид письма, если это не письмо человека: auto_reply — автоответ, bulk — рассылка, service — уведомление сервиса; null — письмо человека или заявка
folder роль папки: inbox, sent, spam, drafts, trash, archive, all, other
mailbox, uid папка на сервере и номер письма в ней — по ним письмо адресуют методы почты
signature отрезанная подпись: имя, должность, телефоны
quoteCut true — цитата прошлых писем отрезана
form разобранная заявка с формы сайта — ниже; иначе null
truncated true — письмо больше предела, текст сокращён: целиком — в getEmail
hasHtml у письма есть html-версия

Остальные поля конверта — в статье Формат уведомления.

Ответ в цепочке

Письмо с заголовком In-Reply-To приходит как Ответ цитатой: stanzaId — idMessage письма, на которое ответили, participant пустой. Текст — без цитаты, quoteCut: true:

"messageData": {
  "typeMessage": "quotedMessage",
  "extendedTextMessageData": {"text": "Суббота подходит, спасибо!", "stanzaId": "b86fac5e3d96da636ca0", "participant": ""},
  "quotedMessage": {"stanzaId": "b86fac5e3d96da636ca0", "participant": ""},
  "emailMessageData": {
    "subject": "Re: Запись на пробное",
    "messageId": "CAF1xq7d2Lm9VbT1@mail.gmail.com",
    "threadId": "c62b698304229a98aad3",
    "inReplyTo": "20261013091500.4f2a@school.ru",
    "from": "maria@client.ru",
    "to": ["info@school.ru"],
    "cc": [],
    "replyTo": [],
    "auto": null,
    "folder": "inbox",
    "signature": "",
    "quoteCut": true,
    "form": null,
    "truncated": false,
    "mailbox": "INBOX",
    "uid": 1533,
    "hasHtml": true
  }
}

Вложения

Письмо с вложениями приходит как Файлы и альбомы: typeMessage — по первому вложению, остальные — в additionalFiles, у каждого своя ссылка. Текст письма — в caption, emailMessageData — рядом. Письмо целиком в формате .eml — getEmailRaw.

Заявка с формы сайта

Письмо совпало с правилом emailForms — в form разобранные поля. Собеседник — почта из формы; если её нет, Reply-To письма, а если нет и его — отдельный чат form:{idMessage}, ответить в который письмом нельзя:

"emailMessageData": {
  "subject": "Новая заявка с сайта",
  "messageId": "1791823900.77213@forms.school.ru",
  "threadId": "c04ef2a6ffdb12894548",
  "inReplyTo": null,
  "from": "forms@school.ru",
  "to": ["info@school.ru"],
  "cc": [],
  "replyTo": [],
  "auto": null,
  "folder": "inbox",
  "signature": "",
  "quoteCut": false,
  "form": {
    "rule": "site",
    "fields": {"name": "Мария", "phone": "+7 999 123-45-67", "message": "Хочу на пробное"},
    "missing": [],
    "complete": true,
    "phone": "+79991234567",
    "email": null,
    "name": "Мария"
  },
  "truncated": false,
  "mailbox": "INBOX",
  "uid": 1540,
  "hasHtml": false
}
Поле form Описание
rule имя правила из emailForms
fields заполненные поля формы: ключ — имя поля правила
missing обязательные поля, которых нет
complete true — все обязательные поля есть
phone телефон в виде +79991234567, если нашёлся
email, name почта и имя из формы

Здесь в форме нет почты, поэтому chatId — "form:c04ef2a6ffdb12894548": ответить можно по телефону в мессенджер. Правила форм — Заявки с форм сайта.

Свои письма

Письмо из «Отправленных» или ответ из почтовой программы — outgoingMessageReceived: chatId — первый получатель, sender — адрес ящика, folder: "sent".

У письма через API (sendEmail, replyEmail, sendMessage) — outgoingAPIMessageReceived, а emailMessageData короче:

"emailMessageData": {
  "subject": "Re: Запись на пробное",
  "messageId": "20261013091500.4f2a@school.ru",
  "threadId": "c62b698304229a98aad3",
  "inReplyTo": "CAF1xq7d2Lm9VbT0@mail.gmail.com",
  "to": ["maria@client.ru"],
  "cc": [],
  "bcc": [],
  "refused": {}
}

refused — адреса, которые почтовый сервер отверг сразу при отправке, с причиной.

Недоставка и прочтение

Отчёты почтовых серверов приходят не письмами, а статусами вашего письма — outgoingMessageStatus:

  • недоставка — status: "failed", причина из отчёта — в description, например "550 5.1.1 User unknown";
  • отчёт о прочтении — status: "read", если получатель его отправил.

Какие письма приходят

  • Все письма отслеживаемых папок: рассылки и уведомления сервисов помечены в auto, но не отсеяны. Не нужны — настройка emailSkipBulk: "yes".
  • С момента подключения ящика — или с даты since из connectMailbox, не больше 200 писем. Старые письма читайте getMessages и searchMessages.
  • После простоя Gratio дочитывает пропущенные письма, до 200. Больше — придёт syncGap.

Как обработать

Завести обращение в helpdesk: одна цепочка — одно обращение, рассылки — мимо:

def on_email(body: dict) -> None:
    if body["typeWebhook"] != "incomingMessageReceived" or body["instanceData"]["channel"] != "email":
        return
    data = body["messageData"]
    mail = data["emailMessageData"]
    if mail["auto"]:                              # автоответ, рассылка, уведомление сервиса
        return
    if data["typeMessage"] == "textMessage":
        text = data["textMessageData"]["textMessage"]
    elif "fileMessageData" in data:
        text = data["fileMessageData"]["caption"]
    else:
        text = data.get("extendedTextMessageData", {}).get("text", "")
    ticket = find_or_create_ticket(thread=mail["threadId"], subject=mail["subject"])
    add_ticket_message(ticket, author=body["senderData"]["chatId"], text=text, letter_id=body["idMessage"])
    if mail["form"]:
        attach_lead(ticket, mail["form"])

Что дальше

Была ли статья полезна?