MillionSend Docs
Referência da API

Referência da API

A API HTTP do MillionSend — compatível com Resend, gerada a partir do código do servidor.

As páginas de endpoint desta seção são geradas a partir das próprias definições de rota da API no momento do build, então sempre refletem o código. A especificação bruta está em /openapi.json (OpenAPI 3.1). As páginas de endpoint estão disponíveis apenas em inglês.

URL base

https://api.millionsend.com

Autenticação

Todo endpoint (exceto o webhook de ingestão de eventos do SES) exige uma chave de API criada no painel:

Authorization: Bearer ms_...

Chaves têm um nível de permissão: chaves de acesso total podem usar todos os endpoints, chaves de somente envio ficam confinadas a /emails* (qualquer outra coisa retorna 403 restricted_api_key) — e mesmo ali, GET /emails, GET /emails/{id} e DELETE /emails/{id} exigem acesso total, já que as leituras devolvem os corpos armazenados e todo o arquivo do time. Uma chave também pode ficar restrita a um único domínio, limitando de quais endereços from ela pode enviar.

Compatibilidade com Resend

Os formatos de requisição e resposta correspondem aos da API do Resend, então os SDKs oficiais do Resend funcionam contra o MillionSend apontando a URL base para ele. O CI executa o pacote oficial resend do npm contra todos os endpoints como um teste de conformidade. As poucas diferenças restantes são deliberadas e explícitas:

  • Anexos aceitam apenas content em base64 inline — uma URL em path é rejeitada com 422 (nunca é buscada), assim como content_id (imagens inline).
  • Contatos são globais ao time — os endpoints de audience são servidos como aliases de segments, e os endpoints de contato funcionam com ou sem id de audience (veja Contatos).
  • POST /domains aceita um region opcional, que precisa ser uma das regiões do SES que a instalação atende — os valores que o schema da requisição lista, a primeira sendo a padrão — e é recusado com 422 caso contrário. Um domínio tem uma região: para mudá-la, exclua o domínio e adicione de novo.
  • Broadcasts suportam canceled, um status fora da união do Resend, e o envio de broadcast, POST /emails e POST /emails/batch podem retornar 403 sending_paused quando sua taxa de bounce ou reclamação cruza os limites de enforcement do SES. Só o envio de broadcast também pode retornar 403 broadcasts_paused enquanto a taxa agregada da plataforma na região SES do remetente se recupera; é por região, não afeta e-mail transacional e libera sozinho.
  • O envio de um broadcast responde com finishes_at (o instante estimado em que o último e-mail sai, ou null), estimated: true e, quando a audiência passa da capacidade disponível agora, um warning (paced ou queued_behind, com days e uma mensagem). As leituras de broadcast trazem sent_count e um finishes_at ao vivo; um cancelamento responde com canceled_remaining. Uma audiência que precisa de mais de 24 dias de capacidade é recusada com 422 broadcast_too_large. Veja Broadcasts.
  • POST /emails e POST /emails/batch retornam 429 daily_quota_exceeded em um plano diário (Free, Starter) quando a cota diária de envio acabou e a fila de espera está cheia — tente de novo depois da virada do dia em UTC — e 429 monthly_quota_exceeded em um plano mensal (Pro, Scale) no volume incluído com o excedente desligado; ative o excedente em Cobrança ou espere o período renovar (a mensagem informa a data). Um lote é aceito ou recusado por inteiro.
  • POST /contacts, POST /contacts/batch e o alias de audience retornam 403 plan_limit_reached quando um contato novo levaria o time além do limite de contatos do plano (1.000 no Free; planos pagos são ilimitados). Contatos existentes continuam sendo atualizados; em um lote só os novos falham.
  • GET /usage existe (o Resend não tem endpoint de uso): o plano efetivo, seus limites (emails_per_day em planos diários, emails_per_month em planos mensais, domains, contacts), o total aceito hoje e, em um plano mensal, um objeto period — emails_sent, included, overage_enabled, overage_usd_per_1k, starts_at, ends_at. Instâncias auto-hospedadas reportam cloud: false com plano, limites e period nulos.
  • DELETE /emails/{id} existe (o Resend não tem exclusão de emails).
  • headers personalizados seguem uma lista de permitidos: qualquer nome X-* (exceto X-SES-* e X-MillionSend-*) mais In-Reply-To, References, Importance, Priority, Comments, Keywords, Organization e o par de descadastro em um clique — List-Unsubscribe (um ou mais alvos <https://…> ou <mailto:…>) com List-Unsubscribe-Post (List-Unsubscribe=One-Click); qualquer outro é 422. Os dois vêm juntos, e List-Unsubscribe precisa de um alvo https. Em um envio com topic_id, o par que você informa substitui o gerado: os pedidos de um clique passam a chegar no seu endpoint, o MillionSend não registra opt-out por eles, e um placeholder {{{UNSUBSCRIBE_URL}}} no corpo continua apontando para a página do MillionSend.
  • Um envio em que todos os destinatários de to estão na lista de supressão ou saíram do topic_id é recusado com 422 all_recipients_suppressed (mensagem All recipients are suppressed); destinatários removidos de um envio que ainda tem alguém são simplesmente omitidos.
  • to, cc e bcc juntos não podem passar de 50 destinatários, e cada endereço precisa ser uma única caixa postal — um nome de exibição contendo @ é rejeitado, e endereços aceitos voltam na forma canônica Nome <usuario@host>.
  • Qualquer coisa não suportada é rejeitada com 422 em vez de descartada em silêncio (ex.: tls na atualização de domínio).

Erros

Erros usam o formato do Resend:

{ "statusCode": 422, "name": "validation_error", "message": "..." }

Idempotência

POST /emails e POST /emails/batch aceitam um header Idempotency-Key. Repetir com a mesma chave e o mesmo payload retorna a resposta original em vez de enviar de novo; a mesma chave com payload diferente retorna 409.

Paginação

Endpoints de listagem aceitam limit (1–100, padrão 20) e cursores after / before carregando o id de um item de uma página anterior. As respostas incluem has_more.

Nesta página