Документация / White-label API

White-label API

Если вы перепродаёте отправку (например, вы агентство, которое ведёт почту клиентов, или сервис со своим экраном «подключите домен»), ваш бэкенд может управлять fmailer напрямую: создавать домены, получать DNS-записи, которые нужно опубликовать, выпускать токены домена и регистрировать вебхуки. Всё это из вашего продукта: ваши пользователи не видят эту панель и не встречают название fmailer.

Для этого нужен white-label токен — ключ уровня аккаунта, который выпускается в панели и работает с отдельным машинным API по адресу /api/wl/v1/.

Что понадобится

White-label подключается на уровне аккаунта. Это не часть тарифа, и купить его нельзя — мы включаем его для вашего аккаунта по запросу. До этого на странице White-label в настройках аккаунта написано, как с нами связаться, а после там появляются ваши DNS-домены и токены. Подключение действует на весь аккаунт, поэтому токен не привязан ни к одному из ваших доменов.

В DNS клиента — ваши хосты, а не наши

Ради этого white-label и нужен. Здесь две настройки: DNS-домены и выбор DNS-домена при выпуске токена. По умолчанию записи, которые мы генерируем для домена, называют нас в трёх местах — ровно в тех, что ваш клиент копирует в свою DNS-панель и видит при каждой проверке зоны:

ЗаписьНазывает
SPFv=spf1 a mx include:spf.fmailer.ru ~all
CNAME статистикиstats.<клиент> → stats.fmailer.ru
DMARCrua=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 токеном:

bash
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-домен, один раз, а не для каждого клиента:

ТипИмяУказывает наЧто сломается без неё
CNAMEspf.mail.reseller.ruspf.fmailer.ruSPF-политика, которую домены ваших клиентов подключают через include:. Все их письма перестанут проходить SPF.
CNAME или Astats.mail.reseller.rustats.fmailer.ruТрекинг открытий и переходов. Ссылки трекинга и пиксели открытий в письмах клиентов перестанут резолвиться.
MXdmarc.mail.reseller.rureports.fmailer.ru (приоритет 10)Ящик, куда доставляются сводные DMARC-отчёты по доменам клиентов. Отчёты никуда не придут, а вместе с ними пропадёт сигнал о сломанном DKIM-ключе или о цепочке SPF, которая перестала вас авторизовать.
TXT*._report._dmarc.dmarc.mail.reseller.ruv=DMARC1Разрешение по RFC 7489 §7.1 на отправку этих отчётов. Без него их молча не отправляют.
CNAME или Amx.mail.reseller.rumx.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.

Настройте это до того, как подключите первого клиента
DNS-домен читается один раз, в момент создания домена, и фиксируется на нём. Если добавить DNS-домен позже, сменить основной или выпустить токен под другой DNS-домен, это подействует только на новые домены. У всех уже подключённых клиентов останутся те записи, которые им выдали и которые они опубликовали. Если переписать то, что человек уже внёс в свою зону, почта у него сломается в удобный нам момент, а не ему.
Больше это никто за вас не проверит
Проверка записей сверяет опубликованный клиентом SPF с той строкой, которую мы сохранили, и сам spf.mail.reseller.ru при этом не резолвится. Поэтому, если вы не опубликуете эту запись, у каждого клиента экран настройки станет зелёным, а каждое письмо не пройдёт SPF у получателя. Заметить это можно только кнопкой Проверить DNS на странице DNS-домена.

Выпуск токена

  1. Откройте Аккаунт → White-label в панели.
  2. Нажмите Выпустить токен, укажите название и при желании срок действия, затем выберите DNS-домен и права. Если DNS-домен не выбрать, токен будет использовать основной.
  3. Скопируйте секрет. Он начинается с wl_, за которым идут 48 символов, и показывается ровно один раз: он хранится в виде хеша и восстановить его нельзя. Если потеряли — отзовите токен и выпустите новый.

В списке потом видна только маска (wl_ab12cd34…), чтобы отличать токены друг от друга, и время последнего использования. Токены нельзя редактировать: чтобы изменить права, срок или DNS-домен, выпустите новый и отзовите старый. Отзыв действует сразу: интеграция с этим токеном получит ошибку на следующем же запросе.

Аутентификация

Передавайте секрет в заголовке в каждом запросе:

bash
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, в котором названо нужное право:

json
{
    "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:readDNS-записи, которые нужно опубликовать; обычный массив, без страниц. Единственный источник этих записей
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>/чтение / записькарточка, изменение или удаление маршрута
Хост приёма настраивается на пути домена, но требует прав inbound
/domains/<uuid>/inbound/ относится к домену, но требует inbound:*, а не domains:*: этот метод меняет то, для каких адресов наш SMTP-сервер вообще примет почту, и токен, суженный до управления доменами, не должен получать это заодно. В этом API он нужен потому, что у вашего клиента нет панели: без него white-label домен мог бы иметь маршруты и не принять ни одного письма. Настройте его первым: до этого POST /inbound-routes/ отвечает 400. Маршруты, шаблоны адресов и тело inbound.received описаны в разделе Приём писем.
Удаление работает только для доменов, созданных этим API
DELETE /api/wl/v1/domains/<uuid>/ удаляет домен, только если он был создан через этот API. Для обычных доменов вашего аккаунта, которые вы добавили в панели, придёт 400. Так ошибка в вашем коде не остановит вашу собственную отправку.

Оплата доменов, созданных через API

Домен, созданный через 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.
json
{
    "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-записи не снимают подтверждение: домен, у которого перестали резолвиться записи, остаётся подтверждённым и продолжает отправлять письма — так задумано. domain.unverified приходит только тогда, когда подтверждение действительно сняли, а это действие сотрудника сервиса. Если нужно реагировать на поломку DNS, следите за полем dns_error_at домена.

Пример: весь сценарий

Порядок шагов — не пожелание: токен домена нельзя выпустить для домена, который ещё не подтверждён. Скрипт, который пытается вернуть клиенту логин и пароль тем же вызовом, что создаёт домен, всегда будет падать.

1. Создать домен

bash
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 и запросите их следующим шагом.

json
{
    "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"
}

2. Получить записи для публикации

Обычный массив, без обёртки со страницами. Показывайте записи в своём интерфейсе и опрашивайте этот метод, чтобы отображать состояние каждой: verified_at проставляется, когда запись сошлась, checked_at — когда её проверяли последний раз, а last_result объясняет, почему не сошлась.

bash
curl https://api.fmailer.ru/api/wl/v1/domains/0e0f3b6f-3d9e-4a8a-9c65-2b9a5b3f1f22/records/ \
  -H "Authorization: Bearer $WL_TOKEN"
json
[
    {
        "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 есть среди перечисленных.
mxMX для приёма сверяется по тому, куда фактически придёт письмо. Свой хост клиента с 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: в свою запись, и ваш экран — то место, где об этом стоит сказать.

3. Опубликовать и проверить

Когда клиент добавит записи у своего DNS-провайдера, запросите проверку сразу, не дожидаясь регулярной:

bash
curl -X POST https://api.fmailer.ru/api/wl/v1/domains/0e0f3b6f-3d9e-4a8a-9c65-2b9a5b3f1f22/verify/ \
  -H "Authorization: Bearer $WL_TOKEN"

4. Выпустить токен домена

Сначала подтвердите домен. Токен выпускается только для домена, который уже прошёл проверку DNS, поэтому это действительно четвёртый шаг: пока verified_at не проставлен, метод отвечает 400.

bash
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": "приложение клиента"
  }'
json
{
    "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 этого метода
Метаданные, которые возвращает OPTIONS /api/wl/v1/smtp-tokens/, описывают модель, а не контракт создания, и расходятся с ним в трёх местах: domain помечен «только для чтения», хотя это обязательное поле запроса; password помечен обязательным, хотя вы его не передаёте; а перечисленные поля — это поля чтения. Ответ на создание — тот сокращённый объект выше, без enabled и created_at: они появляются, когда вы запрашиваете токен методом GET.

5. Подписаться на события

bash
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"
  }'

Подпись, повторы и правила доставки те же, что и у обычных вебхуков: схема подписи и расписание повторов описаны в разделе Вебхуки.

6. По желанию: включить приём писем

bash
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/ этого домена рядом с остальными, и после этого можно создавать маршруты:

bash
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. Шаблоны адресов, метки после плюса и формат тела описаны в разделе Приём писем.