TypeScript SDK
Official TypeScript/JavaScript SDK for Posthawk. Zero dependencies, React Email support, and full type safety. Works in Node.js, Bun, Deno, and any runtime with a native fetch API.
npm install posthawkInstall the SDK from npm. Ships as both ESM and CommonJS with full TypeScript declarations. To use React Email components with the react prop, also install the optional peer dependency: npm install @react-email/render
Example
npm install posthawknew Posthawk(key)PosthawkInitialize the client and send your first email. The constructor accepts a plain API key string (uses the default cloud URL) or a config object for self-hosted instances.
Example
import { Posthawk } from 'posthawk';
// Cloud (default base URL: https://api.posthawk.dev)
const posthawk = new Posthawk('ck_live_...');
// Self-hosted
const posthawk = new Posthawk({
apiKey: 'ck_live_...',
baseUrl: 'https://api.yourdomain.com',
});
const { data, error } = await posthawk.emails.send({
from: 'hello@yourdomain.com',
to: 'user@example.com',
subject: 'Welcome!',
html: '<h1>Hello!</h1>',
});
if (error) {
console.error(error.message);
} else {
console.log('Sent!', data.jobId);
}posthawk.emails.send(params){ data, error }Send an email immediately, or schedule it for later by including scheduledFor. Returns { data, error }. At least one of html, text, react, or templateId is required.
| Parameter | Type | Description |
|---|---|---|
fromrequired | string | Sender email address (must be from a verified domain) |
torequired | string | string[] | Recipient email address(es) |
cc | string | string[] | CC recipient(s) |
bcc | string | string[] | BCC recipient(s) |
subjectrequired | string | Email subject line |
html | string | HTML email body |
text | string | Plain text email body |
react | ReactElement | React Email component (requires @react-email/render) |
templateId | string | Posthawk template ID |
variables | Record<string, string> | Template variable substitution |
headers | Record<string, string> | Custom email headers (e.g. List-Unsubscribe) |
scheduledFor | string | Date | Schedule for later delivery (ISO 8601 or Date object) |
timezone | string | IANA timezone for scheduled time |
metadata | Record<string, unknown> | Custom metadata attached to the email |
tags | Record<string, unknown> | Custom tags for filtering and search |
Example
const { data, error } = await posthawk.emails.send({
from: 'hello@yourdomain.com',
to: ['user@example.com', 'other@example.com'],
subject: 'Welcome to Acme',
html: '<h1>Welcome!</h1><p>Thanks for signing up.</p>',
metadata: { userId: 'usr_123' },
tags: { campaign: 'onboarding' },
});
// Schedule for later
const { data, error } = await posthawk.emails.send({
from: 'hello@yourdomain.com',
to: 'user@example.com',
subject: 'Reminder',
text: "Don't forget your meeting!",
scheduledFor: '2026-03-15T09:00:00Z',
timezone: 'America/New_York',
});posthawk.emails.get(jobId){ data, error }Get the delivery status of a previously queued email by its job ID.
| Parameter | Type | Description |
|---|---|---|
jobIdrequired | string | Job ID returned from the send method |
Example
const { data, error } = await posthawk.emails.get('abc-123-def');
if (data) {
console.log(data.status); // 'pending' | 'processing' | 'completed' | 'failed'
console.log(data.result); // { messageId: '...', emailLogId: '...' }
}posthawk.scheduled.list(params?){ data, error }List scheduled emails with optional filtering by status and pagination.
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status: scheduled, sent, cancelled, failed |
limit | number | Pagination limit |
offset | number | Pagination offset |
Example
const { data } = await posthawk.scheduled.list({
status: 'scheduled',
limit: 10,
});
// data.data → ScheduledEmail[]
// data.total → numberposthawk.scheduled.get(id){ data, error }Get the details of a specific scheduled email by its ID.
| Parameter | Type | Description |
|---|---|---|
idrequired | string | Scheduled email UUID |
Example
const { data } = await posthawk.scheduled.get('scheduled-uuid');
if (data) {
console.log(data.data.scheduled_for); // ISO 8601 datetime
console.log(data.data.status); // 'scheduled' | 'sent' | 'cancelled'
}posthawk.scheduled.cancel(id){ data, error }Cancel a scheduled email before it sends. Only works for emails that have not yet been processed.
| Parameter | Type | Description |
|---|---|---|
idrequired | string | Scheduled email UUID |
Example
const { data, error } = await posthawk.scheduled.cancel('scheduled-uuid');
// data.message → "Scheduled email cancelled successfully"posthawk.scheduled.reschedule(id, params){ data, error }Change the send time of a scheduled email. Accepts a Date object or ISO 8601 string.
| Parameter | Type | Description |
|---|---|---|
idrequired | string | Scheduled email UUID |
scheduledForrequired | string | Date | New send time (ISO 8601 or Date object) |
Example
await posthawk.scheduled.reschedule('scheduled-uuid', {
scheduledFor: new Date('2026-04-01T10:00:00Z'),
});react: WelcomeEmail({ name })Use React Email components directly — the SDK renders them to HTML for you. Pass any React element via the react prop. The SDK dynamically imports @react-email/render at runtime, so the peer dependency is only needed if you use this feature.
Example
import { Posthawk } from 'posthawk';
import { WelcomeEmail } from './emails/welcome';
const posthawk = new Posthawk('ck_live_...');
const { data, error } = await posthawk.emails.send({
from: 'hello@yourdomain.com',
to: 'user@example.com',
subject: 'Welcome aboard!',
react: WelcomeEmail({ name: 'Alex' }),
});{ data, error }SDK methods never throw for API errors. Every call returns a discriminated union of { data: T, error: null } or { data: null, error: PosthawkError }. The only case where the SDK throws is a missing API key in the constructor.
Example
const { data, error } = await posthawk.emails.send({ ... });
if (error) {
console.error(error.message); // Human-readable error message
console.error(error.statusCode); // HTTP status code (e.g. 400, 429)
return;
}
// data is guaranteed non-null here
console.log(data.jobId);posthawk.domains.*{ data, error }Manage sending domains programmatically — add, verify, enable inbound receiving, configure webhooks, and set sending limits. Ideal for SaaS platforms that let their users add custom sending domains.
Example
// Add a domain
const { data, error } = await posthawk.domains.create({
domain: 'mail.customer.com',
region: 'us-east-1',
});
// data.dns_records → DNS records to configure
// List all domains
const { data: domains } = await posthawk.domains.list();
// Trigger verification check
const { data: status } = await posthawk.domains.verify('domain-uuid');
// status.sending_enabled → true when verified
// Enable inbound receiving
await posthawk.domains.enableReceiving('domain-uuid');
// Set a webhook for inbound emails
await posthawk.domains.updateWebhook('domain-uuid', {
webhookUrl: 'https://yourapp.com/api/incoming-email',
});
// Test the webhook
await posthawk.domains.testWebhook('domain-uuid');
// Set per-domain sending limits
await posthawk.domains.updateLimits('domain-uuid', {
maxDailyEmails: 1000,
maxMonthlyEmails: 25000,
});
// Remove limits (set to null for unlimited)
await posthawk.domains.updateLimits('domain-uuid', {
maxDailyEmails: null,
maxMonthlyEmails: null,
});
// Delete a domain
await posthawk.domains.delete('domain-uuid');import type { ... } from 'posthawk'All types are exported from the package for full type safety in your application.
Example
import type {
PosthawkConfig,
SendEmailRequest,
SendEmailResponse,
EmailJobStatus,
ScheduledEmail,
ScheduledListParams,
ScheduledListResponse,
RescheduleRequest,
PosthawkResponse,
CreateDomainRequest,
Domain,
DomainResponse,
DomainListResponse,
DomainDeleteResponse,
UpdateDomainWebhookRequest,
WebhookTestResponse,
} from 'posthawk';