Skip to content

Email Steps & SMTP

Pulsabase Edge Functions include a native Email step for sending transactional emails without an external library. You can use Pulsabase’s built-in SMTP or bring your own provider.

Configure your SMTP settings in Dashboard → Settings → Email.

Pulsabase provides a shared SMTP service for development and low-volume production use. No configuration required — it’s enabled by default.

SettingValue
ProviderPulsabase Managed SMTP
From addressnoreply@mail.pulsabase.io
Rate limit200 emails/day (free tier)
Section titled “Option B — Bring Your Own SMTP (Recommended for Production)”

Connect any SMTP-compatible provider (SendGrid, Resend, Postmark, AWS SES, IONOS, etc.):

FieldExample
SMTP Hostsmtp.sendgrid.net
SMTP Port587 (TLS) or 465 (SSL)
Usernameapikey
PasswordSG.xxxxx (your API key)
From NameAcme Inc.
From Emailhello@acme.com

Email templates are managed in Dashboard → Settings → Email Templates. Each template has:

  • Slug — unique identifier for use in edge steps (e.g., welcome, password-reset)
  • Subject — static or dynamic (supports {{variables}})
  • Body — HTML or plain text (supports {{variables}})

Templates support the same {{}} interpolation as edge steps:

<!-- Template: welcome -->
<h1>Welcome, {{name}}!</h1>
<p>Your account at <strong>{{project_name}}</strong> is ready.</p>
<p><a href="{{app_url}}">Get started →</a></p>

Variables are filled at send-time from the email step’s variables object.

In any edge function, add a step of type Email:

FieldDescription
toRecipient email address (string or {{variable}})
templateTemplate slug (e.g., welcome)
variablesKey-value pairs injected into the template
reply_toOptional: reply-to address
ccOptional: CC list
bccOptional: BCC list
Function: send-welcome-email
Trigger: after INSERT on users (via after hook)
Step 1: fetch_user → [Database]
SELECT name, email FROM users WHERE id = {{input.id}}
Step 2: send_welcome → [Email]
to: {{steps.fetch_user.body.email}}
template: welcome
variables: {
name: {{steps.fetch_user.body.name}},
project_name: "Acme",
app_url: "https://app.acme.com"
}
Function: password-reset
Step 1: generate_token → [Database]
INSERT INTO reset_tokens (user_id, token, expires_at)
VALUES ({{input.user_id}}, gen_random_uuid(), NOW() + INTERVAL '1 hour')
RETURNING token
Step 2: send_reset → [Email]
to: {{input.email}}
template: password-reset
variables: {
reset_url: "https://app.acme.com/reset?token={{steps.generate_token.body.token}}"
}

When testing functions in Dry Run mode, email steps are simulated — no email is actually sent. The __logs output shows exactly what would have been sent:

{
"__logs": [
{
"step": "send_welcome",
"type": "email",
"dry_run": true,
"simulated_payload": {
"to": "user@example.com",
"subject": "Welcome to Acme!",
"template": "welcome",
"variables": { "name": "Jane", "project_name": "Acme" }
}
}
]
}
  • Use after hooks for transactional emails (welcome, notifications) — they run async and don’t slow down the API response
  • Test with dry run before enabling a function on a hook
  • Configure reply-to if you want users to be able to reply to transactional emails
  • Monitor deliverability — check your SMTP provider’s bounce/complaint rates in their dashboard, not in Pulsabase