Newsletters
Public REST API for managing newsletters, subscribers, and issues. Full CRUD plus subscribe / DOI confirm / unsubscribe / send-issue endpoints. Pairs with the embed widget and hosted subscribe page.
Newsletters are the recurring-send counterpart to broadcasts. Each newsletter has its own subscriber list, branding, double-opt-in (DOI) flow, and history of issues. The newsletter API is exposed at `/v1/newsletters/...` and is API-key authenticated, scoped to your workspace. Public sign-up flows: • **Hosted subscribe page** — `/n/:slug` (Next.js dashboard app) renders a public subscribe page with your branding. Newsletter signups land here when shared via QR code or social link. • **Embed widget** — `/n/:slug/embed` is an iframe-friendly subscribe form you can drop into any site with a single `<iframe>` tag. Branded to your newsletter's accent color and logo. • **Direct API** — `POST /v1/newsletters/:id/subscribers` accepts subscribe requests programmatically, perfect for headless integrations. Double opt-in (DOI): When DOI is enabled (the default), a new subscriber is created with `status: 'pending'` and a confirmation email is sent. The subscriber clicks the confirm link, which sets `status: 'active'`. Issues only fan out to active subscribers. Auto-suppression cascade: When a subscriber's email is added to `suppression_list` (hard bounce, repeated soft bounces, complaint, or manual), all matching newsletter_subscribers rows for that email in the same workspace are automatically flipped to `status: 'unsubscribed'` with the suppression reason recorded in `metadata.unsubscribe_reason`. Bounce / Complaint badges show on the subscriber row in the dashboard so you can tell dead addresses from voluntary opt-outs at a glance. Issue throughput: Sending an issue queues one email per active subscriber, drained at the shared **12 emails/sec** worker limit (which sits under AWS SES's **14/sec** account ceiling). A newsletter with 5,000 active subscribers takes ~7 minutes to fully ship. The dashboard shows live progress while the issue is sending.
/v1/newslettersAPI KeyList all newsletters in the current workspace.
/v1/newsletters/:idAPI KeyGet a single newsletter by ID, including branding, DOI settings, and subscriber count.
| Parameter | Type | In | Description |
|---|---|---|---|
idrequired | string | path | Newsletter UUID |
/v1/newslettersAPI KeyCreate a new newsletter. Accepts name, slug, from_email, from_name, accent_color, logo_url, doi_enabled, and other branding fields.
| Parameter | Type | In | Description |
|---|---|---|---|
namerequired | string | body | Display name (e.g. "Weekly Digest") |
slugrequired | string | body | URL slug — used in /n/:slug public pages |
from_emailrequired | string | body | Sender email — domain must be verified for sending |
from_name | string | body | Display name on outbound emails |
description | string | body | Short description shown on the subscribe page |
doi_enabled | boolean | body | Whether to require double-opt-in confirmation. Default: true |
accent_color | string | body | Hex color for branded UI (subscribe page, confirm email, embed widget) |
logo_url | string | body | URL of your newsletter logo |
/v1/newsletters/:idAPI KeyUpdate a newsletter. Only provided fields are updated.
| Parameter | Type | In | Description |
|---|---|---|---|
idrequired | string | path | Newsletter UUID |
/v1/newsletters/:idAPI KeyDelete a newsletter. Cascades to subscribers and issues.
| Parameter | Type | In | Description |
|---|---|---|---|
idrequired | string | path | Newsletter UUID |
/v1/newsletters/:id/subscribersAPI KeyList subscribers for a newsletter. Supports status filter (active / pending / unsubscribed), search, and pagination.
| Parameter | Type | In | Description |
|---|---|---|---|
idrequired | string | path | Newsletter UUID |
status | string | query | active, pending, or unsubscribed |
search | string | query | Match against email or name |
page | number | query | Page number (default: 1) |
/v1/newsletters/:id/subscribers/:subscriberIdAPI KeyGet a single subscriber.
/v1/newsletters/:id/subscribersAPI KeyAdd a subscriber. With DOI enabled, creates a pending subscriber and sends the confirmation email. With DOI disabled, creates an active subscriber immediately. Idempotent — duplicate emails return the existing subscriber.
| Parameter | Type | In | Description |
|---|---|---|---|
idrequired | string | path | Newsletter UUID |
emailrequired | string | body | Subscriber email (lowercased) |
name | string | body | Subscriber display name |
source | string | body | Tag indicating where the signup came from (e.g. "embed", "footer", "import") |
metadata | object | body | Arbitrary JSON metadata to attach to the subscriber |
/v1/newsletters/:id/subscribers/:subscriberId/unsubscribeAPI KeyUnsubscribe a subscriber. Flips status to unsubscribed and records the timestamp. The public one-click unsubscribe link in newsletter footers also lands here.
/v1/newsletters/:id/subscribers/:subscriberId/resend-confirmationAPI KeyResend the DOI confirmation email to a pending subscriber. Useful when the original was lost in spam.
/v1/newsletters/:id/subscribers/:subscriberIdAPI KeyPermanently delete a subscriber. Use POST /unsubscribe to keep the record for analytics.
/v1/newsletters/:id/issuesAPI KeyList issues for a newsletter, ordered by created_at desc.
| Parameter | Type | In | Description |
|---|---|---|---|
idrequired | string | path | Newsletter UUID |
/v1/newsletters/:id/issues/:issueIdAPI KeyGet a single issue with HTML body, subject, and stats.
/v1/newsletters/:id/issuesAPI KeyCreate a draft issue.
| Parameter | Type | In | Description |
|---|---|---|---|
idrequired | string | path | Newsletter UUID |
subjectrequired | string | body | Email subject line |
html_bodyrequired | string | body | HTML body (will be wrapped in newsletter branding) |
preheader | string | body | Short preview text shown in inbox |
scheduled_for | string | body | ISO 8601 datetime to send at; if omitted, the issue is created as a draft you must explicitly send |
/v1/newsletters/:id/issues/:issueIdAPI KeyUpdate a draft or scheduled issue. Sent issues are immutable.
/v1/newsletters/:id/issues/:issueId/sendAPI KeySend an issue immediately (or schedule it if scheduled_for is set on the issue). Fans out to active subscribers; pending and unsubscribed subscribers are skipped. Suppression list is checked at send time.
/v1/newsletters/:id/issues/:issueId/send-testAPI KeySend a test render of an issue to a single email address (typically your own) so you can preview the final email before broadcasting to all subscribers. Does not affect issue status or delivery counts.
| Parameter | Type | In | Description |
|---|---|---|---|
idrequired | string | path | Newsletter UUID |
issueIdrequired | string | path | Issue UUID |
torequired | string | body | Recipient email address for the test send |
Request
curl -X POST https://api.posthawk.dev/v1/newsletters/news-uuid/issues/issue-uuid/send-test \
-H "Content-Type: application/json" \
-H "X-API-Key: your_api_key" \
-d '{ "to": "you@yourdomain.com" }'