Skip to main content

On Receive (Streaming)

The onReceive method provides a persistent connection to our event stream with typed callbacks for each event type. Unlike webhooks, you don’t need to expose a public URL or handle signature verification.
This is the recommended way to handle reply-to-bot flows, chat interfaces, or real-time dashboards.

Zero-Config Usage

Just pass the callbacks you need. No cursor tracking, no deduplication logic — we handle it all server-side.
import { createClient } from '@msgmorph/sdk'

const client = createClient({ apiKey: 'em_...' })

// Listen for replies - events are auto-acknowledged after your handler completes
client.onReceive({
  onReplied: (reply) => {
    console.log('Reply from:', reply.from)
    console.log('Message:', reply.body)
  }
})

How It Works

Behind the scenes, onReceive:
  • Server-side tracking: Uses your API key to track which events you’ve processed
  • Automatic acknowledgment: Events are acknowledged after your handler completes
  • Replay on reconnect: Unacknowledged events are replayed if your app disconnects
  • No duplicates: You never see the same event twice
This means if your server crashes mid-processing, you’ll get those events again on restart. No data loss.

All Event Handlers

const controller = client.onReceive({
  batchSize: 10,      // Optional: events per batch

  onSent: (event) => console.log('Sent:', event.subject),
  onDelivered: (event) => console.log('Delivered to:', event.recipients),
  onReplied: (event) => console.log('Reply:', event.body),
  onBounced: (event) => console.log('Bounced:', event.bounceType),
  onFailed: (event) => console.log('Failed:', event.error),
  onComplained: (event) => console.log('Spam complaint'),
  onRejected: (event) => console.log('Rejected:', event.reason),
  onDelayed: (event) => console.log('Delayed:', event.delayType),

  onError: (err) => console.error('Stream error:', err)
})

// Stop listening when done
controller.abort()

Event Types

onSent
function
Called when an email is successfully queued for sending.
onDelivered
function
Called when the receiving mail server confirms delivery.
onReplied
function
Called when a recipient replies to your email.
onBounced
function
Called when an email bounces (hard or soft bounce).
onFailed
function
Called when sending fails permanently.
onComplained
function
Called when a recipient marks your email as spam.

Manual Acknowledgment

For critical workflows where you need to ensure database writes complete before acknowledging:
client.onReceive({
  ackMode: 'manual',  // Don't auto-ack
  
  onReplied: async (event) => {
    await saveToDatabase(event)  // Make sure this completes
    // Note: Manual ack not yet implemented via SDK
    // Server will replay unacked events on next connect
  }
})
For most use cases, auto-ack (the default) is recommended. It provides exactly-once semantics for well-behaved handlers.

Resiliency

The stream automatically handles keep-alives (heartbeats). If the connection drops:
  1. Your onError callback fires
  2. SDK auto-reconnects with exponential backoff
  3. Server replays any unacknowledged events
  4. Your handlers process them normally