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

@ovixa/email-client

v0.2.0

Published

Client SDK for Ovixa Email service

Readme

@ovixa/email-client

Client SDK for the Ovixa Email service. Send transactional emails using templates.

Installation

npm install @ovixa/email-client
# or
pnpm add @ovixa/email-client

Quick Start

import { OvixaEmail } from '@ovixa/email-client';

const email = new OvixaEmail({
  apiKey: process.env.OVIXA_EMAIL_API_KEY,
  defaultFrom: '[email protected]',
});

// Send a templated email
await email.send({
  to: '[email protected]',
  template: 'welcome',
  data: { name: 'John' },
});

// Get available templates for this API key
const { templates } = await email.getTemplates();

API Reference

Constructor

new OvixaEmail(config: EmailClientConfig)

| Option | Type | Required | Default | Description | | ---------------- | -------- | -------- | ------------------------ | ------------------------ | | apiKey | string | Yes | - | Your API key | | emailUrl | string | No | https://email.ovixa.io | Service URL | | defaultFrom | string | No | - | Default sender address | | defaultReplyTo | string | No | - | Default reply-to address | | timeout | number | No | 30000 | Request timeout (ms) |

Methods

send(options) - Send Templated Email

const result = await email.send({
  to: '[email protected]', // string or string[]
  template: 'verification', // template ID
  data: {
    // template data
    name: 'John',
    verifyUrl: 'https://...',
  },
  from: '[email protected]', // optional, overrides default
  replyTo: '[email protected]', // optional, overrides default
});

// Returns: { success: true, messageId: 'msg-xxx' }

getTemplates() - List Available Templates

Returns templates available for your API key. If your API key has template restrictions configured, only the allowed templates will be returned.

const { templates } = await email.getTemplates();

for (const template of templates) {
  console.log(`${template.id}: requires ${template.requiredData.join(', ')}`);
}

rateLimitInfo - Get Rate Limit Status

// After any API call, check rate limit status
console.log(email.rateLimitInfo);
// { limit: 100, remaining: 95, reset: 1700000000 }

Available Templates

| Template | Required Data | Description | | ------------------ | ------------------------------------ | ----------------------------- | | verification | name, verifyUrl | Email verification link | | password-reset | name, resetUrl | Password reset link | | welcome | name | Welcome message | | login-alert | name, device, location, time | New login notification | | account-deletion | name, deletedAt | Account deletion confirmation |

All templates also accept optional appName (defaults to "Ovixa").

Template Restrictions

API keys can be configured with template restrictions to limit which templates they can use:

  • No restrictions: API key can use all templates (default)
  • Restricted: API key can only use specific templates (e.g., welcome,verification)
  • Disabled: API key cannot use any templates (empty string)

Client vs Server Validation

The SDK validates that a template name is valid (i.e., it exists as a known template type), but it does not check whether your API key is authorized to use that template. Server-side validation is authoritative — a request may pass client-side validation but still be rejected by the server with a TEMPLATE_NOT_ALLOWED error (HTTP 403) if your API key doesn't have access.

| Validation Layer | Checks | Error Code | | ---------------- | ---------------------------------- | ---------------------- | | Client (SDK) | Template name is a known type | INVALID_TEMPLATE | | Server | API key is authorized for template | TEMPLATE_NOT_ALLOWED |

Best Practices for Restricted Keys

If your API key may have template restrictions, call getTemplates() first to discover which templates are available:

const email = new OvixaEmail({
  apiKey: process.env.OVIXA_EMAIL_API_KEY,
  defaultFrom: '[email protected]',
});

// Discover available templates for this API key
const { templates } = await email.getTemplates();
const availableIds = templates.map((t) => t.id);

// Check before sending
if (availableIds.includes('welcome')) {
  await email.send({
    to: '[email protected]',
    template: 'welcome',
    data: { name: 'John' },
  });
}

Handling Restricted Template Errors

When calling send() with a template your API key cannot access, the server returns a TEMPLATE_NOT_ALLOWED error:

try {
  await email.send({
    to: '[email protected]',
    template: 'login-alert', // Valid template, but key may not have access
    data: { name: 'John', device: 'Chrome', location: 'NY', time: 'now' },
  });
} catch (error) {
  if (error instanceof OvixaEmailError && error.code === 'TEMPLATE_NOT_ALLOWED') {
    console.log('This API key cannot send login-alert emails');
  }
}

Webhook Verification

Verify webhook signatures from email delivery events:

import { verifyWebhook, extractWebhookHeaders } from '@ovixa/email-client/webhooks';

// In your webhook handler
async function handleWebhook(request: Request) {
  const payload = await request.text();
  const headers = extractWebhookHeaders(request.headers);

  try {
    const event = await verifyWebhook({
      payload,
      signature: headers.signature,
      timestamp: headers.timestamp,
      messageId: headers.messageId,
      secret: process.env.WEBHOOK_SECRET,
    });

    switch (event.type) {
      case 'email.delivered':
        console.log('Email delivered:', event.data.email_id);
        break;
      case 'email.bounced':
        console.log('Email bounced:', event.data.email_id);
        break;
      case 'email.complained':
        console.log('Spam complaint:', event.data.email_id);
        break;
    }

    return new Response('OK', { status: 200 });
  } catch (error) {
    if (error instanceof WebhookVerificationError) {
      return new Response('Invalid signature', { status: 401 });
    }
    throw error;
  }
}

Webhook Event Types

| Event | Description | | ------------------ | ---------------------------------- | | email.sent | Email accepted by provider | | email.delivered | Email delivered to recipient | | email.bounced | Email bounced (hard/soft) | | email.complained | Recipient marked as spam | | email.opened | Email opened (if tracking enabled) | | email.clicked | Link clicked (if tracking enabled) |

Error Handling

import { OvixaEmail, OvixaEmailError } from '@ovixa/email-client';

try {
  await email.send({ ... });
} catch (error) {
  if (error instanceof OvixaEmailError) {
    console.error(`Error [${error.code}]: ${error.message}`);

    if (error.code === 'RATE_LIMITED' && error.retryAfter) {
      console.log(`Retry after ${error.retryAfter} seconds`);
    }
  }
}

Error Codes

| Code | Description | | ---------------------- | ---------------------------------------------- | | MISSING_API_KEY | API key not provided | | INVALID_API_KEY | API key is invalid | | INVALID_TEMPLATE | Template ID not found | | TEMPLATE_NOT_ALLOWED | API key is not authorized to use this template | | MISSING_DATA | Required template data missing | | INVALID_EMAIL | Invalid email address format | | RATE_LIMITED | Too many requests (check retryAfter) | | PROVIDER_ERROR | Email provider error | | NETWORK_ERROR | Network connectivity error | | TIMEOUT | Request timed out | | REQUEST_FAILED | Generic request failure |

Security Notes

  • API keys are never logged or included in error messages
  • HTTPS is enforced - HTTP URLs trigger a warning (except localhost)
  • Email format validation is performed client-side before sending
  • Webhook signatures use HMAC-SHA256 with constant-time comparison
  • Timestamp validation prevents replay attacks (5-minute window)
  • Rate limit info is exposed for client-side handling

TypeScript Support

Full TypeScript support with exported types:

import type {
  EmailClientConfig,
  EmailTemplate,
  SendEmailOptions,
  SendEmailResponse,
  TemplateInfo,
  TemplatesResponse,
  RateLimitInfo,
  EmailErrorCode,
} from '@ovixa/email-client';

import type {
  WebhookVerifyOptions,
  WebhookEvent,
  WebhookEventType,
  WebhookEventData,
} from '@ovixa/email-client/webhooks';

License

MIT