n8n7 min read

n8n email nodes: inbox placement before you scale

Self-hosting n8n means you own the entire automation stack — including the deliverability risk. Chain a placement test node before your bulk send and you catch spam-folder regressions for free.

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.

Why n8n users especially need this

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:

  1. HTTP Request → POST /api/tests to create a test and obtain a token.
  2. Wait node (5 minutes) — long enough for seed mailboxes to receive and categorise the message.
  3. 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 }]]
    }
  }
}
Native integration in beta

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.

→ Join the beta waitlist

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

  1. Import the workflow above into your n8n instance.
  2. 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.
  3. Run once manually. Confirm the token comes back, the Wait fires, the fetch returns a score.
  4. Add the IF node and hook your existing Send node onto the TRUE branch. Slack / Teams / email alert on FALSE.
  5. Activate the workflow. Every future send is now placement-gated at zero incremental cost.

Can I run the placement check asynchronously instead of Wait 5min?

Yes. Split into two workflows: one that creates the test, one triggered by our webhook (when the beta integration lands). For now, the Wait approach is the simplest and costs you nothing unless you are running thousands of concurrent executions.

Will the placement API rate-limit my polling?

Free tier allows reasonable polling for one token. If you are running this on every send, use Wait ≥ 5min rather than aggressive polling every 10 seconds.

Does this work with the Gmail node?

Yes. The placement test creates a separate message to seed mailboxes — it does not intercept your Gmail send. Both branches run in parallel with the same HTML body.

What if I use custom SMTP on a fresh IP?

Then you especially need this. Fresh IPs are unwarmed and your first 500 sends will determine the reputation of that IP for months. Placement-test the first 50 before scaling.
Related reading

Check your deliverability across 20+ providers

Gmail, Outlook, Yahoo, Mail.ru, Yandex, GMX, ProtonMail and more. Real inbox screenshots, SPF/DKIM/DMARC, spam engine verdicts. Free, no signup.

Run Free Test →

Unlimited tests · 20+ seed mailboxes · Live results · No account required