Inbound Email
Receive and process incoming emails. Two routing models: legacy `inbound_domains` (self-hosted, custom provider config) and modern `cloud_domains` (cloud edition, dual-region SES with SNS).
Posthawk supports two inbound routing models:
1. Cloud Domains (recommended for cloud users)
If you registered the domain via POST /v1/domains and called POST /v1/domains/:id/receiving/enable, Posthawk handles inbound routing automatically:
- Creates a BYODKIM identity in eu-west-1 (Stockholm doesn't support SES inbound, so cloud always routes through Ireland regardless of your sending region)
- Adds an SES receipt rule that forwards emails to the
posthawk-inboundSNS topic - The worker's
/webhooks/sns/inboundendpoint validates SNS signatures, parses the email, and inserts intoinbound_emailswithcloud_domain_idset - If a webhook URL is configured on the domain (
PATCH /v1/domains/:id/webhook), Posthawk POSTs the parsed email to your URL with HMAC signature
You configure everything via the Domains API — no separate inbound domain setup needed.
2. Inbound Domains (legacy / self-hosted)
For self-hosted users or custom routing, the original /inbound/domains API lets you register a domain with a provider:
cloudflare— Cloudflare Email Routing forwards to your Posthawk instance (requires MX records pointing to Cloudflare)aws_ses— AWS SES receipt rule forwards to an SNS topic, then a webhookcustom— bring your own MTA / DIY setup, just hit the inbound webhook directly
Configure DNS, verify the domain, then incoming emails are parsed and optionally forwarded to your webhook URL.
Common to both
The inbound_emails table has a CHECK constraint requiring exactly ONE of inbound_domain_id or cloud_domain_id to be set per row, so you can tell which routing path delivered the email. Webhook deliveries to your URL include an HMAC signature in X-Posthawk-Signature (same format as outbound webhooks); verify it with your domain's webhook secret.
/inbound/domainsRegister a domain for inbound email processing.
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your JWT.
Body
domainstringrequiredFully qualified domain name
providerstringoptional"cloudflare" | "aws_ses" | "custom". Defaults to "cloudflare".
webhookUrlstringoptionalURL to forward parsed emails to
curl -X POST https://your-posthawk-instance.com/inbound/domains \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your_jwt_token" \
-d '{
"domain": "mail.yourdomain.com",
"provider": "cloudflare",
"webhookUrl": "https://yourapp.com/api/incoming-email"
}'{
"success": true,
"data": {
"id": "uuid",
"domain": "mail.yourdomain.com",
"provider": "cloudflare",
"is_verified": false,
"webhook_url": "https://yourapp.com/api/incoming-email"
},
"message": "Inbound domain created. Configure MX records to start receiving emails."
}/inbound/domainsList all configured inbound domains.
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your JWT.
curl https://api.posthawk.dev/inbound/domains \
-H "Authorization: Bearer your_jwt_token"{
"success": true,
"data": [
{
"id": "uuid",
"domain": "mail.yourdomain.com",
"provider": "cloudflare",
"is_verified": true,
"is_active": true
}
]
}/inbound/domains/:id/verifyVerify domain ownership after DNS records are configured.
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your JWT.
Path Parameters
idstringrequiredDomain UUID
curl -X POST https://api.posthawk.dev/inbound/domains/{id}/verify \
-H "Authorization: Bearer your_jwt_token"{
"success": true,
"data": { "id": "uuid", "is_verified": true },
"message": "Domain verified successfully"
}/inbound/domains/:idGet details of a specific inbound domain.
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your JWT.
Path Parameters
idstringrequiredDomain UUID
curl https://api.posthawk.dev/inbound/domains/{id} \
-H "Authorization: Bearer your_jwt_token"{
"success": true,
"data": {
"id": "uuid",
"domain": "mail.yourdomain.com",
"provider": "cloudflare",
"is_verified": true,
"is_active": true,
"webhook_url": "https://yourapp.com/api/incoming-email"
}
}/inbound/domains/:idUpdate an inbound domain — change the webhook URL or toggle active status.
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your JWT.
Path Parameters
idstringrequiredDomain UUID
Body
webhookUrlstringoptionalNew webhook URL for email forwarding
isActivebooleanoptionalEnable or disable inbound processing for this domain
curl -X PATCH https://your-posthawk-instance.com/inbound/domains/uuid \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your_jwt_token" \
-d '{
"webhookUrl": "https://yourapp.com/api/new-webhook",
"isActive": true
}'{
"success": true,
"data": {
"id": "uuid",
"domain": "mail.yourdomain.com",
"webhook_url": "https://yourapp.com/api/new-webhook",
"is_active": true
},
"message": "Inbound domain updated"
}/inbound/domains/:idDelete an inbound domain and stop receiving emails for it.
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your JWT.
Path Parameters
idstringrequiredDomain UUID
curl -X DELETE https://api.posthawk.dev/inbound/domains/{id} \
-H "Authorization: Bearer your_jwt_token"{
"success": true,
"message": "Inbound domain deleted"
}/inbound/emailsList received inbound emails with optional filtering and pagination.
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your JWT.
Query Parameters
domainIdstringoptionalFilter by inbound domain
limitnumberoptionalPagination limit
offsetnumberoptionalPagination offset
curl https://api.posthawk.dev/inbound/emails \
-H "Authorization: Bearer your_jwt_token"{
"success": true,
"data": [
{
"id": "uuid",
"from_address": "sender@example.com",
"to_addresses": ["you@yourdomain.com"],
"subject": "Hello!",
"received_at": "2025-06-01T12:00:00Z"
}
],
"total": 1
}/inbound/emails/:idGet the full details of a specific inbound email including body, headers, and attachments.
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your JWT.
Path Parameters
idstringrequiredInbound email UUID
curl https://api.posthawk.dev/inbound/emails/{id} \
-H "Authorization: Bearer your_jwt_token"{
"success": true,
"data": {
"id": "uuid",
"from_address": "sender@example.com",
"from_name": "John Doe",
"to_addresses": ["you@yourdomain.com"],
"subject": "Hello!",
"html": "<p>Email body here</p>",
"text": "Email body here",
"attachments": [],
"received_at": "2025-06-01T12:00:00Z",
"webhook_status": "delivered"
}
}