Прямой ответ
Транзакционное письмо отправляется по последствию действия пользователя — подтверждение заказа, сброс пароля, уведомление об оплате. Отличие от маркетингового письма определяется требованием к своевременности и репутацией отправителя.
| Признак | Транзакционное | Маркетинговое |
|---|---|---|
| Повод | Действие пользователя | Инициатива сервиса |
| Ожидание доставки | Минуты | Часы либо дни |
| Влияние репутации | Критично | Высокое |
| Отказ от получения | Не применяется | Обязателен |
| Объём | Низкий, привязан к событиям | Массовый |
Смешение двух типов в одном потоке отправки приводит к тому, что массовая рассылка снижает репутацию отправителя, и транзакционные письма попадают в спам. Разделение выполняется отдельным доменом либо поддоменом отправки.
Отправка через SMTP с фиксацией состояния:
import smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg["From"] = "Нутмаркет <noreply@mail.noutmarket.example>"
msg["To"] = user_email
msg["Subject"] = "Заказ 1001 подтверждён"
msg["Message-ID"] = f"<order-1001-{notification_id}@mail.noutmarket.example>"
msg.set_content(text_body)
msg.add_alternative(html_body, subtype="html")
with smtplib.SMTP(SMTP_HOST, 587, timeout=15) as s:
s.starttls()
s.login(SMTP_USER, SMTP_PASSWORD)
s.send_message(msg)
Идентификатор сообщения уникален, поэтому повторная отправка распознаётся принимающей стороной как дубль. О настройке локальной отправки — в статье про SMTP в разработке.
Состояние доставки фиксируется отдельно от факта передачи провайдеру, поскольку принятие письма не означает доставку в ящик.
Что определяет доставку транзакционного письма
Передача письма провайдеру — первый шаг, а не результат. Четыре фактора влияют на фактическое получение.
Репутация отправляющего домена. Домен, с которого отправляется массовая рассылка с жалобами, снижает доставляемость всех писем, включая транзакционные. Разделение доменов устраняет влияние.
Аутентификация отправителя. Записи SPF, DKIM и DMARC подтверждают право отправлять письма от имени домена; о настройке — в статье про доставляемость писем.
Содержание письма. Ссылки на домены, отличные от домена отправителя, и вложения повышают вероятность фильтрации. Транзакционное письмо содержит минимум ссылок.
Адрес получателя. Несуществующий адрес приводит к постоянному отказу, а повторные отправки на него снижают репутацию.
Практическое следствие: отказ доставки обрабатывается по типу. Временный отказ означает повтор через интервал, постоянный — прекращение попыток и фиксацию адреса как недействительного.
UPDATE notifications
SET status = CASE WHEN $2 = 'permanent' THEN 'failed' ELSE 'queued' END,
attempts = attempts + 1
WHERE id = $1 AND status = 'sending';
Отдельный вопрос — повторная отправка. Пользователь запрашивает повторное письмо, поэтому запрос выполняется с проверкой предыдущей отправки: повтор в пределах короткого интервала возвращает существующее письмо, а не создаёт новое.
Уведомление о недоставке приходит асинхронно и обрабатывается отдельно; о разделении записи и отправки — в статье про архитектуру уведомлений.
Отсрочка отправки и группировка уведомлений
Не каждое транзакционное письмо требует немедленной отправки. Три сценария допускают отсрочку без потери ценности.
Серия событий за короткий интервал. Пользователь выполняет несколько действий подряд, и каждое порождает письмо. Группировка в одно сообщение снижает число писем и воспринимается лучше, чем поток отдельных уведомлений.
Отложенное подтверждение. Письмо отправляется после завершения асинхронной операции, поэтому немедленная отправка привела бы к сообщению о незавершённом действии.
Учёт времени получателя. Сообщение, не требующее немедленной реакции, отправляется в приемлемое время по местному часовому поясу получателя.
Практическое следствие: отсрочка задаётся временем отправки в записи уведомления, а обработчик выбирает записи, у которых время наступило. Немедленная отправка остаётся поведением по умолчанию для событий, где своевременность критична.
Отдельный вопрос — группировка. Объединение нескольких событий в одно письмо требует накопления записей в течение интервала и отмены отправки при наступлении завершающего события. Сложность группировки оправдана при высоком числе событий на пользователя, а при редких событиях приводит к лишней задержке.
Проверка своевременности выполняется по возрасту записи: превышение заданного интервала между созданием и отправкой указывает на сбой обработчика либо на накопление очереди.
Практика: организация транзакционной отправки
1. Используйте отдельный домен для транзакционных писем. Массовая рассылка не влияет на репутацию домена, с которого отправляются подтверждения заказов.
2. Настройте аутентификацию отправителя. Записи SPF, DKIM и DMARC обязательны, поскольку письмо без аутентификации фильтруется.
3. Фиксируйте уникальный идентификатор сообщения. Повторная отправка распознаётся как дубль, а разбор обращения выполняется по идентификатору.
4. Различайте временный и постоянный отказ. Повтор выполняется только при временном отказе, постоянный прекращает попытки.
5. Храните состояние доставки. Принятие письма провайдером не означает доставку, поэтому состояние уточняется по уведомлению о недоставке.
6. Ограничивайте частоту повторов по запросу пользователя. Несколько запросов подряд не должны создавать несколько писем.
7. Проверяйте адрес перед отправкой. Отправка на заведомо недействительный адрес снижает репутацию отправителя.
Рекомендации:
- Не отправляйте вложения в транзакционных письмах. Вложение повышает вероятность фильтрации, поэтому документ передаётся ссылкой.
- Разделяйте текст и HTML-версию. Письмо только в HTML фильтруется чаще, а текстовая версия обеспечивает читаемость.
- Учитывайте часовые пояса при оценке своевременности. Время отправки хранится с отметкой времени, иначе оценка задержки некорректна.
- Мониторьте долю недоставленных писем. Рост доли указывает на проблему с репутацией либо с настройкой аутентификации.
- Документируйте состав транзакционных писем. Перечень событий, требующих письма, фиксируется, поскольку пропуск события обнаруживается пользователем.
Практический сценарий: письма о сбросе пароля попадают в спам
Контекст: пользователи сообщали, что письма для восстановления доступа не приходят либо оказываются в папке спама.
- Диагностика. Письма отправлялись с того же домена, что и еженедельная маркетинговая рассылка. Доля жалоб на рассылку выросла, и письма с домена начали фильтроваться, включая транзакционные.
- Причина. Reputation домена определяется всей исходящей почтой, поэтому массовая рассылка с жалобами снизила доставляемость транзакционных писем. Разделение доменов отсутствовало.
- Действия. (а) Выделили отдельный поддомен для транзакционных писем и настроили для него записи SPF, DKIM и DMARC; (б) перенесли отправку подтверждений и сброса пароля на новый поддомен; (в) добавили текстовую версию письма и сократили число ссылок до одной; (г) внедрили обработку отказов с разделением временных и постоянных; (д) добавили метрику доли недоставленных писем по каждому поддомену.
- Результат. Транзакционные письма доставляются независимо от репутации маркетингового домена, а доля недоставленных отслеживается отдельно.
Вывод: репутация отправляющего домена общая для всей исходящей почты, поэтому смешение транзакционных и маркетинговых писем снижает доставляемость первых. Разделение доменов с собственной аутентификацией устраняет влияние массовой рассылки.
FAQ
Нужно ли разделять домены для транзакционных и маркетинговых писем? Да. Репутация домена общая, поэтому жалобы на рассылку снижают доставляемость транзакционных писем.
Что означает принятие письма провайдером? Письмо передано и поставлено в очередь отправки. Доставка в ящик получателя выполняется позже и может завершиться отказом.
Как обрабатывать постоянный отказ доставки? Прекратить попытки и зафиксировать адрес как недействительный. Повторные отправки на недействительный адрес снижают репутацию.
Можно ли отправлять транзакционное письмо без аутентификации домена? Технически да, но письмо фильтруется чаще, поэтому записи SPF, DKIM и DMARC обязательны.
Источники: RFC 5321 (SMTP), Python smtplib, PostgreSQL explicit locking — ссылки в шапке.