n8n is the most flexible open-source automation platform for teams that do not want to live inside Zapier's pricing model. It also gives you direct access to SMTP, Gmail API, SendGrid, Mailgun, Amazon SES, and every other email transport you want to chain together. The flexibility is the problem: any wrong header, any missed DKIM signature, any forgotten SPF include and your first 500 emails land in Spam before anyone notices.
The fix is simple: add one HTTP Request node that triggers a real placement test, pauses the workflow long enough to collect the results, and branches on placement score. If the score is below your threshold, the bulk send never happens — you get a Slack alert instead.
Hosted automation platforms warm up their senders for you. n8n does not. If you hooked up a brand-new SendGrid API key or a fresh SMTP domain, your first workflow run is the canary. Seed it first.
The email nodes you are probably using
n8n ships several email-sending integrations out of the box. The three most common in production workflows:
- Send Email (generic SMTP) — plain SMTP to any server. No built-in retry, no bounce tracking. Fast but brittle.
- Gmail — OAuth to a personal or Workspace account. Subject to Gmail's 2,000 msg/day sending limit and to the unauthorised script detection if used heavily.
- SendGrid / Mailgun / SES / Postmark — ESP HTTP API. Best deliverability, but only if the sending domain is verified and warm.
All three produce an email. None of them tell you which folder the email landed in. That is what the placement test node solves.
The placement-test pattern
The workflow idea is three nodes inserted before your actual Send:
- HTTP Request → POST /api/tests to create a test and obtain a
token. - Wait node (5 minutes) — long enough for seed mailboxes to receive and categorise the message.
- HTTP Request → GET /api/tests/:token to fetch the placement report and branch on it.
Before step 1 fires, your workflow should also send the same email body to the test's seedAddresses. The simplest way is a parallel branch that sends to both the real list and the seed list.
Example workflow JSON
Save this to a file and import it via Settings → Import Workflow in n8n. It is a minimal skeleton — replace the sender node with your actual Gmail / SendGrid / SMTP node.
{
"name": "Campaign send with placement guard",
"nodes": [
{
"parameters": {
"method": "POST",
"url": "https://check.live-direct-marketing.online/api/tests",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{ "name": "Content-Type", "value": "application/json" }
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{ "name": "subject", "value": "={{ $json.subject }}" },
{ "name": "from", "value": "campaigns@yourbrand.com" }
]
}
},
"name": "Create placement test",
"type": "n8n-nodes-base.httpRequest",
"position": [400, 300]
},
{
"parameters": {
"amount": 5,
"unit": "minutes"
},
"name": "Wait 5 minutes",
"type": "n8n-nodes-base.wait",
"position": [640, 300]
},
{
"parameters": {
"method": "GET",
"url": "=https://check.live-direct-marketing.online/api/tests/{{ $node[\"Create placement test\"].json.token }}"
},
"name": "Fetch report",
"type": "n8n-nodes-base.httpRequest",
"position": [880, 300]
},
{
"parameters": {
"conditions": {
"number": [
{
"value1": "={{ $json.score }}",
"operation": "smaller",
"value2": 70
}
]
}
},
"name": "If placement < 70",
"type": "n8n-nodes-base.if",
"position": [1120, 300]
}
],
"connections": {
"Create placement test": {
"main": [[{ "node": "Wait 5 minutes", "type": "main", "index": 0 }]]
},
"Wait 5 minutes": {
"main": [[{ "node": "Fetch report", "type": "main", "index": 0 }]]
},
"Fetch report": {
"main": [[{ "node": "If placement < 70", "type": "main", "index": 0 }]]
}
}
}A native integration is in private beta. You will be able to drop an Inbox Check node directly into n8n — no HTTP Request wiring, no polling loops, automatic report attachment to the execution log.
Branching logic and alerts
After the fetch, an IF node checks the placement score. Below a threshold you define (we recommend 70 for new domains, 85 for warm domains), route to a Slack notification node instead of the real send. Example thresholds we see in production:
- 95+ — send to full list. Domain is warm, all checks green.
- 80–95 — send to full list but tag the run for review. Placement dipped but is not broken.
- 60–80 — send to only 10% of the list. Retest in 48 hours.
- < 60 — halt. Investigate authentication, content, and any recent DNS changes.
Self-hosted n8n gotchas
Outbound HTTP and corporate proxies
If your n8n runs inside a VPC with an egress proxy, the HTTP Request node needs the proxy configured at the container level. Without it, calls to the placement API hang until timeout — the Wait node then fires before a token is returned.
Long-running workflows and the Wait node
The Wait node on durations longer than a minute requires n8n to be running in main process mode with EXECUTIONS_MODE=regular or in queue mode with Redis. In own process mode, long waits cause execution timeouts.
Credential rotation
When you rotate your ESP API key, restart your n8n worker. We have seen workflows cache a dead credential for hours and silently fail all sends while the placement test succeeded (because it used a separate credential).
Rollout checklist
- Import the workflow above into your n8n instance.
- Replace the bodyParameters with the actual subject and from your campaigns use. Add the HTML body if you want the seed mailboxes to render the real content.
- Run once manually. Confirm the token comes back, the Wait fires, the fetch returns a score.
- Add the
IFnode and hook your existing Send node onto the TRUE branch. Slack / Teams / email alert on FALSE. - Activate the workflow. Every future send is now placement-gated at zero incremental cost.