Referência da API
A API HTTP do Ritto Mail — 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.ritto.email
Autenticação
Todo endpoint exige uma chave de API criada no painel em app.ritto.email:
Authorization: Bearer rm_...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}, GET /emails/{id}/attachments* e
DELETE /emails/{id} exigem acesso total, já que as leituras devolvem os
corpos e anexos 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 Ritto Mail 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 de envio que o Ritto Mail oferece — 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. Só o envio de broadcast também pode retornar403 broadcasts_pausedenquanto a taxa agregada da plataforma na região de envio 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 monthly_quota_exceededno volume incluído do plano quando os envios não podem seguir (sem excedente no Hobby e no Starter, excedente desligado no Pro e no Enterprise); ative o excedente em Cobrança, suba de plano ou espere o período renovar (a mensagem informa a data). Sem assinatura ativa eles retornam403 plan_required. 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 (de 1.000 no Hobby a 25.000 no Pro; veja Cobrança). 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_month,domains,contacts; nenhum plano tem limite diário, entãoemails_per_day_capvemnull), o total aceito hoje e um objetoperiod—emails_sent,included,overage_enabled,overage_usd_per_1k,starts_at,ends_at.DELETE /emails/{id}existe (o Resend não tem exclusão de emails).GET /emails/{id}incluiattachments(id, filename, size, content_type; só metadados).GET /emails/{id}/attachmentslista os anexos no formato do Resend, eGET /emails/{id}/attachments/{attachment_id}devolve um deles com o arquivo em base64 no campocontent(download_urleexpires_atsão semprenull: não há link assinado de CDN). Ambos respondem410 attachments_purgeddepois que a política de retenção removeu o conteúdo do email.- Rotas inexistentes respondem
404 not_foundno mesmo formato JSON de erro das demais. headerspersonalizados seguem uma lista de permitidos: qualquer nomeX-*(excetoX-SES-*eX-Ritto-Mail-*) 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 Ritto Mail não registra opt-out por eles, e um placeholder{{{UNSUBSCRIBE_URL}}}no corpo continua apontando para a página do Ritto Mail.- 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
As listagens trazem os itens do mais recente para o mais antigo (pela data de
criação, como na Resend). Aceitam limit (1–100, padrão 20) e cursores after
/ before carregando o id de um item de uma página anterior: after (o último
id de uma página) busca os próximos itens, mais antigos; before (o primeiro id
de uma página) busca os anteriores, mais recentes. As respostas incluem
has_more.