[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-c4bc8734-62c8-4a53-9bc1-009cc0d18aac":3},{"id":4,"body":5,"uuid":6,"created_at":7,"updated_at":8,"brand":9,"header":10,"short_body":11,"image":12,"published":13,"published_at":14,"tags":15},33,"\u003Cp>Вопрос «\u003Cstrong>SMTP или API\u003C\u002Fstrong>» звучит почти в каждом подключении, и почти всегда за ним стоит другой вопрос: сколько кода придётся написать сейчас и о чём придётся жалеть через полгода. В FMailer доступны три способа отправки — SMTP-релей, REST API и официальный Python SDK, — и все три работают через один и тот же токен домена, попадают в один и тот же журнал и порождают одни и те же вебхуки.\u003C\u002Fp>\n\u003Cp>Разница не в доставке. Письмо, отправленное по SMTP, и письмо, отправленное POST-запросом, подписываются одним и тем же ключом DKIM вашего домена, уходят через одну очередь и получают один и тот же ответ принимающего сервера. Разница в том, что вы можете сказать о письме при отправке и что узнаете о нём в ответе.\u003C\u002Fp>\n\u003Cp>Ниже — короткий ответ для тех, кто выбирает прямо сейчас, а затем разбор каждого способа: что он умеет, чего не умеет, как выглядит код и в каких проектах он оказывается правильным. В конце — сравнительная таблица, частые ошибки и ответы на типичные вопросы.\u003C\u002Fp>\n\u003Ch2>Короткий ответ\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>SMTP\u003C\u002Fstrong> — если приложение уже отправляет почту. Меняются хост, логин и пароль в конфиге, код не трогается вообще. Самый быстрый переезд: обычно час вместе с DNS.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>REST API\u003C\u002Fstrong> — если вы пишете интеграцию с нуля или вам нужно то, чего в SMTP нет: серверные шаблоны, \u003Ccode>idempotency_key\u003C\u002Fcode>, метки, пакетная отправка, явный флаг рассылки, \u003Ccode>message_id\u003C\u002Fcode> в ответе, отслеживание кликов.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>SDK\u003C\u002Fstrong> — тот же REST API, но на Python и в две строки, с асинхронной отправкой через пул потоков. Разумный старт для Django- и FastAPI-бэкендов, где не хочется собирать HTTP-запрос руками.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Можно пользоваться всеми тремя одновременно: способ отправки записывается у каждого письма отдельным полем, статистика и вебхуки от этого не меняются.\u003C\u002Fp>\n\u003Ch2>Один токен на все три способа\u003C\u002Fh2>\n\u003Cp>Начать стоит с того, что объединяет все варианты. В FMailer учётные данные выдаются \u003Cstrong>на домен\u003C\u002Fstrong>, а не на аккаунт: у токена есть логин и пароль, и это ровно те же логин и пароль, которые:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>подставляются в SMTP-клиент как имя пользователя и пароль;\u003C\u002Fli>\n\u003Cli>уходят в объекте \u003Ccode>auth\u003C\u002Fcode> в теле POST-запроса к REST API;\u003C\u002Fli>\n\u003Cli>передаются как HTTP Basic при чтении отправленного через \u003Ccode>GET\u003C\u002Fcode>-методы.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Отдельный токен на каждый домен — это не формальность. Разработка, продакшен и маркетинговый домен разводятся разными учётными данными, и утечка одного из них не открывает остальные. Токен при этом открывает только \u003Ccode>\u002Fexternal\u002F\u003C\u002Fcode> — это не ключ от аккаунта: домены, биллинг и команда ему недоступны.\u003C\u002Fp>\n\u003Cp>Ещё одна общая вещь: \u003Cstrong>отправитель проверяется по подключённому домену\u003C\u002Fstrong>. Адрес в \u003Ccode>From\u003C\u002Fcode> должен принадлежать домену, который вы подтвердили, — иначе письмо будет отклонено, каким бы способом вы его ни отправляли. Это защита от подделки отправителя, и она одинакова для SMTP и API.\u003C\u002Fp>\n\u003Cp>И третья: DNS не нужен, чтобы увидеть первое письмо. У каждого аккаунта есть \u003Cstrong>песочница\u003C\u002Fstrong> — учётные данные на общем домене с настоящим DKIM и настоящей доставкой; отправлять из неё можно только на адрес своего аккаунта. Интеграцию можно собрать и проверить, пока кто-то ещё ищет доступ к DNS-панели.\u003C\u002Fp>\n\u003Ch2>SMTP-релей: переезд без единой строки кода\u003C\u002Fh2>\n\u003Cp>SMTP — протокол 1982 года, и это его главное достоинство: его умеет всё. Django, Laravel, Rails, Spring Boot, ASP.NET, WordPress, 1С-Битрикс, самописный скрипт на PHP, который никто не открывал четыре года, — везде есть форма с полями «сервер», «порт», «логин», «пароль».\u003C\u002Fp>\n\u003Ch3>Параметры подключения\u003C\u002Fh3>\n\u003Cpre>\u003Ccode>host:  smtp.fmailer.ru\nport:  587, 8587   — STARTTLS\n       465, 8465   — SSL\u002FTLS\n       25,  8025   — без шифрования\nlogin: логин токена домена\npass:  пароль токена домена\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Дублирующие порты (8587, 8465, 8025) нужны, когда хостинг-провайдер блокирует стандартные — это встречается чаще, чем хотелось бы. Порты без шифрования оставлены для окружений, где TLS невозможен; в продакшене их использовать не надо: по ним в открытом виде уезжает пароль токена.\u003C\u002Fp>\n\u003Ch3>Как это выглядит в коде\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-python\">import smtplib\nfrom email.message import EmailMessage\n\nmsg = EmailMessage()\nmsg[&quot;From&quot;] = &quot;Магазин &lt;noreply@mail.example.ru&gt;&quot;\nmsg[&quot;To&quot;] = &quot;client@example.ru&quot;\nmsg[&quot;Subject&quot;] = &quot;Подтверждение заказа №4417&quot;\nmsg.set_content(&quot;Заказ принят. Доставка — завтра до 18:00.&quot;)\n\nwith smtplib.SMTP(&quot;smtp.fmailer.ru&quot;, 587) as smtp:\n    smtp.starttls()\n    smtp.login(&quot;&lt;логин токена&gt;&quot;, &quot;&lt;пароль токена&gt;&quot;)\n    smtp.send_message(msg)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>В реальном проекте этого кода обычно нет вовсе — есть четыре строки в настройках. Для Django, Laravel, Ruby on Rails, Express.js, Spring Boot, ASP.NET, NestJS, Next.js, FastAPI, Flask, Symfony и популярных CMS готовые конфигурации лежат в документации отдельными страницами.\u003C\u002Fp>\n\u003Ch3>Что стоит знать про SMTP заранее\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>Несколько получателей — это несколько писем.\u003C\u002Fstrong> Письмо, адресованное нескольким людям, превращается в системе в отдельное письмо на каждого адресата, и для \u003Ccode>To\u003C\u002Fcode>, и для \u003Ccode>Cc\u003C\u002Fcode>, и для \u003Ccode>Bcc\u003C\u002Fcode>. У каждого свой \u003Ccode>Message-ID\u003C\u002Fcode>, свой вебхук доставки и своё место в лимитах тарифа — именно поэтому отказ, открытие или отписку можно отнести к конкретному человеку. Потолок — 100 получателей на письмо; следующему адресу сервер ответит \u003Ccode>452 4.5.3 Too many recipients\u003C\u002Fcode>, и это просьба отправить остальных отдельной транзакцией, а не отказ.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Заголовок \u003Ccode>Bcc\u003C\u002Fcode> удаляется при приёме.\u003C\u002Fstrong> Так и положено серверу отправки (RFC 5321 §7.2): скрытые получатели уже есть в конверте, а уцелевший \u003Ccode>Bcc\u003C\u002Fcode> сообщил бы каждому читателю, кто ещё был в письме. Большинство почтовых библиотек убирают его сами — поэтому те, что не убирают, и остаются незамеченными.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Адрес конверта по умолчанию совпадает с вашим \u003Ccode>From\u003C\u002Fcode>.\u003C\u002Fstrong> Это необычно и сделано намеренно: SPF проверяется по вашему домену и выравнивается с \u003Ccode>From\u003C\u002Fcode> для DMARC. Обратная сторона — отложенные отказы приходят на адрес из \u003Ccode>From\u003C\u002Fcode>, а если это \u003Ccode>noreply@\u003C\u002Fcode>, их никто не прочитает. Для такого случая домену задаётся \u003Cstrong>return path\u003C\u002Fstrong> — адрес, который вы действительно читаете, обязательно на самом домене, а не на поддомене.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Тип письма определяется эвристикой.\u003C\u002Fstrong> По SMTP невозможно сказать «это рассылка» явно — нет такого поля в протоколе. FMailer классифицирует письмо сам и на массовом проставляет заголовки отписки в один клик, но эвристика есть эвристика: она может ошибиться в обе стороны. Если вы отправляете маркетинговые письма, это первый аргумент в пользу API, где флаг \u003Ccode>mass_mail\u003C\u002Fcode> ставится руками.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Отслеживание кликов по SMTP не работает.\u003C\u002Fstrong> Переписывание ссылок через ваш хост \u003Ccode>stats.&lt;домен&gt;\u003C\u002Fcode> доступно только для писем, отправленных через API: по SMTP приходит уже готовый MIME, части которого могут быть закодированы, и правка адресов внутри них — верный способ испортить тело письма. Пиксель открытий при этом работает в обоих случаях.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Ответ сервера — это код SMTP, а не JSON.\u003C\u002Fstrong> Вы узнаете, что письмо принято, но \u003Ccode>message_id\u003C\u002Fcode>, идемпотентность и машинный разбор результата остаются за бортом.\u003C\u002Fp>\n\u003Ch3>Когда SMTP — правильный выбор\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>Приложение уже отправляет почту, и надо просто сменить провайдера.\u003C\u002Fli>\n\u003Cli>Письма отправляет коробочный продукт или CMS, куда своего кода не добавить.\u003C\u002Fli>\n\u003Cli>Стек не Python и не JavaScript, а писать HTTP-клиент ради писем не хочется.\u003C\u002Fli>\n\u003Cli>Нужен самый быстрый способ увидеть первое доставленное письмо.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Проверить, что соединение вообще устанавливается, можно до всякого кода:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">openssl s_client -starttls smtp -crlf -connect smtp.fmailer.ru:587\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Если соединение не устанавливается — почти всегда дело в том, что исходящий SMTP закрыт на стороне вашего хостинга. Это, кстати, второй по частоте аргумент в пользу API: HTTPS наружу открыт всегда.\u003C\u002Fp>\n\u003Ch2>REST API: когда письму нужно больше, чем текст и адрес\u003C\u002Fh2>\n\u003Cp>REST API живёт на \u003Ccode>https:\u002F\u002Fapi.fmailer.ru\u003C\u002Fcode>, все запросы — \u003Ccode>POST\u003C\u002Fcode> с телом в JSON, логин и пароль токена передаются в объекте \u003Ccode>auth\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Ch3>Отправка готового письма\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\">curl -X POST https:\u002F\u002Fapi.fmailer.ru\u002Fexternal\u002Fsend_email_simple\u002F \\\n  -H &quot;Content-Type: application\u002Fjson&quot; \\\n  -d '{\n    &quot;recipient&quot;: &quot;client@example.ru&quot;,\n    &quot;subject&quot;: &quot;Подтверждение заказа №4417&quot;,\n    &quot;body&quot;: &quot;&lt;p&gt;Заказ принят. Доставка — завтра до 18:00.&lt;\u002Fp&gt;&quot;,\n    &quot;text&quot;: &quot;Заказ принят. Доставка — завтра до 18:00.&quot;,\n    &quot;sender&quot;: &quot;Магазин &lt;noreply@mail.example.ru&gt;&quot;,\n    &quot;idempotency_key&quot;: &quot;order-4417&quot;,\n    &quot;tags&quot;: {&quot;stream&quot;: &quot;order&quot;, &quot;lang&quot;: &quot;ru&quot;},\n    &quot;auth&quot;: {&quot;username&quot;: &quot;&lt;логин токена&gt;&quot;, &quot;password&quot;: &quot;&lt;пароль токена&gt;&quot;}\n  }'\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Ответ — не «принято», а перечисление того, что именно принято:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n    &quot;ok&quot;: true,\n    &quot;emails&quot;: [\n        {\n            &quot;ok&quot;: true,\n            &quot;message_id&quot;: &quot;&lt;uuid@mail.example.ru&gt;&quot;,\n            &quot;uuid&quot;: &quot;uuid&quot;,\n            &quot;recipient&quot;: &quot;client@example.ru&quot;,\n            &quot;kind&quot;: &quot;to&quot;,\n            &quot;idempotency_key&quot;: &quot;order-4417&quot;,\n            &quot;replayed&quot;: false\n        }\n    ]\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>message_id\u003C\u002Fcode> — это тот же идентификатор, который придёт потом в вебхуке доставки. Сохранив его рядом с заказом, вы связываете событие с сущностью в своей базе, не запрашивая ничего дополнительно. По SMTP такой связи в ответе нет.\u003C\u002Fp>\n\u003Ch3>Что API умеет, а SMTP — нет\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>\u003Ccode>idempotency_key\u003C\u002Fcode>.\u003C\u002Fstrong> Ваш собственный идентификатор события — номер заказа, идентификатор попытки входа. Повторный запрос с тем же ключом не создаёт второе письмо, поэтому ретрай после таймаута безопасен. В ответе такое письмо приходит с \u003Ccode>replayed: true\u003C\u002Fcode> — без второй отправки и без повторного расхода лимитов. Это самое ценное, что даёт API, и первое, что стоит поставить.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Серверные шаблоны.\u003C\u002Fstrong> Вместо темы и тела передаётся \u003Ccode>tpl\u003C\u002Fcode> — slug шаблона из панели, \u003Ccode>lang\u003C\u002Fcode> и \u003Ccode>params\u003C\u002Fcode> с данными для подстановки:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n    &quot;recipient&quot;: &quot;client@example.ru&quot;,\n    &quot;tpl&quot;: &quot;order-confirmed&quot;,\n    &quot;lang&quot;: &quot;ru&quot;,\n    &quot;sender&quot;: &quot;Магазин &lt;noreply@mail.example.ru&gt;&quot;,\n    &quot;idempotency_key&quot;: &quot;order-4417&quot;,\n    &quot;params&quot;: {&quot;order&quot;: &quot;4417&quot;, &quot;delivery&quot;: &quot;завтра до 18:00&quot;},\n    &quot;auth&quot;: {&quot;username&quot;: &quot;&lt;логин токена&gt;&quot;, &quot;password&quot;: &quot;&lt;пароль токена&gt;&quot;}\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Вёрстка письма перестаёт быть частью релиза приложения: у одного шаблона может быть русская и английская версия, а оформление настраивается под ваш бренд.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Явный флаг рассылки.\u003C\u002Fstrong> \u003Ccode>mass_mail: true\u003C\u002Fcode> добавляет заголовки \u003Ccode>List-Unsubscribe\u003C\u002Fcode> и \u003Ccode>List-Unsubscribe-Post\u003C\u002Fcode> — ту самую отписку в одно нажатие, которую Gmail, Yahoo и Apple ожидают от массовых отправителей. Флаг работает в одну сторону: \u003Ccode>true\u003C\u002Fcode> помечает письмо рассылкой, \u003Ccode>false\u003C\u002Fcode> просто оставляет решение эвристике.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Метки.\u003C\u002Fstrong> \u003Ccode>tags\u003C\u002Fcode> — объект вида \u003Ccode>{\"stream\": \"password-reset\", \"lang\": \"en\"}\u003C\u002Fcode>. В письмо они не попадают и на доставку не влияют; нужны они для чтения почты обратно: фильтр в журнале, поле в вебхуке, разбивка в аналитике. Именно метки отвечают на вопрос, на который не отвечает ничто другое: к какому потоку относится письмо. До 10 меток на письмо.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Вложения.\u003C\u002Fstrong> Массив \u003Ccode>attachments\u003C\u002Fcode> с именем файла и байтами в base64: до 20 файлов, по 10 МБ каждый и 10 МБ суммарно, всё тело запроса — не больше 16 МБ. Исполняемые типы отклоняются по расширению и заявленному типу — тот же список, который применяет к вашей почте Gmail на приёме.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Пакетная отправка.\u003C\u002Fstrong> \u003Ccode>POST \u002Fexternal\u002Fsend_email_batch\u002F\u003C\u002Fcode> принимает до 100 писем в одном запросе: \u003Ccode>auth\u003C\u002Fcode> читается один раз на всю пачку, обычные и шаблонные письма можно смешивать. Ошибка в одном элементе отклоняет всю пачку с указанием индекса — это лучше, чем наполовину ушедшая рассылка. Ответ плоский и упорядоченный, по элементу на письмо.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Темы подписки.\u003C\u002Fstrong> Поле \u003Ccode>topic\u003C\u002Fcode> привязывает письмо к теме («Новости продукта», «Статусы заказов»), и получатель, отказавшийся от темы, продолжает получать всё остальное, включая сброс пароля. Неизвестный slug — это ошибка 400, а не тихое игнорирование.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Чтение отправленного.\u003C\u002Fstrong> Тот же токен, которым вы отправляете, умеет читать — через HTTP Basic:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">curl https:\u002F\u002Fapi.fmailer.ru\u002Fexternal\u002Femails\u002F \\\n  -u &quot;$TOKEN_USERNAME:$TOKEN_PASSWORD&quot;\n\ncurl &quot;https:\u002F\u002Fapi.fmailer.ru\u002Fexternal\u002Fstats\u002F?start=2026-08-01&amp;end=2026-08-28&quot; \\\n  -u &quot;$TOKEN_USERNAME:$TOKEN_PASSWORD&quot;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Пять \u003Ccode>GET\u003C\u002Fcode>-методов: список писем, одно письмо вместе с ушедшим HTML, его события, весь поток событий домена и итоги за период. Все ограничены доменом токена. Методы событий — платная возможность тарифа.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Понятные отказы.\u003C\u002Fstrong> Превышение часового или месячного объёма — это \u003Ccode>HTTP 400\u003C\u002Fcode> с текстом \u003Ccode>Hourly limit exceeded\u003C\u002Fcode> или \u003Ccode>Monthly limit exceeded\u003C\u002Fcode>, и такой запрос имеет смысл повторить с выдержкой. Остановленная отправка с домена — \u003Ccode>HTTP 403\u003C\u002Fcode> с \u003Ccode>code: domain_banned\u003C\u002Fcode>; здесь повторы не помогут. Разница между «подожди» и «не пытайся» видна в коде, а не в логе.\u003C\u002Fp>\n\u003Ch3>Когда REST API — правильный выбор\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>Новый бэкенд, где отправка пишется с нуля.\u003C\u002Fli>\n\u003Cli>Письма отправляются в ответ на события, которые могут повториться при ретрае, — оплата, регистрация, код подтверждения.\u003C\u002Fli>\n\u003Cli>Нужны рассылки с корректной отпиской в один клик.\u003C\u002Fli>\n\u003Cli>Вёрстку письма меняет маркетинг, а не разработчик.\u003C\u002Fli>\n\u003Cli>Нужна аналитика по потокам писем или отслеживание кликов.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>SDK: тот же API, но короче\u003C\u002Fh2>\n\u003Cp>Для Python есть официальный SDK — обёртка над теми же двумя методами отправки. Он опубликован в PyPI под именем \u003Ccode>postwing\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">pip install postwing\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre>\u003Ccode class=\"language-python\">from postwing import PostwingSdk\n\nsdk = PostwingSdk(\n    username=&quot;&lt;логин токена&gt;&quot;,\n    password=&quot;&lt;пароль токена&gt;&quot;,\n)\n# Для fmailer.ru базовый адрес указывается явно:\nsdk.SERVER_URL = &quot;https:\u002F\u002Fapi.fmailer.ru&quot;\n\nsdk.send_simple(\n    recipient=&quot;client@example.ru&quot;,\n    sender=&quot;Магазин &lt;noreply@mail.example.ru&gt;&quot;,\n    subject=&quot;Подтверждение заказа №4417&quot;,\n    body=&quot;&lt;p&gt;Заказ принят. Доставка — завтра до 18:00.&lt;\u002Fp&gt;&quot;,\n    text=&quot;Заказ принят. Доставка — завтра до 18:00.&quot;,\n    idempotency_key=&quot;order-4417&quot;,\n)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Отправка по шаблону — метод \u003Ccode>send\u003C\u002Fcode>:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-python\">sdk.send(\n    tpl=&quot;order-confirmed&quot;,\n    recipient=&quot;client@example.ru&quot;,\n    sender=&quot;Магазин &lt;noreply@mail.example.ru&gt;&quot;,\n    lang=&quot;ru&quot;,\n    params={&quot;order&quot;: &quot;4417&quot;, &quot;delivery&quot;: &quot;завтра до 18:00&quot;},\n    idempotency_key=&quot;order-4417&quot;,\n)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>Асинхронная отправка\u003C\u002Fh3>\n\u003Cp>У обоих методов есть \u003Ccode>_async\u003C\u002Fcode>-версии, работающие через пул потоков. Они возвращают \u003Ccode>Future\u003C\u002Fcode>, поэтому отправку можно сделать неблокирующей, а результат — забрать позже или обработать колбэком:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-python\">def on_complete(success, error):\n    if error:\n        logger.error(&quot;Письмо не ушло: %s&quot;, error)\n\nsdk.send_simple_async(\n    recipient=&quot;client@example.ru&quot;,\n    sender=&quot;Магазин &lt;noreply@mail.example.ru&gt;&quot;,\n    subject=&quot;Подтверждение заказа №4417&quot;,\n    body=&quot;&lt;p&gt;Заказ принят.&lt;\u002Fp&gt;&quot;,\n    idempotency_key=&quot;order-4417&quot;,\n    callback=on_complete,\n)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Размер пула задаётся параметром \u003Ccode>max_workers\u003C\u002Fcode>, а \u003Ccode>fail_silently=True\u003C\u002Fcode> превращает исключение в \u003Ccode>False\u003C\u002Fcode> — для случаев, когда неотправленное письмо не должен уронить запрос пользователя. Логирование настраивается уровнем: на \u003Ccode>DEBUG\u003C\u002Fcode> в лог попадают URL, полезная нагрузка (с замаскированным \u003Ccode>auth\u003C\u002Fcode>) и ответ сервера — этого обычно достаточно, чтобы понять, почему письмо не ушло, не поднимая прокси.\u003C\u002Fp>\n\u003Ch3>Границы SDK\u003C\u002Fh3>\n\u003Cp>SDK намеренно небольшой, и это стоит знать заранее.\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Он покрывает \u003Cstrong>два метода отправки\u003C\u002Fstrong> — простое письмо и шаблонное. Пакетной отправки, вложений, меток и \u003Ccode>GET\u003C\u002Fcode>-методов чтения в нём нет: для них нужен обычный HTTP-запрос.\u003C\u002Fli>\n\u003Cli>Методы возвращают \u003Ccode>True\u003C\u002Fcode> или бросают \u003Ccode>PostwingSdkException\u003C\u002Fcode>. \u003Cstrong>\u003Ccode>message_id\u003C\u002Fcode> они не возвращают\u003C\u002Fstrong> — если вы связываете письмо с сущностью в своей базе, обращайтесь к REST API напрямую.\u003C\u002Fli>\n\u003Cli>Таймаут запроса фиксированный, пятисекундный.\u003C\u002Fli>\n\u003Cli>Асинхронные методы — это пул потоков внутри вашего процесса, а не очередь. Перезапуск приложения теряет то, что не успело уйти.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Иначе говоря, SDK хорош там, где письмо простое и его надо отправить в две строки. Как только письму нужны метки, вложения или пачка — вы всё равно окажетесь на уровне HTTP.\u003C\u002Fp>\n\u003Ch2>Сравнение возможностей\u003C\u002Fh2>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Возможность\u003C\u002Fth>\n\u003Cth>SMTP\u003C\u002Fth>\n\u003Cth>REST API\u003C\u002Fth>\n\u003Cth>Python SDK\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>Подключение без изменений в коде\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>нет\u003C\u002Ftd>\n\u003Ctd>нет\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Работает с любым стеком\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>только Python\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Требует открытого исходящего SMTP\u003C\u002Ftd>\n\u003Ctd>да\u003C\u002Ftd>\n\u003Ctd>нет\u003C\u002Ftd>\n\u003Ctd>нет\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>idempotency_key\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>нет\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Серверные шаблоны\u003C\u002Ftd>\n\u003Ctd>нет\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Явный флаг рассылки \u003Ccode>mass_mail\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>нет (эвристика)\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>message_id\u003C\u002Fcode> в ответе\u003C\u002Ftd>\n\u003Ctd>нет\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>нет\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Метки (\u003Ccode>tags\u003C\u002Fcode>)\u003C\u002Ftd>\n\u003Ctd>нет\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>нет\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Вложения\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong> (MIME)\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong> (base64)\u003C\u002Ftd>\n\u003Ctd>нет\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Пакетная отправка до 100 писем\u003C\u002Ftd>\n\u003Ctd>нет\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>нет\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Темы подписки (\u003Ccode>topic\u003C\u002Fcode>)\u003C\u002Ftd>\n\u003Ctd>нет\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>нет\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Отслеживание кликов\u003C\u002Ftd>\n\u003Ctd>нет\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Пиксель открытий\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Вебхуки о событиях\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Чтение журнала тем же токеном\u003C\u002Ftd>\n\u003Ctd>нет\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>да\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>нет\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>Что одинаково во всех трёх колонках: ключ DKIM вашего домена, SPF и DMARC, разделение транзакционного и массового потоков, журнал с настоящим ответом принимающего сервера, репутация домена и лимиты тарифа.\u003C\u002Fp>\n\u003Ch2>Как выбрать: пять типичных ситуаций\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>У вас Django-приложение, которое уже шлёт почту через чужой SMTP.\u003C\u002Fstrong> Меняете четыре настройки, отправляете тестовое письмо, смотрите журнал. Переезд занимает час, и это правильный первый шаг даже если в итоге вы хотите API: сначала письма идут, потом улучшается интеграция.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Вы пишете новый сервис, где письмо порождает событие.\u003C\u002Fstrong> Сразу REST API с \u003Ccode>idempotency_key\u003C\u002Fcode>. Ретрай очереди, повтор webhook'а от платёжного провайдера, двойной клик пользователя — всё это в какой-то момент случится, и ключ идемпотентности превратит второе письмо в \u003Ccode>replayed: true\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Вам нужна рассылка по своей базе.\u003C\u002Fstrong> Только API: \u003Ccode>mass_mail: true\u003C\u002Fcode>, \u003Ccode>topic\u003C\u002Fcode>, пакетная отправка. Без явного флага вы полагаетесь на эвристику там, где ошибка стоит жалобы на спам, а жалоба — репутации домена.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Стек не Python — Go, PHP, C#, Node.js.\u003C\u002Fstrong> SMTP для быстрого старта, REST API для всего остального: это обычный \u003Ccode>POST\u003C\u002Fcode> с JSON, клиента для него писать не нужно ни в одном языке.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Коробочный продукт, CMS, 1С, оборудование.\u003C\u002Fstrong> SMTP, и других вариантов обычно нет. Зато он есть везде.\u003C\u002Fp>\n\u003Ch3>Смешивать — нормально\u003C\u002Fh3>\n\u003Cp>Способ отправки записывается у каждого письма, и в журнале по нему можно фильтровать. Вполне рабочая схема: легаси-часть продолжает ходить по SMTP, новый код отправляет через API, рассылки уходят пачками, а вебхуки и статистика при этом общие. Начать с SMTP и перейти на API позже — это не переделка, а добавление.\u003C\u002Fp>\n\u003Ch2>Частые ошибки\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>Отправка письма прямо в HTTP-запросе пользователя.\u003C\u002Fstrong> Любой способ отправки — это сетевой вызов, и он может занять секунды. Ставьте отправку в очередь (Celery, RQ, что угодно) и отвечайте пользователю сразу. Асинхронные методы SDK — это пул потоков в вашем же процессе, а не замена очереди.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Ретраи без \u003Ccode>idempotency_key\u003C\u002Fcode>.\u003C\u002Fstrong> Очередь, которая перезапускает упавшую задачу, — это правильно. Очередь, которая перезапускает задачу «отправить код подтверждения» без ключа идемпотентности, — это два разных кода в почте у пользователя и звонок в поддержку.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Пустой \u003Ccode>text\u003C\u002Fcode>.\u003C\u002Fstrong> Письмо всегда уходит как \u003Ccode>multipart\u002Falternative\u003C\u002Fcode>. Если текстовую версию не передать, FMailer соберёт её из HTML сам, но вывод не знает, какие части вёрстки были украшением, и не увидит фразы, которую вы написали бы для читателя без стилей. Почтовые провайдеры сравнивают обе части при оценке письма.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Рассылка без \u003Ccode>mass_mail\u003C\u002Fcode>.\u003C\u002Fstrong> Эвристика может не распознать в вашем письме массовое, и тогда в нём не окажется заголовков отписки. Дальше человек, которому надоела рассылка, нажимает «Спам». Отписка стоит одного адреса, жалоба — репутации домена.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Порт 25 в продакшене.\u003C\u002Fstrong> По незашифрованному соединению пароль токена уезжает в открытом виде. Дублирующие порты 8587 и 8465 существуют ровно для того, чтобы не приходилось выбирать между шифрованием и заблокированным провайдером портом.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Токен в репозитории или в конфиге фронтенда.\u003C\u002Fstrong> Токен домена позволяет отправлять письма от вашего имени и читать журнал отправки. Его место — в переменных окружения или в хранилище секретов, а сама отправка — всегда на сервере, никогда из браузера или мобильного приложения.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Игнорирование разницы между 400 и 403.\u003C\u002Fstrong> \u003Ccode>Hourly limit exceeded\u003C\u002Fcode> — это «повтори позже», и повтор с выдержкой сработает. \u003Ccode>domain_banned\u003C\u002Fcode> — постоянный отказ, и повторы только сожгут очередь. Обрабатывайте их по-разному.\u003C\u002Fp>\n\u003Ch2>Частые вопросы\u003C\u002Fh2>\n\u003Ch3>Что быстрее — SMTP или API?\u003C\u002Fh3>\n\u003Cp>На практике разница незаметна на фоне самой доставки: и то, и другое ставит письмо в очередь за миллисекунды. Заметная разница возникает на объёме: одно письмо по SMTP — это несколько сетевых обменов и обычно новое соединение, тогда как пакетный метод API принимает до 100 писем одним запросом. Для потока в несколько писем в минуту это неважно; для рассылки — важно.\u003C\u002Fp>\n\u003Ch3>Доставляемость через API лучше, чем через SMTP?\u003C\u002Fh3>\n\u003Cp>Нет. Письмо подписывается тем же ключом DKIM, отправляется той же инфраструктурой и получает тот же ответ принимающего сервера. На доставляемость влияет аутентификация домена, качество базы, доля жалоб и содержание письма — а не транспорт, которым письмо попало к отправляющему сервису.\u003C\u002Fp>\n\u003Ch3>Можно ли пользоваться SMTP и API одновременно?\u003C\u002Fh3>\n\u003Cp>Да, и это распространённая схема. Токен один, домен один, журнал общий; у каждого письма записан способ отправки, по которому можно фильтровать.\u003C\u002Fp>\n\u003Ch3>Нужен ли SDK, если есть REST API?\u003C\u002Fh3>\n\u003Cp>Нет, это удобство, а не необходимость. SDK экономит несколько строк на Python и даёт готовую асинхронную отправку. Как только нужны вложения, метки, пакетная отправка или \u003Ccode>message_id\u003C\u002Fcode> в ответе, вы всё равно возвращаетесь к обычному HTTP-запросу.\u003C\u002Fp>\n\u003Ch3>Есть ли SDK для других языков?\u003C\u002Fh3>\n\u003Cp>Официальный SDK сейчас один — Python. Для остальных языков API остаётся обычным \u003Ccode>POST\u003C\u002Fcode> с JSON: любой HTTP-клиент справится без дополнительной библиотеки.\u003C\u002Fp>\n\u003Ch3>Как отправлять письма, если хостинг блокирует исходящий SMTP?\u003C\u002Fh3>\n\u003Cp>Двумя способами: попробовать дублирующие порты 8587 и 8465, которые для этого и заведены, либо перейти на REST API — исходящий HTTPS открыт практически везде. Проверить доступность порта можно командой \u003Ccode>openssl s_client -starttls smtp -crlf -connect smtp.fmailer.ru:587\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Ch3>Нужно ли настраивать SPF, DKIM и DMARC, если я отправляю через API?\u003C\u002Fh3>\n\u003Cp>Да, и это не зависит от способа отправки. Панель генерирует три записи, которые нужно опубликовать у регистратора; дальше FMailer сам опрашивает DNS и следит, чтобы они не пропали при переезде на другой хостинг. Проверить текущее состояние домена можно бесплатными чекерами в разделе «Инструменты», ещё до регистрации.\u003C\u002Fp>\n\u003Ch3>Как узнать, что письмо дошло, при отправке по SMTP?\u003C\u002Fh3>\n\u003Cp>Так же, как и при отправке через API: вебхуками и журналом. По каждому письму виден настоящий SMTP-ответ принимающего сервера — с кодом и текстом, а не «ошибка доставки». Разница только в том, что при отправке через API вы получаете \u003Ccode>message_id\u003C\u002Fcode> сразу в ответе и можете сохранить его рядом с заказом.\u003C\u002Fp>\n\u003Ch3>Можно ли начать с SMTP и перейти на API потом?\u003C\u002Fh3>\n\u003Cp>Да, и это самый частый путь. SMTP снимает срочность — письма идут, — а API добавляется там, где нужны шаблоны, идемпотентность или рассылки. Учётные данные при этом не меняются: токен домена один и тот же.\u003C\u002Fp>\n\u003Ch2>Итог\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>SMTP\u003C\u002Fstrong> отвечает на вопрос «как начать отправлять сегодня». \u003Cstrong>REST API\u003C\u002Fstrong> отвечает на вопрос «как сделать так, чтобы отправка была надёжной через полгода»: идемпотентность, шаблоны, метки, пачки, \u003Ccode>message_id\u003C\u002Fcode> и понятные коды отказа. \u003Cstrong>SDK\u003C\u002Fstrong> — короткий путь к API для Python-проектов, у которого есть свои границы.\u003C\u002Fp>\n\u003Cp>Правильный ответ для большинства проектов состоит из двух шагов: подключиться по SMTP за час, а затем перевести на API те письма, где повтор запроса, шаблон или рассылка действительно что-то значат. Один токен, один домен, один журнал — переход ничего не ломает.\u003C\u002Fp>\n\u003Ch2>Попробовать FMailer\u003C\u002Fh2>\n\u003Cp>Подключите домен на fmailer.ru: панель сгенерирует SPF, DKIM и DMARC и проверит их сама, а первое письмо можно отправить через SMTP, не меняя код, — или через API, если начинаете с нуля. Пока DNS расходится, интеграцию можно собрать в песочнице: настоящий DKIM, настоящая доставка, те же вебхуки.\u003C\u002Fp>\n\u003Cp>\u003Ca href=\"https:\u002F\u002Ffmailer.ru\">Подключить домен на fmailer.ru →\u003C\u002Fa>\u003C\u002Fp>","c4bc8734-62c8-4a53-9bc1-009cc0d18aac","2026-09-02T20:17:03.051772+03:00","2026-09-02T20:17:43.799451+03:00","fmailer","SMTP, REST API или SDK: что выбрать для отправки писем","SMTP-релей, REST API или Python SDK — три способа отправлять письма из приложения через FMailer. Сравниваем возможности, примеры кода и сценарии выбора.","https:\u002F\u002Fapi.fmailer.ru\u002Fmedia\u002Fblog\u002Frecord_c4bc8734-62c8-4a53-9bc1-009cc0d18aac\u002FChatGPT_Image_Sep_2_2026_08_14_32_PM.png",true,"2026-09-02T20:16:20+03:00",[16,17,18,19],"отправка писем через api","email api для приложения","python sdk для отправки писем","smtp релей"]