Skip to main content

Send Email (SMTP and Resend)

Category: Backend & APIs

Get this pack →

This page is generated from the Air Pipe marketplace. Browse it live to install into your organization.

One pack, two ways to send transactional email from an Air Pipe route — pick whichever fits your stack:

  • SMTP (smtp.yml) — Air Pipe's native email: action speaks SMTP directly (via STARTTLS). Works with any mail server: Gmail, Fastmail, Postmark, Mailgun, Amazon SES SMTP, or your own relay. No SDK, self-hosted-friendly.
  • Resend (resend.yml) — Air Pipe calls the Resend HTTP API. No SMTP server to run; just an API key and a verified domain.

Both routes take the same request shape{ to, subject, html } — so you can switch providers without changing your callers.


Which one should I use?

SMTP (smtp.yml)Resend (resend.yml)
HowNative email: action opens an SMTP connectionHTTP POST to api.resend.com
Best forExisting mail infra, self-hosted, any providerFastest setup, no server to manage
You needSMTP host + user + passA Resend API key + verified domain
Leaves your infraOnly the SMTP connection you configureHTTPS call to Resend

Deploy one file for real use — they're independent. Both ship here (distinct /email/smtp and /email/resend routes) so you can compare.


Endpoints

MethodRouteSends viaBody
POST/email/smtpNative SMTP{ to, subject, html }
POST/email/resendResend API{ to, subject, html }

Both validate the body (all three fields required → 400), send, and return { status: "sent", provider, … }. A delivery failure returns 502.


Setup

Add the managed variables for the option you're using (Air Pipe dashboard, or ap_vars self-hosted):

SMTP (smtp.yml):

NameValue
SMTP_HOSTSMTP server hostname (e.g. smtp.postmarkapp.com)
SMTP_USERSMTP username
SMTP_PASSSMTP password / app password / API-key-as-password
SMTP_FROMVerified sender — plain or named (Acme <[email protected]>)

The smtp: block defaults to port 587 + STARTTLS. For implicit TLS use port: 465; for an internal unencrypted relay use port: 25 + tls: false. port and tls are literal config values (not ap_vars), so edit them in smtp.yml directly.

Resend (resend.yml):

NameValue
RESEND_API_KEYYour Resend API key (starts with re_)
RESEND_FROMVerified sender (Acme <[email protected]>)

Quick start

BASE=https://your-airpipe-host

# Send via SMTP
curl -sX POST $BASE/email/smtp -H 'content-type: application/json' -d '{
"to": "[email protected]",
"subject": "Welcome to Acme",
"html": "<h1>Welcome!</h1><p>Thanks for signing up.</p>"
}'
# → { "to": "...", "subject": "...", "status": "sent", "provider": "smtp" }

# Send via Resend — identical body
curl -sX POST $BASE/email/resend -H 'content-type: application/json' -d '{
"to": "[email protected]",
"subject": "Welcome to Acme",
"html": "<h1>Welcome!</h1><p>Thanks for signing up.</p>"
}'
# → { "id": "…", "status": "sent", "provider": "resend" }

Notes

  • Same body, two providers — swap smtpresend in the path; callers don't change.
  • Quotes & newlines are safe — the Resend route runs every user value through ->json_escape, so a " or newline in the subject/body can't break the JSON payload.
  • HTML email — both routes take an html body. (The native SMTP action also supports a text: field for a plaintext part; add it to smtp.yml if you want multipart.)
  • AWS SES — the native action reserves an aws_ses option, but SMTP and Resend are the two supported paths today; for SES, use its SMTP interface with smtp.yml.

Configuration

resend.yml

name: SendEmailResend
description: Send transactional email via the Resend HTTP API — the managed, no-SMTP-server alternative. Air Pipe calls Resend's REST endpoint directly with your API key.

docs: true

# Resend (https://resend.com) is a developer-friendly email API: no SMTP server
# to run, just an API key and a verified sending domain. This is the managed
# option — Air Pipe POSTs to Resend over HTTPS instead of opening an SMTP socket.
#
# Free tier: 100 emails/day (3,000/month). Verify your domain in the Resend
# dashboard before sending from it.
#
# Required managed variables:
# RESEND_API_KEY — your Resend API key (starts with "re_")
# RESEND_FROM — verified sender, plain or named ("Acme <[email protected]>")

interfaces:

# POST /email/resend
# Body: { "to": "...", "subject": "...", "html": "<...>" }
email/resend:
output: http
method: POST
summary: Send email via Resend
description: Send an HTML email through the Resend API. Same request shape as the SMTP route.
tags: [email, resend]
request_example:
to: [email protected]
subject: Welcome to Acme
html: "<h1>Welcome!</h1><p>Thanks for signing up.</p>"
response_example:
id: 4ef9a417-02e9-4d39-ad75-9611e0fcc33c
status: sent
provider: resend

actions:
- name: Validate
input: a|body|
hide_data_on_success: true
assert:
http_code_on_error: 400
error_message: "to, subject, and html are required"
tests:
- value: to
is_not_null: true
is_not_empty: true
description: Recipient email address.
- value: subject
is_not_null: true
is_not_empty: true
description: Email subject line.
- value: html
is_not_null: true
is_not_empty: true
description: HTML body of the email.

# Every user-supplied value goes through ->json_escape so a quote or newline
# in the subject/body can't break the JSON payload sent to Resend.
- name: SendResend
run_when_succeeded:
actions: [Validate]
http_code_on_error: 400
http:
url: https://api.resend.com/emails
method: POST
headers:
content-type: application/json
authorization: "Bearer a|ap_var::RESEND_API_KEY|"
body: |
{
"from": "a|ap_var::RESEND_FROM|",
"to": ["a|Validate::to->json_escape|"],
"subject": "a|Validate::subject->json_escape|",
"html": "a|Validate::html->json_escape|"
}
assert:
http_code_on_error: 502
error_message: "Resend rejected the email"
tests:
- value: status
is_equal_to: 200

- name: Result
run_when_succeeded: [SendResend]
input: a|SendResend|
post_transforms:
- extract_value: body
- add_attribute:
status: sent
provider: resend

smtp.yml

name: SendEmailSmtp
description: Send transactional email through any SMTP server (Gmail, Fastmail, Postmark, Mailgun, Amazon SES SMTP, or your own relay) using Air Pipe's native email action — no provider SDK, no HTTP client to wire up.

docs: true

# Air Pipe's `email:` action speaks SMTP directly, so you only need your
# provider's host + credentials — no SDK. This is the self-hosted-friendly
# option: any mail server works, and nothing leaves your infrastructure except
# the SMTP connection you configure.
#
# TLS / PORTS (set in the `smtp:` block below):
# 587 + tls: true → STARTTLS (recommended default — used here)
# 465 → implicit TLS / SMTPS
# 25 + tls: false → unencrypted (internal relays / testing only)
#
# Required managed variables:
# SMTP_HOST — SMTP server hostname (e.g. smtp.gmail.com, smtp.postmarkapp.com)
# SMTP_USER — SMTP username
# SMTP_PASS — SMTP password / app password / API-key-as-password
# SMTP_FROM — verified sender, plain or named ("Acme <[email protected]>")

interfaces:

# POST /email/smtp
# Body: { "to": "...", "subject": "...", "html": "<...>" }
email/smtp:
output: http
method: POST
summary: Send email via SMTP
description: Send an HTML email through your SMTP server using the native email action.
tags: [email, smtp]
request_example:
to: [email protected]
subject: Welcome to Acme
html: "<h1>Welcome!</h1><p>Thanks for signing up.</p>"
response_example:
status: sent
provider: smtp
to: [email protected]

actions:
- name: Validate
input: a|body|
hide_data_on_success: true
assert:
http_code_on_error: 400
error_message: "to, subject, and html are required"
tests:
- value: to
is_not_null: true
is_not_empty: true
description: Recipient email address.
- value: subject
is_not_null: true
is_not_empty: true
description: Email subject line.
- value: html
is_not_null: true
is_not_empty: true
description: HTML body of the email.

# The native email action opens an SMTP connection and delivers the message.
# a|...| directives interpolate into the string fields (not into `port`/`tls`,
# which must stay literal because the config is typed).
- name: SendSmtp
run_when_succeeded:
actions: [Validate]
http_code_on_error: 400
email:
smtp:
server: a|ap_var::SMTP_HOST|
user: a|ap_var::SMTP_USER|
pass: a|ap_var::SMTP_PASS|
port: 587
tls: true
from: a|ap_var::SMTP_FROM|
to: a|Validate::to|
subject: a|Validate::subject|
html: a|Validate::html|

- name: Result
run_when_succeeded:
actions: [SendSmtp]
http_code_on_error: 502
input: a|Validate|
post_transforms:
- remove_keys: [html]
- add_attribute:
status: sent
provider: smtp