TypeScript SDK

The official TypeScript SDK for Receiver.email. Typed, promise based, and built around minting an inbox and waiting for what lands in it.

Installation

bash
npm install @receiverdotemail/sdk

Also works with yarn and pnpm:

bash
yarn add @receiverdotemail/sdk
bash
pnpm add @receiverdotemail/sdk

Configuration

TypeScript
import { ReceiverClient } from "@receiverdotemail/sdk";

// Reads RECEIVER_API_KEY (and RECEIVER_API_URL) from the environment
const client = new ReceiverClient();

// Or pass everything explicitly
const client = new ReceiverClient({
  apiKey: process.env.RECEIVER_API_KEY,        // recv_live_...
  baseUrl: "https://api.receiver.email/api/v1", // Optional
  timeout: 30000,                               // Optional: per-request ms
  userAgent: "my-tool/1.0",                     // Optional
});

With an API key, projectId is optional on every email and inbox method: the client calls whoami() once and remembers the key's project. Pass projectId (to the constructor or a method) to skip that request or to use a dashboard token.

The Recommended Pattern

Mint an address, use it, then wait from its createdAt. Mail from before the address existed can never match, so retries and parallel runs stay independent.

TypeScript
const inbox = await client.inboxes.create({ prefix: "signup" });
await app.signUp(inbox.address);

const otp = await client.emails.waitForOtp({
  recipient: inbox.address,
  since: inbox.createdAt,
  timeout: 60,
});
await app.verify(otp.code);

Inboxes and Identity

inboxes.create(): Mint an Address

TypeScript
const inbox = await client.inboxes.create({ prefix: "signup" }); // prefix optional
// {
//   address: "[email protected]",
//   prefix: "signup-k2m9qa",
//   projectId: "proj_abc123",
//   createdAt: "2026-11-05T10:00:00.000Z"
// }

whoami(): Who the Key Belongs To

TypeScript
const me = await client.whoami();
if (me.authType === "apikey") {
  console.log(me.project.name, me.project.emailSuffix); // "Signup tests", "[email protected]"
}

Waiting for Email

waitFor(): Long-Poll for an Email

Resolves with the first email matching the filters received after since. timeout is in seconds, 30 by default, and can be any length: the SDK repeats 60 second server waits until it runs out, then throws ReceiverTimeoutError. It never resolves to null.

TypeScript
const email = await client.emails.waitFor({
  recipient: inbox.address,       // Optional filters
  sender: "[email protected]",
  subject: "Reset",               // Substring, case-insensitive
  since: inbox.createdAt,         // Default: the last 60 seconds
  timeout: 60,                    // Seconds (default 30)
});

console.log(email.subject);
console.log(email.links);          // Every URL in the email
console.log(email.textBody);       // get() adds HTML, headers and attachments

waitForOtp(): Wait for a Code

Same filters and looping as waitFor(), plus codeLength (4, 6 or 8). Returns the newest code, where it was found, the email it came from and every other code in the matching emails.

TypeScript
const otp = await client.emails.waitForOtp({
  recipient: inbox.address,
  since: inbox.createdAt,
  codeLength: 6,                  // Optional
  timeout: 60,
});

otp.code;         // "847291"
otp.source;       // "subject" | "body"
otp.email;        // { id, sendTo, sender, subject, receivedAt, ... }
otp.candidates;   // Every code found, newest first

getOtp(): Check Without Waiting

TypeScript
const otp = await client.emails.getOtp({ recipient: inbox.address, since: inbox.createdAt });
if (otp === null) {
  // No code yet
}

Timeouts

TypeScript
import { ReceiverTimeoutError } from "@receiverdotemail/sdk";

try {
  await client.emails.waitFor({ recipient: inbox.address, since: inbox.createdAt, timeout: 30 });
} catch (error) {
  if (error instanceof ReceiverTimeoutError) {
    console.log(error.since);          // The since the API used
    console.log(error.waitedSeconds);  // How long it waited
  }
  throw error;
}

To wait for the next email on an address, pass the receivedAt of the one you already have as since.

stream(): Emails as They Arrive

An async iterator over new emails (Server-Sent Events). It reconnects on its own and never repeats or skips an email; break out of the loop to close it.

TypeScript
for await (const event of client.emails.stream({ recipient: inbox.address })) {
  console.log(event.email.subject, event.otp?.code);
  if (event.otp) break;
}

sendTest(): Send Yourself a Test Email

TypeScript
const inbox = await client.inboxes.create();
await client.emails.sendTest({ to: inbox.address }); // 10 per project per hour
const otp = await client.emails.waitForOtp({ recipient: inbox.address, since: inbox.createdAt });

Reading Email

list() and listAll()

TypeScript
const page = await client.emails.list({
  recipient: "[email protected]",  // Optional filters
  receivedAfter: new Date("2026-11-01"),
  limit: 50,                                             // Max 100
});
if (page.hasMore) {
  const next = await client.emails.list({ cursor: page.nextCursor });
}

// Or iterate every page
for await (const email of client.emails.listAll({ sender: "[email protected]" })) {
  console.log(email.subject);
}

get() and delete()

TypeScript
const email = await client.emails.get("email_xyz789");
console.log(email.htmlBody, email.headers, email.attachments);

await client.emails.delete("email_xyz789");

// With a dashboard token, or another project, pass the project first
await client.emails.get("proj_abc123", "email_xyz789");

Convenience Methods

TypeScript
await client.emails.findByRecipient("[email protected]");
await client.emails.findBySender("[email protected]");
await client.emails.findBySubject("Password Reset");

Webhooks

Managing webhooks needs a dashboard access token, not an API key (see the API reference). Verifying deliveries works anywhere:

TypeScript
import { verifyWebhookSignature } from "@receiverdotemail/sdk";

const ok = verifyWebhookSignature({
  secret: process.env.RECEIVER_WEBHOOK_SECRET!,   // whsec_...
  header: req.headers["receiver-signature"],
  body: rawBody,                                   // The raw request body string
});

// Managing webhooks: a client made with a dashboard access token
const admin = new ReceiverClient({ apiKey: accessToken });
const hook = await admin.webhooks.create({ projectId: "proj_abc123", url: "https://example.com/hooks/receiver" });
await admin.webhooks.list("proj_abc123");
await admin.webhooks.test("proj_abc123", hook.id);
await admin.webhooks.deliveries("proj_abc123", hook.id);
await admin.webhooks.delete("proj_abc123", hook.id);

Projects, Organizations and API Keys

TypeScript
const project = await client.projects.get("proj_abc123");
console.log(project.emailSuffix); // "[email protected]"

// These need a dashboard access token
await client.projects.list("org_abc123");
await client.projects.create({ organizationId: "org_abc123", name: "CI Tests" });
await client.organizations.list();

const key = await client.apiKeys.create({ projectId: "proj_abc123", name: "CI Pipeline" });
console.log(key.key); // Full key, only shown once
await client.apiKeys.revoke("proj_abc123", key.id);

Error Handling

TypeScript
import {
  ReceiverError,
  ReceiverTimeoutError,
  RateLimitError,
  AuthenticationError,
  NotFoundError,
} from "@receiverdotemail/sdk";

try {
  await client.emails.waitForOtp({ recipient, since });
} catch (error) {
  if (error instanceof ReceiverTimeoutError) {
    // Nothing arrived in time
  } else if (error instanceof RateLimitError) {
    console.log("Retry at", error.resetAt);
  } else if (error instanceof AuthenticationError) {
    // Bad or revoked key
  } else if (error instanceof NotFoundError) {
    // Wrong project or email id
  } else if (error instanceof ReceiverError) {
    console.log(error.statusCode, error.code, error.message);
  }
}

Upgrading to 2.0

  • waitFor() throws ReceiverTimeoutError instead of resolving to null.
  • since replaces receivedAfter on waits (the old name still works).
  • projectId is optional with an API key.