close

DEV Community

Milos Ilic
Milos Ilic

Posted on • Originally published at primetimemail.com

How to Send Transactional Emails in Node.js (2026 Guide)

Every app that has users needs to send emails — welcome messages, password resets, order confirmations. This guide shows you how to set it up cleanly in Node.js using a REST API, with real code you can copy and run today.

What is a transactional email?

Transactional emails are triggered by user actions — not marketing campaigns. Examples include:

  • Welcome email when someone signs up
  • Password reset link
  • Order confirmation
  • Account verification
  • Invoice or receipt

Unlike newsletters, these emails are sent one at a time in response to something the user did. They have very high open rates and are critical to your product experience.

Prerequisites

  • Node.js 18+ installed
  • A PrimeTimeMail account (free, no card required)
  • Your API key from the dashboard

Step 1 — Get your API key

Sign up at primetimemail.com. After email verification, your API key arrives in your inbox. It looks like ptm_live_.... Keep it safe — treat it like a password.

Step 2 — Send your first email

No SDK needed. Use the native fetch API built into Node.js 18+:

const response = await fetch('https://api.primetimemail.com/v1/send', {
  method: 'POST',
  headers: {
    'X-API-Key': 'ptm_live_your_key_here',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    to: 'user@example.com',
    subject: 'Welcome to MyApp!',
    html: '<h1>Welcome!</h1><p>Thanks for signing up.</p>'
  })
});

const data = await response.json();
console.log(data); // { id: '...', status: 'sent' }
Enter fullscreen mode Exit fullscreen mode

Step 3 — Store your API key safely

Never hardcode your API key. Use environment variables:

# .env
PTM_API_KEY=ptm_live_your_key_here
Enter fullscreen mode Exit fullscreen mode
import 'dotenv/config';

const response = await fetch('https://api.primetimemail.com/v1/send', {
  method: 'POST',
  headers: {
    'X-API-Key': process.env.PTM_API_KEY,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    to: 'user@example.com',
    subject: 'Welcome!',
    html: '<p>Thanks for signing up.</p>'
  })
});
Enter fullscreen mode Exit fullscreen mode

Step 4 — Create a reusable email helper

// lib/email.js
export async function sendEmail({ to, subject, html }) {
  const res = await fetch('https://api.primetimemail.com/v1/send', {
    method: 'POST',
    headers: {
      'X-API-Key': process.env.PTM_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ to, subject, html })
  });

  if (!res.ok) {
    const err = await res.json();
    throw new Error(err.error || 'Failed to send email');
  }

  return res.json();
}
Enter fullscreen mode Exit fullscreen mode

Now use it anywhere:

await sendEmail({
  to: user.email,
  subject: 'Welcome to MyApp!',
  html: `<h1>Hey ${user.name}!</h1><p>Thanks for signing up.</p>`
});
Enter fullscreen mode Exit fullscreen mode

Step 5 — Handle errors properly

try {
  await sendEmail({
    to: newUser.email,
    subject: 'Verify your email',
    html: `<a href="${verifyUrl}">Verify your email →</a>`
  });
} catch (error) {
  console.error('Failed to send welcome email:', error.message);
  // Don't crash the signup flow — log it and move on
}
Enter fullscreen mode Exit fullscreen mode

Tip: Don't block your signup flow on email sending. Send it async, catch errors silently, and log them for monitoring.

Step 6 — Use custom From name

Set a friendly sender name in your dashboard under Settings → From Name. Instead of noreply@primetimemail.com, your users will see MyApp <noreply@yourdomain.com>.

Full working example

import express from 'express';
import 'dotenv/config';

const app = express();
app.use(express.json());

async function sendEmail({ to, subject, html }) {
  const res = await fetch('https://api.primetimemail.com/v1/send', {
    method: 'POST',
    headers: {
      'X-API-Key': process.env.PTM_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ to, subject, html })
  });
  if (!res.ok) throw new Error('Email failed');
  return res.json();
}

app.post('/register', async (req, res) => {
  const { email, name } = req.body;

  try {
    await sendEmail({
      to: email,
      subject: 'Welcome to MyApp!',
      html: `<h1>Hey ${name}!</h1><p>Your account is ready.</p>`
    });
  } catch (err) {
    console.error('Welcome email failed:', err.message);
  }

  res.json({ success: true });
});

app.listen(3000);
Enter fullscreen mode Exit fullscreen mode

What's next?

  • Add webhooks to track bounces and complaints
  • Set up a custom sending domain for better deliverability
  • Monitor your email logs in the dashboard

Built with PrimeTimeMail — transactional email API for developers. 3,000 emails/month free.

Top comments (0)