
Product
Reachability for Ruby Now in Beta
Reachability analysis for Ruby is now in beta, helping teams identify which vulnerabilities are truly exploitable in their applications.
The official SDK for the Inbound Email API v2. This SDK provides a simple and intuitive hierarchical interface for managing email receiving, sending, domains, email addresses, and webhook endpoints.
Version 4.0.0 introduces a new hierarchical structure with inbound.email.address.* methods and consistent { data, error } response patterns.
Choose your preferred package name - both contain identical functionality:
# Full scoped name
npm install @inboundemail/sdk
# Short alias
npm install inbnd
Both packages are published simultaneously and contain the same code. Use whichever name you prefer!
Both package names work identically:
// Using full scoped name
import { Inbound } from '@inboundemail/sdk'
// Or using short alias
import { Inbound } from 'inbnd'
const inbound = new Inbound(process.env.INBOUND_API_KEY!)
// Send an email (with { data, error } pattern)
const { data: email, error } = await inbound.email.send({
from: 'hello@yourdomain.com',
to: 'user@example.com',
subject: 'Hello World',
html: '<h1>Hello World</h1><p>This is your first email!</p>',
})
if (error) {
console.error('Failed to send email:', error)
} else {
console.log('Email sent:', email.id)
}
The SDK now uses a unified email structure under inbound.email.*:
// ๐ง Received Email Management (NEW - unified under email.received)
inbound.email.received.list() // List received emails
inbound.email.received.get(id) // Get specific received email
inbound.email.received.thread(id) // Get email thread
inbound.email.received.markRead(id) // Mark as read
inbound.email.received.archive(id) // Archive email
inbound.email.received.reply(params) // Reply to received email
// ๐ค Sent Email Management (NEW - organized under email.sent)
inbound.email.send(data) // Send email immediately
inbound.email.schedule(data) // Schedule email
inbound.email.sent.get(id) // Get sent email by ID
inbound.email.sent.reply(id, data) // Reply to sent email
inbound.email.sent.listScheduled() // List scheduled emails
inbound.email.sent.getScheduled(id) // Get specific scheduled email
inbound.email.sent.cancel(id) // Cancel scheduled email
// ๐ Universal Email Access
inbound.email.get(id) // Get ANY email (received or sent)
// ๐ฎ Email Address Management (nested under email)
inbound.email.address.create(data) // Create email address
inbound.email.address.list() // List email addresses
inbound.email.address.get(id) // Get address details
inbound.email.address.update(id, data) // Update address routing
inbound.email.address.delete(id) // Remove address
// ๐ Domain Management
inbound.domain.create(data) // Add new domain
inbound.domain.list() // List all domains
inbound.domain.verify(id) // Verify domain
inbound.domain.getDnsRecords(id) // Get DNS records
// ๐ Endpoint Management (Webhooks & Forwarding)
inbound.endpoint.create(data) // Create endpoint
inbound.endpoint.list() // List endpoints
inbound.endpoint.test(id) // Test endpoint
The old inbound.mail.* methods are deprecated but still work with console warnings:
// โ DEPRECATED - Will be removed in v6.0.0
inbound.mail.list() // Use inbound.email.received.list() instead
inbound.mail.get(id) // Use inbound.email.received.get() instead
inbound.mail.markRead(id) // Use inbound.email.received.markRead() instead
// ... etc
All methods now return a consistent { data, error } pattern:
// Success case
const { data, error } = await inbound.mail.list()
if (error) {
console.error('Error:', error)
return
}
console.log('Emails:', data.emails)
// Or with destructuring
const { data: emails, error: emailsError } = await inbound.mail.list()
const { data: domains, error: domainsError } = await inbound.domain.list()
The SDK includes a streamlined reply() method that makes it easy to reply to emails directly from webhook handlers:
// Works with either package name
import { Inbound, type InboundWebhookPayload, isInboundWebhook } from '@inboundemail/sdk'
// import { Inbound, type InboundWebhookPayload, isInboundWebhook } from 'inbnd'
import { NextRequest, NextResponse } from 'next/server'
const inbound = new Inbound(process.env.INBOUND_API_KEY!)
export async function POST(request: NextRequest) {
const payload: InboundWebhookPayload = await request.json()
if (!isInboundWebhook(payload)) {
return NextResponse.json({ error: 'Invalid webhook' }, { status: 400 })
}
const { email } = payload
// Reply to emails with new { data, error } pattern
const { data, error } = await inbound.reply(email, {
from: 'support@yourdomain.com',
text: 'Thanks for your message! We\'ll get back to you soon.'
})
if (error) {
console.error('Reply failed:', error)
return NextResponse.json({ error }, { status: 500 })
}
return NextResponse.json({ success: true, messageId: data.messageId })
}
The new hierarchical structure makes email address management more intuitive:
// List all email addresses
const { data: addresses, error } = await inbound.email.address.list()
// Create a new email address
const { data: newAddress, error: createError } = await inbound.email.address.create({
address: 'support@yourdomain.com',
domainId: 'domain-123'
})
// Update routing for an email address
const { data: updated, error: updateError } = await inbound.email.address.update('address-123', {
endpointId: 'webhook-456',
isActive: true
})
// Delete an email address
const { data: deleted, error: deleteError } = await inbound.email.address.delete('address-123')
// Create and verify a domain
const { data: domain, error } = await inbound.domain.create({
domain: 'yourdomain.com'
})
if (!error) {
// Get DNS records needed for verification
const { data: dnsRecords } = await inbound.domain.getDnsRecords(domain.id)
console.log('Add these DNS records:', dnsRecords)
// Verify domain after DNS setup
const { data: verification } = await inbound.domain.verify(domain.id)
}
// Create a webhook endpoint
const { data: webhook, error } = await inbound.endpoint.create({
name: 'My Webhook',
type: 'webhook',
config: {
url: 'https://yourapp.com/webhook',
timeout: 30000,
retryAttempts: 3
}
})
// Test the endpoint
if (!error) {
const { data: testResult } = await inbound.endpoint.test(webhook.id)
console.log('Test result:', testResult)
}
// Quick reply to an email
const { data, error } = await inbound.quickReply(
'email-123',
'Thanks for your message!',
'support@yourdomain.com',
{ idempotencyKey: 'quick-reply-123' }
)
// One-step domain setup with webhook
const { data: setup } = await inbound.setupDomain(
'newdomain.com',
'https://yourapp.com/webhook'
)
// Create email forwarder
const { data: forwarder } = await inbound.createForwarder(
'info@yourdomain.com',
'team@yourdomain.com'
)
// Schedule a reminder
const { data: reminder } = await inbound.scheduleReminder(
'user@example.com',
'Meeting Tomorrow',
'tomorrow at 9am',
'reminders@yourdomain.com',
{ idempotencyKey: 'reminder-meeting-456' }
)
All previous method names still work for backwards compatibility:
// These are equivalent:
inbound.email === inbound.emails
inbound.domain === inbound.domains
inbound.endpoint === inbound.endpoints
inbound.email.address === inbound.emailAddresses
// Legacy usage still works:
const { data } = await inbound.emails.send(emailData)
const { data } = await inbound.domains.list()
const { data: email, error } = await inbound.email.send({
from: 'hello@yourdomain.com',
to: ['user@example.com', 'admin@example.com'],
subject: 'Welcome!',
html: '<h1>Welcome to our service!</h1>',
text: 'Welcome to our service!',
attachments: [
{
filename: 'welcome.pdf',
path: './welcome.pdf'
}
]
})
const { data: scheduled, error } = await inbound.email.schedule({
from: 'hello@yourdomain.com',
to: 'user@example.com',
subject: 'Scheduled Email',
html: '<p>This email was scheduled!</p>',
scheduled_at: 'in 1 hour', // Natural language
timezone: 'America/New_York'
})
// Or with specific date
const { data: scheduled2 } = await inbound.email.schedule({
from: 'hello@yourdomain.com',
to: 'user@example.com',
subject: 'New Year Email',
html: '<p>Happy New Year!</p>',
scheduled_at: '2024-01-01T00:00:00Z' // ISO 8601
})
// List scheduled emails
const { data: scheduledEmails } = await inbound.email.listScheduled({
status: 'scheduled',
limit: 10
})
// Get specific scheduled email
const { data: scheduledEmail } = await inbound.email.getScheduled('email-id')
// Cancel scheduled email
const { data: cancelled } = await inbound.email.cancel('email-id')
// List received emails
const { data: emails } = await inbound.mail.list({
limit: 50,
status: 'processed',
timeRange: '7d'
})
// Get specific email
const { data: email } = await inbound.mail.get('email-123')
// Get email thread/conversation
const { data: thread } = await inbound.mail.thread('email-123')
// Mark email as read/unread
await inbound.mail.markRead('email-123')
await inbound.mail.markUnread('email-123')
// Archive/unarchive emails
await inbound.mail.archive('email-123')
await inbound.mail.unarchive('email-123')
// Bulk operations
const { data: result } = await inbound.mail.bulk(
['email-1', 'email-2', 'email-3'],
{ isRead: true }
)
import { EmailTemplate } from './EmailTemplate'
const { data, error } = await inbound.email.send({
from: 'hello@yourdomain.com',
to: 'user@example.com',
subject: 'Welcome!',
react: EmailTemplate({ name: 'John', welcomeUrl: 'https://app.com' })
})
Prevent duplicate emails by using idempotency keys:
const { data, error } = await inbound.email.send({
from: 'hello@yourdomain.com',
to: 'user@example.com',
subject: 'Important Email',
text: 'This email will only be sent once'
}, {
idempotencyKey: 'unique-key-123'
})
// Works with all email sending methods
await inbound.email.schedule({
from: 'hello@yourdomain.com',
to: 'user@example.com',
subject: 'Scheduled Email',
text: 'This scheduled email is idempotent',
scheduled_at: 'tomorrow at 9am'
}, {
idempotencyKey: 'scheduled-email-456'
})
// Also works with replies
await inbound.email.reply('email-123', {
from: 'support@yourdomain.com',
text: 'This reply will only be sent once'
}, {
idempotencyKey: 'reply-789'
})
const { data, error } = await inbound.email.send(emailData)
if (error) {
// Handle different error types
if (error.includes('Invalid API key')) {
console.error('Authentication failed')
} else if (error.includes('Rate limit')) {
console.error('Rate limit exceeded')
} else {
console.error('Unknown error:', error)
}
return
}
// Success case
console.log('Email sent successfully:', data.id)
The SDK is fully typed with TypeScript:
// Type imports work with either package name
import type {
ApiResponse,
PostEmailsRequest,
PostEmailsResponse,
InboundWebhookPayload
} from '@inboundemail/sdk'
// } from 'inbnd'
// Type-safe email sending
const emailRequest: PostEmailsRequest = {
from: 'hello@yourdomain.com',
to: 'user@example.com',
subject: 'Typed Email',
html: '<p>This is type-safe!</p>'
}
const response: ApiResponse<PostEmailsResponse> = await inbound.email.send(emailRequest)
MIT License - see LICENSE file for details.
FAQs
Official SDK for Inbound Email API
We found that inbnd demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.ย It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
Reachability analysis for Ruby is now in beta, helping teams identify which vulnerabilities are truly exploitable in their applications.

Research
/Security News
Malicious npm packages use Adspect cloaking and fake CAPTCHAs to fingerprint visitors and redirect victims to crypto-themed scam sites.

Security News
Recent coverage mislabels the latest TEA protocol spam as a worm. Hereโs whatโs actually happening.