AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL unreviewed Apache-2.0 Self-run

Resend

skill-stonegiantstudio-skills-resend · by stonegiantstudio

Resend email API expertise for transactional emails, notifications, and batch sends. Triggers when sending emails, implementing email notifications, password resets, order confirmations, or any file importing from resend. Use for email sending, webhooks, templates, deliverability, and error handling.

No reviews yet
0 installs
42 views
0.0% view→install

Install

$ agentstack add skill-stonegiantstudio-skills-resend

Open-source listing, not yet scanned by AgentStack. Follow the source repository for install instructions.

Security review

⚠ Flagged

1 finding(s); flagged for manual review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures
  • high Reads credentials/environment and may exfiltrate them.

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Reliability & compatibility

Not yet reviewed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Resend? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Resend Email API

Resend is the email API for developers. TypeScript-first, excellent DX.

Always consult resend.com/docs for latest API.

> Upstream: Based on resend/resend-skills. Check weekly for updates.

Quick Start

# Install
 add resend

# Environment
RESEND_API_KEY=re_xxxxxxxxx  # Get from resend.com/api-keys

Decision: Single vs Batch

| Approach | Endpoint | Use Case | |----------|----------|----------| | Single | POST /emails | Individual emails, attachments, scheduled sends | | Batch | POST /emails/batch | 2-100 distinct emails, reduce API calls |

Choose batch when: 2+ emails, no attachments, no scheduling, reducing API calls matters (rate limit: 2 req/sec default)

Choose single when: One email, needs attachments, needs scheduling, different timing per recipient

Single Email

import { Resend } from "resend";

const resend = new Resend(process.env.RESEND_API_KEY);

// With idempotency key (always use in production)
const { data, error } = await resend.emails.send(
  {
    from: "Acme ",
    to: ["user@example.com"],
    subject: "Welcome to Acme",
    html: "Thanks for signing up!",
  },
  { idempotencyKey: `welcome-email/${userId}` }
);

if (error) {
  console.error("Failed:", error.message);
  return;
}
console.log("Sent:", data.id);

Required Parameters

| Parameter | Type | Description | |-----------|------|-------------| | from | string | Sender: "Name " | | to | string[] | Recipients (max 50) | | subject | string | Subject line | | html or text | string | Body content |

Optional Parameters

| Parameter | Type | Description | |-----------|------|-------------| | cc | string[] | CC recipients | | bcc | string[] | BCC recipients | | replyTo | string[] | Reply-to addresses | | scheduledAt | string | ISO 8601 datetime | | attachments | array | Files (max 40MB total) | | tags | array | Key/value pairs for tracking | | headers | object | Custom headers |

Batch Email

const { data, error } = await resend.batch.send(
  [
    {
      from: "Acme ",
      to: ["user1@example.com"],
      subject: "Order Shipped",
      html: "Your order has shipped!",
    },
    {
      from: "Acme ",
      to: ["user2@example.com"],
      subject: "Order Confirmed",
      html: "Your order is confirmed!",
    },
  ],
  { idempotencyKey: `batch-orders/${batchId}` }
);

Limitations:

  • No attachments (use single sends)
  • No scheduling (use single sends)
  • Max 100 emails per request
  • Atomic: one invalid email fails entire batch

Large Batches (100+ Emails)

function chunkArray(array: T[], size: number): T[][] {
  const chunks: T[][] = [];
  for (let i = 0; i 
    resend.batch.send(chunk, {
      idempotencyKey: `batch-${batchId}/chunk-${index}`,
    })
  )
);

React Router Integration

// app/lib/email.server.ts
import { Resend } from "resend";

const resend = new Resend(process.env.RESEND_API_KEY);

export async function sendWelcomeEmail(user: { email: string; name: string }) {
  const { error } = await resend.emails.send(
    {
      from: "Acme ",
      to: [user.email],
      subject: "Welcome to Acme!",
      html: `Hi ${user.name}, welcome aboard!`,
    },
    { idempotencyKey: `welcome/${user.email}` }
  );

  if (error) {
    console.error("Email failed:", error);
    throw new Error("Failed to send welcome email");
  }
}
// app/routes/signup.tsx
import { sendWelcomeEmail } from "~/lib/email.server";

export async function action({ request }: Route.ActionArgs) {
  const formData = await request.formData();
  const user = await createUser(formData);

  // Fire and forget (or await if critical)
  sendWelcomeEmail(user).catch(console.error);

  return redirect("/dashboard");
}

Idempotency Keys

Always use in production - prevents duplicate sends on retry.

| Format | Example | |--------|---------| | Single | /welcome-email/user-123 | | Batch | batch-/batch-orders/batch-456 |

  • Expires after 24 hours
  • Max 256 characters
  • Same key + same payload = returns original response (no resend)
  • Same key + different payload = 409 error

Error Handling

| Code | Action | |------|--------| | 400, 422 | Fix request, don't retry | | 401, 403 | Check API key / domain, don't retry | | 409 | Idempotency conflict - new key or fix payload | | 429 | Rate limited - exponential backoff | | 500 | Server error - exponential backoff |

async function sendWithRetry(
  emailFn: () => Promise,
  maxRetries = 3
) {
  for (let attempt = 0; attempt  setTimeout(r, delay));
  }

  return { data: null, error: new Error("Max retries exceeded") };
}

Webhooks

Track delivery status in real-time.

| Event | Use Case | |-------|----------| | email.delivered | Confirm delivery | | email.bounced | Remove from list, alert user | | email.complained | Auto-unsubscribe (spam complaint) | | email.opened | Track engagement (marketing only) | | email.clicked | Track engagement (marketing only) |

Always verify signatures:

import { Webhook } from "resend";

export async function action({ request }: Route.ActionArgs) {
  const payload = await request.text();
  const headers = Object.fromEntries(request.headers);

  const webhook = new Webhook(process.env.RESEND_WEBHOOK_SECRET!);

  try {
    const event = webhook.verify(payload, headers);

    switch (event.type) {
      case "email.bounced":
        await handleBounce(event.data);
        break;
      case "email.complained":
        await handleComplaint(event.data);
        break;
    }

    return new Response("OK", { status: 200 });
  } catch {
    return new Response("Invalid signature", { status: 401 });
  }
}

Templates

const { data, error } = await resend.emails.send({
  from: "Acme ",
  to: ["user@example.com"],
  subject: "Welcome!",
  template: {
    id: "tmpl_abc123",
    variables: {
      USER_NAME: "John",      // Case-sensitive!
      ORDER_TOTAL: "$99.00",
    },
  },
});

Important:

  • Variable names are case-sensitive (USER_NAMEuser_name)
  • Max 20 variables per template
  • Must be published in dashboard (drafts don't work)
  • Can't combine with html, text, or react params

Tags

tags: [
  { name: "user_id", value: "usr_123" },
  { name: "email_type", value: "welcome" },
  { name: "plan", value: "enterprise" },
]

Use for: filtering in dashboard, correlating webhooks, analytics.

Testing

Never use fake addresses at real providers (test@gmail.com destroys reputation).

| Address | Result | |---------|--------| | delivered@resend.dev | Simulates success | | bounced@resend.dev | Simulates hard bounce | | complained@resend.dev | Simulates spam complaint |

Deliverability Checklist

Required:

  • [ ] Valid SPF, DKIM, DMARC records
  • [ ] Links match sending domain (send from @acme.com → link to acme.com)
  • [ ] Include plain text version (or let Resend auto-generate)
  • [ ] Avoid "no-reply" addresses (use support@, hello@)
  • [ ] Body under 102KB (Gmail clips larger)

Recommended:

  • [ ] Use subdomains (notifications.acme.com for transactional)
  • [ ] Disable tracking for transactional emails (password resets, receipts)

Domain Warm-up (New Domains)

| Day | Max Emails | Max/Hour | |-----|------------|----------| | 1 | 150 | - | | 2 | 250 | - | | 3 | 400 | - | | 4 | 700 | 50 | | 5 | 1,000 | 75 | | 6 | 1,500 | 100 | | 7 | 2,000 | 150 |

Monitor: Bounce rate < 4%, Spam complaints < 0.08%

Common Mistakes

| Mistake | Fix | |---------|-----| | Retrying without idempotency key | Always include - prevents duplicates | | Batch with attachments | Use single sends for attachments | | Retrying 400/422 errors | Fix request, don't retry | | Tracking on transactional | Disable for password resets, receipts | | Using "no-reply" sender | Use real address (support@) | | Not verifying webhook signatures | Always verify - security critical | | Testing with fake emails | Use @resend.dev test addresses | | High volume from new domain | Warm up gradually |

Resources

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.