SES: envio de e-mails na AWS
Como enviar e-mails transacionais com o Amazon SES, a reputação do remetente e os cuidados para não cair em caixas de spam.
Revisada em · 16 min de leitura
O Amazon SES (Simple Email Service) é uma solução de envio de e-mails baseada na nuvem, projetada para ser uma plataforma econômica, flexível e altamente escalável para desenvolvedores e empresas de marketing digital.
Aqui estão os pontos principais sobre como ele funciona e suas vantagens:
1. Para que serve?
O SES serve tanto para envios de e-mails transacionais (confirmações de compra, redefinição de senha) quanto para e-mails de marketing (newsletters, ofertas) em larga escala. Ele elimina a complexidade e o custo de manter uma solução de e-mail interna, garantindo altas taxas de entregabilidade.
2. Como funciona?
O serviço atua como uma ponte entre sua aplicação e os provedores de e-mail (ISPs) dos destinatários:
- Integração: Sua aplicação pode enviar e-mails através do SES usando a interface SMTP (Simple Mail Transfer Protocol) ou via API usando os SDKs da AWS (disponíveis para Java, Python, PHP, .NET, etc.).
- Fluxo de Envio:
- Sua aplicação faz uma solicitação de envio ao SES.
- O SES valida a solicitação e a envia para o provedor de internet (ISP) do destinatário.
- O ISP entrega a mensagem na caixa de entrada ou a rejeita (caso o endereço não exista ou seja classificado como spam).
3. Gerenciamento de Entregabilidade e Reputação
Um dos grandes desafios no envio de e-mails em massa é não cair na caixa de spam. O SES fornece ferramentas para monitorar a eficácia dos seus envios:
- Feedback: O serviço notifica sua aplicação sobre o status da entrega, incluindo sucessos, rejeições (bounces) e reclamações de spam.
- Monitoramento: Ele se integra nativamente com o Amazon CloudWatch, permitindo que você visualize métricas de envio e configure alarmes caso a taxa de erros suba muito.
4. Custo e Nível Gratuito
O modelo de cobrança é “pague pelo que usar” (pay-per-use), baseado no número de e-mails enviados e dados transferidos. A página de preços consultada em 2026-10-27 indica US$ 0,10 por mil e-mails no modelo à la carte, mas preços mudam, então confirme antes de planejar um orçamento.
- Nível gratuito: a página de preços consultada em 2026-10-27 não traz a cota de 62.000 mensagens por mês para aplicações hospedadas na Amazon EC2. Ela menciona os créditos do AWS Free Tier aplicáveis a serviços elegíveis. A cota de 62.000 fica não verificada até ser conferida na documentação atual.
Em resumo, o Amazon SES é a ferramenta ideal quando você precisa que sua aplicação envie e-mails de forma confiável e profissional, sem precisar gerenciar servidores de e-mail complexos.
Até aqui foi a visão geral. As seções a seguir tratam do que você precisa fazer para o SES realmente enviar: verificar o domínio com DKIM, SPF e DMARC, sair do sandbox, enviar pela CLI e por código, e acompanhar bounces e complaints. A regra que organiza tudo é simples: o SES só envia de identidades que você provou controlar, e o provedor do destinatário só confia em mensagens que assinam e autenticam o próprio domínio.
5. Verificar o domínio: DKIM, SPF e DMARC
Antes de enviar qualquer coisa, a identidade precisa ser verificada. Você pode verificar um endereço de e-mail, que é confirmado por um link enviado para a caixa, ou um domínio inteiro, que é o caminho recomendado para uma aplicação. Verificar o domínio permite enviar de qualquer endereço dentro dele, como no-reply@exemplo.com.br, e ainda prova ao mundo que o domínio autorizou o SES.
Essa prova vem de três registros DNS, cada um com um papel diferente.
O DKIM (DomainKeys Identified Mail) assina cada mensagem com uma chave privada que só o SES tem. O destinatário busca a chave pública no DNS e confere a assinatura. Com o Easy DKIM, o SES gera a chave de 2048 bits para você, e a verificação se faz por registros CNAME que apontam para chaves publicadas pela AWS. Para cada token que o SES devolve, você publica um CNAME com nome no formato token._domainkey.exemplo.com.br apontando para um alvo sob dkim.amazonses.com. Esse formato segue a documentação de Easy DKIM, mas não copiei a forma literal da página nesta revisão, então confira o nome exato no console ou na saída do comando. A documentação da API avisa que, com alguns provedores de DNS, a verificação pode levar 72 horas ou mais.
O SPF (Sender Policy Framework) declara quais servidores podem enviar mensagens em nome de um domínio, por meio de um registro TXT. Por padrão, o SES usa um subdomínio próprio como endereço de retorno (o MAIL FROM), e nesse caso o SPF já vale sem você fazer nada. Para usar o seu próprio subdomínio como MAIL FROM, você publica dois registros nele: um MX apontando para feedback-smtp.<região>.amazonses.com com prioridade 10, e um TXT com "v=spf1 include:amazonses.com ~all". O MX também é como o SES recebe os bounces e complaints de volta, por isso ele é obrigatório nesse caso. O subdomínio precisa ser diferente dos que você usa para enviar e para receber e-mail.
O DMARC é a política que amarra os dois anteriores. Ele é publicado num TXT com o nome _dmarc.exemplo.com.br e diz ao destinatário o que fazer quando a mensagem falha na autenticação e para onde mandar relatórios. A documentação da SES mostra o formato v=DMARC1;p=quarantine;rua=mailto:..., mas recomenda começar em modo de monitoramento, com p=none, para observar os relatórios antes de endurecer a política. Uma política reject aplicada cedo demais pode derrubar envios legítimos que ainda não estão alinhados.
Para uma mensagem passar no DMARC, ela precisa ser autenticada por SPF ou por DKIM com o domínio alinhado ao From. O Easy DKIM sozinho já basta para o alinhamento de DKIM, e é por isso que ele é o ponto de partida.
Pela CLI, o fluxo começa com a criação da identidade de domínio. A saída traz os tokens DKIM que você precisa transformar em registros:
aws sesv2 create-email-identity --email-identity exemplo.com.br
A resposta tem o formato da API CreateEmailIdentity: um objeto DkimAttributes com SigningEnabled, Status e Tokens, além de IdentityType (DOMAIN) e VerifiedForSendingStatus (começa em false). Depois que os CNAMEs estão publicados, o acompanhamento é feito com:
aws sesv2 get-email-identity --email-identity exemplo.com.br
Quando a verificação termina, VerifiedForSendingStatus vira true e o status do DKIM muda para indicar sucesso. Para configurar o MAIL FROM próprio:
aws sesv2 put-email-identity-mail-from-attributes \
--email-identity exemplo.com.br \
--mail-from-domain mail.exemplo.com.br \
--behavior-on-mx-failure USE_DEFAULT_VALUE
O parâmetro --behavior-on-mx-failure define o que acontece se o MX não estiver correto. A interface da documentação oferece duas opções: usar o MAIL FROM padrão do SES, ou recusar a mensagem. Os nomes de API correspondentes (USE_DEFAULT_VALUE e REJECT_MESSAGE) não foram conferidos na referência nesta revisão, então confirme com aws sesv2 put-email-identity-mail-from-attributes help antes de usar. Escolha recusar quando preferir falhar a deixar a mensagem sair sem o alinhamento que você configurou.
O diagrama segue a ordem real: a identidade gera os tokens, você publica os registros na zona (que pode ser a do Route 53 ou de outro provedor), o SES consulta o DNS até encontrar os registros e só então libera o envio. A caixa de DMARC fica separada das outras porque ela não é verificada pelo SES; ela é lida pelo destinatário. Por isso um domínio pode aparecer como verificado e mesmo assim ter as mensagens marcadas como spam, se o DMARC ficou de fora.
6. Sandbox contra produção
Toda conta nova entra no sandbox, um modo de teste que existe para proteger a reputação do SES e de outros clientes. Nele, a aplicação só pode enviar para endereços e domínios verificados, ou para os endereços do simulador de caixa de correio da própria SES. O limite é de 200 mensagens em 24 horas e de 1 mensagem por segundo. A documentação também diz que o sandbox fica separado por região, então a conta pode estar em produção em uma região e em sandbox em outra.
O diagrama compara os dois modos com o mesmo envio. No sandbox, a mensagem para um endereço não verificado é recusada, e o desenho mostra isso com a seta barrada. Em produção, o mesmo envio passa, desde que os endereços From continuem verificados. Sair do sandbox não dispensa a verificação de identidade: ela continua exigida para o remetente.
Para pedir a saída do sandbox, o console mostra um aviso no painel da conta com as opções Get set up e depois Request production access. Pela CLI, o comando é este, com os valores trocados pelos seus:
aws sesv2 put-account-details \
--production-access-enabled \
--mail-type TRANSACTIONAL \
--website-url https://exemplo.com.br \
--additional-contact-email-addresses ops@exemplo.com.br \
--contact-language EN
Os valores aceitos para --mail-type são TRANSACTIONAL e MARKETING, e o idioma aceita EN ou JA, segundo a documentação. O português não é uma opção, por isso o exemplo usa EN. A AWS responde a pedidos em até 96 horas, segundo a documentação de produção, e pode pedir mais informações antes de aprovar. Um pedido bem feito costuma descrever como você trata bounces e complaints, porque isso é exatamente o que a AWS quer ver antes de liberar o envio em volume.
Para conferir o estado atual da conta, use get-account. Os campos que importam são ProductionAccessEnabled, que diz se a conta saiu do sandbox, e SendQuota, com Max24HourSend, MaxSendRate e SentLast24Hours:
aws sesv2 get-account --query '{Producao:ProductionAccessEnabled,Cota:SendQuota}'
A saída esperada é um objeto com Producao (false no sandbox) e Cota com os três valores. Os limites de produção são próprios de cada conta e crescem com o histórico de envio, então a cota de uma conta nova não diz nada sobre a de uma conta antiga.
7. Enviando um e-mail
O caminho mais curto para testar é a própria CLI, com um arquivo que descreve a mensagem:
{
"FromEmailAddress": "no-reply@exemplo.com.br",
"Destination": {"ToAddresses": ["destino@exemplo.com.br"]},
"Content": {
"Simple": {
"Subject": {"Data": "Teste", "Charset": "UTF-8"},
"Body": {"Text": {"Data": "Olá", "Charset": "UTF-8"}}
}
}
}
aws sesv2 send-email --cli-input-json file://envio.json
A saída é {"MessageId": "..."}, com um identificador que você guarda para rastrear o evento depois. O parâmetro --cli-input-json lê o corpo inteiro do arquivo, o que evita escapar aspas no shell. O Content pode ser Simple, quando a API monta a mensagem para você, Raw, quando você mesmo monta um MIME (necessário para anexos complexos), ou Template, com variáveis de personalização.
Em código, o SDK faz a mesma chamada. O exemplo em Go usa o SDK v2 e trata dois erros que valem a pena reconhecer: MessageRejected, quando o conteúdo é recusado, e SendingPausedException, quando a conta está com o envio pausado.
package main
import (
"context"
"errors"
"fmt"
"log"
"os"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/sesv2"
"github.com/aws/aws-sdk-go-v2/service/sesv2/types"
)
func enviar(ctx context.Context, cli *sesv2.Client, de, para, assunto, texto string) (string, error) {
out, err := cli.SendEmail(ctx, &sesv2.SendEmailInput{
FromEmailAddress: aws.String(de),
Destination: &types.Destination{
ToAddresses: []string{para},
},
Content: &types.EmailContent{
Simple: &types.Message{
Subject: &types.Content{Data: aws.String(assunto), Charset: aws.String("UTF-8")},
Body: &types.Body{
Text: &types.Content{Data: aws.String(texto), Charset: aws.String("UTF-8")},
},
},
},
})
if err != nil {
var rejeitada *types.MessageRejected
var pausada *types.SendingPausedException
if errors.As(err, &rejeitada) {
return "", fmt.Errorf("conteudo recusado pelo SES: %w", err)
}
if errors.As(err, &pausada) {
return "", fmt.Errorf("envio pausado na conta: %w", err)
}
return "", err
}
return aws.ToString(out.MessageId), nil
}
func main() {
ctx := context.Background()
cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(os.Getenv("AWS_REGION")))
if err != nil {
log.Fatal(err)
}
cli := sesv2.NewFromConfig(cfg)
id, err := enviar(ctx, cli, "no-reply@exemplo.com.br", "destino@exemplo.com.br", "Teste", "Olá")
if err != nil {
log.Fatal(err)
}
fmt.Println("MessageId:", id)
}
Os pontos que mais pesam nesse código estão em duas linhas. A primeira é o Charset explícito em assunto e corpo, que evita que caracteres como acentos cheguem com codificação errada. A segunda é o errors.As, que é a forma idiomática de Go de reconhecer um tipo de erro mesmo quando ele vem envolvido em outro. Sem esse teste, você só vê uma mensagem de texto, e não consegue decidir o que fazer com ela (tentar de novo, avisar o operador, registrar).
O mesmo envio em TypeScript usa o cliente v3, com SendEmailCommand. Os erros viram classes, e o teste é um instanceof:
import { SESv2Client, SendEmailCommand, MessageRejected, SendingPausedException } from "@aws-sdk/client-sesv2";
const cliente = new SESv2Client({ region: process.env.AWS_REGION ?? "us-east-1" });
export async function enviarEmail(de: string, para: string, assunto: string, texto: string): Promise<string | undefined> {
try {
const resposta = await cliente.send(
new SendEmailCommand({
FromEmailAddress: de,
Destination: { ToAddresses: [para] },
Content: {
Simple: {
Subject: { Data: assunto, Charset: "UTF-8" },
Body: { Text: { Data: texto, Charset: "UTF-8" } },
},
},
}),
);
return resposta.MessageId;
} catch (erro) {
if (erro instanceof MessageRejected) {
throw new Error(`conteudo recusado pelo SES: ${erro.message}`, { cause: erro });
}
if (erro instanceof SendingPausedException) {
throw new Error(`envio pausado na conta: ${erro.message}`, { cause: erro });
}
throw erro;
}
}
Os dois exemplos foram conferidos por compilação: o Go contra o módulo service/sesv2 do SDK v2 e o TypeScript com tsc --strict contra @aws-sdk/client-sesv2 na versão 3.1158.0, ambos em 2026-10-27. Eles não foram executados contra uma conta real, porque nesta revisão não houve envio de verdade.
O diagrama mostra o caminho de uma mensagem, de cima para baixo. A API confere se a identidade pode enviar antes de tudo, e é ali que nascem os erros de sandbox e de cota. Uma mensagem aceita ganha a assinatura DKIM e segue para o servidor do destinatário, e qualquer falha volta pelo caminho de bounce até um tópico SNS, que avisa a aplicação. Repare que o SendEmail devolve sucesso antes da entrega: o MessageId diz que o SES aceitou a mensagem, não que a caixa do destinatário a recebeu. Por isso o acompanhamento de bounces é obrigatório em produção.
8. Bounces, complaints e supressão
Um bounce é uma mensagem que não pôde ser entregue. O hard bounce é uma falha permanente, como um endereço que não existe. O soft bounce é temporário, como uma caixa cheia, e o SES tenta de novo algumas vezes antes de desistir. Só os hard bounces contam para a taxa de bounce da conta, e a documentação recomenda não insistir em endereços que deram hard bounce.
Um complaint acontece quando o destinatário marca a mensagem como spam e o provedor encaminha essa reclamação de volta. Ele pesa mais que um bounce, porque indica que a pessoa não queria receber aquilo.
O SES mantém uma lista de supressão global, que contém endereços com hard bounce recente em qualquer cliente. Um envio para um endereço nessa lista não chega a sair: a chamada responde com sucesso, mas o SES trata a mensagem como hard bounce. Um endereço pode ficar nessa lista por até 14 dias, e você também pode manter uma lista de supressão própria, no nível da conta.
Para receber esses eventos, o caminho recomendado é um configuration set com um destino de evento apontando para um tópico SNS. Cada mensagem enviada com esse configuration set gera eventos de BOUNCE, COMPLAINT, DELIVERY e outros, e a aplicação decide o que fazer. Pela CLI:
aws sesv2 create-configuration-set --configuration-set-name envios-transacionais
aws sesv2 create-configuration-set-event-destination \
--configuration-set-name envios-transacionais \
--event-destination-name eventos-sns \
--event-destination file://evento.json
O arquivo evento.json descreve o destino:
{
"Enabled": true,
"MatchingEventTypes": ["BOUNCE", "COMPLAINT"],
"SnsDestination": {"TopicArn": "arn:aws:sns:us-east-1:123456789012:ses-eventos"}
}
Para enviar com esse conjunto, use o parâmetro --configuration-set-name no send-email. A estrutura do evento acima segue a referência da API do SES; ela não foi colada da página literal nesta revisão, então confira os nomes com aws sesv2 create-configuration-set-event-destination help antes de depender dela.
Quando um endereço já deu hard bounce ou complaint, a aplicação deve parar de enviar para ele. Além da supressão automática, você pode registrar o endereço explicitamente com put-suppressed-destination, informando o endereço e o motivo (BOUNCE ou COMPLAINT). Assim o envio é bloqueado pela sua própria lista, sem depender da global.
Sobre os limiares de taxa de bounce e complaint que levam a SES a colocar uma conta em revisão ou a pausar o envio, a página de métricas de reputação consultada não mostra os números no texto que li. Os valores de 5% e 0,1% que circulam em outras fontes não foram verificados aqui. Para a sua conta, o valor certo está no dashboard de reputação do console, que mostra a taxa atual.
9. Erros comuns e como resolver
O primeiro erro é MessageRejected ao enviar para um endereço que você não esperava, e quase sempre é o sandbox recusando um destinatário não verificado. A solução é verificar o destinatário no ambiente de teste ou pedir a saída do sandbox. A mensagem exata da resposta não foi conferida nesta revisão, então use o MessageRejected como sinal e leia o detalhe completo no log da sua aplicação.
MailFromDomainNotVerifiedException aparece quando o domínio de MAIL FROM não tem o MX correto, e a configuração REJECT_MESSAGE faz o SES recusar as mensagens até o problema ser corrigido. Verifique os registros da seção 5 e confira se há exatamente um MX no subdomínio, porque a documentação diz que dois MX invalidam a configuração.
SendingPausedException e AccountSuspendedException são problemas de reputação, não de código. A conta foi pausada ou restringida, e a solução está no histórico de bounces e complaints, não na aplicação. Corrija a origem dos problemas (listas antigas, endereços inválidos) e siga o processo de revisão do console.
Mensagens que chegam sempre no spam quase sempre têm um destes problemas: o DKIM não está verificado, o DMARC ficou fora ou o From não está alinhado ao domínio autenticado. Use um teste de caixa de entrada, como enviar para um endereço de teste de um provedor e olhar o cabeçalho Authentication-Results, que mostra os resultados de SPF, DKIM e DMARC. Se os três passam e a mensagem ainda cai no spam, o conteúdo ou o histórico de envio passa a ser o assunto, e não a configuração.
Um atraso grande na verificação do DKIM costuma ser DNS. Se o registro não aparece depois de muito tempo, confira o nome exato, porque um ponto a mais ou a falta de um sufixo impede a verificação sem mensagem de erro clara.
Por fim, não trate o MessageId como prova de entrega. Ele confirma que o SES aceitou a mensagem. Quem diz se ela chegou é o evento de DELIVERY ou de BOUNCE no configuration set.
10. Exercícios
- Verifique um domínio de teste, publique os registros DKIM e confira com
get-email-identityatéVerifiedForSendingStatusvirartrue. Depois descreva o que aconteceria se o DKIM passasse e o DMARC falhasse. - Envie uma mensagem para o simulador de caixa de correio da SES e depois para um endereço não verificado no sandbox. Compare as duas respostas e explique a diferença com base na seção 6.
- Monte o configuration set com o destino SNS e envie um e-mail para um endereço inexistente. Descreva a sequência de eventos que você espera ver, e compare com o que realmente chegou.
11. Glossário
- Identidade: endereço ou domínio que você provou controlar e a partir do qual pode enviar.
- DKIM: assinatura criptográfica da mensagem, verificada por uma chave pública publicada no DNS.
- SPF: registro que lista os servidores autorizados a enviar pelo domínio de MAIL FROM.
- DMARC: política que combina SPF e DKIM com o
Fromvisível e diz o que fazer com falhas. - MAIL FROM: endereço técnico de retorno da mensagem, usado para bounces.
- Sandbox: modo de teste da conta nova, com limite de destinatários e de volume.
- Configuration set: conjunto de regras de envio que pode mandar eventos para outros serviços.
- Supressão: lista de endereços que não devem receber mensagens, por bounce ou complaint.
Fontes
Consultadas em 2026-10-27:
- Sair do sandbox do SES (request production access)
- Easy DKIM
- SPF com domínio MAIL FROM personalizado
- DMARC
- Entregabilidade no SES: bounces, complaints e supressão
- Dashboard de reputação
- CreateEmailIdentity (referência da API SESv2)
- GetAccount (referência da API SESv2)
- SendEmail (referência da API SESv2)
- Preços do SES
- Referência de comandos da AWS CLI 2.33.18 instalada localmente, conferida com
aws sesv2 <comando> help.
Não verificado nesta revisão: o limiar de bounce e de complaint que pausa uma conta, o formato literal do nome do CNAME de DKIM, a mensagem exata de erro do sandbox, o formato completo de EventDestination, os nomes de API de --behavior-on-mx-failure e a cota gratuita de 62.000 mensagens por mês a partir da EC2.