SendGrid Inbound Parse: the complete setup and troubleshooting guide
Set up the SendGrid Inbound Parse webhook end to end - MX record, dashboard config, the multipart payload fields - plus fixes for when it is not working.
Dvir Atias
Founder
SendGrid Inbound Parse is a feature that receives email sent to a domain you control, parses each message into its parts - sender, subject, text, HTML, headers, and attachments - and POSTs the result to a URL you choose as multipart/form-data. It turns inbound email into an HTTP request your application can handle like any other webhook. This guide walks through the full setup, shows exactly what the payload contains, and then works through the most common reasons Inbound Parse is not working.
How does SendGrid Inbound Parse work?
The flow has three moving parts. An MX record on your receiving domain routes mail for that domain to SendGrid's mail servers. A Host and URL mapping in your SendGrid account says "any mail for this hostname should be posted to that URL." Then, when a message arrives, SendGrid accepts it over SMTP, breaks the MIME message into fields, and makes an HTTP POST to your URL. Your endpoint reads the form fields and does whatever your app needs - creates a ticket, files a reply, feeds an agent. You give SendGrid the receiving end of a domain and get back an HTTP callback: no mailbox to poll, no IMAP connection to hold open.
How do you set up the SendGrid Inbound Parse webhook?
Setup is four steps. Budget a few minutes for the MX record to propagate before you test.
- Pick a subdomain to receive on. Use a dedicated subdomain such as
parse.yourdomain.comrather than your root domain, so inbound parsing stays separate from the MX records handling your normal human mail. Addresses then look likeanything@parse.yourdomain.com. - Add the MX record. In your DNS provider, create an MX record on that subdomain pointing to
mx.sendgrid.netwith priority10. This is the record that hands inbound mail to SendGrid. As of July 2026 the host ismx.sendgrid.net; confirm the exact value in the dashboard when you add the setting. - Add the Host and URL in the dashboard. In SendGrid, go to Settings - Inbound Parse and add a host and URL. Choose the domain, enter the subdomain (the Host), and enter the receiving URL where SendGrid will POST - for example
https://your-app.com/inbound. The URL must be publicly reachable over HTTPS and return a 2xx. - Handle the POST at your URL. SendGrid sends each message as
multipart/form-data. Read the fields (see the table below), do your work, and respond200. Optionally enable POST the raw, full MIME message ("send raw") to parse the MIME yourself instead of taking SendGrid's pre-split fields, and check the spam-check box to run a spam filter and include the score.
Once the MX record has propagated, send a test email to any address at your subdomain and watch the request hit your endpoint.
What does the Inbound Parse payload look like?
The default (non-raw) payload is a set of form fields. The most useful ones:
| Field | What it contains |
|---|---|
to | The recipient address the mail was sent to |
from | The sender, including display name |
subject | The decoded subject line |
text | The plain-text body |
html | The HTML body, when the message has one |
headers | The raw headers as a single block |
envelope | JSON with the SMTP to and from |
attachments | The number of attachments |
attachment1, attachment2, ... | The attachment files themselves |
attachment-info | JSON metadata (filename, type) per attachment |
charsets | JSON mapping each field to its character set |
SPF | The result of the SPF check on the sender |
dkim | The DKIM verification result |
spam_score | Spam score, if the spam check is enabled |
sender_ip | The IP the message was sent from |
If you enable send raw, you instead get an email field with the complete unparsed MIME message. The charsets field tells you the encoding of each field, so you can decode non-ASCII subjects and bodies correctly instead of getting mojibake.
Why is SendGrid Inbound Parse not working?
Almost every "Inbound Parse not working" case is one of a handful of issues. Work down this checklist in order.
- MX record has not propagated. Run
dig MX parse.yourdomain.com. If it does not returnmx.sendgrid.net, mail is not reaching SendGrid yet. DNS can take minutes to a few hours. Make sure the record is on the exact subdomain you configured, not the root domain. - Host and URL mismatch. The Host in the dashboard must match the subdomain in the MX record character for character. A message to
parse.yourdomain.comwill not fire a setting configured forinbound.yourdomain.com. - Your URL is not returning 2xx. This is the big one. If your endpoint returns a 4xx or 5xx, times out, or throws, SendGrid retries for a while and then drops the message. A slow handler that does heavy work before responding can time out - acknowledge with a 200 first, then process asynchronously.
- Endpoint is not publicly reachable over HTTPS.
localhost, a private IP, or a self-signed certificate will not work. During development, use a tunnel such as ngrok and point the URL at the public tunnel address. - You are parsing the body as JSON. The payload is
multipart/form-data, not JSON. Use a multipart form parser (for examplemulterin Node orrequest.formin Flask), notJSON.parse. - The spam check silently dropped it. If you enabled the spam-check option, heavily flagged mail may not be delivered. Disable it while debugging to rule this out.
- Payload too large. Big attachments can exceed your framework or proxy body-size limit, so the request is rejected before your code runs. Raise the limit, or enable send raw and stream it.
Rule of thumb: if mail reaches SendGrid but no POST arrives, the problem is the URL side; if mail never reaches SendGrid at all, the problem is DNS.
The modern alternative: inbound mail that is already parsed JSON
Inbound Parse is solid, but notice how much of the work above is plumbing: edit DNS, wait for MX propagation, stand up a public HTTPS endpoint, parse multipart/form-data, and handle SendGrid's retry semantics before you write any product logic. If your goal is simply "let my application - or my AI agent - receive email," most of that is incidental.
AgenticEmail is inbox-native: an inbox is a resource you create with one API call, and inbound mail arrives as parsed JSON, not a form POST you reassemble.
curl -X POST https://api.agenticemail.dev/v1/inboxes \
-H "Authorization: Bearer $AGENTICEMAIL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "username": "support" }'That returns a live address like support@inbox.agenticemail.dev that receives immediately - no MX record to add on the shared domain. Read the mail as structured JSON, with sender, subject, text, html, and attachments already split out:
curl https://api.agenticemail.dev/v1/inboxes/ibx_123/messages \
-H "Authorization: Bearer $AGENTICEMAIL_API_KEY"To be pushed each message instead of polling, register a webhook. It is signed per the Standard Webhooks spec (webhook-id, webhook-timestamp, and webhook-signature headers against a whsec_ secret), so you can verify authenticity the same way you would with any email webhook:
curl -X POST https://api.agenticemail.dev/v1/webhooks \
-H "Authorization: Bearer $AGENTICEMAIL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://your-app.com/inbound", "event_types": ["message.received"] }'If you cannot expose a public endpoint, connect to the WebSocket stream at wss://api.agenticemail.dev/v1/events?token=am_... for the same events on a long-lived connection. Custom domains get guided DNS setup (SPF, DKIM, DMARC, and MX) with optional Cloudflare auto-configuration - the same records Inbound Parse needs, done for you. If you are weighing SendGrid specifically, the SendGrid webhook guide covers the outbound event side for comparison.
You can start on the free tier and read the full reference in the docs.
Frequently asked questions
Is SendGrid Inbound Parse free?
Yes. Inbound Parse is included with SendGrid accounts, including free plans, as of July 2026. You are not charged per inbound message the way you are for outbound sends. Your own costs are the domain and whatever hosts your receiving endpoint.
Can Inbound Parse receive attachments?
Yes. Attachments arrive as attachment1, attachment2, and so on in the payload, with an attachment-info field carrying the filename and content type for each, and the attachments field giving the count. Very large attachments can exceed your framework's body-size limit, so raise it if big files are being rejected.
Does Inbound Parse work on a root domain?
It can, but a dedicated subdomain such as parse.yourdomain.com is the recommended setup. Putting the parse MX record on your root domain reroutes all mail for that domain to SendGrid, which breaks normal email to human mailboxes on the same domain. A subdomain isolates inbound parsing from the rest of your mail.
Why is SendGrid Inbound Parse not posting to my URL?
The usual causes are an MX record that has not propagated, a Host that does not exactly match your subdomain, or a URL that is not returning a 2xx. Check dig MX on the subdomain first, then confirm the endpoint is public over HTTPS and answers with a 200. SendGrid retries non-2xx responses for a time and then drops the message.
How is this different from an inbox API?
Inbound Parse gives you a callback for mail sent to a domain you have wired up in DNS. An inbox API like AgenticEmail treats the inbox as the resource: you create it with one call, it receives instantly, and inbound mail arrives as parsed JSON over webhook or WebSocket - no MX setup, no multipart parsing. See giving your agent an inbox for the full pattern.