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
contentem base64 inline — uma URL empathé rejeitada com422(nunca é buscada), assim comocontent_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 /domainsaceita umregionopcional, 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 com422caso 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 /emailsePOST /emails/batchpodem retornar403 sending_pausedquando sua taxa de bounce ou reclamação cruza os limites de enforcement do SES. Só o envio de broadcast também pode retornar403 broadcasts_pausedenquanto 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, ounull),estimated: truee, quando a audiência passa da capacidade disponível agora, umwarning(pacedouqueued_behind, comdayse uma mensagem). As leituras de broadcast trazemsent_counte umfinishes_atao vivo; um cancelamento responde comcanceled_remaining. Uma audiência que precisa de mais de 24 dias de capacidade é recusada com422 broadcast_too_large. Veja Broadcasts. POST /emailsePOST /emails/batchretornam429 daily_quota_exceededem 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 — e429 monthly_quota_exceededem 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/batche o alias de audience retornam403 plan_limit_reachedquando 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 /usageexiste (o Resend não tem endpoint de uso): o plano efetivo, seus limites (emails_per_dayem planos diários,emails_per_monthem planos mensais,domains,contacts), o total aceito hoje e, em um plano mensal, um objetoperiod—emails_sent,included,overage_enabled,overage_usd_per_1k,starts_at,ends_at. Instâncias auto-hospedadas reportamcloud: falsecom plano, limites e period nulos.DELETE /emails/{id}existe (o Resend não tem exclusão de emails).headerspersonalizados seguem uma lista de permitidos: qualquer nomeX-*(excetoX-SES-*eX-MillionSend-*) maisIn-Reply-To,References,Importance,Priority,Comments,Keywords,Organizatione o par de descadastro em um clique —List-Unsubscribe(um ou mais alvos<https://…>ou<mailto:…>) comList-Unsubscribe-Post(List-Unsubscribe=One-Click); qualquer outro é422. Os dois vêm juntos, eList-Unsubscribeprecisa de um alvohttps. Em um envio comtopic_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
toestão na lista de supressão ou saíram dotopic_idé recusado com422 all_recipients_suppressed(mensagemAll recipients are suppressed); destinatários removidos de um envio que ainda tem alguém são simplesmente omitidos. to,ccebccjuntos 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ônicaNome <usuario@host>.- Qualquer coisa não suportada é rejeitada com
422em vez de descartada em silêncio (ex.:tlsna 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.