Если вы перепродаёте отправку (например, вы агентство, которое ведёт почту клиентов, или сервис со своим экраном «подключите домен»), ваш бэкенд может управлять fmailer напрямую: создавать домены, получать DNS-записи, которые нужно опубликовать, выпускать токены домена и регистрировать вебхуки. Всё это из вашего продукта: ваши пользователи не видят эту панель и не встречают название fmailer.
Для этого нужен white-label токен — ключ уровня аккаунта, который выпускается в панели и работает с отдельным машинным API по адресу /api/wl/v1/.
White-label подключается на уровне аккаунта. Это не часть тарифа, и купить его нельзя — мы включаем его для вашего аккаунта по запросу. До этого на странице White-label в настройках аккаунта написано, как с нами связаться, а после там появляются ваши DNS-домены и токены. Подключение действует на весь аккаунт, поэтому токен не привязан ни к одному из ваших доменов.
Ради этого white-label и нужен. Здесь две настройки: DNS-домены и выбор DNS-домена при выпуске токена. По умолчанию записи, которые мы генерируем для домена, называют нас в трёх местах — ровно в тех, что ваш клиент копирует в свою DNS-панель и видит при каждой проверке зоны:
| Запись | Называет |
|---|---|
| SPF | v=spf1 a mx include:spf.fmailer.ru ~all |
| CNAME статистики | stats.<клиент> → stats.fmailer.ru |
| DMARC | rua=mailto:dmarc@reports.fmailer.ru |
Все три заменяются вашим доменом. В разделе Аккаунт → White-label → DNS-домены добавьте DNS-домен: домен, которым владеете вы, например mail.reseller.ru. После этого домены, созданные через API, получают include:spf.mail.reseller.ru, CNAME на stats.mail.reseller.ru и rua=mailto:dmarc@dmarc.mail.reseller.ru. В зоне клиента нет ни слова про fmailer.
DNS-доменов может быть несколько, по одному на каждый бренд, под которым вы перепродаёте отправку. У каждого своя страница с записями и их проверкой. Токен выпускается либо под конкретный DNS-домен, и тогда записи всех созданных им доменов указывают на него, либо без него. Во втором случае используется тот DNS-домен, который основной в момент вызова. Первый добавленный DNS-домен становится основным, потом основной можно сменить. Переименовать DNS-домен нельзя: чтобы переехать, добавьте новый и удалите старый. Уже подключённые домены сохраняют записи, которые опубликовали их владельцы.
DNS-домены относятся к той витрине, на которой вы их добавили, а не к аккаунту целиком. Если вы продаёте ещё и через другую нашу витрину, у неё свои DNS-домены и свои записи: там они указывают на другие хосты, поэтому одна зона не подойдёт обеим витринам. Настройте каждую там, где продаёте.
DNS-доменами можно управлять и через API, в /api/whitelabel/zones/, но только ключом от панели, а не white-label токеном:
curl -X POST https://api.fmailer.ru/api/whitelabel/zones/ \
-H "Authorization: Bearer $ВАШ_JWT_ИЗ_ПАНЕЛИ" \
-H "Content-Type: application/json" \
-d '{"dns_domain": "mail.reseller.ru"}'Так задумано: утёкший машинный ключ не должен уметь переписать DNS всех будущих клиентов, поэтому DNS-домены управляются только из панели, как выпуск и отзыв токенов.
Метки spf., stats., dmarc. и mx. фиксированы, от вас нужна только доменная часть. В своей зоне вы публикуете пять записей на каждый DNS-домен, один раз, а не для каждого клиента:
| Тип | Имя | Указывает на | Что сломается без неё |
|---|---|---|---|
CNAME | spf.mail.reseller.ru | spf.fmailer.ru | SPF-политика, которую домены ваших клиентов подключают через include:. Все их письма перестанут проходить SPF. |
CNAME или A | stats.mail.reseller.ru | stats.fmailer.ru | Трекинг открытий и переходов. Ссылки трекинга и пиксели открытий в письмах клиентов перестанут резолвиться. |
MX | dmarc.mail.reseller.ru | reports.fmailer.ru (приоритет 10) | Ящик, куда доставляются сводные DMARC-отчёты по доменам клиентов. Отчёты никуда не придут, а вместе с ними пропадёт сигнал о сломанном DKIM-ключе или о цепочке SPF, которая перестала вас авторизовать. |
TXT | *._report._dmarc.dmarc.mail.reseller.ru | v=DMARC1 | Разрешение по RFC 7489 §7.1 на отправку этих отчётов. Без него их молча не отправляют. |
CNAME или A | mx.mail.reseller.ru | mx.fmailer.ru | Куда доставляется почта для входящих маршрутов ваших клиентов. Нужна только тем клиентам, которые принимают письма. |
Строку с SPF стоит прочитать внимательно: по имени spf.mail.reseller.ru должна лежать политика, которая авторизует наших отправителей. Проще всего опубликовать её как CNAME на spf.fmailer.ru (это value записи) — тогда она сама обновляется вместе с нашими отправляющими IP. Своя TXT-политика с include:spf.fmailer.ru или актуальная TXT-копия нашей тоже подойдут, а пустая v=spf1 никого не авторизует и проверку не пройдёт. v=DMARC1 на wildcard, напротив, публикуется ровно так, как показано: это и есть вся запись.
Третья и четвёртая записи нужны для отчётов DMARC. Сводные отчёты по доменам ваших клиентов адресуются в ящик в вашем домене, поэтому MX должен указывать на нас. Домен из отчёта принадлежит клиенту, а ящик вам, и по RFC 7489 §7.1 почтовый сервис, который составляет отчёт, сначала ищет разрешение на такую отправку. Это разрешение задаётся одним wildcard, а не записью на каждого клиента. Страница DNS-домена резолвит все пять имён, показывает состояние каждого и подсказывает, чего не хватает: после публикации нажмите Проверить DNS.
spf.mail.reseller.ru при этом не резолвится. Поэтому, если вы не опубликуете эту запись, у каждого клиента экран настройки станет зелёным, а каждое письмо не пройдёт SPF у получателя. Заметить это можно только кнопкой Проверить DNS на странице DNS-домена. wl_, за которым идут 48 символов, и показывается ровно один раз: он хранится в виде хеша и восстановить его нельзя. Если потеряли — отзовите токен и выпустите новый. В списке потом видна только маска (wl_ab12cd34…), чтобы отличать токены друг от друга, и время последнего использования. Токены нельзя редактировать: чтобы изменить права, срок или DNS-домен, выпустите новый и отзовите старый. Отзыв действует сразу: интеграция с этим токеном получит ошибку на следующем же запросе.
Передавайте секрет в заголовке в каждом запросе:
curl https://api.fmailer.ru/api/wl/v1/domains/ \
-H "Authorization: Bearer wl_ваш-токен" White-label токен работает только с /api/wl/v1/. Он не может прочитать ваш профиль или счёт и не может выпустить другой токен: остальной API его не принимает и отвечает 401.
Ограничение: 120 запросов в минуту на токен. Лимит считается по токену, а не по аккаунту, поэтому разные интеграции не мешают друг другу. При превышении — 429.
Чтение и запись разделены: интеграция, которая только показывает состояние в вашем интерфейсе, может держать ключ, которым ничего нельзя создать или удалить.
| Право | Что разрешает |
|---|---|
domains:read | список и карточка домена, чтение DNS-записей |
domains:write | создание, удаление и перепроверка доменов |
smtp_tokens:read | список и карточка токена домена |
smtp_tokens:write | создание и удаление токенов домена |
webhooks:read | список и карточка эндпоинта вебхука |
webhooks:write | создание, изменение и удаление эндпоинтов |
inbound:read | чтение хоста приёма, маршрутов и полученных писем |
inbound:write | настройка хоста приёма, управление маршрутами, удаление писем |
Если права не сужать, токен получает все восемь — обычный случай, когда один бэкенд ведёт весь сценарий подключения. Запрос за пределами прав токена отклоняется с 403, в котором названо нужное право:
{
"detail": "This token is missing the \"domains:write\" scope"
} Базовый адрес — https://api.fmailer.ru/api/wl/v1/. Всё адресуется по uuid, включая фильтр ?domain=. Версия указана в пути с самого начала, поэтому будущая /v2/ не сломает вашу интеграцию.
| Метод | Путь | Право | Что делает |
|---|---|---|---|
GET | /domains/ | domains:read | список доменов аккаунта |
POST | /domains/ | domains:write | создать домен, тело {"domain": "example.ru"}; создаются ключи DKIM, а записи для публикации читаются отдельно, из /records/ |
GET | /domains/<uuid>/ | domains:read | карточка домена |
GET | /domains/<uuid>/records/ | domains:read | DNS-записи, которые нужно опубликовать; обычный массив, без страниц. Единственный источник этих записей |
POST | /domains/<uuid>/verify/ | domains:write | проверить DNS сразу, не дожидаясь регулярной проверки |
DELETE | /domains/<uuid>/ | domains:write | удалить домен; только домены, созданные через этот API, иначе 400 |
GET | /smtp-tokens/?domain=<uuid> | smtp_tokens:read | токены домена |
POST | /smtp-tokens/ | smtp_tokens:write | создать токен домена: domain, username (локальная часть — @<domain> добавляем мы), необязательный note; домен должен быть уже подтверждён, иначе 400; пароль возвращается один раз, в этом ответе |
GET / DELETE | /smtp-tokens/<uuid>/ | чтение / запись | карточка или удаление токена домена |
GET | /webhooks/?domain=<uuid> | webhooks:read | эндпоинты вебхуков домена |
POST | /webhooks/ | webhooks:write | зарегистрировать эндпоинт: url, enabled, events, description |
GET / PATCH / PUT / DELETE | /webhooks/<uuid>/ | чтение / запись | карточка, изменение или удаление эндпоинта |
GET / PUT | /domains/<uuid>/inbound/ | inbound:read / inbound:write | хост, на котором домен принимает почту, и MX-цель для него; PUT {"hostname": ""} выключает приём |
GET | /inbound-routes/?domain=<uuid> | inbound:read | маршруты приёма домена |
POST | /inbound-routes/ | inbound:write | создать маршрут: domain, pattern, endpoint, description |
GET / PATCH / PUT / DELETE | /inbound-routes/<uuid>/ | чтение / запись | карточка, изменение или удаление маршрута |
/domains/<uuid>/inbound/ относится к домену, но требует inbound:*, а не domains:*: этот метод меняет то, для каких адресов наш SMTP-сервер вообще примет почту, и токен, суженный до управления доменами, не должен получать это заодно. В этом API он нужен потому, что у вашего клиента нет панели: без него white-label домен мог бы иметь маршруты и не принять ни одного письма. Настройте его первым: до этого POST /inbound-routes/ отвечает 400. Маршруты, шаблоны адресов и тело inbound.received описаны в разделе Приём писем. DELETE /api/wl/v1/domains/<uuid>/ удаляет домен, только если он был создан через этот API. Для обычных доменов вашего аккаунта, которые вы добавили в панели, придёт 400. Так ошибка в вашем коде не остановит вашу собственную отправку. Домен, созданный через POST /api/wl/v1/domains/, попадает на отдельный white-label тариф, за который не выставляются счета. Напоминаний об оплате и выбора тарифа тоже нет: платить за каждый такой домен не нужно ни вам, ни вашему клиенту. Лимиты отправки для них мы настраиваем индивидуально в рамках вашей white-label договорённости — напишите нам, если их нужно поднять.
White-label меняет то, кто обращается, но не то, что входит в тариф домена. Все остальные ограничения остаются: регистрация вебхука на домене, в тариф которого вебхуки не входят, по-прежнему даёт 403 — ровно как в панели. То же касается шаблонов, аналитики и выгрузок.
Помимо событий письма есть два события жизненного цикла домена. Подписка на них по желанию: добавьте их в массив events эндпоинта — через API выше или на экране Вебхуки в панели. На существующие эндпоинты они сами не добавляются.
| Событие | Когда приходит |
|---|---|
domain.verified | записи домена прошли проверку, и он стал подтверждённым |
domain.unverified | подтверждение домена было снято |
Повторная проверка, которая ничего не изменила, событий не создаёт: уже подтверждённый домен не шлёт вебхук при каждом обходе.
У домена с приёмом почты есть ещё два события того же формата: inbound.dns_error, когда MX-запись хоста приёма перестаёт вести к нам, и inbound.dns_restored, когда её восстанавливают. У ваших клиентов нет панели, где была бы видна сломанная MX-запись, поэтому узнать о поломке вы сможете только из этих событий. Подробнее — в разделе «Приём писем».
message_id, ни email — их нет вообще, а не null. Как и в любом событии, приходят domain и domain_id. Обработчик, который принимает оба вида событий, должен ветвиться по полю event. {
"event_id": "5d1f7ce0-6f0f-4d09-9f27-3fd06d6fa1a1",
"event": "domain.verified",
"domain": "mail.client.ru",
"domain_id": "0e0f3b6f-3d9e-4a8a-9c65-2b9a5b3f1f22",
"timestamp": "2026-08-03T09:41:07.512340+00:00",
"data": { "verified_at": "2026-08-03T09:41:07.480112+00:00" }
}domain.unverified приходит только тогда, когда подтверждение действительно сняли, а это действие сотрудника сервиса. Если нужно реагировать на поломку DNS, следите за полем dns_error_at домена. Порядок шагов — не пожелание: токен домена нельзя выпустить для домена, который ещё не подтверждён. Скрипт, который пытается вернуть клиенту логин и пароль тем же вызовом, что создаёт домен, всегда будет падать.
curl -X POST https://api.fmailer.ru/api/wl/v1/domains/ \
-H "Authorization: Bearer $WL_TOKEN" \
-H "Content-Type: application/json" \
-d '{"domain": "mail.client.ru"}' В ответе 201 приходит сам домен — его uuid, тариф, состояние подтверждения. DNS-записей в нём нет: сохраните uuid и запросите их следующим шагом.
{
"uuid": "0e0f3b6f-3d9e-4a8a-9c65-2b9a5b3f1f22",
"domain": "mail.client.ru",
"verified_at": null,
"last_check_at": null,
"dns_error_at": null,
"plan": {
"uuid": "c4a1e0d6-9b52-4f8e-83a1-7c5d0e2f9b41",
"name": "White label",
"price": "0.00",
"on_request": true,
"webhooks": "all",
"inbound_enabled": true,
"max_inbound_routes": 25
},
"created_at": "2026-08-03T09:40:12.884210+00:00"
} Обычный массив, без обёртки со страницами. Показывайте записи в своём интерфейсе и опрашивайте этот метод, чтобы отображать состояние каждой: verified_at проставляется, когда запись сошлась, checked_at — когда её проверяли последний раз, а last_result объясняет, почему не сошлась.
curl https://api.fmailer.ru/api/wl/v1/domains/0e0f3b6f-3d9e-4a8a-9c65-2b9a5b3f1f22/records/ \
-H "Authorization: Bearer $WL_TOKEN"[
{
"uuid": "b81c6f4a-1e77-4d2e-9a55-6f0d1c3b7e90",
"domain": "0e0f3b6f-3d9e-4a8a-9c65-2b9a5b3f1f22",
"type": "TXT",
"name": "@",
"full_name": "mail.client.ru",
"value": "v=spf1 a mx include:spf.mail.reseller.ru ~all",
"suggested_value": "v=spf1 a mx include:spf.mail.reseller.ru ~all",
"match": "spf",
"required": true,
"verified_at": null,
"checked_at": null,
"last_result": null
},
{
"uuid": "3a0e9d21-55b8-4c07-8f13-9d2b6a4e1c58",
"domain": "0e0f3b6f-3d9e-4a8a-9c65-2b9a5b3f1f22",
"type": "TXT",
"name": "default._domainkey",
"full_name": "default._domainkey.mail.client.ru",
"value": "v=DKIM1; k=rsa; p=MIIBIjANBgkqhki...",
"suggested_value": "v=DKIM1; k=rsa; p=MIIBIjANBgkqhki...",
"match": "exact",
"required": true,
"verified_at": null,
"checked_at": null,
"last_result": null
}
] Экран настройки стоит строить вокруг поля match: оно говорит, как ответ DNS сверяется со значением value, и в зависимости от него клиенту нужно давать разные инструкции.
match | Что сказать клиенту |
|---|---|
exact | Опубликовать значение дословно. Такая запись стоит на имени, куда больше никто не пишет — ключ DKIM, CNAME статистики. |
spf | Дописать в существующую, а не добавить рядом. RFC 7208 разрешает одну SPF-запись на имя, поэтому клиент, который уже откуда-то отправляет, должен вписать наш include: в свою. Мы сверяем по смыслу, так что порядок термов и чужие отправители значения не имеют. |
dmarc | То же самое для _dmarc: мы проверяем не совпадение строки, а то, что наш ящик rua есть среди перечисленных. |
mx | MX для приёма сверяется по тому, куда фактически придёт письмо. Свой хост клиента с A-записью на наш адрес — тоже верно, и приоритет он выбирает сам. |
Интерфейс, который для всех строк говорит «опубликуйте ровно это», скажет клиенту с правильной зоной, что она сломана. Именно таких обращений в поддержку это поле и помогает избежать.
Публиковать нужно suggested_value — именно это значение и стоит показывать клиенту. Оно есть всегда. Это та же запись, что и value, но если DNS-панель клиента читает поле как файл зоны, значение приходит в этом виде: в кавычках и разбитое на строки не длиннее 255 символов. Google Cloud DNS, Route 53 и BIND считают каждый пробел в значении без кавычек концом одной строки, а DNS склеивает строки без разделителя: SPF-запись выше, вставленная как есть, опубликуется как v=spf1amxinclude:spf.mail.reseller.ru~all. Выглядит правдоподобно и не авторизует ни одного отправителя.
Какая это панель, мы определяем по NS-записям зоны. Если клиент говорит, что мы определили неверно, передайте ?dns_host=<код> (коды те же, что в поле dns_host домена), и значения вернутся подготовленными для этой панели. value — то, что генерируем мы; показывать его стоит только тому, кто спросит, а у большинства доменов оба значения совпадают. Проверку проходят оба варианта.
Если у клиента уже есть своя SPF-запись, мы об этом сообщаем, но ничего не объединяем: в found_value лежит ответ DNS, а last_result говорит, что строка не совпала. На одно имя допускается только одна запись v=spf1, и это единственный случай, когда публиковать наше значение как есть нельзя: клиенту нужно добавить наш include: в свою запись, и ваш экран — то место, где об этом стоит сказать.
Когда клиент добавит записи у своего DNS-провайдера, запросите проверку сразу, не дожидаясь регулярной:
curl -X POST https://api.fmailer.ru/api/wl/v1/domains/0e0f3b6f-3d9e-4a8a-9c65-2b9a5b3f1f22/verify/ \
-H "Authorization: Bearer $WL_TOKEN"Сначала подтвердите домен. Токен выпускается только для домена, который уже прошёл проверку DNS, поэтому это действительно четвёртый шаг: пока verified_at не проставлен, метод отвечает 400.
curl -X POST https://api.fmailer.ru/api/wl/v1/smtp-tokens/ \
-H "Authorization: Bearer $WL_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"domain": "0e0f3b6f-3d9e-4a8a-9c65-2b9a5b3f1f22",
"username": "app",
"note": "приложение клиента"
}'{
"id": 4711,
"uuid": "7b1a4c2e-8f30-4f5b-9d61-0c4e2a7b8d33",
"domain": "0e0f3b6f-3d9e-4a8a-9c65-2b9a5b3f1f22",
"username": "app@mail.client.ru",
"note": "приложение клиента",
"_password": "пароль-показывается-один-раз"
}username — обязательное поле, и это локальная часть, а не адрес: @<domain> мы добавляем сами. Отправьте app — токен будет входить как app@mail.client.ru; отправите app@mail.client.ru — получите домен дважды. Используйте буквы, цифры, подчёркивание и дефис, как в панели. note необязателен и нужен вам: по нему ваша поддержка понимает, какой интеграции клиента принадлежит токен. Пароль выбирать не нужно — он генерируется.
_password возвращается в этом ответе и больше нигде — храните его как любой другой секрет. В списке и в карточке токена поля с паролем нет вообще, ни в каком виде: если потеряли — удалите токен и выпустите новый. Дальше пара логин/пароль используется с smtp.fmailer.ru точно так же, как описано в разделе Отправка через SMTP.
OPTIONS /api/wl/v1/smtp-tokens/, описывают модель, а не контракт создания, и расходятся с ним в трёх местах: domain помечен «только для чтения», хотя это обязательное поле запроса; password помечен обязательным, хотя вы его не передаёте; а перечисленные поля — это поля чтения. Ответ на создание — тот сокращённый объект выше, без enabled и created_at: они появляются, когда вы запрашиваете токен методом GET. curl -X POST https://api.fmailer.ru/api/wl/v1/webhooks/ \
-H "Authorization: Bearer $WL_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"domain": "0e0f3b6f-3d9e-4a8a-9c65-2b9a5b3f1f22",
"url": "https://your-app.example.ru/hooks/mail",
"events": ["delivered", "bounced", "domain.verified"],
"description": "клиент 4711"
}'Подпись, повторы и правила доставки те же, что и у обычных вебхуков: схема подписи и расписание повторов описаны в разделе Вебхуки.
curl -X PUT https://api.fmailer.ru/api/wl/v1/domains/0e0f3b6f-3d9e-4a8a-9c65-2b9a5b3f1f22/inbound/ \
-H "Authorization: Bearer $WL_TOKEN" \
-H "Content-Type: application/json" \
-d '{"hostname": "inbound.mail.client.ru"}'mx_target в ответе — это значение, на которое клиент нацелит свою MX-запись. Для white-label домена это имя в вашем домене, поэтому никогда не собирайте эту строку сами. Созданная MX-запись появляется в /records/ этого домена рядом с остальными, и после этого можно создавать маршруты:
curl -X POST https://api.fmailer.ru/api/wl/v1/inbound-routes/ \
-H "Authorization: Bearer $WL_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"domain": "0e0f3b6f-3d9e-4a8a-9c65-2b9a5b3f1f22",
"pattern": "support",
"endpoint": "9f2c1b7e-4d38-4a10-8b6c-5e0a3d7f2c19"
}' Письмо, подошедшее под маршрут, приходит на ваш эндпоинт событием inbound.received. Шаблоны адресов, метки после плюса и формат тела описаны в разделе Приём писем.