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

@euromail/sdk

v0.2.0

Published

Official TypeScript SDK for EuroMail transactional email service

Readme

@euromail/sdk

Official TypeScript SDK for the EuroMail transactional email service.

Installation

npm install @euromail/sdk

Quick Start

import { EuroMail } from "@euromail/sdk";

const euromail = new EuroMail({
  apiKey: "em_live_your_api_key_here",
});

const result = await euromail.sendEmail({
  from: "[email protected]",
  to: "[email protected]",
  subject: "Hello from EuroMail",
  html_body: "<h1>Welcome!</h1><p>Your account is ready.</p>",
});

console.log(`Email queued: ${result.id}`);

Configuration

const euromail = new EuroMail({
  apiKey: "em_live_...",     // Required
  timeout: 30000,            // Default: 30000ms
});

Sending Emails

Direct send

const result = await euromail.sendEmail({
  from: "[email protected]",
  to: "[email protected]",
  subject: "Order Confirmation",
  html_body: "<h1>Thanks for your order!</h1>",
  text_body: "Thanks for your order!",
  reply_to: "[email protected]",
  tags: ["order", "confirmation"],
  metadata: { order_id: "12345" },
});

Send with template

await euromail.sendEmail({
  from: "[email protected]",
  to: "[email protected]",
  template_alias: "welcome-email",
  template_data: {
    name: "John",
    activation_url: "https://example.com/activate/abc123",
  },
});

Send with attachments

await euromail.sendEmail({
  from: "[email protected]",
  to: "[email protected]",
  subject: "Your Invoice",
  html_body: "<p>Please find your invoice attached.</p>",
  attachments: [
    {
      filename: "invoice.pdf",
      content: base64EncodedContent,
      content_type: "application/pdf",
    },
  ],
});

Batch send

const batch = await euromail.sendBatch({
  emails: [
    {
      from: "[email protected]",
      to: "[email protected]",
      subject: "Hello User 1",
      text_body: "Welcome aboard!",
    },
    {
      from: "[email protected]",
      to: "[email protected]",
      subject: "Hello User 2",
      text_body: "Welcome aboard!",
    },
  ],
});

console.log(`Sent: ${batch.data.length}, Errors: ${batch.errors.length}`);

Idempotent sends

await euromail.sendEmail({
  from: "[email protected]",
  to: "[email protected]",
  subject: "Payment Receipt",
  html_body: "<p>Payment received.</p>",
  idempotency_key: "payment-receipt-12345",
});

Retrieve and list emails

const email = await euromail.getEmail("email-uuid");

const emails = await euromail.listEmails({
  page: 1,
  per_page: 50,
  status: "delivered",
});

Domains

// Add a sending domain
const domain = await euromail.addDomain("mail.yourdomain.com");
console.log("Configure these DNS records:", domain.dns_records);

// Trigger verification
const verification = await euromail.verifyDomain(domain.id);
if (verification.fully_verified) {
  console.log("Domain verified! SPF, DKIM, DMARC, and return-path all confirmed.");
}

// List all domains
const domains = await euromail.listDomains({ page: 1, per_page: 25 });

// Remove a domain
await euromail.deleteDomain(domain.id);

Templates

// Create a template with Jinja2-style variables
const template = await euromail.createTemplate({
  alias: "welcome-email",
  name: "Welcome Email",
  subject: "Welcome, {{ name }}!",
  html_body: "<h1>Hello {{ name }}</h1><p>Welcome to {{ company }}.</p>",
  text_body: "Hello {{ name }}, welcome to {{ company }}.",
});

// Update a template
await euromail.updateTemplate(template.id, {
  subject: "Welcome to {{ company }}, {{ name }}!",
});

// List and delete
const templates = await euromail.listTemplates({ page: 1, per_page: 25 });
await euromail.deleteTemplate(template.id);

Webhooks

// Subscribe to delivery events
const webhook = await euromail.createWebhook({
  url: "https://yourdomain.com/webhooks/euromail",
  events: ["delivered", "bounced", "complained", "email.inbound"],
});

// Update webhook
await euromail.updateWebhook(webhook.id, {
  url: "https://yourdomain.com/webhooks/v2",
  events: ["delivered", "bounced"],
  is_active: true,
});

// Send a test event
const test = await euromail.testWebhook(webhook.id);

// List and delete
const webhooks = await euromail.listWebhooks();
await euromail.deleteWebhook(webhook.id);

Supported events: sent, delivered, bounced, opened, clicked, complained, email.inbound

Suppressions

// Suppress an address manually
await euromail.addSuppression("[email protected]", "hard_bounce");

// List all suppressions
const suppressions = await euromail.listSuppressions({ page: 1, per_page: 50 });

// Remove a suppression
await euromail.deleteSuppression("[email protected]");

Contact Lists

// Create a list with double opt-in
const list = await euromail.createContactList({
  name: "Newsletter",
  description: "Monthly product updates",
  double_opt_in: true,
});

// Add a single contact
const contact = await euromail.addContact(list.id, {
  email: "[email protected]",
  metadata: { first_name: "Jane", source: "signup" },
});

// Bulk add contacts
const result = await euromail.bulkAddContacts(list.id, {
  contacts: [
    { email: "[email protected]", metadata: { name: "Alice" } },
    { email: "[email protected]", metadata: { name: "Bob" } },
  ],
});
console.log(`Inserted: ${result.inserted} of ${result.total_requested}`);

// List contacts with filters
const contacts = await euromail.listContacts(list.id, {
  page: 1,
  per_page: 50,
  status: "active",
});

// Remove a contact and delete the list
await euromail.removeContact(list.id, "[email protected]");
await euromail.deleteContactList(list.id);

Inbound Email

// List received emails
const inbound = await euromail.listInboundEmails({ page: 1, per_page: 25 });

// Get details
const email = await euromail.getInboundEmail("inbound-uuid");
console.log(`From: ${email.from_address}, Subject: ${email.subject}`);

// Delete
await euromail.deleteInboundEmail("inbound-uuid");

Inbound Routes

// Route incoming email to a webhook
const route = await euromail.createInboundRoute({
  domain_id: "domain-uuid",
  pattern: "support@",
  match_type: "prefix",
  priority: 10,
  webhook_url: "https://yourdomain.com/inbound/support",
});

// Update route
await euromail.updateInboundRoute(route.id, {
  webhook_url: "https://yourdomain.com/inbound/v2",
  is_active: true,
});

// Catch-all route
await euromail.createInboundRoute({
  domain_id: "domain-uuid",
  pattern: "*",
  match_type: "catch_all",
  priority: 100,
});

// List and delete
const routes = await euromail.listInboundRoutes();
await euromail.deleteInboundRoute(route.id);

Analytics

// Overview for the last 30 days
const overview = await euromail.getAnalyticsOverview({ period: "30d" });
console.log(`Delivery rate: ${overview.data.delivery_rate}%`);

// Custom date range
const custom = await euromail.getAnalyticsOverview({
  from: "2025-01-01",
  to: "2025-01-31",
});

// Time series data
const timeseries = await euromail.getAnalyticsTimeseries({
  period: "7d",
  metrics: "sent,delivered,bounced",
});

// Per-domain breakdown
const domains = await euromail.getAnalyticsDomains({
  period: "30d",
  limit: 10,
});

// Export as CSV
const csv = await euromail.exportAnalyticsCsv({ period: "30d" });

Account

const account = await euromail.getAccount();
console.log(`Plan: ${account.plan}, Used: ${account.emails_sent_this_month}/${account.monthly_quota}`);

// Export all account data (GDPR)
const exportData = await euromail.exportAccount();

// Delete account permanently
await euromail.deleteAccount();

Audit Logs

const logs = await euromail.listAuditLogs({ page: 1, per_page: 50 });
for (const log of logs.data) {
  console.log(`${log.created_at}: ${log.action} on ${log.resource_type}`);
}

Dead Letters

// List failed emails
const deadLetters = await euromail.listDeadLetters({ count: 20 });

// Retry delivery
await euromail.retryDeadLetter("dead-letter-uuid");

// Remove permanently
await euromail.deleteDeadLetter("dead-letter-uuid");

Error Handling

All API errors throw typed exceptions:

import {
  EuroMail,
  EuroMailError,
  AuthenticationError,
  RateLimitError,
  ValidationError,
} from "@euromail/sdk";

try {
  await euromail.sendEmail({ ... });
} catch (error) {
  if (error instanceof AuthenticationError) {
    // Invalid or missing API key (401)
    console.error("Check your API key");
  } else if (error instanceof ValidationError) {
    // Invalid request parameters (422)
    console.error(`${error.code}: ${error.message}`);
  } else if (error instanceof RateLimitError) {
    // Too many requests (429)
    console.error(`Retry after ${error.retryAfter} seconds`);
  } else if (error instanceof EuroMailError) {
    // Other API errors (4xx/5xx)
    console.error(`[${error.status}] ${error.code}: ${error.message}`);
  }
}

API Reference

| Category | Method | Description | |---|---|---| | Emails | sendEmail(params) | Send a single email | | | sendBatch(params) | Send up to 500 emails in one request | | | getEmail(id) | Get email details and status | | | listEmails(params?) | List emails with pagination and status filter | | Templates | createTemplate(params) | Create an email template | | | getTemplate(id) | Get template by ID | | | updateTemplate(id, params) | Update template fields | | | deleteTemplate(id) | Delete a template | | | listTemplates(params?) | List templates with pagination | | Domains | addDomain(domain) | Register a sending domain | | | getDomain(id) | Get domain details and DNS records | | | verifyDomain(id) | Trigger DNS verification | | | deleteDomain(id) | Remove a domain | | | listDomains(params?) | List domains with pagination | | Webhooks | createWebhook(params) | Subscribe to events | | | getWebhook(id) | Get webhook details | | | updateWebhook(id, params) | Update URL, events, or status | | | testWebhook(id) | Send a test event | | | deleteWebhook(id) | Remove a webhook | | | listWebhooks(params?) | List webhooks with pagination | | Suppressions | addSuppression(email, reason?) | Suppress an email address | | | deleteSuppression(email) | Remove a suppression | | | listSuppressions(params?) | List suppressions with pagination | | Contact Lists | createContactList(params) | Create a contact list | | | getContactList(id) | Get list details | | | updateContactList(id, params) | Update list settings | | | deleteContactList(id) | Delete a list | | | listContactLists() | List all contact lists | | | addContact(listId, params) | Add a contact to a list | | | bulkAddContacts(listId, params) | Add multiple contacts | | | listContacts(listId, params?) | List contacts with filters | | | removeContact(listId, email) | Remove a contact | | Inbound | listInboundEmails(params?) | List received emails | | | getInboundEmail(id) | Get inbound email details | | | deleteInboundEmail(id) | Delete an inbound email | | Inbound Routes | createInboundRoute(params) | Create a routing rule | | | getInboundRoute(id) | Get route details | | | updateInboundRoute(id, params) | Update a route | | | deleteInboundRoute(id) | Delete a route | | | listInboundRoutes(params?) | List routes with pagination | | Analytics | getAnalyticsOverview(query?) | Aggregated delivery stats | | | getAnalyticsTimeseries(query?) | Daily metrics over time | | | getAnalyticsDomains(query?) | Per-domain breakdown | | | exportAnalyticsCsv(query?) | Export stats as CSV | | Audit Logs | listAuditLogs(params?) | List account activity | | Dead Letters | listDeadLetters(params?) | List permanently failed emails | | | retryDeadLetter(id) | Retry delivery | | | deleteDeadLetter(id) | Remove from dead letter queue | | Account | getAccount() | Get account info and quota | | | exportAccount() | Export all account data | | | deleteAccount() | Permanently delete account |

Agent Mailboxes

Agent mailboxes provide persistent email addresses for AI agents with at-least-once message delivery via a lease/ack/nack model. The SDK wraps the full lifecycle natively:

import { EuroMail } from "@euromail/sdk";

const euromail = new EuroMail({ apiKey: process.env.EUROMAIL_API_KEY });

// Create a mailbox
const mailbox = await euromail.createMailbox({ display_name: "Support Agent" });

// Long-poll loop for incoming messages
while (true) {
  const leased = await euromail.waitForNextMessage(mailbox.id, { timeout: 30 });
  if (!leased) continue; // 408 timeout — no message arrived, poll again

  const { data: msg, lease_token } = leased;
  try {
    await handle(msg);
    // Ack when done — message will not be redelivered
    await euromail.ackMessage(mailbox.id, msg.id, lease_token);
  } catch (err) {
    // Nack to return the message to the queue for retry
    await euromail.nackMessage(mailbox.id, msg.id, lease_token);
    throw err;
  }
}

Other mailbox methods:

  • listMailboxes({ limit, offset }) — list mailboxes on the account
  • getMailbox(id) — fetch a single mailbox
  • deleteMailbox(id) — remove a mailbox
  • listMessages(mailboxId, { status, limit, offset }) — list messages without acquiring a lease
  • deleteMessage(mailboxId, messageId) — permanently delete a message

See the Agent Mailboxes guide for the full flow, duplicate handling, and horizontal scaling patterns.

Requirements

  • Node.js 18+ (uses native fetch)
  • TypeScript 5.0+ (for type definitions)

License

MIT