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

White-label API

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

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

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

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

В 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 — домен, которым владеете вы, например mail.reseller.ru. После этого домены, созданные через API, получают include:spf.mail.reseller.ru, CNAME на stats.mail.reseller.ru и rua=mailto:dmarc@dmarc.mail.reseller.ru. В зоне клиента нет ни слова про fmailer.

Настройка относится к той витрине, на которой вы её задали, а не к аккаунту целиком. Если вы продаёте ещё и через другую нашу витрину, у неё свой домен для DNS и свой набор записей: хосты, на которые они должны указывать, там другие, поэтому одна зона не может быть верной сразу для обеих. Настройте каждую там, где продаёте.

То же поле пишется через API — но ключом от панели, а не white-label токеном:

bash
curl -X PATCH https://api.fmailer.ru/api/whitelabel/settings/ \
  -H "Authorization: Bearer $ВАШ_JWT_ИЗ_ПАНЕЛИ" \
  -H "Content-Type: application/json" \
  -d '{"dns_domain": "mail.reseller.ru"}'

Так задумано: утёкший машинный ключ не должен уметь переписать DNS всех будущих клиентов, поэтому этот метод остался в панели рядом с выпуском и отзывом токенов.

Что публикуете вы — один раз

Метки spf., stats., dmarc. и mx. фиксированы, от вас нужна только доменная часть. В своей зоне вы публикуете пять записей, один раз — не для каждого клиента:

ТипИмяУказывает наЧто сломается без неё
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 должна лежать политика, которая включает spf.fmailer.ru, и проще всего опубликовать её как CNAME — тогда она сама обновляется вместе с нашими отправляющими IP. Своя TXT-политика тоже подойдёт, если она нас авторизует. А вот value, которое API отдаёт для этой записи — v=spf1, — публиковать буквально нельзя: это маркер, с которым мы сверяемся, и сам по себе он не авторизует никого. С v=DMARC1 на wildcard наоборот: это и есть вся запись целиком.

Средняя пара — для отчётов DMARC. Сводные отчёты по доменам ваших клиентов адресуются в ящик в вашем домене, поэтому MX должен доходить до нас, а поскольку домен из отчёта принадлежит клиенту, а ящик — вам, RFC 7489 §7.1 требует, чтобы принимающая сторона сначала нашла разрешение. Это разрешение — один wildcard, а не запись на каждого клиента. Страница White-label резолвит все пять имён, показывает состояние каждого и подсказывает, чего не хватает: после публикации нажмите Проверить.

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

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

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

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

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

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

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-доступа
smtp_tokens:writeсоздание и удаление SMTP-доступов
webhooks:readсписок и карточка эндпоинта вебхука
webhooks:writeсоздание, изменение и удаление эндпоинтов
inbound:readчтение хоста приёма, маршрутов и полученных писем
inbound:writeнастройка хоста приёма, управление маршрутами, удаление писем

Если права не сужать, токен получает все восемь — обычный случай, когда один backend ведёт весь сценарий подключения. Запрос за пределами прав токена отклоняется с 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:readSMTP-доступы домена
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подтверждение домена было снято

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

Формат тела здесь другой
Эти события относятся к домену, а не к письму, поэтому в теле нет ни 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 домена.

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

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

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_enabled": true,
        "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",
        "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...",
        "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-записью на наш адрес — тоже верно, и приоритет он выбирает сам.

Интерфейс, который для всех строк говорит «опубликуйте ровно это», скажет клиенту с правильной зоной, что она сломана, — а именно такие обращения в поддержку это поле и позволяет предотвратить.

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. Выпустить SMTP-доступ

Сначала подтвердите домен. Доступ выпускается только для домена, который уже прошёл проверку 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. Шаблоны адресов, метки после плюса и формат тела описаны в разделе Приём писем.