MillionSend Docs
Conceitos

Recebimento

Receba e-mails no seu próprio domínio, leia pela API compatível com o Resend, espere por eles, responda e seja avisado pelo webhook email.received a cada mensagem. Beta.

O recebimento transforma um domínio que você verificou num lugar que recebe e-mails. Um e-mail enviado para os endereços dele chega ao servidor de e-mail do MillionSend, fica guardado com criptografia e volta para você pela API, pelo webhook email.received, pelo painel (Caixas de entrada → Todos os recebidos) e pelo servidor MCP. A API é a de recebimento do Resend, então os SDKs oficiais do Resend funcionam com o MillionSend sem mudanças, forward() incluído.

Quais endereços recebem é você quem decide: o endereço de cada caixa de entrada que você criar, que guarda os e-mails em conversas com respostas, rascunhos e regras de segurança para um agente de IA ou para pessoas, e, com o pega-tudo ligado, todos os outros endereços do domínio.

O recebimento está em beta público e aparece com o selo Beta no painel. Toda mensagem ganha uma nota de spam e um nível de confiança no remetente, e passa por um antivírus onde a instância roda um. E-mail infectado fica retido; fora isso, o e-mail num endereço do pega-tudo é guardado do jeito que chegou, com esses resultados para o seu código decidir o que fazer.

Como funciona

O e-mail chega até você do mesmo jeito que chega a qualquer caixa de entrada: o servidor de quem envia consulta o registro MX do domínio do destinatário e entrega no host que ele indica.

  1. O servidor de quem envia lê o registro MX do seu domínio e se conecta ao MX do MillionSend.
  2. Para cada destinatário (RCPT TO), o MX confere se o domínio recebe aqui e se o endereço tem uma caixa de entrada, a não ser que o pega-tudo do domínio esteja ligado. Um endereço de qualquer outro domínio é recusado, então o MX nunca repassa e-mail para lugar nenhum.
  3. A mensagem vai chegando (no máximo 40 MB). O MX verifica SPF, DKIM, DMARC e ARC; o MillionSend passa a mensagem pelo antivírus, onde a instância roda um, dá uma nota de spam e a guarda antes de o MX responder 250. A partir daí a mensagem é sua, e o servidor de quem enviou terminou o trabalho.
  4. O email.received dispara, e a mensagem aparece na API e no painel. O e-mail que uma caixa retém, na quarentena ou durante a análise, dispara quando é entregue ou liberado.

Toda recusa acontece durante essa conversa SMTP, então o servidor de quem envia continua com a mensagem e decide o que fazer com ela. O MillionSend nunca envia um bounce por conta própria.

RespostaQuandoO servidor de quem envia
550 5.1.1O domínio não recebe aqui: é desconhecido ou está com o recebimento desligado.Devolve a mensagem ao remetente.
550 5.1.1O endereço não tem caixa de entrada, ou a caixa dele expirou, e o pega-tudo do domínio está desligado ("No such recipient here").Devolve a mensagem ao remetente.
550 5.7.1A equipe dona do domínio está suspensa.Devolve a mensagem ao remetente.
552 5.3.4A mensagem passa de 40 MB.Devolve a mensagem ao remetente.
452 4.2.2A franquia de recebimento da equipe acabou (veja preços), ou a caixa recebeu mais e-mails do que pode numa hora (200 mensagens numa caixa de agente, 2.000 numa caixa de pessoas).Guarda a mensagem e tenta de novo mais tarde.
Outros 4xx, como 451 4.3.0Um problema temporário do lado do MillionSend, ou mais destinatários do que uma entrega aceita (50).Guarda a mensagem e tenta de novo, por dias se precisar.

Configuração

Domínios de recebimento apontam o registro MX para mx.millionsend-dns.com.

1. Escolha um subdomínio

Quem envia entrega no registro MX com o menor número de prioridade do domínio do destinatário, então o MX de recebimento assume todos os endereços do nome em que for publicado. Publique-o num nome que ainda não recebe e-mail, como inbound.example.com: suas caixas em example.com continuam no seu provedor atual. Só receba num domínio raiz quando nada mais recebe e-mail lá.

2. Adicione e verifique

O recebimento pertence a um nome de domínio exato, e só a um domínio que a sua equipe verificou. Para receber em inbound.example.com, adicione o próprio inbound.example.com em Domínios (ou com POST /domains) e verifique-o com os registros DKIM e MAIL FROM dele, como no envio. Verificar example.com não cobre os subdomínios. Depois de verificado, o subdomínio também pode enviar, então as respostas podem sair dos mesmos endereços.

3. Ative o recebimento

Abra o domínio e ligue Ativar recebimento, ou use a API:

curl -X PATCH "https://api.millionsend.com/domains/{id}" \
  -H "Authorization: Bearer ms_..." \
  -H "Content-Type: application/json" \
  -d '{ "capabilities": { "receiving": "enabled" } }'

Toda resposta de domínio informa o estado, por exemplo { "sending": "enabled", "receiving": "enabled" }. Um nome de domínio recebe em uma equipe só: um nome em que outra equipe já recebe é recusado com 409.

4. Publique o registro MX

Os registros do domínio ganham uma linha de recebimento, junto da opção Ativar recebimento na página do domínio (record: "Receiving" na API). Adicione-a no seu provedor de DNS:

TipoNomeValorPrioridade
MXinbound.example.commx.millionsend-dns.com10

A linha fica verificada quando o DNS público lista mx.millionsend-dns.com entre os registros MX do nome com o menor número de prioridade. O número em si é só uma sugestão; o que importa é que nenhum outro registro MX desse nome tenha um número menor. A checagem é feita ao vivo e nunca afeta o status de envio do domínio. Na auto-hospedagem, o valor é o RECEIVING_MX_HOST da sua instância.

5. Escolha quais endereços recebem

Crie uma caixa de entrada para cada endereço que deve receber, ou ligue o Pega-tudo na página do domínio para receber em todos os endereços (veja Quais endereços recebem). Sem nenhum dos dois, o domínio recusa todos os e-mails.

6. Faça um teste

De qualquer caixa de e-mail, mande uma mensagem para um desses endereços, como hello@inbound.example.com. Ela aparece em Caixas de entrada → Todos os recebidos em segundos, e todo endpoint de webhook inscrito em email.received recebe uma entrega.

Para parar de receber, desligue o recebimento ("receiving": "disabled"). E-mails novos passam a ser recusados em até um minuto; o que já chegou fica até a retenção apagar. Remova também o registro MX.

Quais endereços recebem

Um domínio de recebimento recebe e-mails:

  • no endereço de cada caixa de entrada dele, e nas variações com +tag: orders+4821@ chega à caixa de orders@;
  • com o pega-tudo ligado, em todos os outros endereços também. support@, invoices@, reply+ticket-4821@: qualquer parte local funciona, sem nada para criar antes; decida no seu código o destino de cada mensagem pelo endereço em que ela chegou.

Com o pega-tudo desligado, qualquer outro endereço é recusado durante a conversa SMTP com 550 5.1.1 No such recipient here, então o servidor de quem envia devolve a mensagem ao remetente e nada é guardado. O e-mail para uma caixa aparece nas conversas dela e em Todos os recebidos; o e-mail para um endereço do pega-tudo aparece só em Todos os recebidos.

Donos e admins ligam o pega-tudo na página do domínio, ou com PATCH /domains/{id} e { "receiving_catch_all": true }; as respostas de domínio trazem receiving_catch_all. Ele começa desligado, e foi ligado em todo domínio que já recebia e-mail antes de as caixas existirem. No MillionSend Cloud ele exige um plano pago (o Free responde 403 plan_limit_reached); depois de ligado, continua ligado mesmo depois de um downgrade.

  • received_for lista os endereços dos seus domínios para os quais o servidor de quem envia de fato entregou (os destinatários do envelope SMTP), incluindo quem estava em Cco. to e cc são o que os cabeçalhos da mensagem dizem e podem citar qualquer pessoa, então use received_for para decidir o destino.
  • Uma mensagem é guardada uma vez por equipe. Enviada para três endereços seus, ela vira um e-mail recebido com os três em received_for, uma entrega de webhook e uma mensagem contada. Uma nova tentativa do servidor de quem envia também é reconhecida e guardada uma vez só. Quando a mesma mensagem chega de novo para outros endereços seus (o servidor de quem envia divide os destinatários em várias entregas, por exemplo passando de 50), eles entram em received_for, e chega mais um email.received com o mesmo email_id só com eles.
  • Só o nome exato recebe: e-mail para x@eu.inbound.example.com é recusado, a não ser que esse subdomínio também receba.
  • Quando uma mensagem chega aos domínios de recebimento de duas equipes, cada equipe ganha a própria cópia, só com os próprios destinatários.

Limites

LimiteValor
Tamanho da mensagem40 MB (41.943.040 bytes) para a mensagem inteira como foi enviada: cabeçalhos, texto, HTML e anexos codificados para e-mail. A codificação base64 deixa os anexos cerca de um terço maiores, então cabem uns 30 MB de arquivos. Uma mensagem maior é recusada com 552 5.3.4 enquanto ainda está chegando.
Retenção30 dias na nuvem. Na auto-hospedagem, o conteúdo segue a retenção de e-mails da instância (Configurações → Instância, 30 dias por padrão), e remetente, destinatários e assunto seguem EMAIL_METADATA_RETENTION_DAYS (30 por padrão).
FiltroE-mail infectado fica retido. Fora isso, o e-mail num endereço do pega-tudo é guardado do jeito que chegou: confira authentication, screening e spam_verdict antes de confiar num remetente. As caixas de entrada somam as próprias regras.

Quando a retenção do conteúdo termina, html, text, headers e raw passam a vir null, attachments fica vazio e os links de download param de funcionar; remetente, destinatários e assunto ficam até a própria linha ser apagada.

Preços

O e-mail recebido conta uma vez por mensagem por equipe, não importa para quantos endereços seus ela foi enviada, num contador de uso próprio, ao lado dos seus envios. Quanto custa depende do plano:

PlanoRecebimento grátisDepois disso
FreeDivide a cota diária de envio de 100Adiado com 452 4.2.2 até a meia-noite UTC
Starter3.000 por dia UTC (o dobro da cota diária de 1.500), contados à parte dos enviosAdiado com 452 4.2.2 até a meia-noite UTC
Pro, ScaleO dobro do volume mensal do plano por período de cobrança (200.000 no Pro 100K)Cada e-mail recebido conta como um décimo de e-mail no volume e no excedente
  • Free: e-mails recebidos e envios enchem a mesma contagem diária, com a mesma tolerância de 50% dos envios, então e-mails novos são adiados quando os dois juntos chegam a 150 num dia UTC. Essa contagem é dividida entre todas as equipes Free que você criou. Ou seja, o recebimento pode gastar o dia: envios além da cota esperam até a meia-noite UTC como quaisquer outros, e o recebimento também conta para os avisos de cota.
  • Pro e Scale: depois da franquia grátis, cada décimo e-mail recebido soma um e-mail à contagem do período de cobrança, então mais 1.000 e-mails recebidos contam como 100 e-mails: dentro do volume incluído eles o consomem, além dele são cobrados pela taxa de excedente do seu plano. Depois da franquia, o e-mail recebido é adiado com 452 4.2.2 quando a contagem do período chega a 5 vezes o volume incluído com o excedente ligado, ou ao volume incluído com o excedente desligado ou pausado por um pagamento pendente.
  • Um adiamento é temporário: o servidor de quem envia guarda a mensagem e tenta de novo, normalmente por vários dias, então ela chega assim que houver espaço.

Configurações → Uso mostra os e-mails recebidos e a franquia grátis. Uma instância auto-hospedada conta os e-mails recebidos e nunca os limita. As caixas de entrada em si são grátis.

Lendo e-mails recebidos

Use um SDK do MillionSend a partir da 0.9.0 (emails.receiving), o SDK oficial do Resend apontado para o MillionSend (RESEND_BASE_URL) ou HTTP direto. Todo endpoint abaixo exige uma chave de acesso total; uma chave de somente envio recebe 403 restricted_api_key. Uma chave restrita a um domínio só lê os e-mails recebidos nesse domínio, e uma chave restrita a uma caixa só os e-mails entregues nela.

import { Resend } from "resend";

// RESEND_API_KEY=ms_...  RESEND_BASE_URL=https://api.millionsend.com
const resend = new Resend(process.env.RESEND_API_KEY);

const { data: page } = await resend.emails.receiving.list({ limit: 20 });
const { data: email } = await resend.emails.receiving.get(page.data[0].id);
const { data: files } = await resend.emails.receiving.attachments.list({ emailId: email.id });

Listar

GET /emails/receiving lista do mais novo para o mais antigo, como o Resend: after avança para e-mails mais antigos, before volta para os mais novos. Aceita limit (1–100, padrão 20; o Resend devolve todos quando falta o limit) e um cursor after ou before, como toda listagem, além destes filtros:

ParâmetroLista
inbox_idOs e-mails entregues nessa caixa. Uma caixa que a chave não alcança responde 404, sem esperar o wait; uma chave restrita a uma caixa que pede outra recebe 403 restricted_api_key.
toOs e-mails com esse endereço em received_for.
fromOs e-mails desse endereço, ou de um domínio e dos subdomínios dele.
subject_containsOs e-mails cujo assunto contém o texto (até 200 caracteres).
sinceOs e-mails recebidos depois desse momento (ISO 8601).
unreadtrue ou false, junto com inbox_id (uma chave restrita a uma caixa não precisa dele).
waitVeja Esperar por e-mails novos.
include_quarantined=trueOs retidos também.
quarantined=trueSó os retidos.
curl "https://api.millionsend.com/emails/receiving?limit=20" \
  -H "Authorization: Bearer ms_..."
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "id": "4ef9a417-02e9-4d39-ad75-9611e0fcc33c",
      "to": ["support@inbound.example.com"],
      "from": "ada@example.org",
      "created_at": "2026-10-11T14:37:40.951Z",
      "subject": "Order #1042",
      "bcc": [],
      "cc": [],
      "reply_to": [],
      "received_for": ["support@inbound.example.com"],
      "message_id": "<CAF1x7Qm9@mail.example.org>",
      "attachments": [
        {
          "id": "b344102e-962e-8943-aef6-271c7743e1dd",
          "filename": "invoice.pdf",
          "content_type": "application/pdf",
          "content_id": null,
          "content_disposition": "attachment",
          "size": 48213
        }
      ],
      "inbox_id": null,
      "thread_id": null,
      "inboxes": [],
      "screening": { "verdict": "clean", "sender_trust": "external" },
      "spam_verdict": "pass",
      "virus_verdict": "pass",
      "extracted": { "kinds": [] }
    }
  ]
}

A listagem mostra os e-mails de endereços do pega-tudo e os entregues numa caixa; os retidos ficam de fora, a não ser que você peça. Os itens trazem os campos do MillionSend depois dos do Resend:

  • inbox_id e thread_id: a caixa em que a mensagem foi entregue (com uma chave restrita a uma caixa, a dela), senão null. inboxes lista todas as caixas a que ela chegou, com inbox_id, thread_id e verdict, nas leituras com acesso total.
  • screening: verdict e sender_trust (veja Segurança de agentes).
  • spam_verdict, virus_verdict: veja Spam, vírus e confiança.
  • extracted.kinds: code e link quando a mensagem traz um código de verificação ou um link de acesso; os valores em si só aparecem em Ler um.
  • read, quando a leitura é sobre uma caixa (inbox_id, ou uma chave restrita a uma caixa).

include_quarantined e quarantined pedem uma chave de acesso total que não esteja restrita a uma caixa; uma chave restrita, e o servidor MCP, recebem 403 restricted_api_key.

Esperar por e-mails novos

O wait (de 1 a 50 segundos) segura a requisição até chegar um e-mail que bata com os filtros, e aí responde na hora; no fim do prazo, responde uma lista vazia. Ele funciona junto com os filtros acima, então um agente pode esperar a próxima mensagem para uma caixa, de um remetente:

curl "https://api.millionsend.com/emails/receiving?inbox_id=7eeb9517-aed7-40d7-ad65-500be42ed147&from=example.org&wait=30" \
  -H "Authorization: Bearer ms_..."
  • Ele devolve e-mails mais novos que o before, o cursor desta listagem em direção aos mais novos, ou mais novos que o since. Sem nenhum dos dois, contam os últimos 60 segundos, então uma mensagem que chegou logo antes da chamada não se perde.
  • Se houver mais e-mails do que o limit, ele devolve os mais novos, até o limit, com has_more: true. Para ler todos, liste sem o wait: passe o mesmo before e, depois, o primeiro id de cada página enquanto o has_more for true.
  • A resposta traz também held: as mensagens que bateram e ficaram retidas desde então, como { id, inbox_id, reason, quarantine_reason, created_at } e nada mais, para o agente saber que deve parar de esperar e avisar uma pessoa. Para uma chave restrita a uma caixa ou um cliente MCP, o subject_contains não filtra o held: um e-mail retido nunca é comparado pelo que ele diz.
  • Quando o prazo acaba, a resposta é 200 com { "object": "list", "has_more": false, "data": [], "held": [] }.
  • Uma equipe segura 20 esperas ao mesmo tempo em cada processo da API, e todas as equipes juntas, 200; mais uma responde 429 too_many_waits com retry-after: 1.

50 segundos fica abaixo dos limites de 60 segundos dos clientes MCP e de proxies comuns, como o proxy_read_timeout padrão do nginx.

Ler um

GET /emails/receiving/{id} acrescenta o conteúdo, os resultados de autenticação e um link de download da mensagem original:

{
  "object": "email",
  "id": "4ef9a417-02e9-4d39-ad75-9611e0fcc33c",
  "to": ["support@inbound.example.com"],
  "from": "ada@example.org",
  "created_at": "2026-10-11T14:37:40.951Z",
  "subject": "Order #1042",
  "html": "<p>Hi! The invoice is attached.</p>",
  "html_format": "data_uri",
  "text": "Hi! The invoice is attached.",
  "headers": {
    "from": "Ada Lovelace <ada@example.org>",
    "to": "support@inbound.example.com",
    "subject": "Order #1042",
    "mime-version": "1.0"
  },
  "bcc": [],
  "cc": [],
  "reply_to": [],
  "received_for": ["support@inbound.example.com"],
  "authentication": { "spf": "pass", "dkim": "pass", "dmarc": "pass" },
  "message_id": "<CAF1x7Qm9@mail.example.org>",
  "raw": {
    "download_url": "https://api.millionsend.com/receiving/raw/4ef9a417-02e9-4d39-ad75-9611e0fcc33c?expires=1791733061&signature=…",
    "expires_at": "2026-10-11T15:37:41.000Z"
  },
  "attachments": [
    {
      "id": "b344102e-962e-8943-aef6-271c7743e1dd",
      "filename": "invoice.pdf",
      "content_type": "application/pdf",
      "content_id": null,
      "content_disposition": "attachment",
      "size": 48213
    }
  ],
  "inbox_id": null,
  "thread_id": null,
  "inboxes": [],
  "screening": {
    "verdict": "clean",
    "sender_trust": "external",
    "quarantine_reason": null,
    "reasons": [],
    "injection_score": null,
    "phishing_score": null,
    "hidden_text_removed": false
  },
  "spam_verdict": "pass",
  "spam_score": 0,
  "virus_verdict": "pass",
  "virus_signature": null,
  "extracted_text": "Hi! The invoice is attached.",
  "extracted": {
    "kinds": [],
    "code": null,
    "link": null,
    "link_domain_matches_sender": null,
    "ambiguous": false
  }
}
  • html_format: data_uri, o padrão, troca as referências cid: de imagem do HTML pelas próprias imagens inline. ?html_format=cid mantém as referências; relacione-as ao content_id dos anexos. Qualquer outro valor dá 422. Quando colocar as imagens no HTML passaria de 40 MB (em geral uma conversa longa que repete a mesma imagem muitas vezes), as referências ficam e html_format vem cid, seja qual for o pedido, como o Resend faz a partir de 100 MB: confira o html_format em vez de contar com ele.
  • from é só o endereço; o nome de exibição fica em headers.from.
  • headers traz os cabeçalhos de nível superior com nomes em minúsculas, um valor por nome; um cabeçalho repetido, como received, tem os valores unidos por quebras de linha. Quando a mensagem não traz Return-Path, return-path mostra o remetente do envelope que o servidor de quem envia informou, <> para um bounce.
  • html ou text vem null quando a mensagem não tem essa parte. Uma mensagem cujo MIME não dá para interpretar ainda é guardada e listada; o que não dá para ler dela vem null (headers também, quando nenhum cabeçalho é reconhecido), e o raw traz a mensagem inteira.
  • O html vem exatamente como chegou. O text vem sem caracteres invisíveis (de largura zero, de direção do texto, tags Unicode), com as quebras de linha mantidas.
  • message_id vem do cabeçalho Message-ID da mensagem. Quando ela não tem um, o MillionSend cria <id@received.invalid>.
  • extracted_text é a parte da resposta da mensagem, sem o texto que uma pessoa não vê, com o histórico citado e as assinaturas separados (veja Segurança de agentes).
  • extracted traz o code de verificação ou o link de acesso da mensagem (com link_domain_matches_sender), ou ambiguous: true quando dois são igualmente prováveis. Eles nunca saem de e-mail retido nem de um remetente que falhou na autenticação, e toda leitura de um deles fica registrada na atividade da caixa.

O e-mail retido responde 403 email_quarantined ("This email is quarantined; a person can release it in the dashboard") para chaves restritas a uma caixa e clientes MCP, e o e-mail ainda em análise responde 404 para eles.

authentication informa SPF, DKIM e DMARC com as palavras do Resend:

ValorSignificaResultados da checagem
passA checagem passou.pass
failA checagem falhou.fail
graySem resposta clara: não havia registro ou assinatura para checar, ou o resultado foi neutro.none, neutral, policy, softfail
processing_failedA checagem não terminou, por exemplo por um erro de DNS ou um registro quebrado.temperror, permerror
unknownNão foi checado.

Como no Resend, o DKIM só passa com uma assinatura do domínio do from (ou de outro domínio da mesma organização): uma assinatura válida de qualquer outro domínio vem gray, e policy no painel. O DMARC vem gray quando o domínio do from publica p=none e o SPF ou o DKIM passou, none no painel.

dmarc: "pass" quer dizer que o domínio do from autorizou a mensagem. Qualquer outro valor quer dizer que o from pode ser falso, mas gray também aparece em e-mail legítimo de um domínio que pede para o DMARC não ser aplicado.

O MX também guarda os detalhes por trás dessas palavras: o resultado do DMARC antes de ser resumido, a política que o domínio do remetente publica, os domínios cujas assinaturas DKIM estavam alinhadas e o domínio que o SPF checou. A API mantém as palavras do Resend; o painel usa os detalhes para explicar cada resultado cinza, e o nível de confiança no remetente é calculado a partir deles:

Resultado cinzaO que quer dizer
DMARC, a mensagem passouPassou no DMARC, mas o domínio do remetente usa p=none, então os servidores não são orientados a bloquear e-mails que falham.
DMARC, a mensagem falhouFalhou no DMARC, e o domínio do remetente usa p=none, que não pede para bloquear.
DMARC, sem registroO domínio do remetente não tem política DMARC.
DKIM policyAssinatura válida de outro domínio, não do domínio do remetente, então não prova quem escreveu.
SPF softfailO servidor de envio não está na lista do domínio do remetente, que só pede uma falha leve.
SPF neutral ou noneO domínio do remetente não diz quais servidores podem enviar por ele.

processing_failed quer dizer que não deu para verificar: o DNS demorou ou o registro está malformado.

Spam, vírus e confiança

Cada mensagem traz mais três resultados:

  • spam_verdict: pass, gray ou fail, a partir do spam_score, uma nota local montada com a autenticação, a saudação do servidor de envio, cabeçalhos que faltam, um nome de exibição que mostra outro endereço, um Reply-To de outra organização, texto oculto, encurtadores de link e links com endereço IP, links cujo texto cita outro domínio, e anexos de programas ou scripts. De 50 para cima é reprovado, de 25 a 49 é cinza. Nenhuma lista de bloqueio externa é consultada. Numa caixa, o e-mail reprovado vai para a pasta spam; num endereço do pega-tudo, ele só é informado.
  • virus_verdict: pass, fail (com virus_signature, o malware encontrado), processing_failed (o antivírus não terminou; a mensagem é entregue, com um aviso) ou null (a instância não verifica). O e-mail infectado fica retido: numa caixa ele vai para a quarentena, e num endereço do pega-tudo sai da listagem e não dispara email.received até uma pessoa liberá-lo no painel. O raw dele vem null, os downloads respondem 410 email_blocked, e o encaminhamento pelo servidor o recusa, mesmo depois de liberado.
  • screening.sender_trust: o quanto o remetente está comprovado, de owner até failed; veja Segurança de agentes.

As mensagens recebidas antes de esses resultados existirem vêm com null.

Anexos

GET /emails/receiving/{email_id}/attachments lista todos os anexos de um e-mail de uma vez, como o Resend (passe limit para paginar), e GET /emails/receiving/{email_id}/attachments/{id} devolve um deles, cada um com um link de download novo:

{
  "object": "attachment",
  "id": "b344102e-962e-8943-aef6-271c7743e1dd",
  "filename": "invoice.pdf",
  "size": 48213,
  "content_type": "application/pdf",
  "content_disposition": "attachment",
  "download_url": "https://api.millionsend.com/receiving/attachments/4ef9a417-02e9-4d39-ad75-9611e0fcc33c/b344102e-962e-8943-aef6-271c7743e1dd?expires=1791733061&signature=…",
  "expires_at": "2026-10-11T15:37:41.000Z"
}

filename e content_id ficam de fora quando a parte não tem. Os ids de anexo nunca mudam enquanto o e-mail existir. Os nomes de arquivo são limpos antes de chegar a você: caracteres de controle são removidos, e / e \ viram _.

Downloads

raw.download_url e o download_url de cada anexo funcionam sem chave de API: o próprio link é a credencial.

  • Um link é assinado para um e-mail e um anexo (ou a mensagem original) de uma equipe, e para de funcionar no seu expires_at, uma hora depois da resposta da API; depois disso, ou se for alterado, responde 403. Quem tiver o link consegue baixar até lá, então não o registre em log nem o repasse. Cada chamada aos endpoints acima assina links novos.
  • O download do raw é a mensagem exatamente como o servidor de quem envia a transmitiu (message/rfc822), byte a byte. O download de um anexo traz os bytes decodificados dele, sempre como application/octet-stream; o tipo que o remetente declarou fica em content_type.
  • Downloads sempre são servidos como arquivo (Content-Disposition: attachment), nunca exibidos numa aba do navegador.
  • Uma mensagem com vírus responde 410 email_blocked ("This email carries malware; downloads are blocked") antes de qualquer coisa ser descriptografada.

Encaminhar e responder

resend.emails.receiving.forward() funciona como no Resend: lê o e-mail, baixa o raw.download_url e envia a mensagem de novo por POST /emails.

await resend.emails.receiving.forward({
  emailId: "4ef9a417-02e9-4d39-ad75-9611e0fcc33c",
  from: "Support <support@inbound.example.com>",
  to: "success@simulator.amazonses.com",
});

Por padrão o encaminhamento leva o assunto, o texto, o HTML, as imagens inline e os anexos originais; com passthrough: false e o seu próprio text ou html, o original vai anexado como forwarded_message.eml, com assunto Fwd:. Um encaminhamento é um envio comum: o from precisa ser de um domínio verificado, e ele conta na sua cota de envio. POST /emails aceita imagens inline (content_id), então o encaminhamento padrão mantém as imagens. Mas um envio leva no máximo 10 MiB de anexos (no Resend, 40 MB): o modo padrão funciona quando os anexos do original cabem em 10 MiB, e o passthrough: false, que anexa a mensagem inteira, quando a própria mensagem cabe. Um encaminhamento maior é recusado com 422 antes de entrar na fila.

Para responder na mesma conversa, envie com In-Reply-To e References iguais ao message_id recebido:

await resend.emails.send({
  from: "Support <support@inbound.example.com>",
  to: email.from,
  subject: `Re: ${email.subject}`,
  text: "Thanks, we're on it.",
  headers: { "In-Reply-To": email.message_id, References: email.message_id },
});

Responder e encaminhar pelo servidor

POST /emails/receiving/{id}/reply e POST /emails/receiving/{id}/forward fazem isso numa chamada só, com os cabeçalhos da conversa já preenchidos:

curl -X POST "https://api.millionsend.com/emails/receiving/4ef9a417-02e9-4d39-ad75-9611e0fcc33c/reply" \
  -H "Authorization: Bearer ms_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1042-reply" \
  -d '{ "text": "Thanks, we are on it." }'
  • Responder aceita text ou html, subject, cc, bcc e reply_all. Vai para o Reply-To da mensagem, ou então para o remetente; o reply_all acrescenta os outros destinatários como cc, e o bcc nunca é copiado.
  • Encaminhar aceita to (obrigatório), cc, bcc, um recado em text ou html, subject e mode: inline, o padrão, cita o original abaixo de uma linha ---------- Forwarded message --------- com os anexos dele; attachment anexa a mensagem inteira como forwarded_message.eml. Os dois contam nos 10 MiB de anexos que um envio leva.
  • O from é qualquer endereço de um domínio verificado da sua equipe; por padrão, o endereço da caixa a que a mensagem chegou, senão o primeiro endereço de received_for. O inbox_id escolhe entre as caixas a que a mensagem chegou.
  • requires_approval: true segura o envio para uma pessoa.

Eles respondem 200 com { "object": "email", "id", "thread_id", "inbox_id" }; 202 com um rascunho quando as regras da caixa retêm o envio; 403 para e-mail retido ou infectado (email_quarantined), para as regras da caixa (inbox_send_policy) ou para uma chave restrita fora da própria caixa (restricted_api_key); 404; 409 para um Idempotency-Key reaproveitado; 422; e 429 inbox_send_cap_reached. O e-mail de um endereço do pega-tudo é enviado como qualquer envio: nenhuma regra de caixa vale, a não ser que o from seja o endereço de uma caixa. Veja Caixas de entrada → Responder e encaminhar.

Excluir e-mails recebidos

DELETE /emails/receiving/{id} apaga um e-mail recebido agora, em vez de no fim da retenção: a mensagem, os anexos e o lugar dela em caixas e conversas (uma conversa que fica vazia vai junto). Responde { "object": "email", "id": "…", "deleted": true }. Exige uma chave de acesso total que não esteja restrita a uma caixa, e no painel um dono ou admin (Excluir agora). As entregas de webhook já feitas mantêm a cópia só com metadados até a retenção do próprio log de entregas.

Webhook

email.received dispara uma vez para cada mensagem guardada para a sua equipe, nos endpoints inscritos nele ou em todos os eventos, assim que a mensagem fica visível: na hora para um endereço do pega-tudo, e na entrega para uma caixa. O e-mail que uma caixa retém, na quarentena ou durante a análise, e o e-mail infectado só disparam depois de entregues ou liberados, mesmo quando uma cópia também foi para um endereço do pega-tudo. Como o do Resend, ele só traz metadados: busque o conteúdo com GET /emails/receiving/{email_id}. Quando uma entrega posterior da mesma mensagem chega a mais endereços seus, chega mais um email.received com o mesmo email_id, e o received_for dele só lista os endereços novos, para cada endereço ser avisado uma vez só.

{
  "type": "email.received",
  "created_at": "2026-10-11T14:37:40.951Z",
  "data": {
    "email_id": "4ef9a417-02e9-4d39-ad75-9611e0fcc33c",
    "created_at": "2026-10-11T14:37:40.951Z",
    "from": "ada@example.org",
    "to": ["support@inbound.example.com"],
    "bcc": [],
    "cc": [],
    "received_for": ["support@inbound.example.com"],
    "message_id": "<CAF1x7Qm9@mail.example.org>",
    "subject": "Order #1042",
    "attachments": [
      {
        "id": "b344102e-962e-8943-aef6-271c7743e1dd",
        "filename": "invoice.pdf",
        "content_type": "application/pdf",
        "content_disposition": "attachment",
        "content_id": null
      }
    ]
  }
}

Um endpoint limitado a algumas caixas (inbox_ids) só recebe o email.received dos e-mails entregues numa delas. Assinaturas, novas tentativas e o log de entregas funcionam como em qualquer outro evento; veja Webhooks.

Painel

Caixas de entrada → Todos os recebidos lista tudo o que os seus domínios receberam, com remetente, caixa, assunto e horário, uma indicação quando o e-mail traz um código ou um link, e um selo quando está em quarentena ou é spam; filtre por caixa (ou só pega-tudo) e por resultado. Ao abrir uma mensagem, o HTML aparece isolado, com scripts e imagens remotas bloqueados e imagens inline exibidas, junto com a versão em texto, os cabeçalhos e a mensagem original; os resultados de autenticação, cada cinza explicado; a confiança no remetente, os resultados de spam e de vírus e se saiu texto oculto; o código ou link extraído, com um botão de copiar; uma linha do tempo da chegada até a entrega, a liberação, a resposta ou o encaminhamento; e as ações Responder, Encaminhar, Liberar para e-mails retidos e Excluir agora para donos e admins. Links antigos para E-mails → Recebidos levam para lá.

Numa instância auto-hospedada, Configurações → Recebimento mostra para quem opera o host MX, a última mensagem recebida, o total do dia, o antivírus e o domínio compartilhado das caixas.

MCP

O servidor MCP lê e-mails recebidos sob a permissão receiving:read: list_received_emails, get_received_email, list_received_email_attachments, get_received_email_attachment, wait_for_received_email e get_verification_code, junto com as leituras de caixas. O get_received_email devolve por padrão o texto limpo da resposta (format: "extracted"), e text, html ou raw quando você pede. Responder e encaminhar pedem receiving:reply, e caixas e quarentena, receiving:manage. O update_domain liga e desliga o recebimento e o pega-tudo. Autorizações feitas antes de uma permissão existir não a incluem: entre de novo pelo cliente para concedê-la.

Trate e-mails recebidos como não confiáveis

Qualquer pessoa pode mandar e-mail para um domínio de recebimento. O HTML pode trazer scripts e imagens de rastreamento, links podem levar a páginas de phishing, anexos podem trazer malware e o from pode ser falso; authentication e screening.sender_trust dizem até onde confiar. Um agente de IA que lê e-mails recebidos deve tratá-los como dados, nunca como instruções: as ferramentas MCP os marcam assim, e uma caixa de agente soma quarentena, análise e regras de envio.

Nesta página