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

torpedo-sdk

v0.2.6

Published

TypeScript SDK for the Torpedo email and SMS API.

Readme

torpedo-sdk

npm version npm downloads License: MIT

TypeScript SDK for the Torpedo messaging API. Send transactional email and SMS with a single await.

const torpedo = new Torpedo({ apiKey: process.env.TORPEDO_API_KEY! })

await torpedo.emails.send({
  from: 'Acme <[email protected]>',
  to:   '[email protected]',
  subject: 'Welcome to Acme',
  html: '<h1>Welcome!</h1>',
})

Contents


Installation

npm install torpedo-sdk
# pnpm
pnpm add torpedo-sdk
# yarn
yarn add torpedo-sdk

If you plan to use React Email templates, also install React Email:

npm install react react-dom @react-email/components @react-email/render

Quick start

Get your API key from the Torpedo dashboard and store it in an environment variable — never hard-code it.

import { Torpedo } from 'torpedo-sdk'

const torpedo = new Torpedo({ apiKey: process.env.TORPEDO_API_KEY! })

const message = await torpedo.emails.send({
  from:    'Acme <[email protected]>',
  to:      '[email protected]',
  subject: 'Your receipt',
  html:    '<p>Thanks for your order!</p>',
})

console.log(message.id) // "email_01j..."

The API responds with 202 Acceptedmessage.id is the record you can poll for status updates.


Configuration

Pass options to the Torpedo constructor. All fields except apiKey are optional.

| Option | Type | Default | Description | |---|---|---|---| | apiKey | string | — | Required. Your Torpedo API key. | | timeout | number | 30000 | Per-request timeout in milliseconds. Requests that exceed this are aborted. |

const torpedo = new Torpedo({
  apiKey:  process.env.TORPEDO_API_KEY!,
  timeout: 10_000,  // fail fast in serverless functions
})

Emails

emails.send(input)

Sends a single email. Returns a queued message reference.

const message = await torpedo.emails.send({
  from:    'Acme <[email protected]>',  // must use a verified domain
  to:      '[email protected]',
  subject: 'Welcome',
  html:    '<h1>Hello!</h1>',
  text:    'Hello!',                 // recommended for deliverability
  replyTo: '[email protected]',        // optional
})

console.log(message.id, message.status) // "email_01j..." "queued"

Content rules

| Input | Behaviour | |---|---| | html only | Sent as HTML; no plain-text alternative | | text only | Sent as plain text | | html + text | Full multipart email (recommended) | | template | React Email component rendered to HTML + text automatically |

emails.sendBatch(input)

Sends up to 100 emails in a single API call. Each email is processed independently.

const messages = await torpedo.emails.sendBatch({
  emails: [
    { from: '[email protected]', to: '[email protected]', subject: 'Hi Alice', html: '<p>Hi!</p>' },
    { from: '[email protected]', to: '[email protected]',   subject: 'Hi Bob',   html: '<p>Hi!</p>' },
  ],
})
// messages[0].id, messages[1].id

emails.list(params?)

Returns a cursor-paginated list of emails sent from your workspace.

const page = await torpedo.emails.list({ limit: 25 })

for (const email of page.data) {
  console.log(email.id, email.status)
}

if (page.meta.has_more) {
  const next = await torpedo.emails.list({ cursor: page.meta.next_cursor })
}

emails.get(id)

Returns the full record for a single email, including the HTML and plain-text body.

const email = await torpedo.emails.get('email_01j...')

console.log(email.status)      // "delivered"
console.log(email.deliveredAt) // "2026-06-09T10:00:00.000Z"
console.log(email.html)        // "<h1>Hello!</h1>"

Email statuses

| Status | Meaning | |---|---| | queued | Accepted, waiting to be sent | | sent | Handed off to the delivery provider | | delivered | Confirmed delivered by the provider | | bounced | Hard bounce — address suppressed automatically | | complained | Spam complaint — address suppressed automatically | | failed | Permanent failure after retries |


SMS

SMS requires the Torpedo server to be configured with an SMPP gateway. Check with your workspace admin if SMS is available.

sms.send(input)

const message = await torpedo.sms.send({
  to:   '+15551234567',         // E.164 format required
  body: 'Your code is 482910.',
  from: 'Acme',                 // optional sender ID
})

sms.list(params?)

const page = await torpedo.sms.list({ limit: 25 })

sms.get(id)

const sms = await torpedo.sms.get('sms_01j...')
console.log(sms.status, sms.deliveredAt)

React Email templates

Use React Email components as email bodies. Torpedo renders the component to HTML and plain text before sending — no separate build step required.

React Email support is optional. Install React Email only if you want to send React templates:

npm install react react-dom @react-email/components @react-email/render
import { Torpedo }                   from 'torpedo-sdk'
import { Body, Button, Html, Text } from '@react-email/components'

function WelcomeEmail({ name, url }: { name: string; url: string }) {
  return (
    <Html>
      <Body>
        <Text>Hi {name}, thanks for signing up.</Text>
        <Button href={url}>Get started</Button>
      </Body>
    </Html>
  )
}

const torpedo = new Torpedo({ apiKey: process.env.TORPEDO_API_KEY! })

await torpedo.emails.send({
  from:     'Acme <[email protected]>',
  to:       '[email protected]',
  subject:  'Welcome to Acme',
  template: <WelcomeEmail name="Alice" url="https://acme.com/start" />,
})

Error handling

All API errors throw TorpedoError. Client-side validation failures (bad input before any HTTP call) throw TorpedoValidationError.

import { Torpedo, TorpedoError, TorpedoValidationError } from 'torpedo-sdk'

try {
  await torpedo.emails.send({ from: '[email protected]', to: '...', subject: '...', html: '...' })
} catch (err) {
  if (err instanceof TorpedoError) {
    console.error(err.status)         // 422
    console.error(err.errors)         // [{ message: 'Domain not verified', field: 'from' }]
    console.error(err.response)       // raw Response object
  }

  if (err instanceof TorpedoValidationError) {
    // Thrown before hitting the network — bad input (empty apiKey, missing body, etc.)
    console.error(err.message)
  }
}

Common status codes

| Status | Meaning | |---|---| | 401 | Invalid or missing API key | | 422 | Validation failed (e.g. unverified domain, missing required field) | | 429 | Rate limited — SDK retries automatically with backoff | | 5xx | Server error — SDK retries automatically (up to 3 times) |

Retry behaviour

The SDK retries 429 and 5xx responses automatically — up to 3 times with exponential backoff (500 ms → 1 s → 2 s → … up to 16 s). Every write request is automatically assigned a unique idempotency key, so SDK-level retries never produce duplicate sends.


Webhook signature verification

Torpedo signs every webhook delivery with an HMAC-SHA256 signature and sends it in the X-Signature header (sha256=<hex>). Always verify this signature before processing the payload.

import { verifyWebhookSignature } from 'torpedo-sdk'

// Express example — use raw body middleware, NOT json()
app.post('/webhooks/torpedo', express.raw({ type: '*/*' }), async (req, res) => {
  const valid = await verifyWebhookSignature(
    req.body,                                  // Buffer — do NOT parse JSON first
    process.env.TORPEDO_WEBHOOK_SECRET!,
    req.headers['x-signature'] ?? '',
  )

  if (!valid) {
    return res.status(401).send('Invalid signature')
  }

  const event = JSON.parse(req.body.toString())

  switch (event.event) {
    case 'email.delivered':  /* ... */ break
    case 'email.bounced':    /* ... */ break
    case 'email.complained': /* ... */ break
    case 'email.failed':     /* ... */ break
    case 'domain.verified':  /* ... */ break
    case 'domain.failed':    /* ... */ break
  }

  res.sendStatus(200)
})

Webhook events

| Event | Fired when | |---|---| | email.delivered | Delivery confirmed by the provider | | email.bounced | Hard bounce received; address suppressed | | email.complained | Spam complaint received; address suppressed | | email.failed | Permanent send failure after all retries | | domain.verified | Domain DKIM verification succeeded | | domain.failed | Domain DKIM verification failed |

Important — pass the raw request body to verifyWebhookSignature. Parsing JSON first changes the byte representation and will cause verification to fail.


Requirements

  • Node.js ≥ 20
  • React ≥ 18 and @react-email/render ≥ 2 (only required for React Email template support)

The SDK ships both ESM (import) and CommonJS (require) builds and works in any Node.js environment, edge runtimes (Cloudflare Workers, Vercel Edge), and server-side React frameworks (Next.js, Remix, TanStack Start).