Skip to content

Resonia-Health/veilmail-node

Repository files navigation

@resonia/veilmail-sdk

npm version License: MIT TypeScript Node.js

The official Node.js and TypeScript SDK for Veil Mail — secure transactional and marketing email with automatic PII protection, CASL compliance, and a developer-first API.

Veil Mail is a drop-in alternative to Resend, SendGrid, Mailgun, and Postmark. The SDK mirrors the Resend client shape, so most migrations are a package swap and a constructor change.

// Before — Resend
import { Resend } from 'resend'
const resend = new Resend('re_xxxxx')
await resend.emails.send({ from, to, subject, html })

// After — Veil Mail
import { VeilMail } from '@resonia/veilmail-sdk'
const client = new VeilMail('veil_live_xxxxx')
await client.emails.send({ from, to, subject, html })

Migration guides: from Resend · from SendGrid · from Mailgun · from Postmark

Installation

npm install @resonia/veilmail-sdk
# or
pnpm add @resonia/veilmail-sdk
# or
yarn add @resonia/veilmail-sdk

Quick Start

import { VeilMail } from '@resonia/veilmail-sdk';

const client = new VeilMail('veil_live_xxxxx');

// Send an email
const email = await client.emails.send({
  from: 'hello@example.com',
  to: 'user@example.com',
  subject: 'Welcome!',
  html: '<h1>Welcome to our platform!</h1>',
});

console.log(email.id); // email_xxxxx

Features

  • Full TypeScript support - Complete type definitions for all API operations
  • Automatic PII protection - Emails are scanned for sensitive data before sending
  • Zero dependencies - Uses native fetch (Node.js 18+)
  • Comprehensive error handling - Typed errors for different scenarios

API Reference

Emails

// Send an email
await client.emails.send({
  from: 'hello@example.com',
  to: 'user@example.com',
  subject: 'Hello!',
  html: '<h1>Welcome!</h1>',
});

// Send with a template
await client.emails.send({
  from: 'hello@example.com',
  to: 'user@example.com',
  templateId: 'template_xxxxx',
  variables: { firstName: 'John' },
});

// List emails
const { data, hasMore } = await client.emails.list({ limit: 20 });

// Get a single email
const email = await client.emails.get('email_xxxxx');

// Cancel a scheduled email
await client.emails.cancel('email_xxxxx');

Domains

// Add a domain
const domain = await client.domains.create({ domain: 'mail.example.com' });
console.log(domain.dnsRecords); // DNS records to configure

// Verify domain DNS
await client.domains.verify('domain_xxxxx');

// List domains
const { data } = await client.domains.list();

// Delete a domain
await client.domains.delete('domain_xxxxx');

Templates

// Create a template
const template = await client.templates.create({
  name: 'Welcome Email',
  subject: 'Welcome, {{firstName}}!',
  html: '<h1>Hello {{firstName}}!</h1>',
  variables: [{ name: 'firstName', type: 'string', required: true }],
});

// List templates
const { data } = await client.templates.list();

// Update a template
await client.templates.update('template_xxxxx', {
  subject: 'Updated Subject',
});

// Delete a template
await client.templates.delete('template_xxxxx');

Audiences

// Create an audience
const audience = await client.audiences.create({
  name: 'Newsletter Subscribers',
  description: 'Users who signed up for updates',
});

// Add a subscriber
const subscriber = await client.audiences.subscribers('audience_xxxxx').add({
  email: 'user@example.com',
  firstName: 'John',
  lastName: 'Doe',
});

// Add a subscriber with double opt-in (starts as pending)
const pending = await client.audiences.subscribers('audience_xxxxx').add({
  email: 'user@example.com',
  firstName: 'John',
  doubleOptIn: true, // Status will be 'pending' until confirmed
});

// Confirm a pending subscriber (after they click confirmation link)
const confirmed = await client.audiences.subscribers('audience_xxxxx').confirm('subscriber_xxxxx');
// confirmed.status === 'active', confirmed.confirmedAt is set

// List subscribers (filter by status: 'pending', 'active', 'unsubscribed', etc.)
const { data } = await client.audiences.subscribers('audience_xxxxx').list({
  status: 'active',
});

// Remove a subscriber
await client.audiences.subscribers('audience_xxxxx').remove('subscriber_xxxxx');

Campaigns

// Create a campaign
const campaign = await client.campaigns.create({
  name: 'December Newsletter',
  subject: 'Our December Updates',
  from: 'news@example.com',
  audienceId: 'audience_xxxxx',
  templateId: 'template_xxxxx',
});

// Schedule for later
await client.campaigns.schedule('campaign_xxxxx', {
  scheduledAt: '2024-12-25T09:00:00Z',
});

// Or send immediately
await client.campaigns.send('campaign_xxxxx');

// Pause a sending campaign
await client.campaigns.pause('campaign_xxxxx');

// Resume a paused campaign
await client.campaigns.resume('campaign_xxxxx');

// Cancel a campaign
await client.campaigns.cancel('campaign_xxxxx');

Webhooks

// Create a webhook
const webhook = await client.webhooks.create({
  url: 'https://example.com/webhooks/veilmail',
  events: ['email.delivered', 'email.bounced'],
});
console.log(webhook.secret); // Use this to verify signatures

// Test a webhook
const result = await client.webhooks.test('webhook_xxxxx');

// Rotate signing secret
await client.webhooks.rotateSecret('webhook_xxxxx');

Compliance

Veil Mail has built-in email compliance features for marketing emails.

Email Types

Set the type field to classify your emails:

// Marketing email — automatically gets compliance features
await client.emails.send({
  from: 'news@yourdomain.com',
  to: 'subscriber@example.com',
  subject: 'Monthly Newsletter',
  html: '<h1>Updates</h1>',
  type: 'marketing', // Adds unsubscribe headers, footer, enforces physical address
});

// Transactional email (default) — no compliance overhead
await client.emails.send({
  from: 'orders@yourdomain.com',
  to: 'customer@example.com',
  subject: 'Order #1234 Confirmed',
  html: '<h1>Order confirmed</h1>',
  // type defaults to 'transactional'
});

Marketing Email Features

When type: 'marketing' is set:

  • List-Unsubscribe headers are added automatically (RFC 2369 + RFC 8058)
  • Unsubscribe footer is appended to the HTML with your physical address
  • Physical address is required (configure in dashboard Settings)
  • Suppression check blocks sends to bounced/unsubscribed/complained addresses

Pre-Send Suppression Check

All emails (transactional and marketing) are checked against the suppression list. If a recipient is suppressed, the API returns a 422 with error code recipient_suppressed:

try {
  await client.emails.send({ ... });
} catch (error) {
  if (error instanceof VeilMailError && error.code === 'recipient_suppressed') {
    console.log('Suppressed:', error.details.suppressedRecipients);
  }
}

Error Handling

The SDK provides typed errors for different scenarios:

import {
  VeilMail,
  VeilMailError,
  AuthenticationError,
  ValidationError,
  PiiDetectedError,
  RateLimitError,
} from '@resonia/veilmail-sdk';

try {
  await client.emails.send({ ... });
} catch (error) {
  if (error instanceof PiiDetectedError) {
    console.log('PII detected:', error.piiTypes);
  } else if (error instanceof RateLimitError) {
    console.log('Rate limited, retry after:', error.retryAfter);
  } else if (error instanceof ValidationError) {
    console.log('Validation error:', error.message);
  } else if (error instanceof AuthenticationError) {
    console.log('Invalid API key');
  } else if (error instanceof VeilMailError) {
    console.log('API error:', error.code, error.message);
  }
}

Configuration

const client = new VeilMail({
  apiKey: 'veil_live_xxxxx',
  baseUrl: 'https://api.veilmail.xyz', // Optional
  timeout: 30000, // Optional, in milliseconds
});

Requirements

  • Node.js 18+ (uses native fetch)

Framework Setup

Next.js 16+ (Turbopack)

When using with Next.js 16 and Turbopack, add the SDK to transpilePackages in your next.config.ts:

const nextConfig = {
  transpilePackages: ['@resonia/veilmail-sdk'],
  // ... other config
};

Monorepo / Local Development

If linking the SDK locally in a monorepo, use file: protocol instead of link: for better Turbopack compatibility:

{
  "dependencies": {
    "@resonia/veilmail-sdk": "file:../path/to/sdk"
  }
}

Migrating from another provider

Veil Mail's SDK and REST API are designed to be a drop-in replacement for the major transactional email providers. See the full side-by-side migration guides:

Resend-specific notes

Veil Mail throws typed errors instead of Resend's { data, error } return pattern:

// Resend pattern
const { data, error } = await resend.emails.send({ ... });
if (error) { /* handle */ }

// Veil Mail pattern
try {
  const email = await client.emails.send({ ... });
} catch (error) {
  // Handle typed errors — AuthenticationError, ValidationError,
  // PiiDetectedError, RateLimitError, VeilMailError
}

Webhook event names map almost one-to-one. The only rename is email.delivery_delayedemail.deferred. Use the X-VeilMail-Signature header for HMAC-SHA256 signature verification.

Resources

Troubleshooting

Package not found after publishing? npm registry propagation can take a few minutes. Wait 2-5 minutes and try again.

Turbopack build errors? Make sure you've added @resonia/veilmail-sdk to transpilePackages in your Next.js config.

License

MIT - See LICENSE for details.

About

Official Node.js / TypeScript SDK for Veil Mail — secure transactional and marketing email with automatic PII protection. A drop-in alternative to Resend, SendGrid, Mailgun, and Postmark.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors