Push Notifications

Get instant push notifications when emails arrive via Server-Sent Events (SSE).

Overview

Push notifications use Server-Sent Events (SSE) to deliver email updates to your application instantly. Unlike webhooks that require a public endpoint, SSE works by establishing a persistent connection from your client to our servers.

Instant Updates

Receive notifications within milliseconds of email arrival.

Simple API

Uses the standard EventSource API available in all browsers.

Auto-reconnect

SDK handles reconnection automatically on connection drops.

When to Use Push Notifications

Push notifications via SSE are ideal when webhooks aren't practical:

Client-side Apps

Browser and mobile apps can't expose webhook URLs. SSE provides real-time updates directly to the client.

Firewalled Environments

No inbound traffic allowed but outbound works? SSE establishes outbound connections that receive updates.

Local Development

No need for ngrok or tunneling. SSE connections work directly from your local machine.

Connection Modes

Choose how notifications are delivered to your connected clients:

Broadcast Mode
Default

All connected clients receive every event

Best for dashboards, monitoring, and scenarios where all clients need to see every notification.

// Default mode - all clients receive every event
mailhooks.realtime.subscribe({
  mode: 'broadcast', // optional, this is the default
  onEmailReceived: (email) => {
    updateDashboard(email);
  },
});

Distributed Mode

One random client per API key receives each event

Best for worker pools and load balancing. When multiple workers connect with the same API key, each notification is delivered to only one worker.

// Distributed mode - load balance across workers
mailhooks.realtime.subscribe({
  mode: 'distributed',
  onEmailReceived: async (email) => {
    // Only ONE worker receives this
    await processEmail(email.id);
  },
});

Event Types

EventDescription
connectedConnection established successfully (includes mode and connectionId)
email.receivedA new email has been received and processed
email.updatedAn email was updated (e.g., marked as read)
heartbeatKeep-alive ping sent every 30 seconds

API Endpoint

GET
/v1/realtime/events

Subscribe to push notifications via SSE

Query Parameters

ParameterDescription
tokenAPI key for authentication (required for EventSource)
modeConnection mode: broadcast (default) or distributed

Example Request

GET /api/v1/realtime/events?token=YOUR_API_KEY&mode=broadcast

Response Format

Each event is sent as a JSON object:

{
  "type": "email.received",
  "timestamp": "2024-01-15T10:30:00.000Z",
  "data": {
    "id": "email_abc123",
    "from": "[email protected]",
    "to": ["[email protected]"],
    "subject": "Hello World",
    "domain": "yourapp.mailhooks.dev",
    "createdAt": "2024-01-15T10:30:00.000Z",
    "hasAttachments": false,
    "attachmentCount": 0
  }
}

SDK Usage

Basic Example

import { Mailhooks } from '@mailhooks/sdk';

const mailhooks = new Mailhooks({ apiKey: 'mh_xxx' });

// Subscribe to push notifications
const subscription = mailhooks.realtime.subscribe({
  onEmailReceived: (email) => {
    console.log('New email from:', email.from);
    console.log('Subject:', email.subject);
  },
  onConnected: (payload) => {
    console.log('Connected!', payload.connectionId);
    console.log('Mode:', payload.mode);
  },
  onError: (error) => {
    console.error('Connection error:', error);
  },
});

// Later, disconnect when done
subscription.disconnect();

Distributed Mode (Worker Load Balancing)

// Each worker connects with the same API key
// Only ONE worker receives each notification
const subscription = mailhooks.realtime.subscribe({
  mode: 'distributed',
  onEmailReceived: async (email) => {
    // Process email - guaranteed only one worker handles this
    await processEmail(email.id);
  },
});

Full Event Handling

const subscription = mailhooks.realtime.subscribe({
  mode: 'broadcast', // or 'distributed'
  onConnected: (payload) => {
    console.log(`Connected: ${payload.connectionId}`);
  },
  onEmailReceived: (email) => {
    console.log(`From: ${email.from}`);
    console.log(`Subject: ${email.subject}`);
  },
  onEmailUpdated: (update) => {
    console.log(`Email ${update.id} updated:`, update.changes);
  },
  onHeartbeat: () => {
    // Sent every 30 seconds to keep connection alive
  },
  onDisconnect: () => {
    console.log('Disconnected - will auto-reconnect');
  },
  onError: (error) => {
    console.error('Error:', error);
  },
  // Options
  autoReconnect: true,      // Default: true
  reconnectDelay: 5000,     // Default: 5000ms
});

// Check connection status
if (subscription.isConnected()) {
  console.log('Currently connected');
}

Node.js Usage

For Node.js environments, install the eventsource polyfill:

npm install eventsource
// At the top of your file
import EventSource from 'eventsource';
(global as any).EventSource = EventSource;

// Now you can use the SDK
import { Mailhooks } from '@mailhooks/sdk';

const mailhooks = new Mailhooks({ apiKey: 'mh_xxx' });
const subscription = mailhooks.realtime.subscribe({
  onEmailReceived: (email) => {
    console.log('New email:', email);
  },
});

Email Received Payload

FieldTypeDescription
idstringUnique email ID
fromstringSender email address
tostring[]Recipient email addresses
subjectstringEmail subject line
domainstringDomain that received the email
createdAtstringISO 8601 timestamp
hasAttachmentsbooleanWhether email has attachments
attachmentCountnumberNumber of attachments

Connected Payload

FieldTypeDescription
tenantIdstringYour account/tenant ID
connectedAtstringISO 8601 timestamp
modestring'broadcast' or 'distributed'
connectionIdstringUnique ID for this connection

Best Practices

Handle Reconnection

Always enable auto-reconnect and handle the onDisconnect callback to ensure your application stays connected during network issues.

Choose the Right Mode

Use broadcast for dashboards where all clients need updates. Use distributed for worker pools to avoid duplicate processing.

Clean Up Connections

Always call subscription.disconnect() when your component unmounts or when the user navigates away to free up resources.

Use with React Query

Invalidate your email queries in the onEmailReceived callback to automatically refresh your UI when new emails arrive.

© 2026 Mailhooks. All rights reserved.