Inbound Email (Receiving)
Zyphr can receive email for your domain, not just send it. Point a receiving
subdomain at Zyphr and every incoming message is parsed, persisted, and delivered
to you as an email.received webhook — with
attachments stored for download and replies correlated back to the messages you
originally sent.
This turns Zyphr from "we hand you the event" into a full two-way email layer: support inboxes, reply handling, ticket ingestion, and inbound automations.
Availability
Inbound receiving is a paid feature, available on Starter and above. It requires a verified custom domain, which is itself a Starter+ capability.
| Plan | Inbound receiving | Included/month | Overage per 1,000 |
|---|---|---|---|
| Free | — (not available) | — | — |
| Starter | ✅ | 5,000 | $0.50 |
| Pro | ✅ | 25,000 | $0.40 |
| Scale | ✅ | 100,000 | $0.30 |
| Enterprise | ✅ | Unlimited | Custom |
Inbound receiving cannot be enabled on the Free plan. If an account that previously had inbound enabled is downgraded to Free while its MX record is still published, a small defensive hard cap (100/month) applies to any mail that still arrives — it is a safety ceiling, not an entitlement. Re-enable inbound by upgrading to Starter or above.
A separate per-hour rate ceiling (distinct from the monthly quota) protects against floods of attacker-controlled inbound volume; it scales with your plan.
How it works
Sender → MX (inbound.yourdomain.com → AWS SES)
→ SES stores the raw message + scans it (SPF/DKIM/DMARC/spam/virus)
→ Zyphr parses it, stores attachments, persists the email
→ email.received webhook fires to your endpoint
You receive a structured webhook; the raw MIME and attachments live in object storage and are exposed via short-lived download URLs (never inlined into the webhook payload).
Setup
1. Enable inbound on a verified domain
In the Dashboard → Domains, open a verified domain and toggle Inbound email on. (On the Free plan this toggle is locked with an upgrade prompt.)
2. Publish the MX record
Enabling inbound surfaces an MX record for a dedicated receiving
subdomain — inbound.yourdomain.com:
| Type | Host | Value | Priority |
|---|---|---|---|
| MX | inbound.yourdomain.com | inbound-smtp.us-east-2.amazonaws.com | 10 |
Zyphr receives on a subdomain (inbound.yourdomain.com), never your apex
(yourdomain.com). Putting an inbound MX on your apex would hijack your existing
mail (Google Workspace, Microsoft 365, etc.). Send your outbound mail from the
apex and set reply_to to an inbound. address if you want replies routed to
Zyphr.
Once the MX propagates, mail addressed to anything @inbound.yourdomain.com
flows into your account.
3. Subscribe to the webhook
Create a webhook subscribed to the email.received event. That
endpoint receives every parsed inbound message.
The email.received webhook
The payload includes the parsed envelope, verdicts, and attachment metadata:
{
"type": "email.received",
"data": {
"id": "68237835-551d-4fe3-9a50-8de5c71c518a",
"from_email": "customer@example.com",
"from_name": "A Customer",
"to_email": "support@inbound.yourdomain.com",
"subject": "Re: your order",
"text_body": "…",
"html_body": "…",
"in_reply_to": "<...@us-east-2.amazonses.com>",
"replied_to_message_id": "cc0d4198-7b74-4e2f-886f-d484897d2aab",
"spf_verdict": "PASS",
"dkim_verdict": "PASS",
"dmarc_verdict": "PASS",
"spam_verdict": "PASS",
"virus_verdict": "PASS",
"attachments": [
{
"filename": "invoice.pdf",
"content_type": "application/pdf",
"size": 248697
}
]
}
}
Authentication / trust verdicts (SPF, DKIM, DMARC) and content scans (spam, virus) are surfaced on every message. Messages that fail spam/virus scanning are quarantined (stored but not delivered); auto-submitted / mailing-list / loop mail is suppressed so you don't ingest noise or create reply loops.
Reply correlation
When a recipient replies to an email you sent through Zyphr, the inbound message
includes replied_to_message_id — the id of your original outbound message.
This lets you thread conversations without parsing headers yourself.
It works because Zyphr records the exact Message-ID that the provider stamps on
each outbound email and matches the reply's In-Reply-To header against it,
scoped to your project. A reply with no matching original simply arrives with
replied_to_message_id: null (a new thread) — it is never dropped.
Replies are correlated to outbound mail sent after inbound was set up. Older
messages may not correlate. To route replies into Zyphr, set reply_to on your
sends to an address at your inbound. subdomain.
Attachments
Attachments are parsed out of the raw message and stored individually. The webhook (and the Receiving API) returns attachment metadata (filename, content type, size) plus a short-lived presigned download URL — the bytes are never embedded in the webhook payload.
The maximum inbound message size (including attachments) is 40 MB, the AWS SES receiving ceiling. Messages above that are rejected at the edge.
Receiving API
Retrieve received emails programmatically. These require an API key with the
email:read scope:
GET /v1/inbound-emails— list received emails (paginated, newest first)GET /v1/inbound-emails/:id— fetch a single received emailGET /v1/inbound-emails/:id/attachments/:index— presigned download URL for an attachment
See the API Reference (tag Inbound Email) for full request and response schemas.
Regional note
Inbound receiving is US-only at launch — received content is processed and stored in the US regardless of sender location. EU data residency for inbound is a planned follow-up.