Create inbound parse and security
Sengrids/twillios documentation about this: https://www.twilio.com/docs/sendgrid/for-developers/parsing-email/inbound-email
SendGrid Inbound Parse Webhook Setup Guide
This guide covers setting up an Inbound Parse Webhook, creating a security policy, and attaching the policy to your webhook.
Table of Contents
- Prerequisites
- MX Record Configuration
- Creating the Inbound Parse Webhook
- Creating a Security Policy
- Attaching Security Policy to Webhook
- Validating Incoming Webhooks
- Testing
Prerequisites
Before configuring Inbound Parse, ensure you have:
- A SendGrid account with API access
- A verified/authenticated domain in SendGrid
- Access to your domain's DNS settings
- A publicly accessible webhook endpoint (URL)
- Your SendGrid API key with appropriate permissions
MX Record Configuration
Create an MX record in your DNS settings to route emails to SendGrid for parsing.
| Field | Value |
|---|---|
| Subdomain/Hostname | Your parsing subdomain (e.g., parse) |
| Record Type | MX |
| Priority | 10 |
| Mail Server | mx.sendgrid.net |
| TTL | Default (or 3600) |
Example: For domain example.com, create MX record for parse.example.com pointing to mx.sendgrid.net.
Warning: Never modify MX records for your primary domain as this will disrupt normal email delivery.
Creating the Inbound Parse Webhook
Option A: Via SendGrid UI
- Log into your SendGrid account
- Navigate to Settings > Inbound Parse
- Click Add Host & URL
- Configure the following:
- Hostname/Subdomain: Enter your parsing subdomain (e.g.,
parse) - Domain: Select your authenticated domain from the dropdown
- Destination URL: Enter your webhook endpoint URL
- Hostname/Subdomain: Enter your parsing subdomain (e.g.,
- Configure additional options:
- Spam Checking: Enable to scan emails (up to 2.5 MB) and include spam reports
- Send Raw: Enable to receive full MIME messages with URL-encoded attachments
- Click Add to save
Option B: Via API
curl -X POST "https://api.sendgrid.com/v3/user/webhooks/parse/settings" \
--header "Authorization: Bearer YOUR_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"url": "https://yourdomain.com/webhook/parse",
"hostname": "parse.yourdomain.com",
"spam_check": true,
"send_raw": false
}'
Webhook Configuration Options
| Parameter | Type | Description |
|---|---|---|
url |
string | Required. The public URL where parsed emails will be POSTed |
hostname |
string | Required. The subdomain.domain that will receive emails |
spam_check |
boolean | Enable spam checking (emails ≤2.5 MB) |
send_raw |
boolean | Send full MIME message instead of parsed fields |
Important Notes
- The webhook URL must be publicly accessible
- SendGrid will not follow redirects - use the final URL directly
- Your endpoint must respond with a 2xx status code to confirm receipt
- If your server returns 5xx errors, SendGrid will retry for up to 3 days
- Maximum message size: 30 MB (including attachments)
- Reserved email local-parts that cannot be used:
abuse,postmaster,unsubscribe
Creating a Security Policy
Security policies ensure that webhook requests are authentic and originate from SendGrid. You can use:
- Signature Verification (ECDSA) - SendGrid signs payloads with a private key
- OAuth Verification - Validates requests with access tokens
- Both - Hybrid approach for enhanced security
API Endpoint
POST https://api.sendgrid.com/v3/user/webhooks/security/policies
Example: Signature-Only Policy
curl -X POST "https://api.sendgrid.com/v3/user/webhooks/security/policies" \
--header "Authorization: Bearer YOUR_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"name": "parse-webhook-signature-policy",
"signature": {
"enabled": true
}
}'
Example: OAuth-Only Policy
curl -X POST "https://api.sendgrid.com/v3/user/webhooks/security/policies" \
--header "Authorization: Bearer YOUR_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"name": "parse-webhook-oauth-policy",
"oauth": {
"client_id": "your_client_id",
"client_secret": "your_client_secret",
"token_url": "https://your-oauth-server.com/token",
"scopes": ["webhooks:read", "webhooks:write"]
}
}'
Example: Hybrid Policy (Both Methods)
curl -X POST "https://api.sendgrid.com/v3/user/webhooks/security/policies" \
--header "Authorization: Bearer YOUR_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"name": "parse-webhook-hybrid-policy",
"oauth": {
"client_id": "your_client_id",
"client_secret": "your_client_secret",
"token_url": "https://your-oauth-server.com/token",
"scopes": ["webhooks:read", "webhooks:write"]
},
"signature": {
"enabled": true
}
}'
Response
The API returns the created policy with its ID and public key (for signature verification):
{
"id": "dd677638-a16d-4e19-95ea-20231c35511b",
"name": "parse-webhook-signature-policy",
"signature": {
"enabled": true,
"public_key": "MFkwEwYHKoZIzj0CAQYI..."
}
}
Important: Store the
public_keysecurely. You'll need it to verify incoming webhook signatures.
Attaching Security Policy to Webhook
Once you have created a security policy, attach it to your Inbound Parse webhook.
API Endpoint
PATCH https://api.sendgrid.com/v3/user/webhooks/parse/settings/{hostname}
Example Request
curl -X PATCH "https://api.sendgrid.com/v3/user/webhooks/parse/settings/parse.yourdomain.com" \
--header "Authorization: Bearer YOUR_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"url": "https://yourdomain.com/webhook/parse",
"spam_check": true,
"send_raw": false,
"security_policy": "dd677638-a16d-4e19-95ea-20231c35511b"
}'
Replace dd677638-a16d-4e19-95ea-20231c35511b with your actual policy ID from the previous step.
Response
{
"url": "https://yourdomain.com/webhook/parse",
"hostname": "parse.yourdomain.com",
"spam_check": true,
"send_raw": false,
"security_policy": "dd677638-a16d-4e19-95ea-20231c35511b"
}
Validating Incoming Webhooks
Signature Verification
When signature verification is enabled, SendGrid includes these headers with each request:
X-Twilio-Email-Event-Webhook-Signature- The ECDSA signatureX-Twilio-Email-Event-Webhook-Timestamp- Request timestamp
Verification Steps
- Extract the signature and timestamp from headers
- Get the raw request body (do not parse it first)
- Verify the ECDSA signature using the public key you stored earlier
Important Considerations
- Inbound Parse uses
multipart/form-dataencoding - Do not parse the request body before validation - use the raw body exactly as received
- Many web frameworks auto-parse multipart data; disable this behavior for validation
Example Verification (Python)
import hashlib
from ecdsa import VerifyingKey, BadSignatureError
from ecdsa.util import sigdecode_der
def verify_signature(public_key_base64, payload, signature, timestamp):
"""
Verify SendGrid webhook signature.
Args:
public_key_base64: Base64 encoded public key from security policy
payload: Raw request body (bytes)
signature: X-Twilio-Email-Event-Webhook-Signature header value
timestamp: X-Twilio-Email-Event-Webhook-Timestamp header value
Returns:
bool: True if signature is valid
"""
import base64
# Decode the public key
public_key_bytes = base64.b64decode(public_key_base64)
verifying_key = VerifyingKey.from_der(public_key_bytes)
# Create the signed payload (timestamp + payload)
signed_payload = timestamp.encode() + payload
# Decode the signature
signature_bytes = base64.b64decode(signature)
try:
verifying_key.verify(
signature_bytes,
signed_payload,
hashfunc=hashlib.sha256,
sigdecode=sigdecode_der
)
return True
except BadSignatureError:
return False
OAuth Verification
When OAuth is enabled, SendGrid includes an access token in the Authorization header:
Authorization: Bearer <access_token>
Validate this token against your OAuth server using standard OAuth token introspection.
Testing
Send a Test Email
- Send an email to your configured parsing address:
test@parse.yourdomain.com
- Check your webhook endpoint for the incoming POST request
- Verify the payload contains expected fields:
from- Sender email addressto- Recipient email addresssubject- Email subjecttext- Plain text bodyhtml- HTML body (if present)attachments- Number of attachmentsSPF- SPF verification resultdkim- DKIM verification result
Webhook Payload Fields
| Field | Description |
|---|---|
headers |
Raw email headers |
from |
Sender email address |
to |
Recipient email address |
cc |
CC recipients |
subject |
Email subject line |
text |
Plain text body |
html |
HTML body |
sender_ip |
IP address of sending server |
spam_report |
Spam analysis (if spam_check enabled) |
spam_score |
Numeric spam score |
SPF |
SPF verification result |
dkim |
DKIM verification status |
attachments |
Number of attachments |
attachment-info |
JSON with attachment metadata |
attachment1, attachment2, etc. |
Actual attachment files |
Quick Reference
API Endpoints Summary
| Action | Method | Endpoint |
|---|---|---|
| Create Parse Webhook | POST | /v3/user/webhooks/parse/settings |
| Get Parse Settings | GET | /v3/user/webhooks/parse/settings |
| Update Parse Webhook | PATCH | /v3/user/webhooks/parse/settings/{hostname} |
| Delete Parse Webhook | DELETE | /v3/user/webhooks/parse/settings/{hostname} |
| Create Security Policy | POST | /v3/user/webhooks/security/policies |
| Get Security Policies | GET | /v3/user/webhooks/security/policies |
| Get Security Policy | GET | /v3/user/webhooks/security/policies/{id} |
| Update Security Policy | PATCH | /v3/user/webhooks/security/policies/{id} |
| Delete Security Policy | DELETE | /v3/user/webhooks/security/policies/{id} |
Base URL: https://api.sendgrid.com