← Back to all posts
2026-10-07 · MailAnvil Team

Send Transactional Email from n8n Workflows with MailAnvil

n8n is where a lot of teams run their operational glue: order webhooks in, Slack alerts out, database writes in between. Email usually belongs in that same chain — a customer signs up, an invoice becomes due, a payment lands — and the workflow that already knows about the event should be the thing that sends the notification.

You don't need a dedicated n8n "email node" for that. MailAnvil is a plain REST API, and n8n's HTTP Request node speaks plain REST better than almost any integration layer. This tutorial covers the whole path: credentials, a basic send, a webhook-triggered send with template variables, error handling, and delivery-status checks.

What you need

Step 1: Store the API key in n8n credentials

Never paste the key directly into node fields. In n8n:

  1. Go to Credentials → New → Header Auth
  2. Name: Authorization
  3. Value: Bearer re_your_key_here

Save it as MailAnvil API. Every HTTP Request node in this tutorial references that credential, so rotating the key later is a one-place change.

Step 2: A basic send with the HTTP Request node

Add an HTTP Request node with these settings:

Field Value
Method POST
URL https://api.mailanvil.com/v1/send
Authentication Generic Credential Type → Header Auth → MailAnvil API
Body Content Type JSON

Body (JSON, switch off "Send Body as JSON Parameters" and use raw JSON):

{
  "from": "[email protected]",
  "to": ["{{ $json.customer_email }}"],
  "subject": "Your order is confirmed",
  "html": "<p>Order <strong>{{ $json.order_id }}</strong> is confirmed. Total: {{ $json.total }}.</p>",
  "text": "Order {{ $json.order_id }} is confirmed. Total: {{ $json.total }}."
}

n8n expressions ({{ }}) are evaluated before the request leaves, so the JSON body is already concrete when MailAnvil receives it.

A successful call returns 202 Accepted:

{
  "id": "em_01JABC...",
  "state": "queued"
}

202 means MailAnvil accepted the message into the queue — it is not yet a delivery confirmation. More on that in Step 5.

Step 3: Webhook-triggered send (the common pattern)

The realistic n8n shape is: a webhook fires, some nodes enrich the payload, then email goes out.

Webhook (POST /webhook/order-paid)
  → Set node: normalize fields
  → HTTP Request: MailAnvil send
  → IF node: response status == 202
      true  → Slack: "receipt sent"
      false → Error path (Step 4)

Set node output example:

{
  "customer_email": "[email protected]",
  "order_id": "INV-2026-0042",
  "total": "Rp 249.000",
  "product": "Pro plan (annual)"
}

The HTTP Request node body stays the same as Step 2 — it just reads from the Set node output.

Step 4: Error handling — don't sleep through a 429

MailAnvil rate-limits with 429. n8n's HTTP Request node has a built-in Retry On Fail option — turn it on, set 3 tries, 5-second wait. For anything you cannot afford to lose, also add an On Error: Continue branch:

For low-volume transactional flows (receipts, OTPs, notifications), retry + alert is enough.

Step 5: Check delivery status

Because /v1/send returns immediately with queued, follow up with a status call:

Response states: queued, delivered, bounced, suppressed, failed.

In practice, don't poll inside the same workflow run. Add a second workflow on a Schedule Trigger (every 5–15 minutes) that queries recent message IDs and routes anything not delivered into a review list. For high-volume sending, use webhooks instead — MailAnvil can push state changes to your own endpoint, which is the right shape for automation platforms anyway.

Step 6: Templates keep the HTML out of n8n

Long HTML bodies inside a JSON node are painful to edit and impossible to review. Prefer stored templates:

  1. Upload the template once via POST /v1/templates (or the dashboard)
  2. In the send body, use template_id + template_data:
{
  "from": "[email protected]",
  "to": ["{{ $json.customer_email }}"],
  "template_id": "tpl_order_receipt",
  "template_data": {
    "order_id": "{{ $json.order_id }}",
    "total": "{{ $json.total }}"
  }
}

Now the email design lives in one place, and n8n workflows only pass variables.

Step 7 (optional): let an AI Agent node send the email

n8n's AI Agent nodes can call tools. MailAnvil also ships an MCP server at mcp.mailanvil.com/mcp, so an agent that supports MCP can discover and call the send tools directly — useful when the "who gets emailed and why" decision itself is AI-driven. For deterministic transactional flows (receipts, OTPs), the plain HTTP Request node is the better choice: fewer moving parts, fully testable.

Common mistakes

That's the full loop: trigger → enrich → send → verify. From here, duplicate the HTTP Request node into your existing workflows; the credential is shared, so each new integration is about five minutes of work.