@postbox/sdk
v0.1.2
Published
Official Postbox SDK for TypeScript and JavaScript: mailbox infrastructure for developers.
Downloads
33
Maintainers
Readme
Postbox SDK for TypeScript
The official Postbox SDK: provision mailboxes, send and receive mail, stream inbound events, and manage domains, all from one typed client.
Published as
postbox. One API key, one base URL, both the data plane (messages, events) and the management plane (domains, mailboxes, keys) behind a single client.
Install
npm install @postbox/sdkNode 18+ (uses the built-in fetch). Zero runtime dependencies.
Quick start
import { Postbox } from "@postbox/sdk";
const postbox = new Postbox({ apiKey: process.env.POSTBOX_API_KEY! });
// Send
await postbox.messages.send({
from: "[email protected]",
to: [{ address: "[email protected]" }],
subject: "Your receipt",
html: "<p>Thanks!</p>",
});
// Provision
const domain = await postbox.domains.create({ domain: "acme.com" });
await postbox.domains.verify(domain.id);
await postbox.mailboxes.create({ /* … */ });Streaming inbound events (SSE)
Streaming is first-class: a native async iterator that reconnects on drop and resumes from the last event it saw:
const controller = new AbortController();
for await (const event of postbox.events.stream({ types: ["message.received"] }, controller.signal)) {
console.log(event.event, event.data);
}
// controller.abort() stops it promptly.Verifying webhooks
verifyWebhook is timing-safe, checks the timestamp against a tolerance window
(replay protection), and verifies the raw body:
import express from "express";
app.post("/webhooks/postbox", express.raw({ type: "application/json" }), (req, res) => {
try {
const event = postbox.verifyWebhook(
req.body, // raw Buffer
req.header("X-Postbox-Signature"),
req.header("X-Postbox-Timestamp"),
process.env.POSTBOX_WEBHOOK_SECRET!,
);
// handle event…
res.sendStatus(200);
} catch {
res.sendStatus(400); // SignatureError
}
});Errors
Every failure is a typed subclass of PostboxError, carrying status, code,
and a requestId (quote it in support tickets):
import { RateLimitError, ValidationError, PostboxError } from "@postbox/sdk";
try {
await postbox.messages.send(/* … */);
} catch (err) {
if (err instanceof ValidationError) console.error(err.issues); // per-field
else if (err instanceof RateLimitError) console.error("retry after", err.retryAfter);
else if (err instanceof PostboxError) console.error(err.status, err.requestId);
}AuthenticationError (401), PermissionError (403), NotFoundError (404),
ConflictError (409), ValidationError (400/422), RateLimitError (429),
ServerError (5xx), TimeoutError, NetworkError, SignatureError.
Configuration
new Postbox({
apiKey: "pb_live_…", // required
baseUrl: "https://api.postboxapp.cloud/v1", // override for self-host/staging
projectId: "proj_…", // sets X-Project-Id for project-scoped keys
timeout: 30_000, // ms per attempt
maxRetries: 2, // extra attempts (≤3 total)
defaultHeaders: {}, // merged into every request
fetch: myFetch, // inject a proxy-aware fetch
hooks: { onRequest, onResponse, onRetry }, // observability
});Reliability
- Retries on network errors, timeouts, 429, and 5xx, with exponential backoff
with full jitter, honoring
Retry-After. - Idempotency: every
POSTsends anIdempotency-Key, reused across a call's retries, so a network blip can't double-send. - Cancellation: pass an
AbortSignal(oroptions.signal) to any call.
