Send emails reliably, at scale

A production-ready microservice for email delivery with built-in Dead Letter Queue support to handle failures gracefully.

Async Processing

Queue-based delivery ensures non-blocking requests

DLQ support

Failed emails are logged for Inspection and retry

Simple API

Clean REST endpoints with JSON request/response


Available Endpoints

POST/api/v1/send
Send an email message
Request Body
{
  "to": "johndoe@example.com",
  "subject": "Welcome to m0b mailer",
  "html": "<h1>This is a test</h1>",
  "appName": "Kayaws"
}
Response Body
{
  "success": true,
  "messageId": "msg_12...xyz",
  "retries": 0
}
POST/api/v1/send-dlq
Send dead messages after retries for logging
Request Body
{
  "to": "johndoe@example.com",
  "subject": "Welcome to m0b mailer",
  "html": "<h1>This is a test</h1>",
  "appName": "Kayaws"
}
Response Body
{
  "success": "true"
}

Basic Usage

Code
Create a reusable helper to send emails with QStash integration
import { Client, PublishToApiResponse } from "@upstash/qstash";
import { MAILER_URL, QSTASH_TOKEN } from "./const";

export interface SendMailOptions {
  to: string;
  subject: string;
  html: string;
  appName?: string;
}

const qstash = new Client({
  token: QSTASH_TOKEN,
});

export interface MailOptions {
  to: string;
  subject: string;
  html: string;
  appName?: string;
}

export interface MailResponseType {
  success: boolean;
  messageId: string;
  retries: number;
}

export default async function sendMail({
  to,
  subject,
  html,
  appName,
}: SendMailOptions): Promise<string> {
  const res: PublishToApiResponse = (await qstash.publishJSON<MailOptions>({
    url: `${MAILER_URL}/api/v1/send`,
    failureCallback: `${MAILER_URL}/api/v1/send-dlq`,
    body: {
      to,
      subject,
      html,
      appName,
    },
  })) as PublishToApiResponse;
  return res.messageId;
}

Developed primarily for internal use and not maintained as a public product.
Community contributions are welcome and appreciated.