| name | resend-debug |
| description | Debug and troubleshoot Resend email issues. Diagnose rate limiting, webhook failures, domain verification, bounce rates, and authentication errors. Use when emails aren't sending, delivery is slow, webhooks aren't triggering, or bounces are high. |
| license | MIT |
| compatibility | Node.js 18+, RESEND_API_KEY env var, access to email logs |
| metadata | {"version":"1.0.0","parent-skill":"resend-email-integration"} |
Resend Debugging Guide
Resend failures fall into 6 categories. This skill helps you identify and fix yours.
Diagnosis Flow
Answer these questions in order:
Q1: Are emails sending at all?
❌ No emails in Resend dashboard
→ Go to Diagnosis #1: Authentication Failure
✅ Emails show "Sent" but not "Delivered"
→ Go to Diagnosis #2: Domain Verification
✅ Emails deliver but bounce rate > 4%
→ Go to Diagnosis #3: High Bounce Rate
✅ Emails send fine but webhooks don't trigger
→ Go to Diagnosis #4: Webhook Failure
✅ Everything works but requests are slow/rate limited
→ Go to Diagnosis #5: Rate Limiting
✅ Webhook verify fails (signature invalid)
→ Go to Diagnosis #6: Webhook Security
Diagnosis #1: Authentication Failure (Emails Not Sending)
Symptoms:
- Error:
401 missing_api_key or 403 restricted_api_key
- No emails appear in Resend dashboard
- Network request fails immediately
Checklist:
-
Is RESEND_API_KEY set?
echo $RESEND_API_KEY
-
Is the key correct?
-
Is the key expired?
-
Does the key have "Full access"?
-
Are you using the SDK correctly?
const resend = new Resend(process.env.RESEND_API_KEY);
const resend = new Resend('re_xxxxxxxxxxxx');
Fix: Update .env.local, restart server, retry.
Diagnosis #2: Domain Verification (Emails "Sent" but Not Delivered)
Symptoms:
- Resend dashboard shows "Sent" status
- Email never arrives in recipient inbox
- Bounce rate high or no bounces reported
- Error:
validation_error with "domain not verified"
Checklist:
-
Is the "from" domain verified?
-
Are SPF/DKIM records added?
-
Did you wait long enough?
-
Are you sending from the verified domain?
await resend.emails.send({
from: 'notify@yourdomain.com',
to: recipient,
});
await resend.emails.send({
from: 'notify@random-domain.com',
to: recipient,
});
-
Using test domain?
from: 'onboarding@resend.dev',
to: 'delivered@resend.dev',
Fix: Verify domain, add DNS records, wait for propagation, retry.
Diagnosis #3: High Bounce Rate (> 4%)
Symptoms:
- Webhooks report
email.bounced events
- Bounce rate in dashboard > 4%
- Resend notifies you reputation is degraded
- Some emails delivered, some bounced
Checklist:
-
What type of bounces? (check webhook events)
Hard bounce = permanent (invalid address, domain doesn't exist)
Soft bounce = temporary (mailbox full, server busy)
-
Are you adding to suppressions? (critical!)
await resend.contacts.create({
email: event.data.to[0],
unsubscribed: true,
});
-
Are you checking suppressions before sending?
const bounced = await db.emailLog.findFirst({
where: {
to: recipientEmail,
status: 'bounced',
},
});
if (bounced) {
console.log(`Email already bounced, skipping`);
return;
}
-
Is your recipient list clean?
-
Are you validating email format?
const isValidEmail = (email) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.(email);
(!(recipientEmail)) {
.();
;
}
Fix: Add bounces to suppressions, validate emails before sending, monitor bounce rate.
Diagnosis #4: Webhook Not Triggering
Symptoms:
- Emails send fine but webhook endpoint never called
- Database EmailLog status stays "pending"
- Resend dashboard shows events but your server doesn't process them
Checklist:
-
Is the webhook endpoint public?
-
Is the endpoint registered in Resend?
-
Are you capturing raw body?
const payload = req.body;
const rawBody = await req.text();
-
Is your verification middleware in place?
-
Are server logs showing requests?
-
Did you test with Resend's test event?
Fix: Verify endpoint is public, register in Resend, check logs.
Diagnosis #5: Rate Limiting (429 Errors)
Symptoms:
- Error:
429 rate_limit_exceeded
- Requests fail intermittently when sending many emails
- Getting:
x-resend-requests-remaining: 0
Checklist:
-
What's your rate limit?
Default: 10 requests/second (per team, not per key)
-
Are you batching emails?
await resend.emails.sendBatch(emails);
for (const email of emails) {
await resend.emails.send(email);
}
-
Are you implementing backoff?
if (error.code === 'rate_limit_exceeded') {
const delayMs = Math.pow(2, attemptNumber) * 1000;
await sleep(delayMs);
retry();
}
-
Are you monitoring remaining quota?
const remaining = response.headers.get('x-resend-requests-remaining');
console.log(`Requests remaining: ${remaining}`);
if (remaining < 5) {
console.();
}
Fix: Batch emails, implement backoff, monitor quota headers.
Diagnosis #6: Webhook Verification Fails (Invalid Signature)
Symptoms:
- Error:
Invalid webhook signature
- Webhook verification fails in
resend.webhooks.verify()
- Status 400 returned to Resend
Checklist:
-
Are you using the SDK to verify?
const event = resend.webhooks.verify({
payload: rawBody,
headers: { ... },
webhookSecret: process.env.RESEND_WEBHOOK_SECRET,
});
-
Is the webhook secret correct?
-
Are you using raw body (not parsed)?
const payload = await req.json();
const payload = await req.text();
-
Are headers being passed correctly?
svix-id: req.headers.get('svix-id')
svix-timestamp: req.headers.get('svix-timestamp')
svix-signature: req.headers.get('svix-signature')
-
Did you update secret after regenerating?
Fix: Use SDK verification, check secret matches, use raw body, restart server.
Quick Reference: 6 Common Errors
| Error | Cause | Fix |
|---|
401 missing_api_key | RESEND_API_KEY not set | Set env var, restart |
403 restricted_api_key | Key has "Sending access only" | Create new key with "Full access" |
validation_error | Domain not verified | Verify domain + wait for DNS |
429 rate_limit_exceeded | Too many requests | Batch emails + implement backoff |
Invalid webhook signature | Secret mismatch or parsed JSON | Check secret, use raw body |
email.bounced webhook | Invalid recipient address | Add to suppressions, validate emails |
Testing Checklist
Before going to production:
If Still Stuck
- Enable verbose logging in your code
- Check Resend dashboard for error details
- Check server logs (all output)
- Read
docs/base/05-errors.md for error code deep dive
- Open issue on GitHub with (sanitized) logs
See resend-email-integration skill for reference docs.