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 ModeDefault
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);
},
});Distributed Mode Requirements
Event Types
| Event | Description |
|---|---|
| connected | Connection established successfully (includes mode and connectionId) |
| email.received | A new email has been received and processed |
| email.updated | An email was updated (e.g., marked as read) |
| heartbeat | Keep-alive ping sent every 30 seconds |
API Endpoint
GET/v1/realtime/events
Subscribe to push notifications via SSE
Query Parameters
| Parameter | Description |
|---|---|
| token | API key for authentication (required for EventSource) |
| mode | Connection mode: broadcast (default) or distributed |
Example Request
GET /api/v1/realtime/events?token=YOUR_API_KEY&mode=broadcastResponse 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
| Field | Type | Description |
|---|---|---|
| id | string | Unique email ID |
| from | string | Sender email address |
| to | string[] | Recipient email addresses |
| subject | string | Email subject line |
| domain | string | Domain that received the email |
| createdAt | string | ISO 8601 timestamp |
| hasAttachments | boolean | Whether email has attachments |
| attachmentCount | number | Number of attachments |
Connected Payload
| Field | Type | Description |
|---|---|---|
| tenantId | string | Your account/tenant ID |
| connectedAt | string | ISO 8601 timestamp |
| mode | string | 'broadcast' or 'distributed' |
| connectionId | string | Unique ID for this connection |
Best Practices
Handle Reconnection
onDisconnect callback to ensure your application stays connected during network issues.Choose the Right Mode
broadcast for dashboards where all clients need updates. Use distributed for worker pools to avoid duplicate processing.Clean Up Connections
subscription.disconnect() when your component unmounts or when the user navigates away to free up resources.Use with React Query
onEmailReceived callback to automatically refresh your UI when new emails arrive.