SMTP и HTTP API
Создавайте ключи API и отправляйте почту по SMTP или через HTTP API: поля запросов, ответы, коды ошибок и примеры.
Отправлять письма через поток можно двумя способами: по SMTP из любого приложения или почтовой библиотеки либо через HTTP API. Оба способа используют одни и те же ключи API, квоты, список подавления и события. Прежде чем начать, подтвердите домен отправки и создайте ключ API.
Ключи API
- Откройте поток и перейдите на вкладку Ключ API.
- Нажмите Создать ключ, введите Название, например
production, и подтвердите. - Скопируйте показанный ключ и нажмите Я сохранил. Ключ показывается только этот один раз. Затем в списке отображается только его Префикс, например
ytm_aB3dE6gH….
Каждый ключ начинается с ytm_. У потока может быть до 20 активных ключей. В списке указано, когда каждый ключ использовался в последний раз. Чтобы вывести ключ из использования, нажмите Отозвать: программы, которые его используют, сразу перестанут работать. Ключ действует только для потока, которому он принадлежит.
храните ключи API на сервере. Не размещайте их в коде для браузера, мобильных приложениях или публичных репозиториях. Любой, у кого есть ключ, может отправлять почту от имени вашего домена.
SMTP
На вкладке Обзор потока показаны настройки SMTP:
| Параметр | Значение |
|---|---|
| Хост | SMTP-хост, указанный для потока, обычно smtp.tm.yeapcloud.net |
| Порт | 587 со STARTTLS или 465 с TLS |
| Имя пользователя | apikey (именно это слово) |
| Пароль | Ключ API этого потока |
| Заголовок категории | X-Yeap-Category |
Используйте хост, указанный в консоли. Некоторые потоки отправляют почту через другую группу серверов с собственным именем хоста.
Как работает отправка по SMTP:
- В заголовке From должен быть адрес на домене, подтверждённом для потока.
- Каждый получатель (To, Cc и Bcc) получает собственную копию с собственным идентификатором письма и засчитывается как одно письмо.
- Чтобы задать категорию, добавьте заголовок, например
X-Yeap-Category: password-reset. Он удаляется перед доставкой. Допустимые символы: буквы, цифры,.,_и-, не более 64 символов. - Размер письма — до 25 MB, до 1,000 получателей на одну SMTP-транзакцию.
- Каждое письмо получает заголовок
X-TM-ID(идентификатор письма, который отображается в журнале отправки и в событиях) и заголовокFeedback-IDдля петель обратной связи почтовых провайдеров. - В маркетинговых потоках любой заданный вами заголовок
List-Unsubscribeзаменяется заголовками отписки в один клик от Yeap Cloud. SMTP не изменяет тело письма, поэтому добавьте почтовый адрес и ссылку для отписки в содержимое самостоятельно. См. Подавление и отписка. - Отслеживание открытий и кликов доступно только через HTTP API.
Пример со стандартной библиотекой Python:
import smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg["From"] = "Example <noreply@example.com>"
msg["To"] = "user@example.org"
msg["Subject"] = "Reset your password"
msg["X-Yeap-Category"] = "password-reset"
msg.set_content("Click the link to reset your password: https://example.com/reset/abc")
with smtplib.SMTP("smtp.tm.yeapcloud.net", 587) as s:
s.starttls()
s.login("apikey", "ytm_your_api_key")
s.send_message(msg)Коды ответа SMTP
Когда сервер отклоняет письмо, код ответа указывает причину. Коды 4xx временные: повторите попытку позже. Коды 5xx постоянные: устраните причину, прежде чем отправлять снова.
| Код | Сообщение | Причина и что делать |
|---|---|---|
535 5.7.8 | Authentication failed / API key revoked | Неверное имя пользователя или ключ либо ключ отозван. Имя пользователя должно быть apikey. |
454 4.7.0 | Temporary authentication failure / Too many authentication failures | Сервер ещё не готов или с вашего IP было слишком много неудачных попыток входа. Подождите и повторите попытку. |
451 4.7.1 | Stream is paused | Поток был автоматически приостановлен. Посмотрите причину в потоке и обратитесь в службу поддержки. |
550 5.7.1 | Stream is banned / suspended / terminated | Поток заблокирован, его подписка приостановлена или он был освобождён. |
550 5.7.1 | From domain … is not verified for this stream | Подтвердите домен или проверьте, что он общий для этого потока или привязан к нему. |
550 5.7.1 | Sandbox mode: recipient is not a verified address | Поток находится в песочнице. Подтвердите получателя или отправляйте на свой домен. |
550 5.7.1 | Recipient is on a suppression list / All recipients are suppressed | Адрес находится в списке подавления (отказ, жалоба, отписка или вручную). |
452 4.7.1 | Hourly sending limit reached, try later | Часовой лимит (или дневной лимит песочницы) исчерпан. Повторите попытку в следующий час. |
550 5.7.1 | Monthly sending quota exhausted | Месячный объём исчерпан, а перерасход не допускается: на бесплатном тарифе или при достижении лимита расходов на перерасход. |
452 4.5.3 | Too many recipients | Более 1,000 получателей в одной транзакции. |
553 5.1.3 | Invalid recipient address | Неверный формат адреса получателя. |
554 5.7.1 | Message rejected: virus detected | Письмо содержало вирус. Поток немедленно блокируется, и все его ключи API отзываются. |
554 5.7.1 | Message rejected as spam | Содержимое оценено как спам. Повторные отклонения приводят к приостановке потока. |
451 4.3.0 | Content scanner unavailable, try again later | Маркетинговая почта принимается только после проверки. Повторите попытку позже. Другие ответы 451 4.3.0 — временные ошибки сервера: повторите попытку. |
554 5.6.0 | Malformed message headers | Исправьте заголовки письма. |
HTTP API
| Параметр | Значение |
|---|---|
| Базовый URL | https://api.tm.yeapcloud.net/v1 (также доступен как https://api.yeap.cloud/api/tm/v1) |
| Аутентификация | Authorization: Bearer ytm_your_api_key |
| Формат | Тела запросов и ответов в JSON, Content-Type: application/json |
| Ограничение частоты | 600 запросов в минуту на ключ API |
При ошибке возвращаются код состояния HTTP и тело следующего вида:
{"error": {"code": "invalid_request", "message": "subject is required (single line)"}}| Статус HTTP | error.code | Значение |
|---|---|---|
| 400 | invalid_json, invalid_request | Тело не является объектом JSON либо пакет пуст или слишком велик |
| 401 | unauthorized | Ключ API отсутствует, имеет неверный формат, отозван или неизвестен |
| 403 | stream_paused, stream_banned, stream_suspended | Поток не может отправлять письма |
| 403 | from_domain_unverified, invalid_from | Домен From не подтверждён для этого потока или адрес From недействителен |
| 403 | postal_address_required | Маркетинговый поток без почтового адреса. Заполните его на вкладке Настройки маркетинга. |
| 403 | virus | Вложение содержало вирус. Поток заблокирован. |
| 404 | not_found | Такого письма в этом потоке нет |
| 422 | invalid_request | Поле отсутствует или недействительно. В message указано, какое именно. |
| 429 | rate_limited | Более 600 запросов в минуту. Снизьте частоту и повторите попытку. |
| 503 | no_node | Сейчас нет доступного сервера отправки. Повторите попытку позже. |
Отправка письма: POST /v1/send
| Поле | Тип | Описание |
|---|---|---|
from | string | Обязательное. Один адрес, например noreply@example.com или Example <noreply@example.com>. Домен должен быть подтверждён для потока. |
to | string или array | Обязательное. Один адрес, строка с адресами через запятую или массив. Элементы массива также могут быть объектами {"email": "...", "name": "..."}. |
cc, bcc | string или array | Необязательное. Тот же формат, что и у to. Адреса Bcc не отображаются в заголовках. |
reply_to | string или array | Необязательное. Адрес Reply-To. |
subject | string | Обязательное. Одна строка, до 998 символов. |
html, text | string | Обязательно хотя бы одно из них. Для наилучшего результата отправляйте оба. |
headers | object | Необязательное. Дополнительные заголовки, например {"X-Campaign": "spring"}. Стандартные заголовки (From, To, Subject, Reply-To, Message-ID, List-Unsubscribe и т. д.) здесь задать нельзя. |
attachments | array | Необязательное. Элементы {"filename": "invoice.pdf", "content_base64": "...", "type": "application/pdf"}. Не более 10 файлов и 10 MB в сумме. |
category | string | Необязательное. Буквы, цифры, ., _, -, до 64 символов. |
tags | array of strings | Необязательное. До 10 тегов для собственной фильтрации, каждый до 64 символов. |
track_opens, track_clicks | boolean | Необязательное. Включает или отключает для этого письма отслеживание открытий (пиксель отслеживания) и кликов (ссылки переписываются через t.tm.yeapcloud.net). Только для HTML. Если не указано, применяется значение потока по умолчанию. По умолчанию оба отключены. |
variables | object | Необязательное. Заменяет заполнители {{name}} в subject, html и text. В html значения экранируются для HTML. Неизвестные заполнители остаются без изменений. |
Один вызов может содержать не более 50 получателей в сумме по to, cc и bcc. Каждый получатель получает отдельную копию с собственным идентификатором письма. Чтобы отправить разное содержимое множеству людей, используйте пакетный эндпоинт.
Пример:
curl -X POST https://api.tm.yeapcloud.net/v1/send \
-H "Authorization: Bearer ytm_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"from": "Example <noreply@example.com>",
"to": ["alice@example.org"],
"subject": "Your order {{order_id}} has shipped",
"html": "<p>Hi {{name}}, your order is on its way.</p>",
"text": "Hi {{name}}, your order is on its way.",
"category": "shipping",
"tags": ["order"],
"variables": {"name": "Alice", "order_id": "A-1024"}
}'Ответ:
{
"message_ids": ["01JAQ3Z9V8K4M2N6P7R5S1T0XW"],
"rejected": []
}В message_ids перечислены принятые копии. В rejected перечислены получатели, которым письмо не было отправлено, каждый с полями rcpt и reason. Вызов может завершиться успешно, даже если часть или все получатели отклонены, поэтому всегда проверяйте rejected:
reason | Значение |
|---|---|
invalid_recipient | Адрес недействителен |
suppressed:<scope>:<reason> | Адрес находится в списке подавления, например suppressed:global:hard_bounce |
sandbox_recipient_not_verified | Поток находится в песочнице, и этот получатель не разрешён |
rate_limited_hourly, rate_limited_sandbox_or_hourly | Часовой лимит (или дневной лимит песочницы) исчерпан |
monthly_quota_exceeded | Месячный объём исчерпан, а перерасход не допускается |
enqueue_failed | Письмо не удалось поставить в очередь. Оно не засчитывается в квоту. Повторите попытку. |
Пример на Python с requests:
import requests
resp = requests.post(
"https://api.tm.yeapcloud.net/v1/send",
headers={"Authorization": "Bearer ytm_your_api_key"},
json={
"from": "Example <noreply@example.com>",
"to": "alice@example.org",
"subject": "Welcome",
"html": "<p>Welcome aboard!</p>",
"text": "Welcome aboard!",
"category": "welcome",
},
timeout=30,
)
resp.raise_for_status()
result = resp.json()
print(result["message_ids"], result["rejected"])Пакетная отправка: POST /v1/batch
Отправляйте до 500 писем одним запросом. У каждого элемента те же поля, что и у /v1/send.
{
"messages": [
{"from": "noreply@example.com", "to": "a@example.org", "subject": "Hi A", "text": "Hello A"},
{"from": "noreply@example.com", "to": "b@example.org", "subject": "Hi B", "text": "Hello B"}
]
}Ответ содержит по одному результату на каждое письмо в том же порядке:
{
"results": [
{"index": 0, "message_ids": ["01JAQ..."], "rejected": []},
{"index": 1, "error": {"code": "invalid_request", "message": "subject is required (single line)"}, "rejected": []}
]
}Поиск писем: GET /v1/messages
| Параметр запроса | Описание |
|---|---|
status | Фильтр по статусу: queued, delivered, deferred, bounced, complained, rejected или failed |
to | Фильтр по адресу получателя |
since | Только письма, созданные в указанное время или позже, например 2026-10-01T00:00:00Z |
limit | Количество результатов на странице, 1–100 (по умолчанию 50) |
before | Для следующей страницы передайте значение next_before из предыдущего ответа |
Ответ имеет вид {"data": [...], "next_before": "..."}. На последней странице next_before равен null. Письма хранятся 30 дней.
GET /v1/messages/{id} возвращает одно письмо и его события:
{
"id": "01JAQ3Z9V8K4M2N6P7R5S1T0XW",
"stream_id": 12,
"source": "api",
"to": "alice@example.org",
"from": "Example <noreply@example.com>",
"subject": "Welcome",
"category": "welcome",
"tags": [],
"status": "delivered",
"smtp_code": 250,
"smtp_response": "250 2.0.0 OK",
"opened_at": null,
"clicked_at": null,
"unsubscribed_at": null,
"created_at": "2026-10-20 10:15:02",
"updated_at": "2026-10-20 10:15:04",
"events": [
{"type": "delivered", "at": "2026-10-20 10:15:04", "rcpt": "alice@example.org", "smtp_code": 250, "mx": "mx.example.org"}
]
}Список подавления: /v1/suppressions
Эти эндпоинты управляют списком подавления потока, которому принадлежит ключ API.
| Запрос | Тело или параметры запроса | Ответ |
|---|---|---|
GET /v1/suppressions | Параметры email (необязательный), limit (1–500, по умолчанию 100), offset | {"data": [{"id", "scope", "email", "reason", "category", "note", "created_at"}]} |
POST /v1/suppressions | {"email": "..."} или {"emails": [...]} (до 1,000), необязательный category | {"added": 2, "invalid": []} |
DELETE /v1/suppressions | {"email": "..."}, необязательный category (в теле или в параметрах запроса) | {"removed": 1} |
Адреса, добавленные через API, получают причину manual. Записи подавления уровня аккаунта и глобальные записи изменить через API нельзя. См. Подавление и отписка.