Comunicações
Enviar e-mail e SMS pelos seus próprios provedores, e por que o seu código nunca nomeia um.
O seu código nomeia um template e um idioma. Ele nunca nomeia um provedor, um assunto ou um corpo. Essa indireção é o desenho inteiro, e tudo abaixo decorre dela.
await nulvless.comms.email.send({
to: '[email protected]',
template: 'grants-invite',
language: 'pt-BR',
params: { organization: 'Acme Inc', role: 'admin' },
});Trocar de provedor, reescrever o texto, adicionar um idioma — nada disso toca nesta chamada.
Traga o seu provedor
As mensagens saem pelas suas contas, não pelas nossas. Cada ambiente configura as dele, e cada provedor é habilitado de forma independente:
emailprovedordirect_smtpQualquer servidor SMTP — host, porta, usuário, senha de app.googleGoogle Workspace.send_gridSendGrid, por chave de API.mailtrapMailtrap — a escolha óbvia para um ambiente de desenvolvimento.smsprovedortwiliovonageComo o provedor é por ambiente, desenvolvimento pode apontar para o Mailtrap enquanto produção aponta para o SendGrid — com os mesmos slugs de template e o mesmo código chamando. Um teste não chega numa caixa de entrada real por configuração, não porque alguém lembrou de proteger a chamada.
Verifique a ligação antes de um usuário depender dela:
await nulvless.comms.email.test('[email protected]');Vale rodar do seu pipeline de deploy contra um ambiente novo. Um provedor mal configurado é invisível até a primeira pessoa precisar de um código de verificação.
A plataforma também manda mensagens
Códigos de confirmação, links de recuperação, convites — a plataforma envia tudo isso pelos mesmos provedores e pelos mesmos templates. Todo ambiente já começa com o conjunto de que precisa, e cada um é editável:
welcome-user · email-otp-verification · email-link-verification ·
email-otp-enable-mfa-request · change-email-identity ·
reset-password-email · grants-invite
E para SMS: phone-otp-verification · phone-link-verification ·
phone-otp-enable-mfa-request · change-phone-identity
É por isso que o e-mail de cadastro chega na voz da sua marca sem você escrever código de envio — e por isso quebrar um desses templates quebra o cadastro.
Um template é por canal. As versões de e-mail e SMS da mesma ideia são slugs
separados, porque um texto que funciona numa caixa de entrada não sobrevive a 160
caracteres. Editar email-otp-verification não faz nada com
phone-otp-verification.
Editar grava a sua versão, não por cima da nossa
Os padrões da plataforma não são registros seus, então editar não os modifica. Salvar grava a sua versão para aquele canal, slug e idioma, e o envio prefere ela:
a sua versão no idioma pedido
↓ não existe
o padrão da plataformaDuas consequências. Apagar a sua versão é como se volta ao padrão — não há nada
para restaurar, porque o padrão nunca foi sobrescrito. E como os padrões vêm só
em inglês, um projeto sem versão em português de welcome-user envia a inglesa
em vez de falhar.
Adicionar um idioma é aditivo pelo mesmo motivo: welcome-user em pt-BR é um
registro separado, e escrevê-lo não toca no de inglês.
O envio é enfileirado
A chamada publica uma mensagem; um worker entrega. Uma chamada que resolve sem erro significa aceita para entrega, não entregue.
Isso tem uma consequência que vale considerar no desenho: falhas de entrega não voltam para você. Elas são gravadas na mensagem, com um código:
template_not_foundentregaO slug não existe para aquele canal e idioma.
missing_template_parametersentregaO template esperava params que você não passou.
disabled_featureentregaO ambiente não permite o template que foi nomeado.
cannot_sendentregaO provedor rejeitou.
Então um template que referencia {{ organization }} quando você não mandou
nenhum não falha no ponto de chamada — falha minutos depois, em silêncio, a menos
que você leia o histórico da mensagem. Trate params como contrato.
Idioma
O language escolhe a variante. Omita e vale o padrão do ambiente.
O envio resolve isso em três passos, e vale conhecer o último:
o idioma que você pediu1Usado quando existe uma versão para ele.
inglês2O fallback, e onde vivem os padrões da plataforma.
a versão mais antiga daquele template3Último recurso quando nenhuma das duas existe — então uma mensagem pode sair num idioma que ninguém pediu.
Esse terceiro passo vale só para templates que você criou: se você escrever
order-shipped só em pt-BR e depois enviar em es-ES, sai o português.
Escreva também a versão em inglês se preferir que isso nunca aconteça.
Defina uma vez no client, em vez de a cada chamada:
createClient(url, key, { language: () => navigator.language });É uma função para poder ser resolvida por requisição em vez de congelada na construção — o que importa num servidor atendendo usuários em locais diferentes.
Template é uma tripla
Tudo exceto create e list endereça um template por canal + slug +
idioma:
await nulvless.comms.templates.get('email', 'welcome-user', 'pt-BR');
await nulvless.comms.templates.update('email', 'welcome-user', params, 'pt-BR');Não há id para guardar. O mesmo slug em dois idiomas são dois registros, e é isso que faz adicionar um idioma ser aditivo em vez de uma migração.
O terceiro canal
Além de e-mail e SMS, um template pode ser um webhook: em vez de renderizar
uma mensagem para uma pessoa, ele posta numa url sua. O ponto de chamada é
idêntico, então uma notificação pode migrar de e-mail para webhook por
configuração — útil para levar algo ao Slack ou ao seu próprio serviço sem tocar
no código que dispara.
Limites de taxa
Envios têm cota por canal e por ambiente. Esgotar uma responde 429 rate_limit_exceeded.
O SMS também tem um intervalo mínimo por número: por padrão, um número recebe
no máximo um SMS a cada 60 segundos. Um segundo envio dentro dessa janela
responde o mesmo 429. Altere esse valor, ou use 0 para desativar, nos limites
de taxa de comunicações do ambiente.
Diferente do serviço de auth, este não manda retryAfter — e o SDK não
exporia mesmo assim. Use o seu próprio backoff em vez de ler um campo que não
está lá.