Scheduled Emails

Schedule emails for future delivery and manage them with list, cancel, reschedule, and send-now operations. All state transitions are race-safe and an audit log records every change.

Each scheduled email moves through a state machine: scheduled → (sending →) sent | failed | cancelled.

State transitions are atomic and race-safe — the worker uses optimistic concurrency (WHERE status='scheduled') so a cancel that lands in the gap between the BullMQ job firing and the SES dispatch always wins, never producing a duplicate send.

Each scheduled email carries an audit_log JSONB array recording every state change with timestamps and details — useful for debugging delivery issues or building a per-email audit trail. An idempotency_token UUID rotates on each reschedule so older queued jobs whose data references the previous token short-circuit at the processor's pre-flight gate.

POST/scheduled

Create a scheduled email directly (an alternative to POST /v1/send with scheduledFor). Requires the `sending` scope.

Authorizations

Authorizationstring · headerrequired

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

Body

fromstringrequired

Sender email address

tostring[]required

Array of recipient emails (non-empty)

ccstring[]optional

Array of CC email addresses

bccstring[]optional

Array of BCC email addresses

replyTostringoptional

Reply-to email address

subjectstringrequired

Email subject

htmlstringoptional

HTML body (html or text required)

textstringoptional

Plain text body (html or text required)

scheduledForstringrequired

ISO 8601 datetime, must be future, max 30 days

timezonestringoptional

Timezone (e.g., "America/New_York")

POST /scheduled
curl -X POST https://your-posthawk-instance.com/scheduled \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your_api_key" \
  -d '{
    "from": "hello@yourdomain.com",
    "to": ["user@example.com"],
    "subject": "Your weekly report",
    "html": "<h1>Weekly Report</h1>",
    "scheduledFor": "2026-06-15T09:00:00Z"
  }'
Response
{
  "success": true,
  "data": {
    "id": "scheduled-uuid",
    "from": "hello@yourdomain.com",
    "to": ["user@example.com"],
    "subject": "Your weekly report",
    "scheduled_for": "2026-06-15T09:00:00Z",
    "status": "scheduled"
  },
  "message": "Email scheduled successfully"
}
POST/v1/send

Schedule an email by including scheduledFor in the send request.

Authorizations

Authorizationstring · headerrequired

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

Body

fromstringrequired

Sender email address

tostring[]required

Array of recipient emails

subjectstringrequired

Email subject

htmlstringoptional

HTML body

textstringoptional

Plain text body

scheduledForstringrequired

ISO 8601 datetime, must be future, max 30 days

timezonestringoptional

Timezone (e.g., "America/New_York")

POST /v1/send
curl -X POST https://your-posthawk-instance.com/v1/send \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your_api_key" \
  -d '{
    "from": "hello@yourdomain.com",
    "to": ["user@example.com"],
    "subject": "Your weekly report",
    "html": "<h1>Weekly Report</h1><p>Here are your stats...</p>",
    "scheduledFor": "2025-06-15T09:00:00Z",
    "timezone": "America/New_York"
  }'
Response
{
  "success": true,
  "scheduled": true,
  "id": "scheduled-uuid",
  "scheduledFor": "2025-06-15T09:00:00.000Z",
  "message": "Email scheduled successfully",
  "statusUrl": "/scheduled/scheduled-uuid"
}
GET/scheduled

List all scheduled emails with optional filtering and pagination.

Authorizations

Authorizationstring · headerrequired

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

Query Parameters

statusstringoptional

Filter by status: scheduled, sending, sent, failed, cancelled

limitnumberoptional

Pagination limit

offsetnumberoptional

Pagination offset

GET /scheduled
curl https://api.posthawk.dev/scheduled \
  -H "Authorization: Bearer your_api_key"
Response
{
  "success": true,
  "data": [
    {
      "id": "uuid",
      "from": "hello@yourdomain.com",
      "to": ["user@example.com"],
      "subject": "Weekly Report",
      "scheduled_for": "2026-06-15T09:00:00Z",
      "status": "scheduled",
      "audit_log": [
        { "at": "2026-04-15T10:00:00Z", "action": "created" }
      ]
    }
  ],
  "total": 1
}
GET/scheduled/:id

Get details of a specific scheduled email.

Authorizations

Authorizationstring · headerrequired

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

Path Parameters

idstringrequired

Scheduled email UUID

GET /scheduled/:id
curl https://api.posthawk.dev/scheduled/{id} \
  -H "Authorization: Bearer your_api_key"
Response
{
  "success": true,
  "data": {
    "id": "uuid",
    "from": "hello@yourdomain.com",
    "to": ["user@example.com"],
    "subject": "Weekly Report",
    "html": "<h1>Weekly Report</h1>...",
    "scheduled_for": "2026-06-15T09:00:00Z",
    "status": "scheduled",
    "idempotency_token": "1f3a8c2d-...",
    "audit_log": [
      { "at": "2026-04-15T10:00:00Z", "action": "created" }
    ],
    "created_at": "2026-04-15T10:00:00Z"
  }
}
DELETE/scheduled/:id

Cancel a pending scheduled email. Race-safe optimistic concurrency: only succeeds if status is still `scheduled`. Returns 409 if the email already started sending or completed.

Authorizations

Authorizationstring · headerrequired

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

Path Parameters

idstringrequired

Scheduled email UUID

DELETE /scheduled/:id
curl -X DELETE https://api.posthawk.dev/scheduled/{id} \
  -H "Authorization: Bearer your_api_key"
Response
{
  "success": true,
  "data": {
    "id": "uuid",
    "status": "cancelled",
    "cancelled_at": "2026-04-28T11:30:01Z"
  },
  "message": "Scheduled email cancelled successfully"
}
PATCH/scheduled/:id/reschedule

Reschedule a pending email to a new time. Rotates the idempotency_token so any in-flight BullMQ job for the previous slot short-circuits at the processor pre-flight gate.

Authorizations

Authorizationstring · headerrequired

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

Path Parameters

idstringrequired

Scheduled email UUID

Body

scheduledForstringrequired

New ISO 8601 datetime

PATCH /scheduled/:id/reschedule
curl -X PATCH https://your-posthawk-instance.com/scheduled/uuid/reschedule \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your_api_key" \
  -d '{ "scheduledFor": "2026-06-20T14:00:00Z" }'
Response
{
  "success": true,
  "data": { "id": "uuid", "scheduled_for": "2026-06-20T14:00:00Z" },
  "message": "Email rescheduled successfully"
}
POST/scheduled/:id/send-now

Fire a scheduled email immediately. Implemented as a reschedule to "now + 1s" through the same race-safe path used for manual reschedules. Useful for "Send now" buttons in admin UIs.

Authorizations

Authorizationstring · headerrequired

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

Path Parameters

idstringrequired

Scheduled email UUID

POST /scheduled/:id/send-now
curl -X POST https://api.posthawk.dev/scheduled/{id}/send-now \
  -H "Authorization: Bearer your_api_key"
Response
{
  "success": true,
  "data": { "id": "uuid", "scheduled_for": "2026-04-28T11:30:01Z" },
  "message": "Email queued for immediate send"
}