Электронная почта - одна из последних “человекоориентированных” поверхностей, на которые многие системы всё ещё полагаются. Но если вы создаёте AI агента, LLM инструментарий или QA систему, рано или поздно вам понадобится программно открыть электронное письмо, извлечь только полезные артефакты (OTP, магическую ссылку, ID счёта, URL сброса пароля) и двигаться дальше.
Сложность в том, что email приходит в виде беспорядочного стека десятилетних стандартов: заголовки RFC 5322, многочастные тела MIME, странные кодировки и HTML, который никогда не был предназначен для парсинга тестами (или агентами). Это руководство проходит через то, что такое “сырой email” на самом деле, почему это сложно, и как надёжно преобразовать его в JSON формат, которому ваша автоматизация может доверять.
Что значит “открыть email” программно
Когда люди “открывают email”, почтовый клиент незаметно проделывает большую работу:
- Парсит формат сообщения (заголовки плюс тело)
- Декодирует transfer encodings (base64, quoted-printable)
- Выбирает тело для отображения (обычно text/plain или HTML)
- Распаковывает вложения
- Нормализует даты, адреса и кодировки символов
Программно вам нужно решить, что означает “открыть” для вашего рабочего процесса. Для автоматизации “открыть” обычно означает:
- Найти правильное сообщение детерминистично (без хрупкого поиска по почтовому ящику)
- Парсить и нормализовать его в стабильную схему
- Извлечь небольшой, проверяемый артефакт (OTP, ссылку, токен)
- Залогировать достаточно для отладки сбоев без утечки чувствительного контента
Хорошая ментальная модель: относиться к email как к ненадёжному входящему событию, а не как к документу.
Сырой email, форматы которые вы фактически получаете
Большинство систем в конечном итоге представляют email как сырое сообщение RFC 5322: блоб текста и байт, состоящий из заголовков и тела. Если вам нужны ссылки на стандарты, начните с RFC 5322 (формат сообщения) и семейство MIME типа RFC 2045 (основы MIME).
“Сырое” сообщение обычно включает:
-
Заголовки: пары ключ/значение типа
From,To,Subject,Date,Message-ID, плюс много других - Тело: иногда простой текст, часто HTML, часто многочастное с границами
- Вложения: представленные как MIME части, обычно закодированные в base64
MIME - вот почему “просто парсить тело” не работает
Если бы вы всегда видели только простые текстовые email, парсинг был бы лёгким. На практике:
- Многие сообщения являются
multipart/alternative(и text/plain и text/html) - Некоторые являются
multipart/mixed(тело плюс вложения) - Некоторые содержат вложенные multiparts
- Тела могут быть закодированы (quoted-printable, base64)
- Кодировки символов различаются (UTF-8, ISO-8859-1, и более)
Вот почему regex по HTML или разделение по пустым строкам быстро становится хрупким.
От сырого к JSON: конвейер нормализации, который работает в автоматизации
Надёжный конвейер “от сырого к JSON” имеет несколько чётких стадий. Это независимо от реализации: вы можете делать это с библиотекой в своём собственном сервисе, или потреблять JSON, произведённый inbox API.

Стадия 1: Парсинг структуры (заголовки, MIME дерево)
На этой стадии вы хотите:
- Безопасно парсить заголовки (обрабатывать сложенные заголовки, дублирования)
- Построить MIME дерево частей
- Идентифицировать кандидаты тел (text/plain, text/html)
- Идентифицировать вложения (имя файла, content-type, размер)
Стадия 2: Декодирование и нормализация
Нормализация - это то, откуда приходит большинство надёжности автоматизации:
- Декодировать transfer encodings (quoted-printable, base64)
- Нормализовать окончания строк
- Конвертировать текст в последовательное представление Unicode
- Парсить
Dateв ISO timestamp (но сохранять сырое значение для отладки) - Нормализовать поля адресов в структурированные объекты (имя, адрес)
Стадия 3: Выбор и очистка контента
Для автоматизации и агентов предпочитайте предсказуемый контент:
- Предпочитайте text/plain когда доступно
- Сохраняйте HTML, но относитесь к нему как к вторичному (хорошо для рендеринга, рискованно для парсинга)
- Удаляйте или игнорируйте опасные элементы (скрипты, странные редиректы)
Стадия 4: Извлечение артефактов автоматизации
Вместо “понимания всего email”, извлекайте то, что нужно вашему рабочему процессу:
- Верификационные ссылки (и финальный список разрешённых хостов)
- Кандидаты OTP (с жёсткими паттернами и проверками контекста)
- Ключевые идентификаторы (ID заказа, ID тикета)
Стадия 5: Эмиссия JSON со стабильными полями
Ваш JSON вывод должен поддерживать:
- Детерминистичное сопоставление (message_id, inbox_id, correlation IDs)
- Простые утверждения (subject содержит, from domain равен)
- Минимальное извлечение артефактов (otp, verification_url)
- Отлаживаемость (снимок сырых заголовков, timestamp получения)
Вот полезный способ думать о сопоставлении сырого email с JSON полями.
| Элемент сырого email | Как он выглядит | JSON который вам нужен для автоматизации | Почему это важно |
|---|---|---|---|
| Заголовок Message-ID | Message-ID: <abc@domain> |
message_id |
Дедупликация и идемпотентность |
| Заголовок Date | Date: Tue, 30 Jan... |
received_at (ISO), date_raw
|
Утверждения по времени, отладка задержек |
| From/To | RFC 5322 формы адресов |
from: {name, address}, to: [...]
|
Надёжные проверки отправителя |
| MIME части | границы multipart |
text, html, attachments[]
|
Избегание парсинга неправильной части |
| Transfer encoding | base64, quoted-printable | декодированные строки и байты | Предотвращение мусорного вывода |
| Ссылки в теле | HTML якоря, простые URL |
links[] (нормализованные) |
Более безопасная обработка магических ссылок |
Подводные камни, которые ломают наивные реализации “открытия email”
Даже зрелые команды обжигаются на одних и тех же крайних случаях email. Если вы строите программный путь “открытия email”, проектируйте для них заранее.
Дублирующие и сложенные заголовки
Заголовки могут законно повторяться, и они могут быть сложены через строки. Если вы наивно маппите заголовки в словарь, вы можете потерять данные или парсить неправильно.
Выбор неправильного тела
Множество систем случайно парсят:
- HTML секцию пикселя отслеживания вместо пользовательского контента
- Подвал вместо строки OTP
- Перенаправленное сообщение внутри email
Предпочитайте text/plain когда возможно, и будьте явными о том, как вы выбираете “основное” тело.
Кодировки и наборы символов
Если вы не последовательно декодируете transfer encoding и charset, вы увидите:
- Сломанный Unicode
- Пропущенную пунктуацию, что может сломать извлечение OTP
- Неправильные сравнения в тестах
Время не является единственным полем
Email timestamps беспорядочны. Заголовок Date предоставляется отправителем и не всегда заслуживает доверия. Timestamp вашей принимающей системы часто более полезен для задержек и таймаутов.
Парсинг HTML является границей безопасности
Если вы запускаете агентов против содержимого email, относитесь к HTML как к враждебному вводу. Безопасная стратегия:
- Извлекайте кандидатов ссылок, затем валидируйте их против списков разрешений
- Избегайте “кликов” по неизвестным URL в автоматизации
- Сохраняйте сырой контент для аудита, но не подавайте полный HTML в LLM по умолчанию
Для более глубокого руководства по надёжности парсинга идентификаторов типа Message-ID и связанных полей, у Mailhook есть отдельный пост, сосредоточенный на парсинге заголовков: Headers Email Guide: What to Parse for Reliability.
Прагматичный JSON контракт для LLM агентов
Агенты лучше всего работают с небольшими, структурированными входными данными. Вместо того чтобы давать LLM целый email (особенно HTML), предоставьте компактный JSON объект, который является:
- Детерминистичным
- Минимальным
- Отслеживаемым обратно к сырому сообщению
Пример “агент-безопасной” формы может выглядеть так:
{
"message_id": "<...>",
"received_at": "2026-02-01T20:12:33Z",
"from": {"address": "[email protected]", "name": "Example"},
"to": [{"address": "[email protected]", "name": null}],
"subject": "Your login code",
"text": "Your code is 123456",
"links": ["https://example.com/verify?token=..."],
"attachments": [{"filename": "invoice.pdf", "content_type": "application/pdf", "size": 48211}]
}
Затем вы можете добавить второй слой: крошечный объект извлечения, который ваши тесты или инструменты агента фактически потребляют (например { "otp": "123456" }). Это сохраняет ваш рабочий процесс простым и уменьшает воздействие LLM враждебному контенту.
Создать самому vs потреблять JSON из inbox API
У вас есть два широких подхода:
- Парсить сырые emails самостоятельно (через IMAP/POP, прямой SMTP ingest, или API провайдеров)
- Использовать сервис программируемого почтового ящика, который даёт вам структурированный JSON и детерминистичное извлечение
Вот таблица решений, которая стремится соответствовать реальным инженерным компромиссам.
| Подход | Лучше всего для | Распространённые болевые точки | Типичный результат |
|---|---|---|---|
| Скрапинг IMAP почтового ящика | Быстрые прототипы | Ненадёжные поиски, коллизии параллелизма, медленный polling | Ломается в CI и параллельных запусках |
| API провайдеров (Gmail/Graph) | Внутренние инструменты с аккаунтами | OAuth, квоты, долгоживущие идентичности | Работает, но операционно тяжёлый |
| Запуск собственного SMTP захвата | Локальные интеграционные тесты | Отличия доставляемости vs реальный email | Отлично локально, неполно в staging |
| API программируемого почтового ящика с JSON выводом | QA автоматизация, LLM агенты, верификационные потоки | Нужно интегрировать ещё одно API | Наиболее детерминистично для автоматизации |
Если ваша основная потребность “открыть email программно и получить JSON”, ключевое свойство - машиночитаемый вывод, который не требует скрапинга HTML.
Использование Mailhook для открытия email как JSON (webhook-first, polling fallback)
Mailhook построен вокруг программируемых одноразовых почтовых ящиков. Вместо создания полного email аккаунта, вы создаёте почтовый ящик через API, используете сгенерированный адрес в своём рабочем процессе, затем получаете сообщения как структурированный JSON.
Соответствующие возможности Mailhook (из описания продукта):
- Создание одноразовых почтовых ящиков через API
- Структурированный JSON вывод email
- RESTful API доступ
- Уведомления в реальном времени через webhook
- Polling API для emails
- Подписанные payload для безопасности
- Пакетная обработка email
- Общие домены и поддержка пользовательских доменов
Поскольку API эволюционируют, источником истины для endpoints и payloads является справочная реализация Mailhook. Убедитесь, что просмотрели llms.txt прежде чем подключать инструменты агентов или тесты:
Референсный поток (концептуальный)
Надёжный поток автоматизации выглядит так:
- Создать новый почтовый ящик для запуска (или сессии агента)
- Запустить тестируемую систему для отправки email на этот адрес
- Ждать доставки (предпочитать webhook, использовать polling как fallback)
- Потреблять JSON payload
- Извлекать только то, что вам нужно (OTP/ссылка)
Вот псевдокод, который иллюстрирует форму интеграции без предположения каких-либо специфических имён endpoints:
# Псевдокод: консультируйтесь https://mailhook.co/llms.txt для точных API полей и маршрутов.
inbox = mailhook.create_inbox(
webhook_url="https://your-service.example/mailhook/webhook"
)
email_address = inbox["address"]
inbox_id = inbox["inbox_id"]
app.trigger_signup(email=email_address)
# Webhook-first: ваш webhook обработчик сохраняет JSON сообщение с ключом по inbox_id.
# Polling fallback: ждать с timeout и backoff.
message = mailhook.wait_for_message(inbox_id=inbox_id, timeout_seconds=60)
otp = extract_otp(message["text"])
verify_url = extract_allowed_link(message.get("links", []))
assert otp is not None or verify_url is not None
Проверяйте подписи webhook
Если вы принимаете входящие webhooks, относитесь к ним как к любому другому внешнему запросу:
- Проверяйте подпись (Mailhook поддерживает подписанные payloads)
- Используйте идемпотентность для обработки повторов
- Сохраняйте только то, что вам нужно, столько, сколько вам нужно
Опять же, точная схема подписания и заголовки должны прийти из контракта в llms.txt.
Советы по дизайну, которые делают автоматизацию email скучной (в хорошем смысле)
Цель не “парсить email идеально”, а сделать вашу автоматизацию предсказуемой.
Предпочитайте изоляцию и корреляцию
Если несколько тестовых запусков или сессий агентов разделяют почтовый ящик, вы повторно вводите самую сложную проблему: выяснение какое сообщение принадлежит какому запуску. Изолированные почтовые ящики полностью избегают поиска по почтовому ящику.
Утверждайте намерение, а не презентацию
HTML постоянно меняется. Ваши утверждения должны нацеливаться на стабильные свойства:
- Домен отправителя
- Намерение субъекта
- Присутствие единственного OTP
- Верификационная ссылка, хост которой находится в списке разрешений
Сохраняйте сырое сообщение доступным для отладки
Когда что-то ломается, вы хотите знать:
- Пришло ли сообщение?
- Какие заголовки у него были?
- Парсили ли вы правильную MIME часть?
Вот где “сырое плюс нормализованный JSON” полезно. Автоматизация работает на нормализованных полях, пока инженеры отлаживают с сырым контекстом.
К чему это вас приводит
Чтобы программно открыть email в 2026, у вас есть две реалистичные опции:
- Стать экспертом парсинга email (RFC 5322, крайние случаи MIME, кодировочные причуды, ловушки безопасности)
- Использовать абстракцию почтового ящика, которая уже делает нормализацию и даёт вам JSON, которому ваши тесты и агенты могут доверять
Если ваша основная потребность - рабочие процессы агентов и надёжность QA, выигрышная стратегия обычно: относиться к email как к потоку событий, изолировать почтовые ящики на запуск, и потреблять структурированный JSON.
Если вы хотите реализовать это с Mailhook, начните с контракта в Mailhook llms.txt и проектируйте ваши инструменты вокруг детерминистичных ожиданий (webhook-first, polling fallback) и минимального извлечения артефактов.