Files
2025-11-30 09:05:59 +08:00

14 KiB

MailHog API Complete Reference

Base URL

http://localhost:8025/api/v1

Authentication

If authentication is enabled via -auth-file, include Basic Auth headers:

curl -u username:password http://localhost:8025/api/v1/messages

Message Endpoints

Get All Messages

curl http://localhost:8025/api/v1/messages

Response Format:

{
  "total": 2,
  "count": 2,
  "start": 0,
  "items": [
    {
      "ID": "1234567890abcdef1234567890abcdef",
      "From": "sender@example.com",
      "To": [
        {
          "Address": "recipient@example.com",
          "Name": "Recipient Name"
        }
      ],
      "Subject": "Test Email",
      "Date": "2023-01-01T12:00:00Z",
      "Content": {
        "Headers": {
          "Content-Type": ["text/plain; charset=utf-8"],
          "Subject": ["Test Email"],
          "From": ["sender@example.com"],
          "To": ["recipient@example.com"]
        },
        "Body": "This is the email body",
        "Size": 1024
      },
      "MIME": {
        "Parts": [...]
      },
      "Created": "2023-01-01T12:00:00Z"
    }
  ]
}

Get Specific Message

curl http://localhost:8025/api/v1/messages/{message-id}

Example:

curl http://localhost:8025/api/v1/messages/1234567890abcdef1234567890abcdef

Get Messages with Limits

# Get first 10 messages
curl "http://localhost:8025/api/v1/messages?limit=10"

# Get messages with offset (pagination)
curl "http://localhost:8025/api/v1/messages?start=10&limit=10"

# Get recent messages (sorted by creation date)
curl "http://localhost:8025/api/v1/messages?start=0&limit=20"

Delete All Messages

curl -X DELETE http://localhost:8025/api/v1/messages

Delete Specific Message

curl -X DELETE http://localhost:8025/api/v1/messages/{message-id}

Search Endpoints

Search Messages

curl -X POST http://localhost:8025/api/v1/search \
  -H "Content-Type: application/json" \
  -d '{"query": "search term"}'

Advanced Search Examples:

# Search by subject
curl -X POST http://localhost:8025/api/v1/search \
  -H "Content-Type: application/json" \
  -d '{"query": "subject:welcome"}'

# Search by sender
curl -X POST http://localhost:8025/api/v1/search \
  -H "Content-Type: application/json" \
  -d '{"query": "from:user@example.com"}'

# Search by recipient
curl -X POST http://localhost:8025/api/v1/search \
  -H "Content-Type: application/json" \
  -d '{"query": "to:recipient@example.com"}'

# Search body content
curl -X POST http://localhost:8025/api/v1/search \
  -H "Content-Type: application/json" \
  -d '{"query": "content:confirmation link"}'

Search Response:

{
  "total": 1,
  "count": 1,
  "start": 0,
  "items": [...]
}

MIME and Attachment Endpoints

Get MIME Content

curl http://localhost:8025/api/v1/messages/{message-id}/mime

Download Attachment

curl http://localhost:8025/api/v1/messages/{message-id}/attachments/{attachment-id}

List Attachments

curl http://localhost:8025/api/v1/messages/{message-id}/attachments

Release Endpoints (Outgoing SMTP)

Release One Message

curl -X POST http://localhost:8025/api/v1/messages/{message-id}/release

With Custom Recipients:

curl -X POST http://localhost:8025/api/v1/messages/{message-id}/release \
  -H "Content-Type: application/json" \
  -d '{"recipients": ["alt@example.com"]}'

Release Multiple Messages

curl -X POST http://localhost:8025/api/v1/messages/release \
  -H "Content-Type: application/json" \
  -d '{"ids": ["id1", "id2", "id3"]}'

Status and Health Endpoints

Get Server Status

curl http://localhost:8025/api/v1/status

Response:

{
  "messages": 42,
  "storage": "memory",
  "version": "v1.0.0"
}

Health Check

curl -f http://localhost:8025/api/v1/health

Utility Endpoints

Get Raw Email Source

curl http://localhost:8025/api/v1/messages/{message-id}/raw

Parse Email Headers

curl http://localhost:8025/api/v1/messages/{message-id}/headers

WebSockets

Real-time Message Updates

const ws = new WebSocket('ws://localhost:8025/api/v1/websocket');

ws.onmessage = function(event) {
    const data = JSON.parse(event.data);

    switch(data.action) {
        case 'messages':
            console.log('New messages:', data.messages);
            break;
        case 'deleted':
            console.log('Deleted messages:', data.ids);
            break;
        case 'released':
            console.log('Released messages:', data.ids);
            break;
    }
};

WebSocket Message Formats:

New Message Notification:

{
    "action": "messages",
    "messages": [
        {
            "ID": "message-id",
            "From": "sender@example.com",
            "To": [{"Address": "recipient@example.com"}],
            "Subject": "Test",
            "Created": "2023-01-01T12:00:00Z"
        }
    ]
}

Message Deleted:

{
    "action": "deleted",
    "ids": ["message-id"]
}

Message Released:

{
    "action": "released",
    "ids": ["message-id"]
}

JavaScript Client Examples

Basic API Client

class MailHogClient {
    constructor(baseUrl = 'http://localhost:8025', auth = null) {
        this.baseUrl = baseUrl;
        this.auth = auth;
    }

    async request(endpoint, options = {}) {
        const url = `${this.baseUrl}/api/v1${endpoint}`;
        const config = {
            headers: {
                'Content-Type': 'application/json',
                ...options.headers
            },
            ...options
        };

        if (this.auth) {
            config.headers.Authorization = `Basic ${btoa(this.auth)}`;
        }

        const response = await fetch(url, config);
        return response.json();
    }

    async getMessages(limit = 50, start = 0) {
        return this.request(`/messages?limit=${limit}&start=${start}`);
    }

    async getMessage(id) {
        return this.request(`/messages/${id}`);
    }

    async searchMessages(query) {
        return this.request('/search', {
            method: 'POST',
            body: JSON.stringify({ query })
        });
    }

    async deleteMessages(ids) {
        if (ids === 'all') {
            return this.request('/messages', { method: 'DELETE' });
        }
        return this.request(`/messages/${ids}`, { method: 'DELETE' });
    }

    async releaseMessage(id, recipients = null) {
        const body = recipients ? { recipients } : undefined;
        return this.request(`/messages/${id}/release`, {
            method: 'POST',
            body: body ? JSON.stringify(body) : undefined
        });
    }
}

// Usage
const client = new MailHogClient();

// Get all messages
client.getMessages().then(data => console.log(data.total));

// Search for specific emails
client.searchMessages('subject:welcome').then(data => console.log(data.items));

WebSocket Integration

class MailHogWebSocket {
    constructor(baseUrl = 'ws://localhost:8025') {
        this.baseUrl = baseUrl;
        this.callbacks = {};
    }

    connect() {
        this.ws = new WebSocket(`${this.baseUrl}/api/v1/websocket`);

        this.ws.onmessage = (event) => {
            const data = JSON.parse(event.data);

            if (this.callbacks[data.action]) {
                this.callbacks[data.action](data);
            }
        };

        this.ws.onerror = (error) => {
            console.error('WebSocket error:', error);
        };

        this.ws.onclose = () => {
            console.log('WebSocket connection closed');
            // Auto-reconnect
            setTimeout(() => this.connect(), 5000);
        };
    }

    on(action, callback) {
        this.callbacks[action] = callback;
    }

    onNewMessages(callback) {
        this.on('messages', callback);
    }

    onMessagesDeleted(callback) {
        this.on('deleted', callback);
    }

    onMessagesReleased(callback) {
        this.on('released', callback);
    }
}

// Usage
const ws = new MailHogWebSocket();
ws.connect();

ws.onNewMessages((data) => {
    console.log('New messages received:', data.messages);
});

Python Client Examples

Requests-based Client

import requests
import json
from typing import List, Dict, Optional

class MailHogClient:
    def __init__(self, base_url: str = "http://localhost:8025", auth: tuple = None):
        self.base_url = base_url
        self.auth = auth
        self.api_url = f"{base_url}/api/v1"

    def _request(self, endpoint: str, method: str = "GET", data: dict = None) -> dict:
        url = f"{self.api_url}{endpoint}"
        response = requests.request(
            method,
            url,
            json=data,
            auth=self.auth
        )
        response.raise_for_status()
        return response.json()

    def get_messages(self, limit: int = 50, start: int = 0) -> dict:
        return self._request(f"/messages?limit={limit}&start={start}")

    def get_message(self, message_id: str) -> dict:
        return self._request(f"/messages/{message_id}")

    def search_messages(self, query: str) -> dict:
        return self._request("/search", method="POST", data={"query": query})

    def delete_messages(self, message_ids: str = "all") -> dict:
        if message_ids == "all":
            return self._request("/messages", method="DELETE")
        return self._request(f"/messages/{message_ids}", method="DELETE")

    def release_message(self, message_id: str, recipients: List[str] = None) -> dict:
        data = {"recipients": recipients} if recipients else None
        return self._request(f"/messages/{message_id}/release", method="POST", data=data)

# Usage
client = MailHogClient()

# Get all messages
messages = client.get_messages()
print(f"Total messages: {messages['total']}")

# Search for specific emails
search_results = client.search_messages("subject:welcome")
print(f"Found {search_results['total']} messages")

Testing Examples

Integration Test Example

// Jest/Mocha integration test example
describe('Email Functionality', () => {
    const mailhog = new MailHogClient();

    beforeEach(async () => {
        // Clear all messages before each test
        await mailhog.deleteMessages('all');
    });

    afterEach(async () => {
        // Clean up after each test
        await mailhog.deleteMessages('all');
    });

    it('should send welcome email', async () => {
        // Trigger email sending in your application
        await sendWelcomeEmail('test@example.com');

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

        // Verify email was received
        const messages = await mailhog.searchMessages('to:test@example.com');
        expect(messages.total).toBe(1);

        const message = messages.items[0];
        expect(message.Subject).toContain('Welcome');
        expect(message.From).toContain('noreply@yourapp.com');
    });

    it('should handle bulk email sending', async () => {
        const recipients = ['user1@test.com', 'user2@test.com', 'user3@test.com'];

        // Send bulk emails
        await sendBulkNotifications(recipients);

        // Wait for processing
        await new Promise(resolve => setTimeout(resolve, 3000));

        // Verify all emails were received
        const messages = await mailhog.getMessages();
        expect(messages.total).toBe(recipients.length);

        // Verify recipients
        const recipientAddresses = messages.items.flatMap(msg =>
            msg.To.map(to => to.Address)
        );
        recipients.forEach(recipient => {
            expect(recipientAddresses).toContain(recipient);
        });
    });
});

Load Testing Example

#!/bin/bash
# load_test_mailhog.sh

BASE_URL="http://localhost:8025"
CONCURRENT_REQUESTS=50
TOTAL_REQUESTS=1000

echo "Starting MailHog API load test..."

# Create temp file for results
RESULT_FILE=$(mktemp)

# Run concurrent curl requests
for ((i=1; i<=CONCURRENT_REQUESTS; i++)); do
    {
        for ((j=1; j<=TOTAL_REQUESTS/CONCURRENT_REQUESTS; j++)); do
            start_time=$(date +%s%N)
            curl -s -o /dev/null -w "%{http_code},%{time_total}" \
                "$BASE_URL/api/v1/messages" >> "$RESULT_FILE"
            echo ",$(( ($(date +%s%N) - start_time) / 1000000 ))" >> "$RESULT_FILE"
        done
    } &
done

# Wait for all background processes
wait

# Analyze results
echo "Load test completed. Analyzing results..."
awk -F',' '
{
    status[$1]++
    if ($1 == "200") {
        sum_time += $2
        sum_response += $3
        count++
    }
}
END {
    printf "Success Rate: %.2f%%\n", (status["200"]/NR)*100
    printf "Average Response Time: %.3f ms\n", sum_time/count*1000
    printf "Average Server Response Time: %.3f ms\n", sum_response/count
    for (code in status) {
        printf "HTTP %s: %d requests\n", code, status[code]
    }
}' "$RESULT_FILE"

rm "$RESULT_FILE"

Error Handling

Common HTTP Status Codes

  • 200 OK: Request successful
  • 400 Bad Request: Invalid request parameters
  • 401 Unauthorized: Authentication required
  • 404 Not Found: Message or endpoint not found
  • 500 Internal Server Error: Server error

Error Response Format

{
    "error": "Error message description",
    "code": "ERROR_CODE"
}

Retry Logic Example

async function robustMailHogRequest(client, endpoint, options = {}) {
    const maxRetries = 3;
    const retryDelay = 1000;

    for (let attempt = 1; attempt <= maxRetries; attempt++) {
        try {
            return await client.request(endpoint, options);
        } catch (error) {
            if (attempt === maxRetries) {
                throw error;
            }

            if (error.status >= 500 || error.status === 0) {
                // Retry on server errors or connection issues
                console.log(`Attempt ${attempt} failed, retrying in ${retryDelay}ms...`);
                await new Promise(resolve => setTimeout(resolve, retryDelay));
                continue;
            }

            // Don't retry on client errors
            throw error;
        }
    }
}