SDK Node.js
📦 TypeScript-first: SDK oficial com tipos completos, retry automático e paginação lazy
O SDK Node.js é a forma mais rápida de integrar a Notifica em aplicações JavaScript/TypeScript. Este guia cobre:
- Instalação e setup
- Envio de notificações (email, SMS, WhatsApp, in-app, push)
- Gerenciamento de subscribers, templates e workflows
- Webhooks e analytics
- Tratamento de erros
Instalação
npm install @notifica/node
Ou com yarn/pnpm:
yarn add @notifica/node
# ou
pnpm add @notifica/node
Requisitos: Node.js 18+ · TypeScript 5+ (opcional, tipos incluídos)
Zero dependências externas. Tree-shakeable.
Setup básico
import { Notifica } from '@notifica/node';
const notifica = new Notifica('nk_live_sua_chave');
const notification = await notifica.notifications.send({
channel: 'email',
recipient: '[email protected]',
templateId: 'welcome',
payload: { name: 'Fernanda' },
});
console.log('Enviada:', notification.id, notification.status);
Autenticação
// Chave de produção
const notifica = new Notifica('nk_live_sua_chave');
// Chave de sandbox (desenvolvimento)
const notifica = new Notifica('nk_test_sua_chave');
Use variáveis de ambiente para não commitar chaves no código:
const notifica = new Notifica(process.env.NOTIFICA_API_KEY!);
Configuração avançada
const notifica = new Notifica('nk_live_...', {
// URL customizada (self-hosted ou staging)
baseUrl: 'https://api.staging.notifica.com.br',
// Timeout em milissegundos
timeout: 60_000,
// Retry com backoff exponencial (padrão: 3 tentativas)
maxRetries: 5,
});
| Opção | Padrão | Descrição |
|---|---|---|
baseUrl | https://api.notifica.com.br | URL base da API |
timeout | 30000 | Timeout em ms |
maxRetries | 3 | Tentativas com backoff exponencial |
Enviando notificações
Todas as notificações são assíncronas e retornam status accepted (202).
Email
const n = await notifica.notifications.send({
channel: 'email',
recipient: '[email protected]',
templateId: 'order-confirmation',
payload: {
name: 'Roberto',
orderId: 'ORD-12345',
},
});
WhatsApp
const n = await notifica.notifications.send({
channel: 'whatsapp',
recipient: '+5511998765432',
templateId: 'welcome',
payload: {
customerName: 'João',
company: 'Acme',
},
});
SMS
const n = await notifica.notifications.send({
channel: 'sms',
recipient: '+5511998765432',
payload: {
content: 'Seu código de verificação é: 583921',
},
});
In-App
const n = await notifica.notifications.send({
channel: 'in_app',
recipient: 'subscriber-id-aqui',
payload: {
title: 'Novo comentário',
body: 'Maria comentou na sua tarefa #123',
},
});
Com chave de idempotência
Para evitar duplicatas em retries:
const n = await notifica.notifications.send({
channel: 'email',
recipient: '[email protected]',
templateId: 'payment-receipt',
payload: { amount: 'R$ 99,90' },
idempotencyKey: 'payment-12345', // Use ID do seu sistema
});
Tracking de entrega
Ver status de uma notificação
const n = await notifica.notifications.get('notification-id');
console.log(n.status); // "delivered"
console.log(n.deliveredAt); // 2024-01-15T14:30:05Z
Listar tentativas de entrega
const attempts = await notifica.notifications.listAttempts('notification-id');
for (const a of attempts) {
console.log(`Tentativa ${a.attemptNumber}: ${a.status} (${a.provider})`);
}
Listar notificações com filtros
const page = await notifica.notifications.list({
status: 'failed',
channel: 'email',
limit: 50,
});
for (const n of page.data) {
console.log(n.id, n.status);
}
// Paginação via cursor
if (page.meta.hasMore) {
const nextPage = await notifica.notifications.list({
cursor: page.meta.cursor,
});
}
Subscribers (Assinantes)
Criar ou atualizar (upsert)
const sub = await notifica.subscribers.create({
externalId: 'user-123',
email: '[email protected]',
phone: '+5511999999999',
name: 'Maria Silva',
customProperties: {
plan: 'pro',
company: 'Acme',
},
});
Se o externalId já existir, o subscriber é atualizado automaticamente.
Listar com busca
const page = await notifica.subscribers.list({
search: 'maria',
limit: 20,
});
console.log(`Encontrados: ${page.total} subscribers`);
Atualizar
const sub = await notifica.subscribers.update('subscriber-id', {
name: 'Maria Santos',
});
Deletar (LGPD)
Soft delete com nullificação de PII:
await notifica.subscribers.delete('subscriber-id');
Preferências de notificação
// Consultar
const prefs = await notifica.subscribers.getPreferences('subscriber-id');
// Atualizar
await notifica.subscribers.setPreferences('subscriber-id', {
preferences: [
{ category: 'marketing', channel: 'email', enabled: false },
{ category: 'transacional', channel: 'email', enabled: true },
],
});
Importação em massa
const result = await notifica.subscribers.bulkImport({
subscribers: [
{ externalId: 'user-1', email: '[email protected]', name: 'Ana' },
{ externalId: 'user-2', email: '[email protected]', name: 'Bruno' },
],
});
console.log(`Importados: ${result.imported} subscribers`);
Templates
Criar template
const tmpl = await notifica.templates.create({
channel: 'email',
slug: 'welcome',
name: 'Email de Boas-vindas',
content: 'Olá {{name}}, bem-vindo ao {{company}}!',
status: 'published',
});
Listar por canal
const templates = await notifica.templates.list({
channel: 'email',
status: 'published',
});
Preview com dados de exemplo
const result = await notifica.templates.preview('template-id', {
variables: { name: 'Maria', company: 'Acme' },
});
console.log(result.output); // "Olá Maria, bem-vindo ao Acme!"
console.log(result.valid); // true
Validar template
const result = await notifica.templates.validate('template-id');
if (!result.valid) {
console.log('Erros:', result.errors);
}
Workflows
Criar workflow
const wf = await notifica.workflows.create({
slug: 'onboarding',
name: 'Onboarding do Usuário',
steps: [
{ type: 'send', channel: 'email', template: 'welcome' },
{ type: 'delay', duration: '24h' },
{ type: 'send', channel: 'email', template: 'getting-started' },
{ type: 'delay', duration: '72h' },
{ type: 'send', channel: 'whatsapp', template: 'check-in' },
],
});
Disparar workflow
const run = await notifica.workflows.trigger('onboarding', {
recipient: 'user-123',
data: { name: 'João', plan: 'pro' },
});
console.log('Workflow run:', run.id, run.status);
Acompanhar execução
const run = await notifica.workflows.getRun('run-id');
console.log('Status:', run.status);
Cancelar workflow em execução
const run = await notifica.workflows.cancelRun('run-id');
Webhooks
Criar webhook
O signing secret só é retornado na criação — guarde-o!
const wh = await notifica.webhooks.create({
url: 'https://api.empresa.com/webhooks/notifica',
events: ['notification.sent', 'notification.delivered', 'notification.failed'],
});
console.log('Secret (guarde!):', wh.secret);
Listar deliveries
const page = await notifica.webhooks.listDeliveries('webhook-id', { limit: 50 });
for (const d of page.data) {
console.log(`Evento: ${d.eventType}, Status HTTP: ${d.responseStatus}`);
}
Testar webhook
await notifica.webhooks.test('webhook-id');
Domínios de envio
Registrar e verificar domínio
// Registrar
const domain = await notifica.domains.create({ domain: 'mail.empresa.com.br' });
console.log('Configure os registros DNS e depois verifique');
// Verificar DNS
const verified = await notifica.domains.verify(domain.id);
if (verified.status === 'verified') {
console.log('Domínio verificado!');
}
// Health check
const health = await notifica.domains.checkHealth(domain.id);
console.log('SPF:', health.spf.status);
console.log('DKIM:', health.dkim.status);
console.log('DMARC:', health.dmarc.status);
Analytics
// Resumo dos últimos 7 dias
const overview = await notifica.analytics.overview('7d');
console.log(`Enviados: ${overview.sent}, Entregues: ${overview.delivered}`);
// Estatísticas por canal
const stats = await notifica.analytics.byChannel('30d');
for (const s of stats) {
console.log(`${s.channel}: ${s.sent} enviados`);
}
// Timeseries para gráficos
const points = await notifica.analytics.timeseries({
period: '7d',
granularity: 'day',
});
// Top templates
const top = await notifica.analytics.topTemplates({ period: '30d', limit: 5 });
Notificações In-App
// Listar não-lidas
const notifications = await notifica.inApp.list('subscriber-id', {
unreadOnly: true,
limit: 20,
});
// Contagem de não-lidas
const count = await notifica.inApp.getUnreadCount('subscriber-id');
console.log(`${count} notificações não lidas`);
// Marcar como lida
await notifica.inApp.markRead('subscriber-id', 'notification-id');
// Marcar todas como lidas
await notifica.inApp.markAllRead('subscriber-id');
Audit Logs (Admin Only)
Audit logs require admin authentication (Bearer token from backoffice login). They are not available with regular API keys (nk_live_... or nk_test_...).
Track security-sensitive actions in your organization:
- API Keys: created, rotated, revoked
- Team Members: invited, removed, role_changed
- Subscriptions: created, plan_changed, cancelled, payment_failed
- Domains: added, verified, removed
- Webhooks: created, updated, deleted
// List recent audit logs
const { data: logs, meta } = await notifica.audit.list({ limit: 50 });
for (const log of logs) {
console.log(`${log.action} by ${log.actor.name} at ${log.created_at}`);
}
// Filter by action type
const apiKeyLogs = await notifica.audit.list({
action: 'api_key.created',
limit: 20,
});
// Filter by resource type
const webhookLogs = await notifica.audit.list({
resource_type: 'webhook',
});
// Filter by date range
const logs = await notifica.audit.list({
start_date: '2024-01-01T00:00:00Z',
end_date: '2024-01-31T23:59:59Z',
});
// Get a specific audit log
const log = await notifica.audit.get('audit_abc123');
// Auto-paginate through all logs
for await (const log of notifica.audit.paginate({ resource_type: 'api_key' })) {
console.log(log.action, log.resource_id);
}
TypeScript Types
import type {
AuditLog,
AuditLogAction,
AuditResourceType,
AuditActorType,
AuditActor,
ListAuditLogsParams,
} from '@notifica/node';
// Actions: 'api_key.created' | 'api_key.rotated' | 'api_key.revoked' | ...
// Resource types: 'api_key' | 'team_member' | 'subscription' | 'domain' | 'webhook'
// Actor types: 'user' | 'api_key' | 'system'
Tratamento de erros
import { NotificaError } from '@notifica/node';
try {
await notifica.notifications.send(params);
} catch (err) {
if (err instanceof NotificaError) {
console.log(`HTTP ${err.statusCode} | ${err.code}: ${err.message}`);
if (err.statusCode === 422) {
// Dados inválidos
console.log('Campos inválidos:', err.details);
}
if (err.statusCode === 429) {
// Rate limited — o SDK faz retry automático por padrão
console.log('Rate limited');
}
if (err.statusCode === 404) {
// Recurso não encontrado
}
if (err.statusCode === 409) {
// Conflito de idempotência
}
}
}
O SDK faz retry automático com backoff exponencial para erros 429 e 5xx. Configure maxRetries: 0 para desabilitar.
Exemplo completo: Express.js
import express from 'express';
import { Notifica } from '@notifica/node';
const app = express();
app.use(express.json());
const notifica = new Notifica(process.env.NOTIFICA_API_KEY!);
app.post('/send-welcome', async (req, res) => {
const { email, name } = req.body;
try {
const n = await notifica.notifications.send({
channel: 'email',
recipient: email,
templateId: 'welcome',
payload: { name },
});
res.json({ notificationId: n.id, status: n.status });
} catch (err) {
res.status(500).json({ error: (err as Error).message });
}
});
app.listen(3000, () => console.log('Servidor rodando em :3000'));
FAQ
Q: Preciso usar TypeScript? A: Não. O SDK funciona com JavaScript puro, mas inclui tipos TypeScript para autocomplete e validação.
Q: Como funciona o retry automático? A: O SDK retenta automaticamente em erros 429 (rate limit) e 5xx (servidor) com backoff exponencial: 1s, 2s, 4s...
Q: Funciona no Deno/Bun?
A: Sim, em qualquer runtime com suporte a fetch nativo.
Q: Como usar com ESM e CommonJS? A: O SDK suporta ambos:
// ESM
import { Notifica } from '@notifica/node';
// CommonJS
const { Notifica } = require('@notifica/node');
Q: O SDK é tree-shakeable? A: Sim. Bundlers como esbuild e webpack eliminam código não utilizado.
Links
- npm: npmjs.com/package/@notifica/node
- GitHub: github.com/notifica-tech/notifica-node
- API Reference: API Reference
- Templates: Email Templates
- Webhooks: Webhooks