SDK de Node.js

SDK oficial @reallyquickemails/sdk para Node.js 18+, Bun, Deno, Cloudflare Workers y Vercel Edge — módulos emails (send, sendBatch), leads, automations, events y senders, manejo de errores tipado e idempotencia con idempotencyKey.

SDK oficial de ReallyQuickEmails para Node.js / TypeScript. Cubre los 5 módulos de la API: emails, leads, automations, events y senders.

Funciona en cualquier runtime con fetch nativo: Node.js 18+, Bun, Deno, Cloudflare Workers, Vercel Edge. NO está diseñado para correr en el browser — el constructor lanza un error si detecta window, para evitar exponer el API key en el bundle del cliente.

Instalación

bash
npm install @reallyquickemails/sdk

Quickstart

Obtener API key

Genera una key en Configuración → Integraciones → API Keys. Para empezar usa una Test key (sk_test_*) — envía emails reales sin contaminar tus métricas de producción. Ver API Keys.

bash
export RQE_API_KEY="sk_test_tu_test_key"

Inicializar el cliente

ts
import { RQE } from '@reallyquickemails/sdk';

const rqe = new RQE({ apiKey: process.env.RQE_API_KEY });

Si omites apiKey, el SDK lee process.env.RQE_API_KEY automáticamente. Para apuntar a un backend distinto (staging, self-hosted) pasa baseUrl o define la variable de entorno RQE_BASE_URL:

ts
const rqe = new RQE({
  apiKey: process.env.RQE_API_KEY,
  baseUrl: 'https://api-staging.ejemplo.com',
});

Mandar tu primer email

ts
const { data, error } = await rqe.emails.send({
  sender: 'soporte@tudominio.com',     // sender verificado
  recipient: 'usuario@example.com',
  subject: 'Bienvenido',
  html: '<p>Hola desde RQE</p>',
});

if (error) {
  console.error('Send falló:', error.message);
} else {
  console.log('Email encolado, id:', data.email_id);
}

El sender debe estar verificado en tu proyecto. Ver dominios.

Manejo de errores

Cada método devuelve { data, error, headers }. Siempre revisa error antes de usar datadata es null cuando hay error.

ts
const { data, error, headers } = await rqe.emails.send({ /* ... */ });

if (error) {
  switch (error.statusCode) {
    case 401: /* API key inválida */ break;
    case 429: /* Rate limit — espera y reintenta */ break;
    default:  console.error(error);
  }
  return;
}

console.log(data.email_id);
console.log('Idempotency replayed:', headers['idempotency-replayed']);
error.nameCuándo
unauthorizedAPI key inválida o ausente
validation_errorBody inválido (campos requeridos faltantes, formato malo)
not_foundRecurso no existe (lead, automation, event)
network_errorFalla de red — no llegó respuesta del backend
parse_errorBackend devolvió respuesta no-JSON
application_errorError genérico backend (5xx)

Módulos

emails — transactional + broadcast

ts
// Single email
await rqe.emails.send({
  sender: 'soporte@brand.com',
  recipient: 'user@example.com',
  subject: 'Confirmación',
  html: '<p>Tu pedido fue recibido.</p>',
  attachments: [{ filename: 'factura.pdf', content: base64Pdf }],
});

// Broadcast — un template a hasta 10,000 recipients
await rqe.emails.sendBatch({
  sender: 'noreply@brand.com',
  subject: 'Hola {{name}}',
  html: '<p>Hola {{name}}, tu plan {{plan}} se renueva pronto.</p>',
  recipients: [
    { email: 'a@x.com', data: { name: 'Alice', plan: 'Pro' } },
    { email: 'b@x.com', data: { name: 'Bob', plan: 'Free' } },
  ],
});

leads — gestión de contactos

ts
// Upsert por email
await rqe.leads.upsert({
  email: 'user@example.com',
  data: { plan: 'pro', signup_date: '2026-04-30' },
  segment_ids: ['seg-uuid-1'],
});

// Bulk hasta 1000
await rqe.leads.upsertMany({
  leads: [
    { email: 'a@x.com', data: { plan: 'pro' } },
    { email: 'b@x.com' },
  ],
});

// Tags por email
await rqe.leads.addTags('user@example.com', ['vip', 'beta']);
const { data } = await rqe.leads.getTags('user@example.com');

// Atributos custom (body raw, NO wrapped)
await rqe.leads.setAttributes('user@example.com', {
  total_orders: 42,
  last_order_date: '2026-04-30',
});

// Listar con paginación
const page = await rqe.leads.list({ page: 1, per_page: 50, search: 'pro' });

// Get / update / delete por UUID
const { data: { lead } } = await rqe.leads.get('lead-uuid');
await rqe.leads.update('lead-uuid', { data: { plan: 'enterprise' } });
await rqe.leads.delete('lead-uuid');

automations — disparar flujos

ts
// Enroll idempotente — pasa trigger_reference_id para que múltiples calls
// con el mismo external_id NO creen runs duplicados, sino que actualicen el run existente
await rqe.automations.enroll('automation-uuid', {
  email: 'user@example.com',
  data: { first_name: 'Adriano', plan: 'pro' },
  trigger_reference_id: 'order-12345',
});

// Manual enroll (UI-style bulk)
await rqe.automations.manualEnroll('automation-uuid', {
  emails: ['a@x.com', 'b@x.com'],
});

// Cancel por recipient
await rqe.automations.cancel('automation-uuid', {
  email: 'user@example.com',
  reason: 'payment_succeeded',
});

// Cancel por external_id (cuando no tienes el email)
await rqe.automations.cancelByExternalId('automation-uuid', {
  external_id: 'order-12345',
});

events — tracking custom

ts
// Single event
await rqe.events.track({
  email: 'user@example.com',
  event: 'offer_accepted',
  properties: { offer_id: 'xyz', amount_usd: 5000 },
});

// Bulk hasta 1000
await rqe.events.bulk([
  { email: 'a@b.com', event: 'page_view', properties: { url: '/pricing' } },
  { email: 'c@d.com', event: 'page_view', properties: { url: '/features' } },
]);

// Listar con filtros + paginación offset
const { data } = await rqe.events.list({
  email: 'user@example.com',
  event: 'offer_accepted',
  since: '2026-04-01T00:00:00Z',
  per_page: 50,
});

senders — listar / verificar remitentes (v0.1.1+)

Permite descubrir programáticamente qué senders están verificados en el proyecto, verificar nuevos vía magic link y consultar el estado actual de un remitente.

Este módulo requiere projectId en el constructor (o RQE_PROJECT_ID env). Encuéntralo en la URL del dashboard: app.reallyquickemails.com/projects/<projectId>/.... Los otros módulos (emails, leads, automations, events) no necesitan este parámetro.

ts
const rqe = new RQE({
  apiKey: process.env.RQE_API_KEY,
  projectId: process.env.RQE_PROJECT_ID,
});

// Listar todos los senders
const { data } = await rqe.senders.list();
// data.senders = [{ email, sender_name, status, type, ... }, ...]

// Filtrar solo verificados
const { data: verified } = await rqe.senders.list({ status: 'verified' });

// Elegir sender en runtime según contexto
const transactionalSender = verified.senders.find(s => s.email.startsWith('noreply@'));
await rqe.emails.send({
  sender: transactionalSender.email,
  recipient: 'user@example.com',
  subject: 'Confirmación',
  html: '<p>...</p>',
});

// Verificar un sender nuevo (se envía un magic link al email)
await rqe.senders.verifyEmail({ email: 'support@brand.com' });

// Check status de un sender específico
const { data: st } = await rqe.senders.status('support@brand.com');
// st.status: 'Success' | 'Pending' | 'Failed' | 'NotStarted'

Casos de uso típicos:

  • Multi-tenant — listar senders del proyecto activo y elegir según el cliente
  • Failover — si un sender falla DKIM/DMARC, fallback automático a otro verified
  • Onboarding wizard — UI muestra senders disponibles para que el cliente elija
  • Health check — poll periódico de status para detectar senders fallados

Idempotency

Para retries seguros (timeouts de red, reintentos de Lambda, etc), pasa idempotencyKey en operaciones de escritura. Hoy soportado en emails.send y emails.sendBatch.

ts
await rqe.emails.send(
  {
    sender: 'soporte@brand.com',
    recipient: 'user@example.com',
    subject: 'Pedido #123',
    html: '<p>Confirmación</p>',
  },
  { idempotencyKey: 'order-123-confirmation' },
);

El backend cachea por 24h la respuesta para el par (project_id, idempotencyKey). Una segunda llamada con la misma key devuelve la respuesta cacheada sin re-procesar y agrega el header Idempotency-Replayed: true.

ts
const { data, headers } = await rqe.emails.send(
  { /* ... */ },
  { idempotencyKey: 'unique-id' },
);
if (headers['idempotency-replayed'] === 'true') {
  console.log('Replay — backend ya había procesado este request');
}

Más sobre el comportamiento del backend en API Keys → Idempotency.

Compatibilidad de runtime

RuntimeSoportadoNotas
Node.js 18+fetch nativo
Node.js 16Sin fetch nativo
Bun
Deno
Cloudflare Workers
Vercel Edge
BrowserEl SDK throw si detecta window para proteger tu API key

Configuración

OpciónTipoDefaultDescripción
apiKeystringprocess.env.RQE_API_KEYTu API key del proyecto
baseUrlstringprocess.env.RQE_BASE_URL o https://api.reallyquickemails.comOverride para staging o self-hosted
projectIdstringprocess.env.RQE_PROJECT_IDUUID del proyecto. Requerido solo por el módulo senders — los demás módulos lo derivan del API key.

Soporte