Receiving emails
Receive incoming email at your verified domain and turn it into events you can drive workflows from
Plunk can receive emails sent to any address at your verified domain, store them in your project, and emit an email.received event you can drive workflows from. This unlocks auto-replies, ticketing, conditional forwarding, and any other "do something when an email arrives" pattern.
What happens when an email arrives
When someone emails an address at your verified domain, Plunk:
- Parses the message and stores it as an inbound
Emailrecord visible in your project's Activity feed. - Creates the sender as a contact in your project (or updates them if they already exist), subscribed by default.
- Tracks an
email.receivedevent on the sender contact, which any workflow can listen to.
The HTML body is sanitized before being stored — scripts, iframes, event handlers, and javascript: URIs are stripped. Plain text is preserved as-is when no HTML part is available.
Setup
Verify your domain for sending
Inbound is only enabled on domains that are fully verified for sending (DKIM + SPF + the bounce-feedback MX). Follow the Verifying domains guide first.
Add the inbound MX record
Open your project's Domains tab, expand the verified domain, and look for the Inbound Email section. Plunk shows you the exact MX record value to add to your DNS — copy it from there and add it as an MX record on your domain.
Conflict with existing email
A domain can only have one primary inbound MX target. If you already use Google Workspace, Microsoft 365, or another provider for receiving email on the apex domain, your existing email will break if you switch the MX to point at Plunk. The usual fix is to receive Plunk inbound on a subdomain (e.g. mail.yourdomain.com or support.yourdomain.com) so you can keep your main mailbox on the existing provider. The subdomain still needs to be verified for sending in Plunk before its MX will be accepted.
Wait for DNS propagation
DNS changes take anywhere from a few minutes to 48 hours to propagate. You can verify the MX record is live:
dig MX yourdomain.comYou should see the value Plunk gave you in the response.
Send a test email
Send an email from any external account to any address at your domain (e.g. anything@yourdomain.com) and check that:
- A new
Emailrow appears in the project's Activity feed with type Inbound. - The sender shows up as a contact in the Contacts tab.
- An
email.receivedevent is recorded on that contact.
What gets stored
Every accepted inbound email shows up in your project's Activity feed alongside outbound emails — you can search, filter, and inspect them the same way.
Plunk stores: the parsed and sanitized HTML body (or plain text if no HTML is available), the headers surfaced in the event payload below, the authentication and spam verdicts, and the message ID.
Plunk does not store: the original raw message, attachments, threading headers (In-Reply-To, References), or headers beyond what's exposed on the event. If you need any of those, forward the email to your own service via a WEBHOOK step in the workflow that fires.
The email.received event
The event is emitted on the sender contact (auto-created if it doesn't exist yet) and carries the full parsed message in its data field:
Prop
Type
The event is always emitted — Plunk does not block emails based on the spam, virus, or authentication verdicts. Filter them yourself in the workflow that fires.
In templates and workflow steps, access these fields via the event variable namespace, e.g. {{event.subject}}, {{event.from}}, or {{event.body}}.
Building workflows on inbound
Auto-reply
A minimal auto-reply on support@yourdomain.com:
- Trigger:
email.received. - Condition: continue only if
event.toequalssupport@yourdomain.comandevent.spamVerdict == "PASS"andevent.virusVerdict == "PASS". - Send email:
- To:
{{event.from}} - Subject:
Re: {{event.subject}} - Body:
Thanks for your message. We've received your email and will respond within one business day.
- To:
Use a TRANSACTIONAL template for the auto-reply so it bypasses subscription checks (the sender is auto-subscribed but you don't want to fail to reply if they later unsubscribe).
Routing by recipient
Route different addresses to different downstream actions in a single workflow:
- Trigger:
email.received. - Condition: branch on
event.to:support@…→ ticketing webhook → auto-reply.sales@…→ CRM webhook → notify Slack.billing@…→ billing system webhook.
Forward to your API
For richer processing (NLP classification, ticket creation, attachments not captured by Plunk), forward the email to your own backend:
- Trigger:
email.received. - Webhook:
- URL:
https://api.example.com/inbound - Method:
POST - Body:
{ "from": "{{event.from}}", "subject": "{{event.subject}}", "body": "{{event.body}}", "messageId": "{{event.messageId}}", "timestamp": "{{event.timestamp}}", "verdicts": { "spam": "{{event.spamVerdict}}", "virus": "{{event.virusVerdict}}", "spf": "{{event.spfVerdict}}", "dkim": "{{event.dkimVerdict}}", "dmarc": "{{event.dmarcVerdict}}" } }
- URL:
Filter spam and virus before processing
Always include a CONDITION step early in the workflow that drops anything where spamVerdict or virusVerdict is FAIL. Plunk does not pre-filter for you.
Multi-project domains
If the same domain is verified in multiple projects, every inbound email is delivered to every project that has it verified — each gets its own Email record, its own contact upsert, and its own email.received event. This is by design (it lets you split a single inbox across staging and production projects, or hand off the same inbound stream to multiple teams).
If you don't want this, only verify the domain in one project at a time.
Billing
Inbound emails count toward your project's email usage at 1 credit per received email, just like outbound. The free tier and paid tiers consume the same pool.
You can set a per-project inbound cap under Billing → Limits. Once the cap is reached, inbound emails are silently dropped for that project until the cap resets — they aren't queued or replayed. Other projects sharing the same domain are unaffected by another project's cap.
Security considerations
- Treat the body as untrusted user input. Plunk sanitizes HTML to prevent the obvious script-injection paths, but the message can still contain phishing links, social-engineering content, and unicode lookalikes. Don't render the body verbatim in any UI you control without re-escaping it for that context.
- Authentication verdicts are advisory. Plunk records SPF / DKIM / DMARC results on the event but does not enforce them. Build your own policy: dropping unauthenticated mail at the workflow's first
CONDITIONstep is a sensible default for sensitive inboxes (billing, account changes). - Senders are auto-subscribed. Inbound senders enter your audience as subscribed contacts. If you don't want that, add an
UPDATE_CONTACTstep at the end of the inbound workflow to setsubscribed: false. - Reply-loop risk. If your auto-reply sends back to a domain you also receive on (or to a list address), you can create an infinite loop. Add a
CONDITIONthat drops messages whereevent.frommatches your own domain.
Limitations
- Catch-all only: Plunk routes anything sent to any address at your domain to the same handler. You filter on
event.toinside the workflow. - No attachments: attachments are dropped during parsing. Forward to your own service if you need them.
- No raw MIME: the original message is not retained.
- No threading: Plunk doesn't group inbound messages into threads or correlate them with outbound replies.
- 40 MB size cap: messages larger than 40 MB are rejected before processing.