torpedo-sdk
v0.2.6
Published
TypeScript SDK for the Torpedo email and SMS API.
Readme
torpedo-sdk
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
- Quick start
- Configuration
- Emails
- SMS
- React Email templates
- Error handling
- Webhook signature verification
- Requirements
Installation
npm install torpedo-sdk
# pnpm
pnpm add torpedo-sdk
# yarn
yarn add torpedo-sdkIf you plan to use React Email templates, also install React Email:
npm install react react-dom @react-email/components @react-email/renderQuick 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 Accepted — message.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].idemails.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/renderimport { 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).
