Overview
When emails arrive at your configured addresses, Inbound sends a webhook to your endpoint with the complete email data. SDK v3.0.0 provides improved TypeScript types and streamlined reply handling.Webhook Payload Structure
Copy
import type { InboundWebhookPayload } from '@inboundemail/sdk'
const payload: InboundWebhookPayload = {
event: 'email.received',
timestamp: '2024-01-15T10:30:00Z',
email: {
id: 'email_abc123',
messageId: '<unique@example.com>',
from: {
text: 'John Doe <john@example.com>',
addresses: [{
name: 'John Doe',
address: 'john@example.com'
}]
},
to: {
text: 'support@yourdomain.com',
addresses: [{
name: null,
address: 'support@yourdomain.com'
}]
},
recipient: 'support@yourdomain.com',
subject: 'Help with my order',
receivedAt: '2024-01-15T10:30:00Z',
parsedData: { /* ... */ },
cleanedContent: {
html: '<p>Hello...</p>',
text: 'Hello...',
hasHtml: true,
hasText: true,
attachments: [],
headers: { /* ... */ }
}
},
endpoint: {
id: 'endp_xyz789',
name: 'Support Webhook',
type: 'webhook'
}
}
Next.js App Router
Copy
import { NextRequest, NextResponse } from 'next/server'
import { Inbound, isInboundWebhook } from '@inboundemail/sdk'
import type { InboundWebhookPayload } from '@inboundemail/sdk'
const inbound = new Inbound(process.env.INBOUND_API_KEY!)
export async function POST(request: NextRequest) {
try {
const payload: InboundWebhookPayload = await request.json()
// Validate webhook payload
if (!isInboundWebhook(payload)) {
return NextResponse.json(
{ error: 'Invalid webhook payload' },
{ status: 400 }
)
}
const { email } = payload
// Process the email
console.log(`New email from: ${email.from?.text}`)
console.log(`Subject: ${email.subject}`)
console.log(`Body: ${email.cleanedContent.text}`)
// Auto-reply example (v3.0.0 syntax - explicit from required)
if (email.subject?.toLowerCase().includes('urgent')) {
await inbound.reply(email, {
from: 'support@yourdomain.com',
text: 'We received your urgent request and will respond within 1 hour.',
tags: [
{ name: 'priority', value: 'urgent' },
{ name: 'type', value: 'auto-reply' }
]
})
}
return NextResponse.json({ success: true })
} catch (error) {
console.error('Webhook error:', error)
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}
Express.js
Copy
import express from 'express'
import { Inbound, isInboundWebhook } from '@inboundemail/sdk'
import type { InboundWebhookPayload } from '@inboundemail/sdk'
const app = express()
app.use(express.json())
const inbound = new Inbound(process.env.INBOUND_API_KEY!)
app.post('/webhook', async (req, res) => {
const payload: InboundWebhookPayload = req.body
if (!isInboundWebhook(payload)) {
return res.status(400).json({ error: 'Invalid webhook payload' })
}
const { email } = payload
// Log email details
console.log('New email:', {
from: email.from?.text,
subject: email.subject,
hasAttachments: email.cleanedContent.attachments.length > 0
})
// Reply to the email (v3.0.0 syntax)
await inbound.reply(email, {
from: 'support@yourdomain.com',
html: '<p>Thanks for your email! We\'ll get back to you soon.</p>',
text: 'Thanks for your email! We\'ll get back to you soon.',
tags: [{ name: 'type', value: 'auto-reply' }]
})
res.json({ success: true })
})
Webhook Headers
Inbound sends these headers with each webhook request:Copy
interface InboundWebhookHeaders {
'content-type': 'application/json'
'user-agent': 'InboundEmail-Webhook/1.0'
'x-webhook-event': 'email.received'
'x-endpoint-id': string // Your endpoint ID
'x-webhook-timestamp': string // ISO timestamp
'x-email-id': string // Email ID
'x-message-id': string // Email Message-ID
}
Helper Functions
The SDK provides utility functions for common tasks:Copy
import {
getEmailText,
getEmailHtml,
getSenderInfo,
getRecipientAddresses,
getAttachmentInfo
} from '@inboundemail/sdk'
// Extract text content
const textContent = getEmailText(email)
// Extract HTML content
const htmlContent = getEmailHtml(email)
// Get sender details
const { name, address } = getSenderInfo(email)
// Get all recipients
const recipients = getRecipientAddresses(email)
// Check attachment types
email.cleanedContent.attachments.forEach(attachment => {
const info = getAttachmentInfo(attachment)
if (info.isImage) {
console.log('Image attachment:', info.filename)
}
})
Error Handling
Always respond with 2xx status codes for successfully received webhooks, even if processing fails. Return error status codes only for invalid payloads.
Copy
export async function POST(request: Request) {
try {
const payload = await request.json()
if (!isInboundWebhook(payload)) {
// Invalid payload - return 400
return new Response('Invalid payload', { status: 400 })
}
try {
await processEmail(payload.email)
} catch (processingError) {
// Log error but return success
console.error('Processing failed:', processingError)
await notifyAdmins(processingError)
}
// Always return success for valid webhooks
return new Response('OK', { status: 200 })
} catch (error) {
// JSON parsing failed
return new Response('Bad request', { status: 400 })
}
}
Testing Webhooks
Local Development with ngrok
Copy
# Install ngrok
npm install -g ngrok
# Start your local server
npm run dev
# In another terminal, expose your local server
ngrok http 3000
# Use the ngrok URL for your webhook endpoint
# https://abc123.ngrok.io/api/webhook
Test Webhook Payload
Copy
// test-webhook.ts
import { InboundWebhookPayload } from '@inboundemail/sdk'
const testPayload: InboundWebhookPayload = {
event: 'email.received',
timestamp: new Date().toISOString(),
email: {
id: 'test_email_123',
messageId: '<test@example.com>',
from: {
text: 'Test User <test@example.com>',
addresses: [{
name: 'Test User',
address: 'test@example.com'
}]
},
to: {
text: 'webhook@yourdomain.com',
addresses: [{
name: null,
address: 'webhook@yourdomain.com'
}]
},
recipient: 'webhook@yourdomain.com',
subject: 'Test Email',
receivedAt: new Date().toISOString(),
parsedData: {
messageId: '<test@example.com>',
date: new Date(),
subject: 'Test Email',
from: null,
to: null,
cc: null,
bcc: null,
replyTo: null,
inReplyTo: undefined,
references: undefined,
textBody: 'This is a test email.',
htmlBody: '<p>This is a test email.</p>',
attachments: [],
headers: {},
priority: undefined
},
cleanedContent: {
html: '<p>This is a test email.</p>',
text: 'This is a test email.',
hasHtml: true,
hasText: true,
attachments: [],
headers: {}
}
},
endpoint: {
id: 'test_endpoint_123',
name: 'Test Webhook',
type: 'webhook'
}
}
// Send test webhook
fetch('http://localhost:3000/api/webhook', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-webhook-event': 'email.received'
},
body: JSON.stringify(testPayload)
})