Files
drizzle-otel/packages/otel-inbound/README.md
Alex Holovach 9efbe90106 update
2025-10-21 10:30:13 -05:00

9.0 KiB

@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.

Inbound Trace Visualization

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 email
  • emails.schedule() - Schedule email for later delivery
  • emails.reply() - Reply to an existing email thread
  • emails.retrieve() - Retrieve email details
  • emails.listScheduled() - List scheduled emails
  • emails.getScheduled() - Get specific scheduled email
  • emails.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