
Security News
Ruby's Bundler 4.0.18 Extends Cooldown to bundle lock and bundle cache
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.
@truncus/email
Advanced tools
Node.js SDK for Truncus — transactional email infrastructure built on AWS SES with deliverability built-in.
npm install @truncus/email
# or
pnpm add @truncus/email
# or
yarn add @truncus/email
import { Truncus } from '@truncus/email';
const truncus = new Truncus({ apiKey: 'tr_live_...' });
const result = await truncus.sendEmail({
to: 'user@example.com',
from: 'hello@mail.yourdomain.com',
subject: 'Welcome!',
html: '<h1>Hello World</h1>',
});
console.log(result.id); // Email ID
console.log(result.status); // 'sent'
const truncus = new Truncus({
apiKey: 'tr_live_...', // Required
baseUrl: 'https://truncus.co', // Optional, defaults to production
timeout: 30000, // Optional, request timeout in ms (default 30s)
});
sendEmail(options)Send a transactional email.
const result = await truncus.sendEmail({
// Required
to: 'user@example.com',
from: 'hello@mail.yourdomain.com', // Must match a verified domain
subject: 'Welcome!',
html: '<h1>Hello World</h1>',
// Optional
text: 'Hello World', // Plain-text fallback
cc: ['cc@example.com'],
bcc: ['bcc@example.com'],
templateId: 'tmpl_...', // Use a saved template instead of html
variables: { name: 'John' }, // Template variables ({{name}})
metadata: { userId: '123' }, // Custom metadata (stored, not sent)
idempotencyKey: 'order-123', // Prevent duplicate sends (auto-generated if omitted)
tenantId: 'tenant_abc', // Multi-tenant suppression isolation
sendAt: '2026-03-10T09:00:00Z', // Schedule for future delivery (ISO 8601, must be future)
sandbox: true, // Skip SES — test without sending
trackOpens: true, // 1×1 tracking pixel (default: true)
trackClicks: true, // Rewrite links through click proxy (default: true)
attachments: [
{
filename: 'invoice.pdf',
content: '<base64-encoded-content>',
content_type: 'application/pdf',
},
],
});
// result.status === 'sent' — sent immediately
// result.status === 'scheduled' — queued for sendAt time
// result.sendAt — ISO 8601 delivery time (scheduled emails)
// result.sandbox — true if sandbox mode was active
getEmail(emailId)Retrieve details for a specific email, including open and click tracking stats.
const email = await truncus.getEmail('email_id');
console.log(email.status); // 'delivered'
console.log(email.openCount); // 3 (total opens, including repeat opens)
console.log(email.openedAt); // '2026-03-04T10:23:00Z' (first open)
console.log(email.clickCount);// 1
cancelEmail(emailId)Cancel a scheduled email before it is dispatched. Only works on emails with status === 'scheduled'.
const result = await truncus.cancelEmail('email_id');
console.log(result.status); // 'cancelled'
listEvents(options?)List email events. Includes 'opened' and 'clicked' event types.
// All recent events
const { events } = await truncus.listEvents();
// Events for a specific email
const { events } = await truncus.listEvents({ emailId: 'email_id' });
// Filter by type
const { events } = await truncus.listEvents({ type: 'opened', limit: 50 });
const { events } = await truncus.listEvents({ type: 'clicked', limit: 50 });
const { events } = await truncus.listEvents({ type: 'delivered' });
Event types: queued | sent | delivered | bounce | complaint | failed | retry_scheduled | retry_attempted | opened | clicked
createDomain(options)Add a sending domain. Returns DNS records to configure.
const domain = await truncus.createDomain({ domain: 'mail.yourdomain.com' });
// Configure these DNS records at your registrar:
console.log(domain.dnsRecords);
// [
// { type: 'TXT', name: '_truncus.mail.yourdomain.com', value: 'truncus-verification=...' },
// { type: 'TXT', name: 'mail.yourdomain.com', value: 'v=spf1 include:amazonses.com ~all' },
// { type: 'CNAME', name: '...._domainkey.mail.yourdomain.com', value: '....dkim.amazonses.com' },
// { type: 'TXT', name: '_dmarc.mail.yourdomain.com', value: 'v=DMARC1; p=quarantine; ...' },
// ]
verifyDomain(domainId)Trigger DNS verification after configuring records.
const result = await truncus.verifyDomain('domain_id');
console.log(result.verified); // true when DNS is correct
listDomains()List all domains with sending stats.
const { domains } = await truncus.listDomains();
domains.forEach((d) => {
console.log(d.domain, d.status, d.sentToday);
});
checkSuppression(email, tenantId?)Check whether an address is on the suppression list before sending.
const status = await truncus.checkSuppression('user@example.com');
console.log(status.suppressed); // true / false
console.log(status.reason); // 'bounce' | 'complaint' | null
For multi-tenant apps, scope the check to a specific tenant:
const status = await truncus.checkSuppression('user@example.com', 'tenant_abc');
getStats()Aggregate sending stats for the account.
const stats = await truncus.getStats();
console.log(stats.sentToday);
console.log(stats.deliveryRate); // 0.98 (98%)
console.log(stats.bounceRate); // 0.01 (1%)
Test your integration without sending real emails:
const result = await truncus.sendEmail({
to: 'user@example.com',
from: 'any@address.com', // Domain validation skipped in sandbox
subject: 'Test',
html: '<p>Test</p>',
sandbox: true,
});
console.log(result.sandbox); // true
console.log(result.status); // 'sent' (simulated)
Sandbox skips SES entirely. No emails are delivered. Open/click tracking is also disabled.
Schedule an email for future delivery. The email is queued immediately and dispatched at sendAt:
const result = await truncus.sendEmail({
to: 'user@example.com',
from: 'hello@mail.yourdomain.com',
subject: 'Your weekly digest',
html: '...',
sendAt: '2026-03-10T09:00:00Z', // Must be a future datetime
});
console.log(result.status); // 'scheduled'
console.log(result.sendAt); // '2026-03-10T09:00:00.000Z'
// Cancel before dispatch
await truncus.cancelEmail(result.id);
Tracking is enabled by default. Truncus injects a 1×1 pixel for opens and rewrites links for click tracking. Unsubscribe links are never rewritten.
// Default — both opens and clicks tracked
const result = await truncus.sendEmail({ ..., html: '...' });
// Disable tracking
const result = await truncus.sendEmail({
...,
html: '...',
trackOpens: false,
trackClicks: false,
});
// Check engagement after sending
const email = await truncus.getEmail(result.id);
console.log(email.openedAt); // First open timestamp
console.log(email.openCount); // Total opens (repeat opens counted)
console.log(email.clickCount); // Total link clicks
Events also fire for opens and clicks — query them with listEvents({ type: 'opened' }) or listEvents({ type: 'clicked' }).
Prevent duplicate sends by providing a stable idempotency key. If you retry with the same key, the original response is returned without sending again:
const result = await truncus.sendEmail({
to: 'user@example.com',
from: 'hello@mail.yourdomain.com',
subject: 'Order Confirmation',
html: '...',
idempotencyKey: `order-${orderId}-confirmation`,
});
An idempotency key is auto-generated (UUID v4) when not provided.
Isolate suppression lists per tenant so a bounce in one customer's account doesn't affect others:
const result = await truncus.sendEmail({
to: 'user@example.com',
from: 'hello@mail.yourdomain.com',
subject: 'Welcome!',
html: '...',
tenantId: 'tenant_123',
});
import { TruncusError } from '@truncus/email';
try {
await truncus.sendEmail({ ... });
} catch (error) {
if (error instanceof TruncusError) {
console.log(error.code); // 'DOMAIN_NOT_VERIFIED'
console.log(error.message); // 'Domain is not verified'
console.log(error.status); // 400
}
}
| Code | Status | Description |
|---|---|---|
MISSING_API_KEY | 401 | Authorization header missing |
INVALID_API_KEY | 401 | API key is invalid or revoked |
SCOPE_REQUIRED | 403 | API key missing required scope |
INVALID_REQUEST | 400 | Request body validation failed |
DOMAIN_NOT_FOUND | 404 | Domain doesn't exist |
DOMAIN_NOT_VERIFIED | 400 | Domain pending DNS verification |
DOMAIN_PAUSED | 400 | Domain paused due to high bounce/complaint rate |
WARMUP_CAP_EXCEEDED | 429 | Daily sending limit reached |
ALL_RECIPIENTS_SUPPRESSED | 422 | All recipients are on suppression list |
PROVIDER_ERROR | 502 | AWS SES returned an error |
TIMEOUT | 408 | Request timed out |
NETWORK_ERROR | 0 | Network connectivity issue |
Full type definitions are included. Key types:
import type {
SendEmailOptions,
SendEmailResponse,
EmailDetails,
EmailStatus,
EventType,
EmailEvent,
Domain,
DomainStatus,
SuppressionStatus,
TruncusConfig,
} from '@truncus/email';
MIT
FAQs
Node.js SDK for Truncus Email API
We found that @truncus/email demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.

Security News
During a UK cyber test, a Mythos 5 agent used sockpuppets, social engineering, and prompt injection to try to get a maintainer to merge malware.

Company News
Socket is now in the AWS Security Hub Extended plan. Adopt it through AWS, apply committed spend, and block malicious open source packages.