Overview
@kubiks/otel-resend provides OpenTelemetry instrumentation for the Resend email service Node.js SDK. Capture spans for every email operation with detailed metadata about recipients, subjects, and delivery status.
Visualize your email operations with detailed span information including recipients, subject lines, and delivery status—without capturing sensitive email content.
Installation
npm install @kubiks/otel-resend
Peer Dependencies: @opentelemetry/api >= 1.9.0, resend >= 3.0.0
Quick Start
import { Resend } from "resend" ;
import { instrumentResend } from "@kubiks/otel-resend" ;
const resend = instrumentResend ( new Resend ( process . env . RESEND_API_KEY ! ));
await resend . emails . send ({
from: "hello@example.com" ,
to: [ "user@example.com" ],
subject: "Welcome" ,
html: "<p>Hello world</p>" ,
});
instrumentResend 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 specifically wraps the resend.emails.send method (and its alias resend.emails.create), creating a single clean span for each email send operation.
Only metadata is captured—email content (HTML, text, attachments) is never included in traces for privacy and security.
Span Attributes
Each span includes rich metadata about the email operation:
Attribute Description Example messaging.systemConstant value resend resendmessaging.operationOperation type sendresend.resourceResource name emailsresend.targetFull operation target emails.sendresend.to_addressesComma-separated TO addresses user@example.com, another@example.comresend.cc_addressesComma-separated CC addresses (if present) cc@example.comresend.bcc_addressesComma-separated BCC addresses (if present) bcc@example.comresend.recipient_countTotal number of recipients 3resend.fromSender email address noreply@example.comresend.subjectEmail subject Welcome to our serviceresend.template_idTemplate ID (if using templates) tmpl_123resend.message_idMessage ID returned by Resend email_123resend.message_countNumber of messages sent (always 1 for single sends) 1
The instrumentation captures email addresses and metadata to help with debugging and monitoring, while avoiding sensitive email content.
Usage Examples
Basic Email
Simple Email
Multiple Recipients
Plain Text Email
import { resend } from "@/lib/resend" ;
await resend . emails . send ({
from: "noreply@example.com" ,
to: "user@example.com" ,
subject: "Welcome to our platform" ,
html: "<h1>Welcome!</h1><p>Thanks for signing up.</p>" ,
});
// Traced with:
// - resend.from: "noreply@example.com"
// - resend.to_addresses: "user@example.com"
// - resend.subject: "Welcome to our platform"
// - resend.recipient_count: 1
With CC and BCC
import { resend } from "@/lib/resend" ;
await resend . emails . send ({
from: "sales@example.com" ,
to: "customer@example.com" ,
cc: [ "manager@example.com" , "team@example.com" ],
bcc: "archive@example.com" ,
subject: "Project Proposal" ,
html: "<p>Please find the proposal attached.</p>" ,
});
// Traced with:
// - resend.to_addresses: "customer@example.com"
// - resend.cc_addresses: "manager@example.com, team@example.com"
// - resend.bcc_addresses: "archive@example.com"
// - resend.recipient_count: 4
BCC addresses are included in the span but remain hidden from other recipients as expected.
Using Email Templates
React Email Template
Resend Template
import { resend } from "@/lib/resend" ;
import { WelcomeEmail } from "@/emails/welcome" ;
await resend . emails . send ({
from: "onboarding@example.com" ,
to: "user@example.com" ,
subject: "Welcome aboard!" ,
react: WelcomeEmail ({ name: "John" }),
});
With Attachments
import { resend } from "@/lib/resend" ;
import fs from "fs" ;
await resend . emails . send ({
from: "documents@example.com" ,
to: "user@example.com" ,
subject: "Your Invoice" ,
html: "<p>Please find your invoice attached.</p>" ,
attachments: [
{
filename: "invoice.pdf" ,
content: fs . readFileSync ( "./invoice.pdf" ),
},
],
});
// Note: Attachment content is NOT captured in traces
Transactional Emails
Password Reset
Email Verification
Order Confirmation
import { resend } from "@/lib/resend" ;
export async function sendPasswordResetEmail ( email : string , token : string ) {
await resend . emails . send ({
from: "security@example.com" ,
to: email ,
subject: "Reset your password" ,
html: `
<h1>Password Reset Request</h1>
<p>Click the link below to reset your password:</p>
<a href="https://example.com/reset?token= ${ token } ">Reset Password</a>
<p>This link expires in 1 hour.</p>
` ,
});
}
Complete Integration Example
Here’s a complete example of Resend with OpenTelemetry in a Next.js application:
import { Resend } from "resend" ;
import { instrumentResend } from "@kubiks/otel-resend" ;
export const resend = instrumentResend (
new Resend ( process . env . RESEND_API_KEY ! )
);
import { resend } from "@/lib/resend" ;
export async function sendWelcomeEmail ( email : string , name : string ) {
try {
const { data , error } = await resend . emails . send ({
from: "onboarding@example.com" ,
to: email ,
subject: `Welcome ${ name } !` ,
html: `
<h1>Welcome to our platform, ${ name } !</h1>
<p>We're excited to have you on board.</p>
` ,
});
if ( error ) {
console . error ( "Failed to send welcome email:" , error );
return { success: false , error };
}
return { success: true , messageId: data ?. id };
} catch ( error ) {
console . error ( "Error sending email:" , error );
return { success: false , error };
}
}
export async function sendNotification (
email : string ,
subject : string ,
message : string
) {
const { data , error } = await resend . emails . send ({
from: "notifications@example.com" ,
to: email ,
subject ,
html: `<p> ${ message } </p>` ,
});
return { data , error };
}
app/api/auth/signup/route.ts
import { NextRequest , NextResponse } from "next/server" ;
import { sendWelcomeEmail } from "@/lib/email" ;
export async function POST ( request : NextRequest ) {
const { email , name } = await request . json ();
// Create user...
// Send welcome email (automatically traced)
const result = await sendWelcomeEmail ( email , name );
if ( ! result . success ) {
return NextResponse . json (
{ error: "Failed to send welcome email" },
{ status: 500 }
);
}
return NextResponse . json ({
success: true ,
messageId: result . messageId
});
}
"use server" ;
import { resend } from "@/lib/resend" ;
export async function sendContactFormEmail (
name : string ,
email : string ,
message : string
) {
const { data , error } = await resend . emails . send ({
from: "contact@example.com" ,
to: "support@example.com" ,
replyTo: email ,
subject: `Contact Form: ${ name } ` ,
html: `
<h2>New Contact Form Submission</h2>
<p><strong>Name:</strong> ${ name } </p>
<p><strong>Email:</strong> ${ email } </p>
<p><strong>Message:</strong></p>
<p> ${ message } </p>
` ,
});
if ( error ) {
return { success: false , error: error . message };
}
return { success: true , messageId: data ?. id };
}
Best Practices
Use Environment Variables
Always store API keys in environment variables: const resend = instrumentResend (
new Resend ( process . env . RESEND_API_KEY ! )
);
Never commit API keys to version control.
Always check for errors when sending emails: const { data , error } = await resend . emails . send ({
from: "noreply@example.com" ,
to: email ,
subject: "Test" ,
html: "<p>Test</p>" ,
});
if ( error ) {
console . error ( "Email error:" , error );
// Handle error appropriately
return ;
}
console . log ( "Email sent:" , data ?. id );
Set up domain verification in Resend for production: // Use your verified domain
from : "noreply@yourdomain.com"
// Not: "noreply@example.com"
Be mindful of Resend rate limits and implement appropriate rate limiting: import { Ratelimit } from "@upstash/ratelimit" ;
import { Redis } from "@upstash/redis" ;
const ratelimit = new Ratelimit ({
redis: Redis . fromEnv (),
limiter: Ratelimit . slidingWindow ( 10 , "1 h" ), // 10 emails per hour
});
export async function sendEmail ( to : string , subject : string , html : string ) {
const { success } = await ratelimit . limit ( to );
if ( ! success ) {
throw new Error ( "Rate limit exceeded" );
}
return await resend . emails . send ({
from: "noreply@example.com" ,
to ,
subject ,
html ,
});
}
Use React Email or Resend templates for maintainable email content: import { WelcomeEmail } from "@/emails/welcome" ;
await resend . emails . send ({
from: "onboarding@example.com" ,
to: email ,
subject: "Welcome!" ,
react: WelcomeEmail ({ name: userName }),
});
Troubleshooting
Ensure OpenTelemetry is properly configured: import { NodeSDK } from "@opentelemetry/sdk-node" ;
const sdk = new NodeSDK ({
// ... configuration
});
sdk . start ();
The message ID is only available after successful email sending. Check for errors: const { data , error } = await resend . emails . send ({ ... });
if ( error ) {
console . error ( "No message ID because of error:" , error );
} else {
console . log ( "Message ID:" , data ?. id );
}
Check your Resend dashboard for delivery status. Common issues:
Domain not verified
Invalid recipient address
Rate limits exceeded
API key issues
Integration with React Email
Email Component
Send Email
// emails/welcome.tsx
import {
Body ,
Container ,
Head ,
Heading ,
Html ,
Link ,
Preview ,
Text ,
} from "@react-email/components" ;
interface WelcomeEmailProps {
name : string ;
}
export function WelcomeEmail ({ name } : WelcomeEmailProps ) {
return (
< Html >
< Head />
< Preview > Welcome to our platform !</ Preview >
< Body style = { main } >
< Container style = { container } >
< Heading style = { h1 } > Welcome , { name } !</ Heading >
< Text style = { text } >
Thanks for joining us . We 're excited to have you on board .
</ Text >
< Link href = "https://example.com/getting-started" style = { link } >
Get Started
</ Link >
</ Container >
</ Body >
</ Html >
);
}
const main = { backgroundColor: "#f6f9fc" , fontFamily: "sans-serif" };
const container = { margin: "0 auto" , padding: "20px 0 48px" };
const h1 = { fontSize: "32px" , fontWeight: "bold" };
const text = { fontSize: "16px" , lineHeight: "26px" };
const link = { color: "#5e6ad2" , textDecoration: "underline" };
Resources
Resend Documentation Learn more about Resend
GitHub Repository View source code and examples
npm Package View package on npm
React Email Build emails with React components
License
MIT