TypeScript SDK
The official shipmail package provides type-safe access to the full API. Works in Node.js 18+, Bun, Deno, and edge runtimes.
Installation
npm install shipmailbun add shipmailQuick start
import ShipMail from "shipmail";
const shipmail = new ShipMail(process.env.SHIPMAIL_API_KEY);
// Send an email
const message = await shipmail.messages.send({
from: "hello@yourdomain.com",
to: ["recipient@example.com"],
subject: "Hello from shipmail",
text: "It works.",
});
console.log(message.id); // msg_...Configuration
Pass a string for the API key alone, or an object for full control:
const shipmail = new ShipMail({
apiKey: process.env.SHIPMAIL_API_KEY,
baseUrl: "https://shipmail.to/api/v1", // default
maxRetries: 2, // default
timeout: 30_000, // ms, default
defaultHeaders: { "X-Custom": "value" },
});| Option | Default | Description |
|---|---|---|
| apiKey | required | Your sm_live_... key. |
| baseUrl | https://shipmail.to/api/v1 | API base URL. |
| maxRetries | 2 | Retry count for 429 and 5xx responses. |
| timeout | 30000 | Request timeout in ms. |
| defaultHeaders | {} | Headers sent with every request. |
| fetch | globalThis.fetch | Custom fetch implementation for testing or proxying. |
Resources
The client exposes each API resource as a property:
| Property | Methods |
|---|---|
| shipmail.domains | create, list, get, update, delete, verify, search, register |
| shipmail.mailboxes | create, list, get, update, delete, updateAutoReply |
| shipmail.messages | send, get |
| shipmail.threads | list, get, reply |
| shipmail.webhooks | create, list, get, update, delete, rotateSecret, test, listDeliveries |
| shipmail.suppressions | list, remove |
| shipmail.status | get |
Error handling
API errors throw typed subclasses of ShipMailError. Each error exposes status, type, requestId, and retryable.
import ShipMail, {
ValidationError,
RateLimitError,
ShipMailError,
} from "shipmail";
try {
await shipmail.messages.send({ ... });
} catch (err) {
if (err instanceof ValidationError) {
console.log(err.details); // per-field errors
} else if (err instanceof RateLimitError) {
console.log(err.retryAfter); // seconds to wait
} else if (err instanceof ShipMailError) {
console.log(err.status, err.requestId);
}
}| Class | Status |
|---|---|
| AuthenticationError | 401 |
| AuthorizationError | 403 |
| ValidationError | 422 |
| NotFoundError | 404 |
| ConflictError | 409 |
| RateLimitError | 429 |
| InternalServerError | 500 |
| ConnectionError | Network failure |
Pagination
List methods return a Page object that implements AsyncIterable. Iterate with for await to automatically fetch all pages:
const domains = shipmail.domains.list();
for await (const domain of domains) {
console.log(domain.name);
}Pass limit and cursor for manual control:
const page = shipmail.domains.list({ limit: 10 });Webhook verification
The SDK exports a standalone verifyWebhook function. It is async because it dynamically imports the crypto module for portability.
import { verifyWebhook } from "shipmail";
const event = await verifyWebhook(
rawBody,
request.headers,
process.env.WEBHOOK_SECRET,
);
console.log(event.event_type); // "message.received"Throws WebhookVerificationError if the signature is invalid or the timestamp is outside the 5-minute tolerance window. Pass { toleranceInSeconds: 600 } to customize.
Per-request options
Every method accepts an optional last argument for request-level overrides:
await shipmail.domains.create(
{ name: "example.com" },
{
idempotencyKey: "550e8400-...",
timeout: 10_000,
signal: AbortSignal.timeout(5000),
},
);