@kubiks/otel-inbound
OpenTelemetry instrumentation for the Inbound email API SDK. Capture spans for every Inbound API operation, enrich them with detailed metadata, and monitor your complete email workflow from traces.
Visualize your email operations with detailed span information including recipients, subjects, scheduling, and webhook processing.
Installation
npm install @kubiks/otel-inbound
# or
pnpm add @kubiks/otel-inbound
Peer Dependencies: @opentelemetry/api >= 1.9.0, @inboundemail/sdk >= 4.0.0
Quick Start
import { Inbound } from "@inboundemail/sdk";
import { instrumentInbound } from "@kubiks/otel-inbound";
const inbound = instrumentInbound(new Inbound(process.env.INBOUND_API_KEY!));
await inbound.emails.send({
from: "hello@example.com",
to: ["user@example.com"],
subject: "Welcome",
html: "<p>Hello world</p>",
});
instrumentInbound wraps the instance you already use — no configuration changes
needed. Every SDK call creates a client span with useful attributes.
What Gets Traced
This instrumentation wraps all Inbound API operations, creating spans for each:
Email Operations
emails.send()- Send emailemails.schedule()- Schedule email for later deliveryemails.reply()- Reply to an existing email threademails.retrieve()- Retrieve email detailsemails.listScheduled()- List scheduled emailsemails.getScheduled()- Get specific scheduled emailemails.cancelScheduled()- Cancel a scheduled email
Management Operations
- Endpoints:
list(),create(),get(),update(),delete() - Addresses:
list(),create(),get(),update(),delete() - Domains:
list(),create(),get(),update(),delete(),getDNS()
Thread & Attachment Operations
- Threads:
list(),get(),actions(),statistics() - Attachments:
download()
Webhook Receivers
- Incoming email webhooks (via
instrumentInboundWebhook)
Span Attributes
Each span includes relevant attributes based on the operation type:
Base Attributes (All Operations)
| Attribute | Description | Example |
|---|---|---|
messaging.system |
Constant value inbound |
inbound |
messaging.operation |
Operation type | send, schedule, etc. |
inbound.resource |
Resource being accessed | emails, endpoints |
inbound.target |
Full operation target | emails.send |
Email Attributes
| Attribute | Description | Example |
|---|---|---|
inbound.message_id |
Message ID returned by Inbound | msg_123 |
inbound.to_addresses |
Comma-separated TO addresses | user@example.com, another@example.com |
inbound.cc_addresses |
Comma-separated CC addresses | cc@example.com |
inbound.bcc_addresses |
Comma-separated BCC addresses | bcc@example.com |
inbound.recipient_count |
Total number of recipients | 3 |
inbound.from |
Sender email address | noreply@example.com |
inbound.subject |
Email subject line | Welcome to our service |
inbound.html_content |
HTML content (if capture enabled) | <p>Hello</p> |
inbound.text_content |
Text content (if capture enabled) | Hello |
Scheduling Attributes
| Attribute | Description | Example |
|---|---|---|
inbound.scheduled_at |
Scheduled delivery time | 2025-01-01T00:00:00Z |
inbound.schedule_id |
Schedule ID from API | sched_123 |
Management Attributes
| Attribute | Description | Example |
|---|---|---|
inbound.endpoint_id |
Endpoint identifier | ep_123 |
inbound.domain_id |
Domain identifier | dom_123 |
inbound.address_id |
Email address identifier | addr_123 |
Thread & Attachment Attributes
| Attribute | Description | Example |
|---|---|---|
inbound.thread_id |
Email thread identifier | thread_123 |
inbound.attachment_id |
Attachment identifier | attach_123 |
Webhook Attributes
| Attribute | Description | Example |
|---|---|---|
inbound.webhook_id |
Webhook identifier from header | webhook_123 |
http.status_code |
HTTP response status code | 200 |
Advanced Usage
Webhook Receiver Instrumentation
Instrument Next.js route handlers that receive incoming emails:
import { instrumentInboundWebhook } from "@kubiks/otel-inbound";
export const POST = instrumentInboundWebhook(async (request: Request) => {
const email = await request.json();
// Process incoming email
console.log('Received email from:', email.from);
console.log('Subject:', email.subject);
// Your email processing logic here
await processIncomingEmail(email);
return Response.json({ success: true });
});
This creates SERVER spans (SpanKind.SERVER) that automatically capture:
- Email metadata from webhook payload
- Webhook headers
- Response status
- Any errors during processing
Configuration Options
Control what data is captured in your spans:
import { instrumentInbound, type InstrumentInboundConfig } from "@kubiks/otel-inbound";
const config: InstrumentInboundConfig = {
// Capture email HTML/text content in spans (default: false)
captureEmailContent: true,
// Maximum content length before truncation (default: 1024)
maxContentLength: 2048,
};
const inbound = instrumentInbound(
new Inbound(process.env.INBOUND_API_KEY!),
config
);
Note: Be cautious when enabling captureEmailContent as it may capture sensitive information in your traces.
Scheduling Emails
await inbound.emails.schedule({
from: "noreply@example.com",
to: "user@example.com",
subject: "Scheduled Newsletter",
html: "<p>Weekly update</p>",
scheduledAt: "2025-01-01T09:00:00Z",
});
// List all scheduled emails
const scheduled = await inbound.emails.listScheduled();
// Cancel a scheduled email
await inbound.emails.cancelScheduled("sched_123");
Reply to Emails
await inbound.emails.reply({
from: "support@example.com",
to: "customer@example.com",
subject: "Re: Support Request",
html: "<p>Thanks for reaching out!</p>",
threadId: "thread_123", // Thread ID from webhook payload
});
Domain Management
// Create a domain
const domain = await inbound.domains.create({
domain: "yourdomain.com",
});
// Get DNS records for verification
const dns = await inbound.domains.getDNS(domain.data.id);
console.log("Add these DNS records:", dns.data.records);
// List all domains
const domains = await inbound.domains.list();
Endpoint Management
// Create webhook endpoint
const endpoint = await inbound.endpoints.create({
url: "https://yourdomain.com/webhook",
events: ["email.received"],
});
// Update endpoint
await inbound.endpoints.update(endpoint.data.id, {
url: "https://yourdomain.com/new-webhook",
});
// Delete endpoint
await inbound.endpoints.delete(endpoint.data.id);
Complete Example with Webhook
import { Inbound } from "@inboundemail/sdk";
import { instrumentInbound, instrumentInboundWebhook } from "@kubiks/otel-inbound";
// Instrument the Inbound client
const inbound = instrumentInbound(
new Inbound(process.env.INBOUND_API_KEY!),
{ captureEmailContent: true }
);
// Send an email
await inbound.emails.send({
from: "hello@yourdomain.com",
to: "user@example.com",
subject: "Welcome!",
html: "<p>Thanks for signing up!</p>",
});
// Webhook handler for receiving emails
export const POST = instrumentInboundWebhook(
async (request: Request) => {
const email = await request.json();
// Automatically reply to incoming emails
await inbound.emails.reply({
from: email.to,
to: email.from,
subject: `Re: ${email.subject}`,
html: "<p>Thanks for your email! We'll get back to you soon.</p>",
threadId: email.threadId,
});
return Response.json({ processed: true });
},
{ captureEmailContent: true }
);
License
MIT
