npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@notifica/node

v0.1.0

Published

SDK oficial do Notifica para Node.js — infraestrutura de notificações para o Brasil

Downloads

96

Readme


WhatsApp, Email, SMS, Push e In-App em uma API unificada. Zero dependências. TypeScript nativo.

Instalação

npm install @notifica/node

Requer Node.js 18+ (usa fetch nativo).

Início Rápido

import { Notifica } from '@notifica/node';

const notifica = new Notifica('nk_live_...');

// Enviar uma notificação via WhatsApp
const notification = await notifica.notifications.send({
  channel: 'whatsapp',
  to: '+5511999999999',
  template: 'welcome',
  data: { name: 'João' },
});

console.log(notification.id);     // 'notif-uuid'
console.log(notification.status); // 'pending'

Configuração

// Simples — apenas API key
const notifica = new Notifica('nk_live_...');

// Configuração completa
const notifica = new Notifica({
  apiKey: 'nk_live_...',
  baseUrl: 'https://app.usenotifica.com.br/v1', // padrão
  timeout: 15000,        // 15s (padrão: 30s)
  maxRetries: 5,         // padrão: 3
  autoIdempotency: true, // padrão: true
});

| Opção | Tipo | Padrão | Descrição | |-------|------|--------|-----------| | apiKey | string | — | Obrigatório. Sua API key (nk_live_... ou nk_test_...) | | baseUrl | string | https://app.usenotifica.com.br/v1 | URL base da API | | timeout | number | 30000 | Timeout em ms | | maxRetries | number | 3 | Retentativas automáticas em 429/5xx | | autoIdempotency | boolean | true | Gerar chave de idempotência automática para POSTs |


Recursos

Notifications — Envio e consulta

// Enviar notificação
const notification = await notifica.notifications.send({
  channel: 'whatsapp',  // 'email' | 'whatsapp' | 'sms' | 'in_app' | 'push'
  to: '+5511999999999',
  template: 'welcome',
  data: { name: 'João' },
  metadata: { source: 'signup-flow' },
});

// Listar (com paginação manual)
const { data, meta } = await notifica.notifications.list({
  channel: 'email',
  status: 'delivered',
  limit: 50,
});
const page2 = await notifica.notifications.list({ cursor: meta.cursor });

// Auto-paginação com async iterator
for await (const n of notifica.notifications.listAll({ channel: 'email' })) {
  console.log(n.id, n.status);
}

// Detalhes + tentativas de entrega
const detail = await notifica.notifications.get('notif-uuid');
const attempts = await notifica.notifications.listAttempts('notif-uuid');

Templates — Gerenciamento

// Criar
const template = await notifica.templates.create({
  channel: 'email',
  slug: 'welcome-email',
  name: 'Email de Boas-Vindas',
  content: 'Olá {{name}}, bem-vindo ao {{company}}!',
  variants: {
    subject: 'Bem-vindo, {{name}}!',
    html_body: '<h1>Olá {{name}}</h1><p>Bem-vindo ao {{company}}!</p>',
  },
  status: 'active',
});

// Listar / obter / atualizar / deletar
const { data } = await notifica.templates.list({ channel: 'email' });
await notifica.templates.update('tpl-uuid', { status: 'active' });
await notifica.templates.delete('tpl-uuid');

// Preview com variáveis
const preview = await notifica.templates.preview('tpl-uuid', {
  variables: { name: 'João', company: 'Empresa Ltda' },
});
console.log(preview.rendered.subject); // "Bem-vindo, João!"

// Preview de conteúdo arbitrário (para editor em tempo real)
const livePreview = await notifica.templates.previewContent({
  content: 'Oi {{name}}!',
  channel: 'email',
  variables: { name: 'Maria' },
});

// Validar template
const validation = await notifica.templates.validate('tpl-uuid');
console.log(validation.valid, validation.warnings);

Workflows — Orquestração

// Criar workflow
const workflow = await notifica.workflows.create({
  slug: 'welcome-flow',
  name: 'Fluxo de Boas-Vindas',
  steps: [
    { type: 'send', channel: 'email', template: 'welcome-email' },
    { type: 'delay', duration: '1h' },
    { type: 'send', channel: 'whatsapp', template: 'welcome-whatsapp' },
  ],
});

// Disparar workflow
const run = await notifica.workflows.trigger('welcome-flow', {
  recipient: '+5511999999999',
  data: { name: 'João', plan: 'pro' },
});

// Gerenciar execuções
const { data: runs } = await notifica.workflows.listRuns({ status: 'running' });
const runDetail = await notifica.workflows.getRun('run-uuid');
await notifica.workflows.cancelRun('run-uuid');

Tipos de step:

| Tipo | Campos | Descrição | |------|--------|-----------| | send | channel, template | Envia notificação pelo canal | | delay | duration (5m, 1h, 1d) | Pausa a execução | | fallback | channels[], template | Tenta canais em ordem até sucesso |

Subscribers — Gerenciamento de destinatários

// Criar/atualizar (upsert por external_id)
const subscriber = await notifica.subscribers.create({
  external_id: 'user-123',
  email: '[email protected]',
  phone: '+5511999998888',
  name: 'João Silva',
  locale: 'pt_BR',
  timezone: 'America/Sao_Paulo',
  custom_properties: { plan: 'pro' },
});

// Listar (com busca)
const { data } = await notifica.subscribers.list({ search: 'joao' });

// Atualizar / deletar (LGPD — nullifica PII, irreversível!)
await notifica.subscribers.update('sub-uuid', { name: 'João S.' });
await notifica.subscribers.delete('sub-uuid');

// Preferências de notificação
const prefs = await notifica.subscribers.getPreferences('sub-uuid');
await notifica.subscribers.updatePreferences('sub-uuid', {
  preferences: [
    { category: 'marketing', channel: 'email', enabled: false },
    { category: 'transactional', channel: 'whatsapp', enabled: true },
  ],
});

// Import em lote (transacional — tudo ou nada)
const result = await notifica.subscribers.bulkImport({
  subscribers: [
    { external_id: 'user-1', email: '[email protected]', name: 'Ana' },
    { external_id: 'user-2', email: '[email protected]', name: 'Bruno' },
  ],
});

Notificações In-App:

const { data } = await notifica.subscribers.listNotifications('sub-uuid', { unread_only: true });
await notifica.subscribers.markRead('sub-uuid', 'notif-uuid');
await notifica.subscribers.markAllRead('sub-uuid');
const count = await notifica.subscribers.getUnreadCount('sub-uuid');

Channels — Configuração de canais

const channel = await notifica.channels.create({
  channel: 'email',
  provider: 'aws_ses',
  credentials: { access_key_id: 'AKIA...', secret_access_key: '...', region: 'us-east-1' },
  settings: { from_address: '[email protected]', from_name: 'Empresa' },
});

const channels = await notifica.channels.list();
const test = await notifica.channels.test('email');

Domains — Domínios de envio

// Registrar domínio e configurar DNS
const domain = await notifica.domains.create({ domain: 'suaempresa.com.br' });
// → Configure os registros em domain.dns_records no seu provedor DNS

const verified = await notifica.domains.verify(domain.id);
const health = await notifica.domains.getHealth(domain.id);
const alerts = await notifica.domains.listAlerts();

Webhooks — Eventos outbound

const webhook = await notifica.webhooks.create({
  url: 'https://meuapp.com.br/webhooks/notifica',
  events: ['notification.delivered', 'notification.failed'],
});
// ⚠️ Salve webhook.signing_secret — mostrado apenas na criação!

await notifica.webhooks.test(webhook.id);
const deliveries = await notifica.webhooks.listDeliveries(webhook.id);

Verificação de assinatura:

app.post('/webhooks/notifica', async (req, res) => {
  const payload = req.body;       // raw body string
  const signature = req.headers['x-notifica-signature'];
  const secret = process.env.WEBHOOK_SECRET!;

  const valid = await notifica.webhooks.verify(payload, signature, secret);
  if (!valid) return res.status(401).send('Assinatura inválida');

  // Processar evento...
  res.status(200).send('OK');
});

// Ou a versão que lança erro automaticamente:
await notifica.webhooks.verifyOrThrow(payload, signature, secret);

API Keys — Gerenciamento de chaves

const key = await notifica.apiKeys.create({
  key_type: 'secret',
  label: 'Backend Production',
  environment: 'production',
});
// ⚠️ Salve key.raw_key — mostrado apenas na criação!

const keys = await notifica.apiKeys.list();
await notifica.apiKeys.revoke('key-uuid');

Analytics — Métricas

const overview = await notifica.analytics.overview({ period: '7d' });
const channels = await notifica.analytics.byChannel({ period: '24h' });
const timeseries = await notifica.analytics.timeseries({ period: '7d', granularity: 'day' });
const top = await notifica.analytics.topTemplates({ period: '30d', limit: 5 });

Tratamento de Erros

O SDK lança erros tipados para cada cenário:

import {
  NotificaError,    // Erro base
  ApiError,         // Qualquer erro da API (4xx, 5xx)
  ValidationError,  // 422 — dados inválidos
  RateLimitError,   // 429 — rate limit excedido
  TimeoutError,     // Timeout de conexão
} from '@notifica/node';

try {
  await notifica.notifications.send({ channel: 'email', to: 'x' });
} catch (error) {
  if (error instanceof ValidationError) {
    console.log(error.status);     // 422
    console.log(error.message);    // "Email inválido"
    console.log(error.details);    // { email: ["is invalid"] }
    console.log(error.requestId);  // "req-abc-123" (para suporte)
  }
  if (error instanceof RateLimitError) {
    console.log(error.retryAfter); // 30 (segundos)
  }
  if (error instanceof ApiError) {
    console.log(error.status, error.code);
  }
}

Idempotência

O SDK gera automaticamente chaves de idempotência para todas as requests POST, prevenindo operações duplicadas em caso de retry de rede.

// Automático (padrão)
await notifica.notifications.send({ channel: 'email', to: '[email protected]' });

// Chave customizada (útil para deduplicação por lógica de negócio)
await notifica.notifications.send(
  { channel: 'email', to: '[email protected]' },
  { idempotencyKey: 'signup-user-123' },
);

Retries Automáticos

Retenta automaticamente em falhas transientes:

  • 429 Too Many Requests — respeita header Retry-After
  • 5xx Server errors — backoff exponencial com jitter
const notifica = new Notifica({
  apiKey: 'nk_live_...',
  maxRetries: 5, // padrão: 3
});

Paginação

// 1. Manual (cursor-based)
let cursor: string | undefined;
do {
  const page = await notifica.notifications.list({ cursor, limit: 100 });
  for (const n of page.data) { console.log(n.id); }
  cursor = page.meta.has_more ? page.meta.cursor ?? undefined : undefined;
} while (cursor);

// 2. Auto-paginação (async iterator)
for await (const n of notifica.notifications.listAll()) {
  console.log(n.id);
}

Tipos TypeScript

Todos os tipos são exportados:

import type {
  Notification,
  SendNotificationParams,
  Template,
  Workflow,
  WorkflowStep,
  Subscriber,
  Channel,
  NotificationStatus,
  PaginatedResponse,
} from '@notifica/node';

Desenvolvimento

# Instalar dependências
npm install

# Type check
npm run typecheck

# Rodar testes (requer Node 22+)
npm test

# Build
npm run build

Contribuindo

Contribuições são bem-vindas! Por favor:

  1. Faça fork do repositório
  2. Crie sua branch (git checkout -b feature/minha-feature)
  3. Commit suas mudanças (git commit -m 'feat: minha feature')
  4. Push para a branch (git push origin feature/minha-feature)
  5. Abra um Pull Request

Requisitos

  • Node.js 18+ (usa fetch nativo e crypto.subtle)
  • Zero dependências externas

Links

Licença

MIT © Notifica