Перейти к основному содержимому

Переход с SendGrid

У SendGrid тело запроса устроено иначе: получатели живут в personalizations, а контент — в массиве content. У нас плоская структура, поэтому основная работа при миграции — «развернуть» payload.

примечание

Соответствия составлены по публичному Mail Send v3 API SendGrid на момент написания. Сверьтесь с их актуальной документацией, если пользуетесь свежими возможностями.

Ключи и клиент​

SendGridMailInfra
Префикс ключаSG.…mi_live_… (боевой), mi_test_… (sandbox)
Переменная окруженияSENDGRID_API_KEYMAILINFRA_API_KEY
База APIhttps://api.sendgrid.com/v3https://app.mailinfra.ru/v1
Эндпоинт отправкиPOST /mail/sendPOST /v1/emails
Тестовый режимmail_settings.sandbox_modeотдельный ключ mi_test_…

На других языках клиент создаётся так же — ключ берётся из MAILINFRA_API_KEY:

from mailinfra import MailInfra
client = MailInfra()
use MailInfra\MailInfra;
$client = new MailInfra();
client, err := mailinfra.New()

Дальше примеры — на TypeScript. Набор методов во всех четырёх SDK один, а имена полей письма следуют языку: в Node — как в API (from, reply_to), в PHP — именованные аргументы в camelCase (from:, replyTo:), в Python — именованные аргументы (from_email=, потому что from — ключевое слово), в Go — поля структуры SendParams (From, FromName, To, CC). Примеры на каждом языке — на страницах Python, Node, PHP и Go.

Отправка письма​

SendGridMailInfraКомментарий
personalizations[].to[].emailtoу нас список адресов, одно письмо = одна персонализация
personalizations[].cc / bcccc / bccвсего до 50 адресов вместе с to
personalizations[].dynamic_template_datavariablesпеременные шаблона
personalizations[].substitutionsvariablesподстановок вида -key- нет, только именованные переменные
from.email, from.namefrom, from_name
reply_to.emailreply_toу нас список, до 10 адресов
subjectsubject
content[{type:"text/plain"}]text
content[{type:"text/html"}]html
template_id (d-…)templateу нас слаг шаблона: welcome, а не UUID
categoriestagsдо 10, до 32 символов
custom_argsmetadataпары «ключ — значение» (до 20), возвращаются в письме и в каждом вебхуке
attachments[].content (base64)attachments[].content_base64, либо content байтами
attachments[].typeattachments[].content_type
attachments[].disposition: "inline", content_idattachments[].content_idкартинка внутри письма по cid:
send_at (unix-время)send_atISO 8601 с часовым поясом, до 7 дней вперёд; отмена и перенос — POST /v1/emails/{id}/cancel и PATCH /v1/emails/{id}
asm (группы отписки)—отписка ведётся через список подавления
ip_pool_name—пулами IP управляет MailInfra

Python​

# было
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail

message = Mail(
from_email="hello@example.ru",
to_emails="user@example.com",
subject="Привет",
html_content="<p>Привет!</p>",
)
response = SendGridAPIClient(os.environ["SENDGRID_API_KEY"]).send(message)
print(response.status_code, response.headers["X-Message-Id"])

# стало
from mailinfra import MailInfra

client = MailInfra()
result = client.emails.send(
from_email="hello@example.ru",
to="user@example.com",
subject="Привет",
html="<p>Привет!</p>",
)
print(result.status, result.id)

Шаблоны​

# было
message = Mail(from_email="no-reply@example.ru", to_emails="user@example.com")
message.template_id = "d-0123456789abcdef"
message.dynamic_template_data = {"user_name": "Анна"}

# стало
client.emails.send(
from_email="no-reply@example.ru",
to="user@example.com",
template="welcome", # слаг из дашборда
variables={"user_name": "Анна"},
)

Шаблоны у нас хранятся в проекте и рендерятся на сервере (Jinja) — как их заводить. Handlebars-конструкции SendGrid ({{#if}}, {{#each}}) нужно переписать на Jinja.

Node.js​

// было
import sgMail from "@sendgrid/mail";
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
await sgMail.send({
to: "user@example.com",
from: "hello@example.ru",
subject: "Привет",
html: "<p>Привет!</p>",
categories: ["welcome"],
});

// стало
import { MailInfra } from "mailinfra";
const client = new MailInfra();
await client.emails.send({
to: "user@example.com",
from: "hello@example.ru",
subject: "Привет",
html: "<p>Привет!</p>",
tags: ["welcome"],
});

Массовая отправка​

У SendGrid «много писем» делается несколькими personalizations в одном запросе. У нас для этого отдельный эндпоинт — батч до 100 писем:

await client.emails.sendBatch([
{ from: "no-reply@example.ru", to: "a@example.com", template: "welcome",
variables: { user_name: "Анна" } },
{ from: "no-reply@example.ru", to: "b@example.com", template: "welcome",
variables: { user_name: "Борис" } },
]);

Разница в трактовке ответа: 202 приходит даже при частичном отказе, у каждого элемента results своё error — проверяйте поштучно.

Идентификатор письма и статусы​

SendGridMailInfra
X-Message-Id в заголовке ответаresult.id в теле ответа
статусы через Event Webhook или Activity APIclient.emails.get(id), client.emails.list(...), client.emails.events(id)
sg_message_id в событияхdata.email.id в вебхуке

Вебхуки​

Отличий три, и они существенные.

SendGridMailInfra
Формат теламассив событий в одном POSTровно одно событие на POST
ПодписьECDSA, заголовок X-Twilio-Email-Event-Webhook-SignatureHMAC-SHA256, X-MailInfra-Signature: t=…,v1=…
Имена событийdelivered, bounce, spamreport, open, click, droppedemail.delivered, email.bounced, email.complained, email.opened, email.clicked, email.failed

Обработчик перестаёт быть циклом по массиву:

# было
for event in request.json():
handle(event["event"], event["sg_message_id"])

# стало
from mailinfra import webhooks

event = webhooks.construct_event(
body=await request.body(),
secret=os.environ["MAILINFRA_WEBHOOK_SECRET"],
signature_header=request.headers["X-MailInfra-Signature"],
)
handle(event.type, event.email_id)

Соответствие событий: bounce → email.bounced, spamreport → email.complained, dropped → email.failed или пропуск получателя (адрес уже в списке подавления — такие видны в skipped_recipients ответа отправки).

Список подавления​

Bounce- и spam-адреса попадают в список подавления автоматически, как и в SendGrid. Экспортируйте свои bounce/unsubscribe-списки из SendGrid и импортируйте CSV в дашборде — иначе первые же рассылки повторно ударят по тем же адресам и испортят репутацию нового домена.

Переезд «одним переключателем»​

SendGrid часто используют как SMTP-релей (smtp.sendgrid.net, логин apikey). У нас ровно такой же режим: SMTP-отправка — хост smtp.mailinfra.ru, логин apikey, пароль — ваш ключ. В этом случае код приложения вообще не меняется: только хост и пароль в конфиге.

Чего у нас нет​

  • групп отписки (asm);
  • подстановок вида -key-;
  • маркетинговых кампаний по спискам контактов (Marketing Campaigns);
  • выделенных IP-пулов под управлением клиента.