Email processing
EmailConnect transforms incoming emails into structured webhook payloads. This guide explains the processing pipeline from email receipt to webhook delivery.
Processing pipeline
1. Email reception
When an email arrives at your configured domain:
- SMTP connection - Sender's mail server connects to
mx1.emailconnect.eu - Spam & virus check - Email is scanned for spam. On Business+ plans, attachments are also scanned for viruses using ClamAV
- Rule evaluation - Alias rules are checked (Maker+ feature)
- Acceptance - Email is accepted for processing
2. Parsing and extraction
EmailConnect extracts structured data from the raw email and enriches it based on your plan.
Message parsing (all plans):
- Sender and recipient addresses, display names
- Subject line, date, and timestamps
- HTML and plain text body
- Link extraction from both HTML and plain text
- Attachments with filenames, MIME types, and sizes (Base64 inline, or a
downloadUrlfor offloaded files) - Email classification —
classification.typeis one ofbounce,auto-reply,mailing-list,read-receipt, ornormal. It is derived from RFC headers, not from content, so it is a fact about the message rather than a guess.normalmeans none of the automated shapes matched - Classification
confidence(definiteorlikely) andsignals— the exact headers behind the verdict, as{ header, value }pairs, so you can audit the call instead of trusting it
Content enrichment (Maker+):
- Markdown conversion — HTML body converted to clean Markdown, useful for LLM pipelines and content extraction. Requires the
includeMarkdownalias setting, which is off by default - Link
anchorTextandsource— Free plans receiveurlanddomainonly - Integrity hashes — SHA-256 hashes of both the processed content and the raw email for audit trails and deduplication
Spam analysis (Maker+):
- rspamd composite score — you compare it against your own cut-off; we don't ship a verdict
- SPF, DKIM, and DMARC authentication results
- Matched rules with individual weights
- Score-based alias rules to accept, block, or tag mail before delivery
Security scanning (Business+):
- ClamAV virus scanning on all attachments
- Infected files are excluded from delivery with
excludeReason: "virus-detected" - Per-attachment and summary-level scan results
3. Webhook delivery
The structured payload is delivered to your configured endpoint:
- Payload construction - JSON payload is built
- Signing - Request is signed for verification
- Delivery - POST request sent to your webhook URL
- Retry handling - Failed deliveries are retried
Webhook payload structure
Looking for the full field-by-field reference? See What's in your webhook payload for every field, enrichment feature, and plan availability.
Basic payload
{
"message": {
"date": "2026-02-12T10:30:00.000Z",
"sender": {
"name": "Alice Martin",
"email": "alice@example.com"
},
"content": {
"text": "Hi there!\n\nJust wanted to follow up on our conversation.\n\nBest regards,\nAlice",
"html": "<p>Hi there!</p><p>Just wanted to follow up on our conversation.</p><p>Best regards,<br>Alice</p>",
"links": []
},
"subject": "Following up",
"recipient": {
"name": null,
"email": "support@yourdomain.com",
"tag": null
},
"attachments": []
},
"classification": {
"type": "normal"
}
}
Full payload with all options
{
"spam": {
"score": -0.99,
"engine": "rspamd",
"symbols": [
{ "name": "DMARC_POLICY_ALLOW", "weight": -0.5, "description": "example.com,none" },
{ "name": "R_DKIM_ALLOW", "weight": -0.2, "description": "example.com:s=selector1" }
],
"authentication": {
"spf": { "result": "pass" },
"dkim": { "result": "pass" },
"dmarc": { "result": "pass" }
}
},
"message": {
"date": "2026-02-12T10:30:00.000Z",
"sender": {
"name": "Alice Martin",
"email": "alice@example.com"
},
"content": {
"text": "Hi team,\n\nPlease find invoice #2026-0042 attached.\n\nBest,\nAlice",
"html": "<p>Hi team,</p><p>Please find invoice #2026-0042 attached.</p><p>Best,<br>Alice</p>",
"markdown": "Hi team,\n\nPlease find invoice #2026-0042 attached.\n\nBest,\nAlice",
"links": [
{
"url": "https://billing.example.com/inv/2026-0042",
"domain": "billing.example.com",
"anchorText": "View it online",
"source": "html"
}
]
},
"subject": "Invoice #2026-0042 attached",
"recipient": {
"name": null,
"email": "invoices+acme@yourdomain.com",
"tag": "acme"
},
"attachments": [
{
"filename": "invoice-2026-0042.pdf",
"contentType": "application/pdf",
"size": 245680,
"content": "JVBERi0xLjQK...",
"virusScan": { "status": "clean" }
}
]
},
"integrity": {
"contentHash": "a3f2b8c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1",
"rawEmailHash": "9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b0a9f8e"
},
"classification": {
"type": "normal",
"confidence": "definite",
"signals": [
{ "header": "return-path", "value": "alice@example.com" }
]
},
"security": {
"virusScan": {
"scanned": true,
"engine": "clamav",
"engineVersion": "1.5.1",
"attachmentsScanned": 1,
"attachmentsSkipped": 0,
"threatsFound": 0
}
}
}
This example was addressed to invoices+acme@yourdomain.com, so recipient.tag is "acme" — anything after the + is handed to you pre-parsed, and the mail is still delivered to the webhook on your invoices@ alias. Untagged mail sets tag to null. See the webhook payload reference for the full rules.
A few things worth calling out in that example:
- Links carry
anchorText, nottext. Each entry is{ url, domain, anchorText?, source? }.urlanddomainare on every plan;anchorTextandsourceare Maker+. classification.signalsis a list of objects, not strings — each is{ header, value }naming the real RFC header that produced the verdict.- The
envelopeblock is not shown above because it is off by default. Unless the alias enables "include envelope", the envelope is reduced to{ messageId }. Turn it on to receiveheaders,allRecipients,returnPath, andprocessed.
See the webhook payload reference for every field and its plan.
Delivery and retries
Successful delivery
A delivery is considered successful when your endpoint returns:
- HTTP 200-299 status code
- Response within timeout period (30 seconds)
Retry policy
If delivery fails, EmailConnect retries with exponential backoff (±10% jitter):
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | ~1 minute |
| 3 | ~5 minutes |
| 4 | ~30 minutes |
| 5 | ~2 hours |
| 6 | ~12 hours |
Actual delays vary by ±10% from the listed values to prevent thundering-herd effects.
Retries are capped by your data retention policy. If the next retry would fall after your retention window expires, the email is marked as failed instead. For example, Free and Maker plans (1-hour retention) get up to 4 attempts — attempt 5 would be at +2h 36m, past the retention window. Business and Platform plans with longer retention (up to 30 or 365 days) can use all 6 attempts.
After 6 failed attempts or retention expiry (whichever comes first), the email is marked as failed and no further retries occur.
Failure reasons
Common delivery failures:
- Endpoint not reachable (network error)
- Endpoint returns 4xx/5xx error
- Timeout (response takes > 30 seconds)
- SSL/TLS certificate errors
- Payload too large for endpoint
Webhook security
Request signing
EmailConnect signs webhook requests following the Standard Webhooks convention. Each request includes three headers:
webhook-id: msg_2KWPBgLl...
webhook-timestamp: 1716300000
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=
The signed content is {webhook-id}.{webhook-timestamp}.{body}, signed with HMAC-SHA256 using your base64-decoded secret.
Node.js
const crypto = require('crypto');
function verifyWebhook(headers, rawBody, secret) {
const signedContent = `${headers['webhook-id']}.${headers['webhook-timestamp']}.${rawBody}`;
const secretBytes = Buffer.from(secret.replace(/^whsec_/, ''), 'base64');
const expected = crypto.createHmac('sha256', secretBytes).update(signedContent).digest('base64');
return headers['webhook-signature'].split(' ').some(sig => {
const [v, val] = sig.split(',');
return v === 'v1' && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(val));
});
}
Python
import hmac, hashlib, base64
def verify_webhook(headers, raw_body, secret):
signed_content = f"{headers['webhook-id']}.{headers['webhook-timestamp']}.{raw_body}"
secret_bytes = base64.b64decode(secret.removeprefix('whsec_'))
expected = base64.b64encode(
hmac.new(secret_bytes, signed_content.encode(), hashlib.sha256).digest()
).decode()
return any(
v == 'v1' and hmac.compare_digest(expected, val)
for sig in headers['webhook-signature'].split(' ')
for v, _, val in [sig.partition(',')]
)
For full verification examples including timestamp checks, see the webhook signing guide.
Processing order
Emails are processed in order of receipt. However, webhook delivery may complete out of order due to:
- Retry delays for failed deliveries
- Processing time variations
- Network latency differences
If order matters for your application, sort on a timestamp from the payload. There is no top-level timestamp field — the two you can use are:
message.date— when the sender's mail client says the email was sent. Present on every payload. Sender-supplied, so it can be skewed or absent.envelope.processed.timestamp— when EmailConnect processed the message. This is our clock, so it is the reliable one for ordering — but it only reaches you if the alias has "include envelope" enabled, which is off by default. With the envelope off, the block is reduced to{ messageId }.
For deduplication rather than ordering, use envelope.messageId — it is stable across redeliveries.
Rate limits
| Plan | API requests per hour | Effective per minute |
|---|---|---|
| Free | 60 | 1 |
| Maker | 600 | 10 |
| Business | 3,000 | 50 |
| Platform | Unlimited | 10,000 cap |
Emails exceeding rate limits are queued and processed when capacity is available.
Monitoring and debugging
Email logs
View processed emails in your dashboard:
- Delivery status (success/failed/pending)
- Response codes from your endpoint
- Retry history
- Full payload preview
Webhook testing
EmailConnect integrates directly with WebhookTest.eu — click "Use WebhookTest" when creating an alias and you can inspect the full payload without leaving your dashboard. No external tools or tab-switching needed.
See Testing webhooks with WebhookTest for detailed setup instructions.
Related topics
- What's in your webhook payload - Complete field reference with enrichment features by plan
- Webhook configuration - Set up webhook endpoints
- Custom webhook headers - Add authentication headers
- Alias options - Configure processing options
- Attachment processing - Handle file attachments