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 APIPOST /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.

GET/v1/newsletters

List all newsletters in the current workspace.

Authorizations

Authorizationstring · headerrequired

Bearer authentication header of the form Bearer <token>, where <token> is your API Key.

GET /v1/newsletters
curl https://api.posthawk.dev/v1/newsletters \
  -H "Authorization: Bearer your_api_key"
GET/v1/newsletters/:id

Get a single newsletter by ID, including branding, DOI settings, and subscriber count.

Authorizations

Authorizationstring · headerrequired

Bearer authentication header of the form Bearer <token>, where <token> is your API Key.

Path Parameters

idstringrequired

Newsletter UUID

GET /v1/newsletters/:id
curl https://api.posthawk.dev/v1/newsletters/{id} \
  -H "Authorization: Bearer your_api_key"
POST/v1/newsletters

Create a new newsletter. Accepts name, slug, from_email, from_name, accent_color, logo_url, double_opt_in, and other branding fields.

Authorizations

Authorizationstring · headerrequired

Bearer authentication header of the form Bearer <token>, where <token> is your API Key.

Body

namestringrequired

Display name (e.g. "Weekly Digest")

slugstringrequired

URL slug — used in /n/:slug public pages

from_emailstringoptional

Sender email — domain must be verified for sending

from_namestringoptional

Display name on outbound emails

descriptionstringoptional

Short description shown on the subscribe page

double_opt_inbooleanoptional

Whether to require double-opt-in confirmation. Default: true

accent_colorstringoptional

Hex color for branded UI (subscribe page, confirm email, embed widget)

logo_urlstringoptional

URL of your newsletter logo

POST /v1/newsletters
curl -X POST https://api.posthawk.dev/v1/newsletters \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your_api_key" \
  -d '{
    "name": "Jane Doe",
    "slug": "weekly-digest",
    "from_email": "news@yourdomain.com",
    "from_name": "Acme Weekly",
    "description": "Production webhook",
    "double_opt_in": true,
    "accent_color": "#8262ff",
    "logo_url": "https://yourdomain.com/logo.png"
  }'
PATCH/v1/newsletters/:id

Update a newsletter. Only provided fields are updated.

Authorizations

Authorizationstring · headerrequired

Bearer authentication header of the form Bearer <token>, where <token> is your API Key.

Path Parameters

idstringrequired

Newsletter UUID

PATCH /v1/newsletters/:id
curl -X PATCH https://api.posthawk.dev/v1/newsletters/{id} \
  -H "Authorization: Bearer your_api_key"
DELETE/v1/newsletters/:id

Delete a newsletter. Cascades to subscribers and issues.

Authorizations

Authorizationstring · headerrequired

Bearer authentication header of the form Bearer <token>, where <token> is your API Key.

Path Parameters

idstringrequired

Newsletter UUID

DELETE /v1/newsletters/:id
curl -X DELETE https://api.posthawk.dev/v1/newsletters/{id} \
  -H "Authorization: Bearer your_api_key"
GET/v1/newsletters/:id/subscribers

List subscribers for a newsletter. Supports status filter (active / pending / unsubscribed), search, and pagination.

Authorizations

Authorizationstring · headerrequired

Bearer authentication header of the form Bearer <token>, where <token> is your API Key.

Path Parameters

idstringrequired

Newsletter UUID

Query Parameters

statusstringoptional

active, pending, or unsubscribed

searchstringoptional

Match against email or name

pagenumberoptional

Page number (default: 1)

GET /v1/newsletters/:id/subscribers
curl https://api.posthawk.dev/v1/newsletters/{id}/subscribers \
  -H "Authorization: Bearer your_api_key"
GET/v1/newsletters/:id/subscribers/:subscriberId

Get a single subscriber.

Authorizations

Authorizationstring · headerrequired

Bearer authentication header of the form Bearer <token>, where <token> is your API Key.

GET /v1/newsletters/:id/subscribers/:subscriberId
curl https://api.posthawk.dev/v1/newsletters/{id}/subscribers/{subscriberId} \
  -H "Authorization: Bearer your_api_key"
POST/v1/newsletters/:id/subscribers

Add 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.

Authorizations

Authorizationstring · headerrequired

Bearer authentication header of the form Bearer <token>, where <token> is your API Key.

Path Parameters

idstringrequired

Newsletter UUID

Body

emailstringrequired

Subscriber email (lowercased)

namestringoptional

Subscriber display name

sourcestringoptional

Tag indicating where the signup came from (e.g. "embed", "footer", "import")

metadataobjectoptional

Arbitrary JSON metadata to attach to the subscriber

POST /v1/newsletters/:id/subscribers
curl -X POST https://api.posthawk.dev/v1/newsletters/{id}/subscribers \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your_api_key" \
  -d '{
    "email": "user@example.com",
    "name": "Jane Doe",
    "source": "embed",
    "metadata": {
      "source": "website"
    }
  }'
POST/v1/newsletters/:id/subscribers/:subscriberId/unsubscribe

Unsubscribe a subscriber. Flips status to unsubscribed and records the timestamp. The public one-click unsubscribe link in newsletter footers also lands here.

Authorizations

Authorizationstring · headerrequired

Bearer authentication header of the form Bearer <token>, where <token> is your API Key.

POST /v1/newsletters/:id/subscribers/:subscriberId/unsubscribe
curl -X POST https://api.posthawk.dev/v1/newsletters/{id}/subscribers/{subscriberId}/unsubscribe \
  -H "Authorization: Bearer your_api_key"
POST/v1/newsletters/:id/subscribers/:subscriberId/resend-confirmation

Resend the DOI confirmation email to a pending subscriber. Useful when the original was lost in spam.

Authorizations

Authorizationstring · headerrequired

Bearer authentication header of the form Bearer <token>, where <token> is your API Key.

POST /v1/newsletters/:id/subscribers/:subscriberId/resend-confirmation
curl -X POST https://api.posthawk.dev/v1/newsletters/{id}/subscribers/{subscriberId}/resend-confirmation \
  -H "Authorization: Bearer your_api_key"
DELETE/v1/newsletters/:id/subscribers/:subscriberId

Permanently delete a subscriber. Use POST /unsubscribe to keep the record for analytics.

Authorizations

Authorizationstring · headerrequired

Bearer authentication header of the form Bearer <token>, where <token> is your API Key.

DELETE /v1/newsletters/:id/subscribers/:subscriberId
curl -X DELETE https://api.posthawk.dev/v1/newsletters/{id}/subscribers/{subscriberId} \
  -H "Authorization: Bearer your_api_key"
GET/v1/newsletters/:id/issues

List issues for a newsletter, ordered by created_at desc.

Authorizations

Authorizationstring · headerrequired

Bearer authentication header of the form Bearer <token>, where <token> is your API Key.

Path Parameters

idstringrequired

Newsletter UUID

GET /v1/newsletters/:id/issues
curl https://api.posthawk.dev/v1/newsletters/{id}/issues \
  -H "Authorization: Bearer your_api_key"
GET/v1/newsletters/:id/issues/:issueId

Get a single issue with HTML body, subject, and stats.

Authorizations

Authorizationstring · headerrequired

Bearer authentication header of the form Bearer <token>, where <token> is your API Key.

GET /v1/newsletters/:id/issues/:issueId
curl https://api.posthawk.dev/v1/newsletters/{id}/issues/{issueId} \
  -H "Authorization: Bearer your_api_key"
POST/v1/newsletters/:id/issues

Create a draft issue.

Authorizations

Authorizationstring · headerrequired

Bearer authentication header of the form Bearer <token>, where <token> is your API Key.

Path Parameters

idstringrequired

Newsletter UUID

Body

subjectstringrequired

Email subject line

html_bodystringrequired

HTML body (will be wrapped in newsletter branding)

preheaderstringoptional

Short preview text shown in inbox

scheduled_forstringoptional

ISO 8601 datetime to send at; if omitted, the issue is created as a draft you must explicitly send

POST /v1/newsletters/:id/issues
curl -X POST https://api.posthawk.dev/v1/newsletters/{id}/issues \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your_api_key" \
  -d '{
    "subject": "Hello from Posthawk",
    "html_body": "<h1>This week at Acme</h1>",
    "preheader": "Your weekly update",
    "scheduled_for": "2026-06-15T09:00:00Z"
  }'
PATCH/v1/newsletters/:id/issues/:issueId

Update a draft or scheduled issue. Sent issues are immutable.

Authorizations

Authorizationstring · headerrequired

Bearer authentication header of the form Bearer <token>, where <token> is your API Key.

PATCH /v1/newsletters/:id/issues/:issueId
curl -X PATCH https://api.posthawk.dev/v1/newsletters/{id}/issues/{issueId} \
  -H "Authorization: Bearer your_api_key"
POST/v1/newsletters/:id/issues/:issueId/send

Send 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.

Authorizations

Authorizationstring · headerrequired

Bearer authentication header of the form Bearer <token>, where <token> is your API Key.

POST /v1/newsletters/:id/issues/:issueId/send
curl -X POST https://api.posthawk.dev/v1/newsletters/{id}/issues/{issueId}/send \
  -H "Authorization: Bearer your_api_key"
POST/v1/newsletters/:id/issues/:issueId/schedule

Schedule a draft issue to send at a future time. The scheduled_for must be a valid ISO 8601 datetime at least 30 seconds out and no more than 30 days ahead. The issue must not already be sent and must have a body and a sender configured.

Authorizations

Authorizationstring · headerrequired

Bearer authentication header of the form Bearer <token>, where <token> is your API Key.

Path Parameters

idstringrequired

Newsletter UUID

issueIdstringrequired

Issue UUID

Body

scheduled_forstringrequired

ISO 8601 datetime to send at (≥30s future, ≤30 days ahead)

POST /v1/newsletters/:id/issues/:issueId/schedule
curl -X POST https://api.posthawk.dev/v1/newsletters/news-uuid/issues/issue-uuid/schedule \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your_api_key" \
  -d '{ "scheduled_for": "2026-06-20T14:00:00Z" }'
POST/v1/newsletters/:id/issues/:issueId/cancel

Cancel a scheduled or in-flight issue send. Already-queued emails still deliver; no further subscribers are fanned out.

Authorizations

Authorizationstring · headerrequired

Bearer authentication header of the form Bearer <token>, where <token> is your API Key.

Path Parameters

idstringrequired

Newsletter UUID

issueIdstringrequired

Issue UUID

POST /v1/newsletters/:id/issues/:issueId/cancel
curl -X POST https://api.posthawk.dev/v1/newsletters/{id}/issues/{issueId}/cancel \
  -H "Authorization: Bearer your_api_key"
Response
{
  "success": true,
  "status": "cancelled",
  "alreadyQueued": 0
}
GET/v1/newsletters/:id/issues/:issueId/stats

Get per-issue delivery and engagement stats: totals for sent, delivered, opened, clicked, bounced, complained, and failed, plus first/last event timestamps.

Authorizations

Authorizationstring · headerrequired

Bearer authentication header of the form Bearer <token>, where <token> is your API Key.

Path Parameters

idstringrequired

Newsletter UUID

issueIdstringrequired

Issue UUID

GET /v1/newsletters/:id/issues/:issueId/stats
curl https://api.posthawk.dev/v1/newsletters/{id}/issues/{issueId}/stats \
  -H "Authorization: Bearer your_api_key"
Response
{
  "total": 5000,
  "pending": 0,
  "sent": 5000,
  "delivered": 4920,
  "opened": 2150,
  "clicked": 430,
  "bounced": 60,
  "complained": 5,
  "failed": 20,
  "first_event_at": "2026-06-15T09:00:00Z",
  "last_event_at": "2026-06-15T09:08:00Z"
}
POST/v1/newsletters/:id/issues/:issueId/send-test

Send 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.

Authorizations

Authorizationstring · headerrequired

Bearer authentication header of the form Bearer <token>, where <token> is your API Key.

Path Parameters

idstringrequired

Newsletter UUID

issueIdstringrequired

Issue UUID

Body

tostringrequired

Recipient email address for the test send

POST /v1/newsletters/:id/issues/:issueId/send-test
curl -X POST https://api.posthawk.dev/v1/newsletters/news-uuid/issues/issue-uuid/send-test \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your_api_key" \
  -d '{ "to": "you@yourdomain.com" }'