Posthawk CLI
The Posthawk CLI ships inside the same npm package as the Node.js SDK. A single global install gives you commands to scaffold new projects, preview React Email templates with hot reload, and send templates directly from the terminal — no dashboard, no wrapper scripts.
npm i -g posthawkInstall once globally. You get the `posthawk` binary plus all the SDK types. The CLI works on macOS, Linux, and Windows with Node.js 18 or newer.
Example
npm i -g posthawk
# verify install
posthawk --versionposthawk loginStore your API key locally in ~/.posthawk/config.json (file mode 0600). The CLI verifies the key against /v1/domains before saving. You can also set POSTHAWK_API_KEY as an env var to override the stored key.
Example
# Interactive login (masked prompt)
posthawk login
# Or via environment variable
export POSTHAWK_API_KEY=ck_live_...
# Check the active key
posthawk whoami
# Remove the stored key
posthawk logoutposthawk init [dir]Scaffold a new project with a React Email starter template, package.json, .env.local, and .gitignore. Run it in an existing directory to add the files without overwriting anything.
Example
# New folder
posthawk init my-emails
cd my-emails
npm install
# Or scaffold into the current folder
posthawk init .
# Output:
# created emails/welcome.tsx
# created .env.local
# created .gitignore
# created package.json
# ✓ Project ready.posthawk preview <file.tsx>Start a local dev server with hot reload for a React Email template. Edit the file and the rendered preview updates instantly via SSE. The preview server has an HTML view and a Plain Text view so you can check both variants of the email.
| Parameter | Type | Description |
|---|---|---|
filerequired | string | Path to a .tsx/.jsx email template with a default export |
--port | number | Override the default port (7321) |
--props | json | JSON string passed as props to the component |
Example
# Start the preview server
posthawk preview emails/welcome.tsx
# Custom port
posthawk preview emails/welcome.tsx --port 8000
# Pass props to the component
posthawk preview emails/welcome.tsx \
--props '{"name":"Alex","actionUrl":"https://example.com"}'
# Output:
# ▸ starting dev server at http://localhost:7321
# ✓ ready in 182ms
# watching for changes · press Ctrl+C to stopposthawk send <file.tsx> --to <email> --from <email>Compile a React Email template, render it to HTML + plain text, and send it via the Posthawk API. The send command honours the same --props flag as preview, so the exact same template renders in both commands.
| Parameter | Type | Description |
|---|---|---|
filerequired | string | Path to a .tsx/.jsx email template |
--torequired | string | Recipient(s), comma-separated |
--fromrequired | string | Sender (must be from a verified domain) |
--subject | string | Overrides the template's default subject |
--cc | string | CC recipients, comma-separated |
--bcc | string | BCC recipients, comma-separated |
--props | json | JSON string passed as props to the component |
--dry-run | flag | Compile and render without sending |
Example
# Minimal send
posthawk send emails/welcome.tsx \
--to alex@acme.io \
--from hello@yourdomain.dev
# With props, custom subject, and cc
posthawk send emails/welcome.tsx \
--to alex@acme.io,sam@acme.io \
--cc noreply@yourdomain.dev \
--from hello@yourdomain.dev \
--subject "Welcome to Acme" \
--props '{"name":"Alex"}'
# Dry-run: compile and render without hitting the API
posthawk send emails/welcome.tsx \
--to test@example.com \
--from hi@example.dev \
--dry-run
# Output:
# ▸ compiling welcome.tsx
# compiled in 195ms
# ▸ sending via api.posthawk.dev
# ✓ sent in 140ms
# id em_01HQT3...
# to alex@acme.ioexport default Component; export const subjectThe CLI expects each template to default-export a React component. Optionally, you can export a `subject` string or function — the CLI uses it as the default subject when --subject is not passed. The `subject` function receives the same props as the component.
Example
// emails/welcome.tsx
import { Body, Container, Heading, Html, Text } from '@react-email/components';
interface WelcomeEmailProps {
name?: string;
}
// Optional subject export — receives the same props as the component
export const subject = ({ name }: WelcomeEmailProps) =>
`Welcome to Posthawk${name ? `, ${name}` : ''}!`;
// Default export is the component
export default function WelcomeEmail({ name = 'there' }: WelcomeEmailProps) {
return (
<Html>
<Body>
<Container>
<Heading>Hi {name},</Heading>
<Text>Welcome to Posthawk!</Text>
</Container>
</Body>
</Html>
);
}posthawk domainsList all domains in your workspace with their verification status. Useful for checking which from addresses you can use with `posthawk send`.
Example
posthawk domains
# Output:
# Domains (3)
#
# ● yourdomain.dev — verified · eu-north-1
# ● notify.acme.io — verified · us-east-1
# ○ staging.acme.io — pending · eu-north-1POSTHAWK_API_KEY / POSTHAWK_BASE_URLAll CLI commands respect the same environment variables as the SDK. Env vars take precedence over the stored config file.
| Parameter | Type | Description |
|---|---|---|
POSTHAWK_API_KEY | string | Overrides the stored API key — essential for CI/CD |
POSTHAWK_BASE_URL | string | Overrides the API URL — use this for self-hosted instances |
NO_COLOR | flag | Disables colored terminal output |
Example
# Run in CI without interactive login
export POSTHAWK_API_KEY=ck_live_...
posthawk send emails/release-notes.tsx \
--to announcements@acme.io \
--from release-bot@acme.dev \
--props "{\"version\":\"${GITHUB_REF_NAME}\"}"
# Self-hosted instance
export POSTHAWK_BASE_URL=https://api.yourdomain.com
posthawk whoamiGitHub Actions workflowShip release notes automatically when a git tag is pushed. The CLI works in any CI environment that has Node.js 18+ and can install global npm packages.
Example
# .github/workflows/release-notes.yml
name: Send Release Notes
on:
push:
tags: ['v*']
jobs:
send:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm i -g posthawk
- name: Send release notes
env:
POSTHAWK_API_KEY: ${{ secrets.POSTHAWK_API_KEY }}
run: |
posthawk send emails/release-notes.tsx \
--to changelog@acme.io \
--from release-bot@acme.dev \
--subject "Acme ${{ github.ref_name }} shipped" \
--props "{\"version\":\"${{ github.ref_name }}\"}"esbuild + React EmailThe CLI compiles your .tsx file at runtime using esbuild, then dynamic-imports the result to render with @react-email/render. Compiled output is cached in node_modules/.posthawk-cache/ so subsequent runs start faster. React and @react-email/* are kept external during compilation so they resolve from your project's own node_modules — meaning you always get the exact same React Email version your templates were authored against.
Example
# What the CLI does when you run 'posthawk send welcome.tsx':
#
# 1. esbuild bundles welcome.tsx → ESM with react/@react-email/*
# left as external imports
# 2. Writes the output to node_modules/.posthawk-cache/<hash>.mjs
# 3. Dynamic-imports that file with a cache-busting ?t=timestamp
# 4. Pulls the default export as the component
# 5. Calls render() from @react-email/render to get HTML + text
# 6. Sends the rendered payload via the SDK
#
# First compile: ~500ms cold. Subsequent compiles: ~200ms warm.