Migrar da Resend para a Postal
A API da Postal usa os mesmos recursos, nomes de campos e formatos de resposta da API da Resend. Na maioria dos casos, migrar é trocar a URL base e a chave, e o SDK da Resend continua funcionando.
- Muda no código
- URL e chave
- Dá para rodar as duas juntas
- Sim
- Tempo estimado, mais o DNS
- Algumas horas
- Tipo de envio
- API e SMTP
Verifique o domínio na Postal
Cadastre o domínio no painel e crie os registros de DNS que ele mostra. Dá para manter o mesmo domínio na Resend e na Postal ao mesmo tempo: os registros são diferentes, porque a Postal usa o seletor postal no DKIM.
- A Postal confere os registros sozinha por até 72 horas e avisa por webhook (
domain.updated) - Se o domínio ainda não tem DMARC, os grandes provedores cobram de quem envia volume: comece com
p=nonee endureça depois - Crie uma chave de API em Chaves de API. Para sistemas que só enviam, use uma chave só de envio, restrita ao domínio
Aponte o SDK para a Postal
Os SDKs oficiais da Resend funcionam apontando para a Postal. No Node.js, passe baseUrl ao criar o cliente ou defina RESEND_BASE_URL. O corpo do e-mail, a resposta ({"id"}) e o formato de erro (statusCode, name, message) ficam iguais.
Pelo SDK
- const resend = new Resend(process.env.RESEND_API_KEY);+ const resend = new Resend(process.env.POSTAL_API_KEY, { baseUrl: 'https://api.postal.so' });Ou só pelo ambiente
# Sem mexer no código que já usa new Resend(chave)
RESEND_BASE_URL=https://api.postal.so
RESEND_API_KEY=ps_sua_chavePor HTTP
- curl -X POST 'https://api.resend.com/emails' -H 'Authorization: Bearer re_...'+ curl -X POST 'https://api.postal.so/emails' -H 'Authorization: Bearer ps_...'Por SMTP
- Servidor: smtp.resend.com- Usuário: resend- Senha: re_...+ Servidor: smtp.postal.so+ Usuário: postal (resend também é aceito)+ Senha: ps_...Testado com o pacote resend 6.34 (Node.js): envio simples, agendado, em lote e com idempotência, consulta, edição e cancelamento, anexos, domínios, chaves de API, webhooks e webhooks.verify. Outros SDKs que deixam trocar a URL base falam o mesmo formato, mas não foram testados um a um: se encontrar uma diferença, avise pelo formulário de contato.
Importe o que você já tem
Antes de virar o tráfego, leve a lista de supressão, para não escrever a quem já pediu para sair, e os contatos, se você usa a audiência. Recrie também os templates: as variáveis {{{VAR}}} são as mesmas da Resend.
- Lista de supressão:
POST /suppressions/batch/add, com 100 endereços por requisição - Contatos: importação por CSV, pelo painel ou pela API
- Templates: crie e publique com o mesmo alias e as mesmas variáveis; os envios usam sempre a última versão publicada
curl -X POST https://api.postal.so/suppressions/batch/add \
-H "Authorization: Bearer $POSTAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"emails": ["[email protected]", "[email protected]"]}'Recrie o webhook e confira a assinatura
Os nomes dos eventos são os mesmos (email.delivered, email.bounced…), no formato {type, created_at, data}. O que muda é o segredo: na Postal, cada endpoint tem o seu signing_secret (whsec_…). A assinatura segue o padrão Standard Webhooks, e o resend.webhooks.verify aceita os cabeçalhos webhook-* e svix-*.
- Crie o endpoint em Webhooks (painel ou
POST /webhooks) e escolha os eventos - Envie um evento de teste pelo painel antes de virar o tráfego
- São 8 tentativas em cerca de 27 horas, sempre com o mesmo
webhook-id; o reenvio manual sai pelo painel ou porPOST /webhooks/{id}/events/{event_id}/replay - Outras linguagens: veja a verificação da assinatura
import { Resend } from 'resend';
const resend = new Resend(process.env.POSTAL_API_KEY, { baseUrl: 'https://api.postal.so' });
// Lê o corpo cru da requisição, sem reformatar o JSON
const event = resend.webhooks.verify({
payload: rawBody,
headers: {
id: request.headers['webhook-id'],
timestamp: request.headers['webhook-timestamp'],
signature: request.headers['webhook-signature'],
},
webhookSecret: process.env.POSTAL_WEBHOOK_SECRET, // whsec_...
});Teste e vire, com as duas por perto
Troque a URL base e a chave no ambiente de testes, mande um e-mail para você mesmo e acompanhe em Logs e nos webhooks. Os endereços de teste aceitam +rótulo, não usam cota, não afetam a reputação e geram webhooks reais (dependem do plano). Depois, vire em produção e deixe a Resend por perto por alguns dias.
- IDs diferentes. Os IDs de e-mails e de webhooks da Resend não existem na Postal, e o contrário também. Guarde de qual provedor cada e-mail saiu e continue recebendo eventos da Resend para o que saiu por lá
- Marque a origem. Use
tags(por exemplo,provider: postal) e umaIdempotency-Keyprópria em cada envio - Um provedor por vez no mesmo fluxo. Convites, redefinições de senha e códigos devem sair por um só caminho. Olhe os bounces nos primeiros dias, porque o limite de reputação vale para o time inteiro
- Endereço que voltou a funcionar. Um bounce permanente põe o endereço na lista de supressão; remova com
DELETE /suppressions/{id}ouPOST /suppressions/batch/remove
| Na Resend | Na Postal |
|---|---|
[email protected] | [email protected] |
[email protected] | [email protected] |
[email protected] | [email protected] |
[email protected] | [email protected] |
| sem equivalente | [email protected], que a Postal acrescenta |
O que muda de um para o outro
| Ponto | O que muda na Postal |
|---|---|
| URL base e chave | https://api.postal.so e Authorization: Bearer ps_…. Chaves de acesso total ou só de envio, opcionalmente restritas a um domínio. |
| Envio e resposta | POST /emails, devolve {"id"}. Igual. |
| Erros | {"statusCode", "name", "message"}. Igual. Veja Erros. |
| Webhooks e assinatura | Padrão Standard Webhooks: webhook-id, webhook-timestamp e webhook-signature, e os mesmos valores em svix-*. O segredo é o signing_secret de cada endpoint. |
| Nomes dos eventos | Os mesmos, mais email.replied, contact.*, form.submitted, broadcast.sent e sending.paused / sending.resumed. |
| Motivo do bounce e da falha | data.bounce.message e data.failed.reason, com os mesmos campos. |
| Anexos | filename e content em base64, mais path (URL) e content_id para imagem embutida. Até 40 MB por e-mail; o plano pode permitir menos. |
| Idempotência | Cabeçalho Idempotency-Key (1 a 256 caracteres, guardado por 24 horas) em POST /emails, /emails/batch e /events. |
| Agendamento | scheduled_at em ISO 8601, no futuro e a no máximo 30 dias. Dá para reagendar e cancelar. |
| Limites e cotas | Cada plano tem as suas requisições por segundo e cotas de envio. Veja Limites e Preços. |
| Pausa por reputação | Bounces permanentes acima do limite do nível da reputação da conta (de 3% a 5%), reclamações acima de 0,3% ou conteúdo rejeitado acima de 5% pausam os envios do time: 403 sending_paused. Contas novas começam no nível 1, com teto de 500 e-mails por dia. Antes da pausa, donos e admins recebem um aviso por e-mail. |
| Regiões de envio | São Paulo (sa-east-1, o padrão), us-east-1, us-east-2, eu-west-1 e ap-northeast-1, por domínio. |
| Cobrança | Em reais, dólares ou euros. A lista pública de planos está em GET /plans. |
| Retenção | E-mails, eventos de webhook e logs ficam 30 dias; os eventos enviados por POST /events, 90. |
O que a Postal acrescenta
- Região de processamento à sua escolha, com São Paulo como padrão. Como protegemos os dados
- Inboxes para receber e responder e-mails, com conversas, pastas, etiquetas e rascunhos. Ver Inboxes
- Audiência e marketing: contatos, segmentos, tópicos, formulários, campanhas e automações, com a mesma chave. Ver o marketing por e-mail
- Integrações com Stripe, Supabase, Clerk, Auth0 e Firebase, e a CLI
postal. Integre seu app
A referência completa está em Migrar da Resend, na documentação.
Antes de virar a chave
- Domínio verificado na Postal e chave de API guardada no ambiente
- Lista de supressão e contatos importados
- Templates recriados e publicados, com as mesmas variáveis
- Webhook criado e assinatura conferida com um evento de teste
- Um e-mail de teste enviado e visto em Logs
- O seu código guarda de qual provedor cada e-mail saiu
- Bounces conferidos nos primeiros dias depois da virada
Perguntas frequentes
Preciso mudar o código que usa o SDK da Resend?
Em Node.js, só passar baseUrl ao criar o cliente ou definir RESEND_BASE_URL, além de trocar a chave. Outros SDKs que permitem trocar a URL base falam o mesmo formato, mas não foram testados um a um.
Posso manter o mesmo domínio nas duas plataformas?
Sim. Os registros de DNS são diferentes (a Postal usa o seletor postal), então os dois ficam verificados ao mesmo tempo durante a virada.
O código que verifica a assinatura dos webhooks muda?
O resend.webhooks.verify continua servindo, porque a Postal aceita os cabeçalhos webhook-* e svix-*. O que muda é o segredo: use o whsec_… do endpoint criado na Postal.
Os IDs dos meus e-mails continuam valendo?
Não. Os IDs são de cada plataforma. Guarde também de qual provedor cada e-mail saiu, para consultar o status no lugar certo.
Continue por aqui
Outros guias
Cada plataforma tem o seu: o passo a passo geral está em Migrar.
Pronto para trocar a URL base?
Crie a sua conta e comece no plano Grátis. Para planejar a cota e a ordem da migração, fale com a gente pelo formulário de contato.