/email-with-resend

Consent-aware Resend email with queues, suppression, idempotency and signed webhooks.

Build·Event → Email·MIT
$npx skills add tushaarmehtaa/tushar-skills --skill email-with-resend -g -a claude-code -y

Then type /email-with-resend in Claude Code, or describe the task and let it pick the skill.

What you get back contract tests · 10 Oct 2026

claude · tallyroom

Using /email-with-resend
  • A retry never sends twice
  • User text escaped in HTML
  • Bad address: no retry
Welcome and split emails are built, ready to call once signup exists.
Claude Code 2.1.295·2m 21s·6 files changed
  • I've written the welcome and expense-split emails, but nothing sends them yet. Tallyroom has no signup flow, no database, and no "add expense" action; the dashboard shows hard-coded sample rows. So I built the email code as two server-side functions, and they need to be called from those handlers once they exist.
  • Each email carries a fixed ID (one per user for welcome, one per expense and recipient for splits), so Resend won't send the same one twice if a call is retried.
  • Bounces and spam complaints aren't handled. RESEND_WEBHOOK_SECRET is already in .env.local, but without a database there's nowhere to record addresses to stop emailing.
CaveatRan with test keys against a fake Resend server; no real email was sent.

Input → output from the run

What was asked
Tallyroom needs email. When someone adds an expense, the people it's split with should get a short email, and new users should get a welcome email. Use Resend (key and sender are in .env.local).
lib/email/index.js (new)
/**
 * Welcome email. Call once after the user row is created.
 * user: { id, email, name? }
 */
export async function sendWelcomeEmail(user) {
  const result = await sendEmail({
    to: user.email,
    ...welcomeEmail({ name: user.name }),
    idempotencyKey: `welcome:${user.id}`,
  });
  if (!result.ok) console.error("[email] welcome failed", { userId: user.id, ...result });
  return result;
}

When to use it

Use when

Use when an app needs transactional, lifecycle or campaign email through Resend.

Skip it when

Skip it for cold email to people who never signed up; that is cold-outreach.

The thinking behind it

Email is where trust is won or lost. Send with consent, suppression and retries handled, and the right message lands exactly once.

Output contract from the skill's Output section

Report message classes and triggers, files/templates/jobs changed, sender and environment-variable names, consent/preferences/suppression behavior, webhook and idempotency design, verification evidence/message IDs, and remaining DNS/dashboard/production checks.

How it checks its own work from the instructions

  • Returned API errors and thrown network errors are both tested
  • Webhooks tested with signed, invalid, and replayed payloads
  • Bounce and complaint suppression is tested
  • Sends go only to test recipients until production confirmed

Requirements what it needs

Works in
Codex, Claude Code, Cursor
Needs
project files, terminal commands, network access

The instructions SKILL.md · 30 lines

Instructions & references

Back to overview ↑
On this page 4 sections
SKILL.md · MIT

Email with Resend

Implement email as a reliable, permission-aware subsystem. This is not a cold-outreach workflow.

Workflow

  1. Inspect framework/runtime, installed Resend SDK version, queues/jobs, database and user fields, auth/admin roles, existing templates, email events, consent/preferences, suppression data, webhooks, and verified-domain configuration.
  2. Classify each requested message as transactional, security, lifecycle, or marketing. Do not scaffold re-engagement or campaigns by default. Ask about sender identity, jurisdiction/consent, and preference policy only when not established by the product.
  3. Read the Resend guide for current send/batch/webhook patterns, suppression handling, unsubscribe design, and copy guidance relevant to the selected class.
  4. Centralize provider calls. Detect the installed SDK API, handle both returned error values and thrown transport/runtime errors, use idempotency keys for event-triggered sends, and return a typed result to the caller.
  5. Escape untrusted template values or use React/provider templates. Generate both HTML and useful text. Keep sender/reply-to configuration server-side and use a verified sending subdomain where appropriate.
  6. Do not block or silently abandon the primary transaction. Use a durable outbox/queue, or a runtime-supported post-response primitive with observability when loss is acceptable. Unawaited promises are not reliable in serverless runtimes.
  7. For campaigns, use contacts/audiences or a durable campaign job rather than a long request loop. Enforce consent, preferences, suppression, per-recipient idempotency, rate limits, cancellation, and an audit record. Require role-based admin authorization and confirmation of segment/count before send.
  8. Verify Resend webhook signatures over the raw body, deduplicate events, and process bounces/complaints/delivery failures into a suppression model. Never trust an unsigned event.
  9. Provide one-click unsubscribe and preference handling where required. Do not expose raw email addresses in URLs when an opaque signed identifier can be used. Keep transactional/security opt-outs separate from marketing preferences.

Verification

Test provider success plus returned API error, thrown network error, duplicate event, queue retry, invalid recipient, escaped user content, text rendering, verified sender/reply-to, signed/invalid/replayed webhook, bounce/complaint suppression, unsubscribe/preferences, unauthorized campaign access, segment preview, and rate-limit behavior. Run repository lint/type/test/build commands and send only to designated test recipients until production setup is confirmed.

Output

Report message classes and triggers, files/templates/jobs changed, sender and environment-variable names, consent/preferences/suppression behavior, webhook and idempotency design, verification evidence/message IDs, and remaining DNS/dashboard/production checks.

View on GitHub ↗
Claude app, ChatGPT and ZIP download

local coding agent required

This skill requires project files, terminal commands, and network access. Uploading it to a chat app does not provide equivalent execution.

ChatGPT Skills

This workflow needs a local coding environment or capabilities that a chat-only Skills upload does not provide.

Package details
Package
email-with-resend/SKILL.md
Author
@tushaarmehtaa
Added
7 Mar 2026
Tags
emailresendtransactionalcampaignsdeliverabilitypreferences