Skip to main content
Yasumu includes an integrated SMTP server that captures all emails sent to it, making it perfect for testing email workflows during development without sending real emails.

How it works

Yasumu’s SMTP module:
  • Runs a local catch-all SMTP server on a configurable port
  • Captures all incoming emails regardless of recipient
  • Stores emails for inspection and debugging
  • Requires no external email services or accounts
  • Works completely offline

SMTP configuration file

SMTP settings are stored in yasumu/smtp.ysl:
@smtp

metadata {
  id: "fyed9v8otb9a9vedyx8wy9f6"
  port: 50611
  username: ""
  password: null
}

Getting started

1

Check SMTP status

Open your workspace and navigate to the Email/SMTP section to see if the server is running.
2

Note the port number

The SMTP server runs on a specific port (e.g., 50611). You’ll use this in your application.
3

Configure your application

Point your application’s SMTP settings to localhost with the Yasumu port.
4

Send test emails

Trigger email sends from your application - they’ll appear in Yasumu.
5

Inspect emails

View captured emails, including headers, body, attachments, and metadata.

Programmatic SMTP management

Getting SMTP configuration

// Get the current SMTP port
const port = await workspace.emails.getSmtpPort();

if (port) {
  console.log(`SMTP server running on port: ${port}`);
} else {
  console.log('SMTP server not configured');
}

Updating SMTP configuration

// Update SMTP settings
await workspace.emails.updateSmtpConfig({
  port: 2525,
  username: 'testuser',
  password: 'testpass'
});

console.log('SMTP configuration updated');

Listing emails

// List emails with pagination
const result = await workspace.emails.listEmails({
  page: 1,
  limit: 20,
  sortBy: 'receivedAt',
  sortOrder: 'desc'
});

console.log(`Total emails: ${result.total}`);
console.log(`Page ${result.page} of ${result.totalPages}`);

for (const email of result.data) {
  console.log(`From: ${email.from}`);
  console.log(`To: ${email.to}`);
  console.log(`Subject: ${email.subject}`);
  console.log(`Received: ${email.receivedAt}`);
  console.log('---');
}

Getting a specific email

// Get email by ID
const email = await workspace.emails.getEmail('email-id-here');

console.log(`Subject: ${email.subject}`);
console.log(`From: ${email.from}`);
console.log(`To: ${email.to.join(', ')}`);
console.log(`Body:\n${email.body}`);

// Access email headers
for (const [key, value] of Object.entries(email.headers)) {
  console.log(`${key}: ${value}`);
}

// Check for attachments
if (email.attachments && email.attachments.length > 0) {
  console.log('Attachments:');
  for (const attachment of email.attachments) {
    console.log(`- ${attachment.filename} (${attachment.size} bytes)`);
  }
}

Deleting emails

// Delete a specific email
await workspace.emails.deleteEmail('email-id-here');
console.log('Email deleted');

// Delete multiple emails
const emails = await workspace.emails.listEmails({ limit: 100 });
for (const email of emails.data) {
  await workspace.emails.deleteEmail(email.id);
}
console.log('All emails cleared');

Configuring your application

Configure your application to send emails through Yasumu’s SMTP server:
const nodemailer = require('nodemailer');

const transporter = nodemailer.createTransport({
  host: 'localhost',
  port: 50611, // Use your Yasumu SMTP port
  secure: false,
  auth: {
    user: '', // Optional
    pass: ''  // Optional
  },
  tls: {
    rejectUnauthorized: false
  }
});

// Send email
await transporter.sendMail({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Test Email',
  text: 'This is a test email',
  html: '<p>This is a <strong>test</strong> email</p>'
});

Testing email workflows

Test registration emails

// Trigger user registration in your app
// then check for the welcome email

const emails = await workspace.emails.listEmails({ limit: 1 });
const latestEmail = emails.data[0];

if (latestEmail.subject === 'Welcome to Our App!') {
  console.log('✓ Registration email sent');
  console.log(`Recipient: ${latestEmail.to}`);
  
  // Verify email content
  if (latestEmail.body.includes('Verify your email')) {
    console.log('✓ Email contains verification link');
  }
}

Test password reset flow

// Trigger password reset in your app
// Extract reset token from email

const emails = await workspace.emails.listEmails({
  limit: 10,
  sortBy: 'receivedAt',
  sortOrder: 'desc'
});

const resetEmail = emails.data.find(e => 
  e.subject.includes('Password Reset')
);

if (resetEmail) {
  // Extract token from email body
  const tokenMatch = resetEmail.body.match(/token=([a-zA-Z0-9]+)/);
  
  if (tokenMatch) {
    const resetToken = tokenMatch[1];
    console.log(`Reset token: ${resetToken}`);
    
    // Use token to test reset endpoint
    // ...
  }
}

Test notification emails

// Monitor for notification emails

setInterval(async () => {
  const emails = await workspace.emails.listEmails({ limit: 5 });
  
  for (const email of emails.data) {
    if (email.subject.includes('New Comment')) {
      console.log('✓ Comment notification received');
      console.log(`To: ${email.to}`);
      
      // Verify email was sent to correct user
      // Check notification content
      // ...
    }
  }
}, 5000); // Check every 5 seconds

Automation and testing

Integration with test suites

import { Yasumu } from '@yasumu/core';

describe('Email Tests', () => {
  let yasumu: Yasumu;
  let workspace: Workspace;

  beforeAll(async () => {
    yasumu = new Yasumu();
    workspace = await yasumu.workspaces.open({ id: 'workspace-id' });
  });

  afterEach(async () => {
    // Clear emails after each test
    const emails = await workspace.emails.listEmails({ limit: 100 });
    for (const email of emails.data) {
      await workspace.emails.deleteEmail(email.id);
    }
  });

  test('should send welcome email on registration', async () => {
    // Trigger registration
    await registerUser({ email: '[email protected]' });

    // Wait for email
    await new Promise(resolve => setTimeout(resolve, 1000));

    // Check email was received
    const emails = await workspace.emails.listEmails({ limit: 1 });
    expect(emails.data).toHaveLength(1);
    expect(emails.data[0].subject).toBe('Welcome!');
    expect(emails.data[0].to).toContain('[email protected]');
  });
});

Email assertions helper

class EmailTester {
  constructor(private workspace: Workspace) {}

  async waitForEmail(
    predicate: (email: EmailData) => boolean,
    timeout = 5000
  ): Promise<EmailData | null> {
    const startTime = Date.now();

    while (Date.now() - startTime < timeout) {
      const emails = await this.workspace.emails.listEmails({ limit: 10 });
      const found = emails.data.find(predicate);

      if (found) return found;

      await new Promise(resolve => setTimeout(resolve, 500));
    }

    return null;
  }

  async assertEmailSent(
    to: string,
    subject: string,
    timeout = 5000
  ): Promise<void> {
    const email = await this.waitForEmail(
      e => e.to.includes(to) && e.subject === subject,
      timeout
    );

    if (!email) {
      throw new Error(`Email not received: ${subject} to ${to}`);
    }
  }

  async assertEmailContains(
    to: string,
    content: string,
    timeout = 5000
  ): Promise<void> {
    const email = await this.waitForEmail(
      e => e.to.includes(to) && e.body.includes(content),
      timeout
    );

    if (!email) {
      throw new Error(`Email with content not found: ${content}`);
    }
  }
}

// Usage
const tester = new EmailTester(workspace);
await tester.assertEmailSent('[email protected]', 'Welcome!');
await tester.assertEmailContains('[email protected]', 'Verify your account');

Best practices

  • Clear between tests: Delete emails after each test to avoid false positives
  • Use unique identifiers: Include test-specific data in emails for easy identification
  • Wait appropriately: Give your application time to send emails before checking
  • Check multiple criteria: Verify recipient, subject, and content
  • Test error cases: Ensure emails are sent (or not sent) in error scenarios
  • Inspect HTML and text: Verify both HTML and plain text versions
  • Test attachments: If your app sends attachments, verify them

Common use cases

Testing email templates

Quickly iterate on email designs by sending test emails and viewing them in Yasumu.

Verification workflows

Test account verification, email confirmation, and two-factor authentication flows.

Notification systems

Validate that notifications are sent correctly for events like comments, mentions, or updates.

Transactional emails

Test order confirmations, receipts, shipping notifications, and other transactional emails.

Scheduled emails

Verify that scheduled digests, reminders, and reports are sent as expected.

Next steps

REST requests

Test APIs that send emails

Environment variables

Configure SMTP settings per environment

Build docs developers (and LLMs) love