Curated Claude Code catalog
Updated 07.05.2026 · 19:39 CET
01 / Skill
resend

resend-skills

Quality
9.0

This repository provides a comprehensive suite of Resend skills, enabling AI agents to interact with the Resend email API for sending, receiving, and managing emails. It's ideal for developers building AI-powered applications that require robust email capabilities, from transactional emails to secure agent inboxes.

USP

Unlike generic email tools, this collection provides specialized skills for AI agents, including a secure inbox and best practices guidance, ensuring reliable and compliant email interactions within AI workflows.

Use cases

  • 01Sending transactional emails via AI agents
  • 02Building secure email inboxes for AI agents
  • 03Managing Resend platform resources from the terminal
  • 04Developing HTML email templates with React components
  • 05Implementing email best practices in AI-driven communication

Detected files (8)

  • skills/email-best-practices/SKILL.mdskill
    Show content (3637 bytes)
    ---
    name: email-best-practices
    description: Use when building email features, emails going to spam, high bounce rates, setting up SPF/DKIM/DMARC authentication, implementing email capture, ensuring compliance (CAN-SPAM, GDPR, CASL), handling webhooks, retry logic, or deciding transactional vs marketing.
    license: MIT
    metadata:
      author: Resend
      version: "1.0.2"
      homepage: https://resend.com/agent-skills
      source: https://github.com/resend/email-best-practices
      openclaw:
        links:
          repository: https://github.com/resend/email-best-practices
          documentation: https://resend.com/docs/email-best-practices-skill
    ---
    
    # Email Best Practices
    
    Guidance for building deliverable, compliant, user-friendly emails.
    
    ## Architecture Overview
    
    ```
    [User] → [Email Form] → [Validation] → [Double Opt-In]
                                                  ↓
                                        [Consent Recorded]
                                                  ↓
    [Suppression Check] ←──────────────[Ready to Send]
            ↓
    [Idempotent Send + Retry] ──────→ [Email API]
                                           ↓
                                  [Webhook Events]
                                           ↓
                  ┌────────┬────────┬─────────────┐
                  ↓        ↓        ↓             ↓
             Delivered  Bounced  Complained  Opened/Clicked
                           ↓        ↓
                  [Suppression List Updated]
                           ↓
                  [List Hygiene Jobs]
    ```
    
    ## Quick Reference
    
    | Need to... | See |
    |------------|-----|
    | Set up SPF/DKIM/DMARC, fix spam issues | [Deliverability](./references/deliverability.md) |
    | Build password reset, OTP, confirmations | [Transactional Emails](./references/transactional-emails.md) |
    | Plan which emails your app needs | [Transactional Email Catalog](./references/transactional-email-catalog.md) |
    | Build newsletter signup, validate emails | [Email Capture](./references/email-capture.md) |
    | Send newsletters, promotions | [Marketing Emails](./references/marketing-emails.md) |
    | Ensure CAN-SPAM/GDPR/CASL compliance | [Compliance](./references/compliance.md) |
    | Decide transactional vs marketing | [Email Types](./references/email-types.md) |
    | Handle retries, idempotency, errors | [Sending Reliability](./references/sending-reliability.md) |
    | Process delivery events, set up webhooks | [Webhooks & Events](./references/webhooks-events.md) |
    | Manage bounces, complaints, suppression | [List Management](./references/list-management.md) |
    
    ## Start Here
    
    **New app?**
    Start with the [Catalog](./references/transactional-email-catalog.md) to plan which emails your app needs (password reset, verification, etc.), then set up [Deliverability](./references/deliverability.md) (DNS authentication) before sending your first email.
    
    **Spam issues?**
    Check [Deliverability](./references/deliverability.md) first—authentication problems are the most common cause. Gmail/Yahoo reject unauthenticated emails.
    
    **Marketing emails?**
    Follow this path: [Email Capture](./references/email-capture.md) (collect consent) → [Compliance](./references/compliance.md) (legal requirements) → [Marketing Emails](./references/marketing-emails.md) (best practices).
    
    **Production-ready sending?**
    Add reliability: [Sending Reliability](./references/sending-reliability.md) (retry + idempotency) → [Webhooks & Events](./references/webhooks-events.md) (track delivery) → [List Management](./references/list-management.md) (handle bounces).
    
  • skills/react-email/SKILL.mdskill
    Show content (12637 bytes)
    ---
    name: react-email
    description: Use when building HTML email templates with React components, adding a visual email editor to an application using the React Email visual editor, rendering emails to HTML, or sending emails with Resend. Covers welcome emails, password resets, notifications, order confirmations, newsletters, transactional emails, and the embeddable email editor component.
    license: MIT
    metadata:
      author: Resend
      version: "2.1.0"
      homepage: https://react.email
      source: https://github.com/resend/react-email
      openclaw:
        install:
          - kind: node
            package: react-email
            label: React Email
        links:
          repository: https://github.com/resend/react-email
          documentation: https://resend.com/docs/react-email-skill
    ---
    
    # React Email
    
    Build and send HTML emails using React components - a modern, component-based approach to email development that works across all major email clients.
    
    ## Installation
    
    ```sh
    npm i react-email
    ```
    
    Or scaffold a new project:
    
    ```sh
    npx create-email@latest
    cd react-email-starter
    npm install
    npm run dev
    ```
    
    This works with any package manager (npm, yarn, pnpm, bun) — substitute accordingly.
    
    The dev server runs at localhost:3000 with a preview interface for templates in the `emails` folder.
    
    ### Adding to an Existing Project
    
    Install the packages and add a script to your `package.json`:
    
    ```json
    {
      "scripts": {
        "email": "email dev --dir emails --port 3000"
      }
    }
    ```
    
    Make sure the path to the emails folder is relative to the base project directory. Ensure `tsconfig.json` includes proper support for JSX.
    
    ## Basic Email Template
    
    Create an email component with proper structure using the Tailwind component for styling:
    
    ```tsx
    import {
      Html,
      Head,
      Preview,
      Body,
      Container,
      Heading,
      Text,
      Button,
      Tailwind,
      pixelBasedPreset
    } from 'react-email';
    
    interface WelcomeEmailProps {
      name: string;
      verificationUrl: string;
    }
    
    export default function WelcomeEmail({ name, verificationUrl }: WelcomeEmailProps) {
      return (
        <Html lang="en">
          <Tailwind
            config={{
              presets: [pixelBasedPreset],
              theme: {
                extend: {
                  colors: {
                    brand: '#007bff',
                  },
                },
              },
            }}
          >
            <Head />
            <Body className="bg-gray-100 font-sans">
              <Preview>Welcome - Verify your email</Preview>
              <Container className="max-w-xl mx-auto p-5">
                <Heading className="text-2xl text-gray-800">
                  Welcome!
                </Heading>
                <Text className="text-base text-gray-800">
                  Hi {name}, thanks for signing up!
                </Text>
                <Button
                  href={verificationUrl}
                  className="bg-brand text-white px-5 py-3 rounded block text-center no-underline box-border"
                >
                  Verify Email
                </Button>
              </Container>
            </Body>
          </Tailwind>
        </Html>
      );
    }
    
    // Preview props for testing
    WelcomeEmail.PreviewProps = {
      name: 'John Doe',
      verificationUrl: 'https://example.com/verify/abc123'
    } satisfies WelcomeEmailProps;
    
    export { WelcomeEmail };
    ```
    
    ## Behavioral Guidelines
    
    - When iterating over the code, only update what the user asked for. Keep the rest intact.
    - If the user asks to use media queries, inform them that most email clients don't support them and suggest a different approach.
    - Never use template variables (like `{{name}}`) directly in TypeScript code. Instead, reference the underlying properties directly. If the user explicitly asks for `{{variableName}}`, place the mustache string only in PreviewProps, never in the component JSX:
    
    ```typescript
    const EmailTemplate = (props) => {
      return (
        <h1>Hello, {props.variableName}!</h1>
      );
    }
    
    EmailTemplate.PreviewProps = {
      variableName: "{{variableName}}",
    };
    
    export default EmailTemplate;
    ```
    
    - Never write the `{{variableName}}` pattern directly in the component structure. If the user insists, explain that this would make the template invalid.
    
    ## Essential Components
    
    See [references/COMPONENTS.md](references/COMPONENTS.md) for complete component documentation.
    
    **Core Structure:**
    - `Html` - Root wrapper with `lang` attribute
    - `Head` - Meta elements, styles, fonts
    - `Body` - Main content wrapper
    - `Container` - Outermost centering wrapper (has built-in `max-width: 37.5em`). Use only once per email.
    - `Section` - Interior content blocks (no built-in max-width). Use for grouping content inside `Container`.
    - `Row` & `Column` - Multi-column layouts
    - `Tailwind` - Enables Tailwind CSS utility classes
    
    **Content:**
    - `Preview` - Inbox preview text, always first inside `<Body>`
    - `Heading` - h1-h6 headings
    - `Text` - Paragraphs
    - `Button` - Styled link buttons (always include `box-border`)
    - `Link` - Hyperlinks
    - `Img` - Images (see Static Files section below)
    - `Hr` - Horizontal dividers
    
    **Specialized:**
    - `CodeBlock` - Syntax-highlighted code
    - `CodeInline` - Inline code
    - `Markdown` - Render markdown
    - `Font` - Custom web fonts
    
    ## Before Writing Code
    
    When a user requests an email template, ask clarifying questions FIRST if they haven't provided:
    
    1. **Brand colors** - Ask for primary brand color (hex code like #007bff)
    2. **Logo** - Ask if they have a logo file and its format (PNG/JPG only - warn if SVG/WEBP)
    3. **Style preference** - Professional, casual, or minimal tone
    4. **Production URL** - Where will static assets be hosted in production?
    
    ## Static Files and Images
    
    ### Directory Structure
    
    Local images must be placed in the `static` folder inside your emails directory:
    
    ```
    project/
    ├── emails/
    │   ├── welcome.tsx
    │   └── static/           <-- Images go here
    │       └── logo.png
    ```
    
    ### Dev vs Production URLs
    
    Use this pattern for images that work in both dev preview and production:
    
    ```tsx
    const baseURL = process.env.NODE_ENV === "production"
      ? "https://cdn.example.com"  // User's production CDN
      : "";
    
    export default function Email() {
      return (
        <Img
          src={`${baseURL}/static/logo.png`}
          alt="Logo"
          width="150"
          height="50"
        />
      );
    }
    ```
    
    **How it works:**
    - **Development:** `baseURL` is empty, so URL is `/static/logo.png` - served by React Email's dev server
    - **Production:** `baseURL` is the CDN domain, so URL is `https://cdn.example.com/static/logo.png`
    
    **Important:** Always ask the user for their production hosting URL. Do not hardcode `localhost:3000`.
    
    ## Styling
    
    See [references/STYLING.md](references/STYLING.md) for comprehensive styling documentation including typography, layout patterns, dark mode, and brand consistency.
    
    ### Key Rules
    
    - Use `Tailwind` with `pixelBasedPreset` (email clients don't support `rem`). Import `pixelBasedPreset` from `react-email`.
    - Never use flexbox or grid — use `Row`/`Column` components or tables for layouts.
    - Avoid CSS/Tailwind media queries (`sm:`, `md:`, `lg:`, `xl:`) — limited email client support.
    - Never use theme selectors (`dark:`, `light:`) — not supported.
    - Never use SVG or WEBP images — warn users about rendering issues.
    - Always specify border type (`border-solid`, `border-dashed`, etc.) — email clients don't inherit it.
    - For single-side borders, reset others first (`border-none border-l border-solid`).
    
    ### Required Classes
    
    | Component | Required Class | Why |
    |-----------|---------------|-----|
    | `Button` | `box-border` | Prevents padding from overflowing the button width |
    | `Hr` / any border | `border-solid` (or `border-dashed`, etc.) | Email clients don't inherit border type |
    | Single-side borders | `border-none` + the side | Resets default borders on other sides |
    
    ### Structure Notes
    - Always define `<Head />` inside `<Tailwind>` when using Tailwind CSS
    - `<Preview>` should always be the first element inside `<Body>`
    - Only include props in `PreviewProps` that the component actually uses
    - Use fixed width/height for known-size elements (logos, icons); responsive sizing (`w-full`, `h-auto`) for content images
    
    ## Rendering
    
    ### Convert to HTML
    
    ```tsx
    import { render } from 'react-email';
    import { WelcomeEmail } from './emails/welcome';
    
    const html = await render(
      <WelcomeEmail name="John" verificationUrl="https://example.com/verify" />
    );
    ```
    
    ### Convert to Plain Text
    
    ```tsx
    const text = await render(<WelcomeEmail name="John" verificationUrl="https://example.com/verify" />, { plainText: true });
    ```
    
    ## Sending
    
    React Email supports sending with any email service provider. See [references/SENDING.md](references/SENDING.md) for complete sending documentation including Resend, Nodemailer, and SendGrid examples.
    
    Quick example using the Resend SDK:
    
    ```tsx
    import { Resend } from 'resend';
    import { WelcomeEmail } from './emails/welcome';
    
    const resend = new Resend(process.env.RESEND_API_KEY);
    
    const { data, error } = await resend.emails.send({
      from: 'Acme <onboarding@resend.dev>',
      to: ['user@example.com'],
      subject: 'Welcome to Acme',
      react: <WelcomeEmail name="John" verificationUrl="https://example.com/verify" />
    });
    ```
    
    The Resend Node SDK automatically handles both HTML and plain-text rendering.
    
    ## CLI Commands
    
    The `react-email` package provides a CLI accessible via the `email` command:
    
    | Command | Description |
    |---------|-------------|
    | `email dev --dir <path> --port <port>` | Start the preview development server (default: `./emails`, port 3000) |
    | `email build --dir <path>` | Build the preview app for production deployment |
    | `email start` | Run the built preview app |
    | `email export --outDir <path> --pretty --plainText --dir <path>` | Export templates to static HTML files |
    | `email resend setup` | Connect the CLI to your Resend account via API key |
    | `email resend reset` | Remove the stored Resend API key |
    
    ## Internationalization
    
    See [references/I18N.md](references/I18N.md) for complete i18n documentation. React Email supports three libraries: next-intl, react-i18next, and react-intl.
    
    ## Email Editor
    
    React Email includes a visual editor (`@react-email/editor`) that can be embedded in your app. It's built on TipTap/ProseMirror and produces email-ready HTML.
    
    See [references/EDITOR.md](references/EDITOR.md) for complete documentation including:
    - `EmailEditor` — batteries-included component with bubble menus, slash commands, and theming
    - `StarterKit` — 35+ email-aware extensions (headings, lists, tables, columns, buttons, etc.)
    - `Inspector` — contextual sidebar for editing styles
    - `EmailTheming` — built-in themes (`basic`, `minimal`) with customizable CSS properties
    - `composeReactEmail` — export editor content to email-ready HTML and plain text
    - Custom extensions via `EmailNode` and `EmailMark`
    
    Quick example:
    
    ```tsx
    import { EmailEditor, type EmailEditorRef } from '@react-email/editor';
    import '@react-email/editor/themes/default.css';
    import { useRef } from 'react';
    
    export function MyEditor() {
      const ref = useRef<EmailEditorRef>(null);
    
      return (
        <EmailEditor
          ref={ref}
          content="<p>Start typing...</p>"
          theme="basic"
        />
      );
    }
    ```
    
    ## Common Patterns
    
    See [references/PATTERNS.md](references/PATTERNS.md) for complete examples including:
    - Password reset emails
    - Order confirmations with product lists
    - Notification emails with code blocks
    - Multi-column layouts
    - Team invitation emails
    
    ## Email Best Practices
    
    1. **Test across email clients** - Gmail, Outlook, Apple Mail, Yahoo Mail
    2. **Keep it responsive** - Max-width around 600px, test on mobile
    3. **Use absolute image URLs** - Host on reliable CDN, always include `alt` text
    4. **Provide plain text version** - Required for accessibility
    5. **Keep file size under 102KB** - Gmail clips larger emails
    6. **Add proper TypeScript types** - Define interfaces for all email props
    7. **Include preview props** - Add `.PreviewProps` for development testing
    8. **Use verified domains** - For production `from` addresses
    
    ## Additional Resources
    
    - [React Email Documentation](https://react.email/docs/llms.txt)
    - [React Email GitHub](https://github.com/resend/react-email)
    - [Resend Documentation](https://resend.com/docs/llms.txt)
    - [Email Client CSS Support](https://www.caniemail.com)
    - Component Reference: [references/COMPONENTS.md](references/COMPONENTS.md)
    - Styling Guide: [references/STYLING.md](references/STYLING.md)
    - Email Editor: [references/EDITOR.md](references/EDITOR.md)
    - Sending Guide: [references/SENDING.md](references/SENDING.md)
    - Internationalization Guide: [references/I18N.md](references/I18N.md)
    - Common Patterns: [references/PATTERNS.md](references/PATTERNS.md)
    
  • skills/resend-cli/SKILL.mdskill
    Show content (8831 bytes)
    ---
    name: resend-cli
    description: >
      Operate the Resend platform from the terminal — send emails (including React Email
      .tsx templates via --react-email), manage domains, contacts, broadcasts, templates,
      webhooks, API keys, logs, automations, and events via the `resend` CLI. Use when the
      user wants to run Resend commands in the shell, scripts, or CI/CD pipelines, or
      send/preview React Email templates. Always load this skill before running `resend`
      commands — it contains the non-interactive flag contract and gotchas that prevent
      silent failures.
    license: MIT
    metadata:
      author: resend
      version: "2.0.1"
      homepage: https://resend.com/docs/cli-agents
      source: https://github.com/resend/resend-cli
      openclaw:
        primaryEnv: RESEND_API_KEY
        requires:
          env:
            - RESEND_API_KEY
          bins:
            - resend
        envVars:
          - name: RESEND_API_KEY
            required: true
            description: Resend API key for authenticating CLI commands
          - name: RESEND_PROFILE
            required: false
            description: Named auth profile for multi-account setups
        install:
          - kind: node
            package: resend-cli
            bins: [resend]
            label: Resend CLI
        links:
          repository: https://github.com/resend/resend-cli
          documentation: https://resend.com/docs/cli
    inputs:
      - name: RESEND_API_KEY
        description: Resend API key for authenticating CLI commands. Get yours at https://resend.com/api-keys
        required: true
      - name: RESEND_PROFILE
        description: Named auth profile for multi-account setups. Selects which stored API key to use (see `resend auth`).
        required: false
    references:
      - references/emails.md
      - references/domains.md
      - references/api-keys.md
      - references/automations.md
      - references/broadcasts.md
      - references/contacts.md
      - references/contact-properties.md
      - references/segments.md
      - references/templates.md
      - references/topics.md
      - references/logs.md
      - references/webhooks.md
      - references/auth.md
      - references/workflows.md
      - references/error-codes.md
    ---
    
    # Resend CLI
    
    ## Installation
    
    Before running any `resend` commands, check whether the CLI is installed:
    
    ```bash
    resend --version
    ```
    
    If the command is not found, install it using one of the methods below:
    
    **cURL (macOS / Linux):**
    ```bash
    curl -fsSL https://resend.com/install.sh | bash
    ```
    
    **Homebrew (macOS / Linux):**
    ```bash
    brew install resend/cli/resend
    ```
    
    **Node.js:**
    ```bash
    npm install -g resend-cli
    ```
    
    **PowerShell (Windows):**
    ```powershell
    irm https://resend.com/install.ps1 | iex
    ```
    
    After installing, verify:
    ```bash
    resend --version
    ```
    
    ## Agent Protocol
    
    The CLI auto-detects non-TTY environments and outputs JSON — no `--json` flag needed.
    
    **Rules for agents:**
    - Supply ALL required flags. The CLI will NOT prompt when stdin is not a TTY.
    - Pass `--quiet` (or `-q`) to suppress spinners and status messages.
    - Exit `0` = success, `1` = error.
    - Error JSON goes to stderr, success JSON goes to stdout:
      ```json
      {"error":{"message":"...","code":"..."}}
      ```
    - Use `--api-key` or `RESEND_API_KEY` env var. Never rely on interactive login.
    - All `delete`/`rm` commands require `--yes` in non-interactive mode.
    
    ## Authentication
    
    Auth resolves: `--api-key` flag > `RESEND_API_KEY` env > config file (`resend login --key`). Use `--profile` or `RESEND_PROFILE` for multi-profile.
    
    ## Global Flags
    
    | Flag | Description |
    |------|-------------|
    | `--api-key <key>` | Override API key for this invocation |
    | `-p, --profile <name>` | Select stored profile |
    | `--json` | Force JSON output (auto in non-TTY) |
    | `-q, --quiet` | Suppress spinners/status (implies `--json`) |
    
    ## Available Commands
    
    | Command Group | What it does |
    |--------------|-------------|
    | `emails` | send, get, list, batch, cancel, update |
    | `emails receiving` | list, get, attachments, forward, listen |
    | `domains` | create, verify, update, delete, list |
    | `logs` | list, get, open |
    | `api-keys` | create, list, delete |
    | `automations` | create, get, list, update, delete, stop, open, runs |
    | `events` | create, get, list, update, delete, send, open |
    | `broadcasts` | create, send, update, delete, list |
    | `contacts` | create, update, delete, segments, topics |
    | `contact-properties` | create, update, delete, list |
    | `segments` | create, get, list, delete, contacts |
    | `templates` | create, publish, duplicate, delete, list |
    | `topics` | create, update, delete, list |
    | `webhooks` | create, update, listen, delete, list |
    | `auth` | login, logout, switch, rename, remove |
    | `whoami` / `doctor` / `update` / `open` / `commands` | Utility commands |
    
    Read the matching reference file for detailed flags and output shapes.
    
    **Dry-run:** Only `emails send` and `broadcasts create` support `--dry-run` (payload validation before send/create). They print `{ "dryRun": true, "request": { ... } }` on stdout without calling the API. There is no `--dry-run` on `emails batch`, `broadcasts send`, or other commands yet.
    
    ## Common Mistakes
    
    | # | Mistake | Fix |
    |---|---------|-----|
    | 1 | **Forgetting `--yes` on delete commands** | All `delete`/`rm` subcommands require `--yes` in non-interactive mode — otherwise the CLI exits with an error |
    | 2 | **Not saving webhook `signing_secret`** | `webhooks create` shows the secret once only — it cannot be retrieved later. Capture it from command output immediately |
    | 3 | **Omitting `--quiet` in CI** | Without `-q`, spinners and status text still go to stderr (not stdout). Use `-q` for JSON on stdout with no spinner noise on stderr |
    | 4 | **Using `--scheduled-at` with batch** | Batch sending does not support `scheduled_at` — use single `emails send` instead |
    | 5 | **Expecting `domains list` to include DNS records** | List returns summaries only — use `domains get <id>` for the full `records[]` array |
    | 6 | **Sending a dashboard-created broadcast via CLI** | Only API-created broadcasts can be sent with `broadcasts send` — dashboard broadcasts must be sent from the dashboard |
    | 7 | **Passing `--events` to `webhooks update` expecting additive behavior** | `--events` replaces the entire subscription list — always pass the complete set |
    | 8 | **Expecting `logs list` to include request/response bodies** | List returns summary fields only — use `logs get <id>` for full `request_body` and `response_body` |
    
    ## Common Patterns
    
    **Send an email:**
    ```bash
    resend emails send --from "you@domain.com" --to user@example.com --subject "Hello" --text "Body"
    ```
    
    **Send a React Email template (.tsx):**
    ```bash
    resend emails send --from "you@domain.com" --to user@example.com --subject "Welcome" --react-email ./emails/welcome.tsx
    ```
    
    **Domain setup flow:**
    ```bash
    resend domains create --name example.com --region us-east-1
    # Configure DNS records from output, then:
    resend domains verify <domain-id>
    resend domains get <domain-id>  # check status
    ```
    
    **Create and send a broadcast:**
    ```bash
    resend broadcasts create --from "news@domain.com" --subject "Update" --segment-id <id> --html "<h1>Hi</h1>" --send
    ```
    
    **CI/CD (no login needed):**
    ```bash
    RESEND_API_KEY=re_xxx resend emails send --from ... --to ... --subject ... --text ...
    ```
    
    **Check environment health:**
    ```bash
    resend doctor -q
    ```
    
    ## When to Load References
    
    - **Sending or reading emails** → [references/emails.md](references/emails.md)
    - **Setting up or verifying a domain** → [references/domains.md](references/domains.md)
    - **Managing API keys** → [references/api-keys.md](references/api-keys.md)
    - **Creating or sending broadcasts** → [references/broadcasts.md](references/broadcasts.md)
    - **Managing contacts, segments, or topics** → [references/contacts.md](references/contacts.md), [references/segments.md](references/segments.md), [references/topics.md](references/topics.md)
    - **Defining contact properties** → [references/contact-properties.md](references/contact-properties.md)
    - **Working with templates** → [references/templates.md](references/templates.md)
    - **Viewing API request logs** → [references/logs.md](references/logs.md)
    - **Creating automations or sending events** → [references/automations.md](references/automations.md)
    - **Setting up webhooks or listening for events** → [references/webhooks.md](references/webhooks.md)
    - **Auth, profiles, or health checks** → [references/auth.md](references/auth.md)
    - **Multi-step recipes** (setup, CI/CD, broadcast workflow) → [references/workflows.md](references/workflows.md)
    - **Command failed with an error** → [references/error-codes.md](references/error-codes.md)
    - **Resend SDK integration** (Node.js, Python, Go, etc.) → Install the [`resend`](https://github.com/resend/resend-skills) skill
    - **AI agent email inbox** → Install the [`agent-email-inbox`](https://github.com/resend/resend-skills) skill
    
  • skills/resend/SKILL.mdskill
    Show content (14303 bytes)
    ---
    name: resend
    description: Use when working with the Resend email API — sending transactional emails (single or batch), receiving inbound emails via webhooks, managing email templates, tracking delivery events, managing domains, contacts, broadcasts, webhooks, API keys, automations, events, viewing API request logs, or setting up the Resend SDK. Always use this skill when the user mentions Resend, even for simple tasks like "send an email with Resend" — the skill contains critical gotchas (idempotency keys, webhook verification, template variable syntax) that prevent common production issues.
    license: MIT
    metadata:
        author: resend
        version: "3.3.3"
        homepage: https://resend.com/agent-skills
        source: https://github.com/resend/resend-skills
        openclaw:
            primaryEnv: RESEND_API_KEY
            requires:
                env:
                    - RESEND_API_KEY
            envVars:
                - name: RESEND_API_KEY
                  required: true
                  description: Resend API key for sending and receiving emails
                - name: RESEND_WEBHOOK_SECRET
                  required: false
                  description: Webhook signing secret for verifying event payloads
            links:
                repository: https://github.com/resend/resend-skills
                documentation: https://resend.com/docs/resend-skill
    inputs:
        - name: RESEND_API_KEY
          description: Resend API key for sending and receiving emails. Get yours at https://resend.com/api-keys
          required: true
        - name: RESEND_WEBHOOK_SECRET
          description: Webhook signing secret for verifying event payloads. Found in the Resend dashboard under Webhooks after creating an endpoint.
          required: false
    references:
        - sending
        - receiving.md
        - templates.md
        - webhooks.md
        - domains.md
        - contacts.md
        - broadcasts.md
        - api-keys.md
        - logs.md
        - contact-properties.md
        - segments.md
        - topics.md
        - automations.md
        - events.md
        - installation.md
        - fetch-all-templates.mjs
    ---
    
    # Resend
    
    ## Quick Send — Node.js
    
    ```typescript
    import { Resend } from 'resend';
    
    const resend = new Resend(process.env.RESEND_API_KEY);
    
    const { data, error } = await resend.emails.send(
      {
        from: 'Acme <onboarding@resend.dev>',
        to: ['delivered@resend.dev'],
        subject: 'Hello World',
        html: '<p>Email body here</p>',
      },
      { idempotencyKey: `welcome-email/${userId}` }
    );
    
    if (error) {
      console.error('Failed:', error.message);
      return;
    }
    console.log('Sent:', data.id);
    ```
    
    **Key gotcha:** The Resend Node.js SDK does NOT throw exceptions — it returns `{ data, error }`. Always check `error` explicitly instead of using try/catch for API errors.
    
    ## Quick Send — Python
    
    ```python
    import resend
    import os
    
    resend.api_key = os.environ["RESEND_API_KEY"]
    
    email = resend.Emails.send({
        "from": "Acme <onboarding@resend.dev>",
        "to": ["delivered@resend.dev"],
        "subject": "Hello World",
        "html": "<p>Email body here</p>",
    }, idempotency_key=f"welcome-email/{user_id}")
    ```
    
    ### Single vs Batch Decision
    
    | Choose | When |
    |--------|------|
    | **Single** (`POST /emails`) | 1 email, needs attachments, needs scheduling |
    | **Batch** (`POST /emails/batch`) | 2-100 distinct emails, no attachments, no scheduling |
    
    Batch is atomic — if one email fails validation, the entire batch fails. Always validate before sending. Batch does NOT support attachments or `scheduled_at`.
    
    ### Idempotency Keys (Critical for Retries)
    
    Prevent duplicate emails when retrying failed requests:
    
    | Key Facts | |
    |-----------|---|
    | **Format (single)** | `<event-type>/<entity-id>` (e.g., `welcome-email/user-123`) |
    | **Format (batch)** | `batch-<event-type>/<batch-id>` (e.g., `batch-orders/batch-456`) |
    | **Expiration** | 24 hours |
    | **Max length** | 256 characters |
    | **Same key + same payload** | Returns original response without resending |
    | **Same key + different payload** | Returns 409 error |
    
    ## Quick Receive (Node.js)
    
    ```typescript
    import { Resend } from 'resend';
    
    const resend = new Resend(process.env.RESEND_API_KEY);
    
    export async function POST(req: Request) {
      const payload = await req.text(); // Must use raw text, not req.json()
    
      const event = resend.webhooks.verify({
        payload,
        headers: {
          'svix-id': req.headers.get('svix-id'),
          'svix-timestamp': req.headers.get('svix-timestamp'),
          'svix-signature': req.headers.get('svix-signature'),
        },
        secret: process.env.RESEND_WEBHOOK_SECRET,
      });
    
      if (event.type === 'email.received') {
        // Webhook has metadata only — call API for body
        const { data: email } = await resend.emails.receiving.get(
          event.data.email_id
        );
        console.log(email.text);
      }
    
      return new Response('OK', { status: 200 });
    }
    ```
    
    **Key gotcha:** Webhook payloads do NOT contain the email body. You must call `resend.emails.receiving.get()` separately.
    
    ## What Do You Need?
    
    | Task | Reference |
    |------|-----------|
    | **Send a single email** | [sending/overview.md](references/sending/overview.md) — parameters, deliverability, testing |
    | **Send batch emails** | [sending/overview.md](references/sending/overview.md) → [sending/batch-email-examples.md](references/sending/batch-email-examples.md) |
    | **Full SDK examples** (Node.js, Python, Go, cURL) | [sending/single-email-examples.md](references/sending/single-email-examples.md) |
    | **Idempotency, retries, error handling** | [sending/best-practices.md](references/sending/best-practices.md) |
    | **Get, list, reschedule, cancel emails** | [sending/email-management.md](references/sending/email-management.md) |
    | **Receive inbound emails** | [receiving.md](references/receiving.md) — domain setup, webhooks, attachments |
    | **Manage templates** (CRUD, variables) | [templates.md](references/templates.md) — lifecycle, aliases, pagination |
    | **Set up webhooks** (events, verification) | [webhooks.md](references/webhooks.md) — verification, CRUD, retry schedule, IP allowlist |
    | **Manage domains** (create, verify, DNS) | [domains.md](references/domains.md) — regions, TLS, tracking, capabilities |
    | **Manage contacts** (CRUD, properties) | [contacts.md](references/contacts.md) — segments, topics, custom properties |
    | **Send broadcasts** (marketing campaigns) | [broadcasts.md](references/broadcasts.md) — lifecycle, scheduling, template variables |
    | **Manage API keys** | [api-keys.md](references/api-keys.md) — permission scoping, domain restrictions |
    | **View API request logs** | [logs.md](references/logs.md) — list and retrieve API call history, debugging |
    | **Define contact properties** | [contact-properties.md](references/contact-properties.md) — custom fields for contacts |
    | **Manage segments** (contact groups) | [segments.md](references/segments.md) — broadcast targeting, contact grouping |
    | **Manage topics** (subscriptions) | [topics.md](references/topics.md) — opt-in/out preferences, broadcast filtering |
    | **Create automations** (event-driven workflows) | [automations.md](references/automations.md) — steps, connections, runs, conditions |
    | **Define and send events** (automation triggers) | [events.md](references/events.md) — schemas, payloads, contact association |
    | **Install SDK** (8+ languages) | [installation.md](references/installation.md) |
    | **Set up an AI agent inbox** | Install the `agent-email-inbox` skill — covers security levels for untrusted input |
    
    ## SDK Version Requirements
    
    Always install the latest SDK version. These are the minimum versions for full functionality (sending, receiving, webhook verification):
    
    | Language | Package | Min Version | Install |
    |----------|---------|-------------|---------|
    | Node.js | `resend` | >= 6.9.2 | `npm install resend` |
    | Python | `resend` | >= 2.21.0 | `pip install resend` |
    | Go | `resend-go/v3` | >= 3.1.0 | `go get github.com/resend/resend-go/v3` |
    | Ruby | `resend` | >= 1.0.0 | `gem install resend` |
    | PHP | `resend/resend-php` | >= 1.1.0 | `composer require resend/resend-php` |
    | Rust | `resend-rs` | >= 0.20.0 | `cargo add resend-rs` |
    | Java | `resend-java` | >= 4.11.0 | See [installation.md](references/installation.md) |
    | .NET | `Resend` | >= 0.2.1 | `dotnet add package Resend` |
    
    > **If the project already has a Resend SDK installed**, check the version and upgrade if it's below the minimum. Older SDKs may be missing `webhooks.verify()` or `emails.receiving.get()`.
    
    See [installation.md](references/installation.md) for full installation commands, language detection, and cURL fallback.
    
    ## Common Setup
    
    ### API Key
    
    Store in environment variable — never hardcode:
    ```bash
    export RESEND_API_KEY=re_xxxxxxxxx
    ```
    
    Get your key at [resend.com/api-keys](https://resend.com/api-keys).
    
    ### Detect Project Language
    
    Check for these files: `package.json` (Node.js), `requirements.txt`/`pyproject.toml` (Python), `go.mod` (Go), `Gemfile` (Ruby), `composer.json` (PHP), `Cargo.toml` (Rust), `pom.xml`/`build.gradle` (Java), `*.csproj` (.NET).
    
    ## Common Mistakes
    
    | # | Mistake | Fix |
    |---|---------|-----|
    | 1 | **Retrying without idempotency key** | Always include idempotency key — prevents duplicate sends on retry. Format: `<event-type>/<entity-id>` |
    | 2 | **Not verifying webhook signatures** | Always verify with `resend.webhooks.verify()` — unverified events can't be trusted |
    | 3 | **Template variable name mismatch** | Variable names are case-sensitive — must match the template definition exactly. Use triple mustache `{{{VAR}}}` syntax |
    | 4 | **Expecting email body in webhook payload** | Webhooks contain metadata only — call `resend.emails.receiving.get()` for body content |
    | 5 | **Using try/catch for Node.js SDK errors** | SDK returns `{ data, error }` — check `error` explicitly, don't wrap in try/catch |
    | 6 | **Using batch for emails with attachments** | Batch doesn't support attachments — use single sends instead |
    | 7 | **Testing with fake emails (test@gmail.com)** | Use `delivered@resend.dev` — fake addresses bounce and hurt reputation |
    | 8 | **Sending with draft template** | Templates must be published before sending — call `.publish()` first |
    | 9 | **`html` + `template` in same send call** | Mutually exclusive — remove `html`/`text`/`react` when using template |
    | 10 | **MX record not lowest priority for inbound** | Ensure Resend's MX has the lowest number (highest priority) or emails won't route |
    | 11 | **403 when sending from `resend.dev`** | The default `onboarding@resend.dev` is a sandbox — it can only deliver to your Resend account email. Verify your own domain first |
    | 12 | **403 domain mismatch** | The `from` address domain must exactly match a verified domain. Verified `send.acme.com` but sending from `user@acme.com` will fail |
    | 13 | **Calling Resend API from the browser (CORS)** | The API does not support CORS — this is intentional to protect your API key. Always call from server-side (API routes, serverless functions) |
    | 14 | **401 `restricted_api_key`** | A sending-only API key was used on a non-sending endpoint (domains, contacts, etc.). Create a full-access key instead |
    
    ## Cross-Cutting Concerns
    
    ### Send + Receive Together
    
    Auto-replies, email forwarding, or any receive-then-send workflow requires both capabilities:
    1. Set up inbound domain first (see [receiving.md](references/receiving.md))
    2. Set up sending (see [sending/overview.md](references/sending/overview.md))
    3. Note: batch sending does NOT support attachments or scheduling — use single sends when forwarding with attachments
    
    ### AI Agent Inbox
    
    If your system processes untrusted email content and takes actions (refunds, database changes, forwarding), install the `agent-email-inbox` skill. This applies whether or not AI is involved — any system interpreting freeform email content from external senders needs security measures.
    
    ### Marketing Emails
    
    The sending capabilities in this skill are for **transactional email** (receipts, confirmations, notifications). For marketing campaigns to large subscriber lists with unsubscribe links and engagement tracking, use Resend Broadcasts — see [broadcasts.md](references/broadcasts.md) for the API.
    
    ### Domain Warm-up
    
    New domains must gradually increase sending volume. Day 1 limit: ~150 emails (new domain) or ~1,000 (existing domain). See the warm-up schedule in [sending/overview.md](references/sending/overview.md).
    
    ### Testing
    
    **Never test with fake addresses at real email providers** (test@gmail.com, fake@outlook.com) — they bounce and destroy sender reputation.
    
    | Address | Result |
    |---------|--------|
    | `delivered@resend.dev` | Simulates successful delivery |
    | `bounced@resend.dev` | Simulates hard bounce |
    | `complained@resend.dev` | Simulates spam complaint |
    
    ### Suppression List
    
    Resend automatically suppresses hard-bounced and spam-complained addresses. Sending to suppressed addresses fires the `email.suppressed` webhook event instead of attempting delivery. Manage in Dashboard → Suppressions.
    
    ### Webhook Event Types
    
    | Event | Trigger |
    |-------|---------|
    | `email.sent` | API request successful |
    | `email.delivered` | Reached recipient's mail server |
    | `email.bounced` | Permanently rejected (hard bounce) |
    | `email.complained` | Recipient marked as spam |
    | `email.opened` / `email.clicked` | Recipient engagement |
    | `email.delivery_delayed` | Soft bounce, Resend retries |
    | `email.received` | Inbound email arrived |
    | `domain.*` / `contact.*` | Domain/contact changes |
    
    See [webhooks.md](references/webhooks.md) for full details, signature verification, and retry schedule.
    
    ## Error Handling Quick Reference
    
    | Code | Action |
    |------|--------|
    | 400, 422 | Fix request parameters, don't retry |
    | 401 | Check API key — `restricted_api_key` means sending-only key used on non-sending endpoint |
    | 403 | Verify domain ownership — common causes: `resend.dev` sandbox, `from` domain mismatch, unverified domain |
    | 409 | Idempotency conflict — use new key or fix payload |
    | 429 | Rate limited — retry with exponential backoff (default rate limit: 2 req/s) |
    | 500 | Server error — retry with exponential backoff |
    
    ## Resources
    
    - [Resend Documentation](https://resend.com/docs)
    - [API Reference](https://resend.com/docs/api-reference)
    - [Dashboard](https://resend.com/emails)
    
  • skills/agent-email-inbox/SKILL.mdskill
    Show content (16300 bytes)
    ---
    name: agent-email-inbox
    description: Use when building any system where email content triggers actions — AI agent inboxes, automated support handlers, email-to-task pipelines, or any workflow processing untrusted inbound email. Always use this skill when the user wants to receive emails and act on them programmatically, even if they don't mention "agent" — the skill contains critical security patterns (sender allowlists, content filtering, sandboxed processing) that prevent untrusted email from controlling your system.
    license: MIT
    metadata:
        author: resend
        version: "3.0.2"
        homepage: https://resend.com/agent-skills
        source: https://github.com/resend/resend-skills
        openclaw:
            primaryEnv: RESEND_API_KEY
            requires:
                env:
                    - RESEND_API_KEY
            envVars:
                - name: RESEND_API_KEY
                  required: true
                  description: Resend API key for sending and receiving emails
                - name: RESEND_WEBHOOK_SECRET
                  required: false
                  description: Webhook signing secret for verifying inbound email event payloads
                - name: SECURITY_LEVEL
                  required: false
                  description: Security level for inbound email processing (strict, moderate, permissive)
                - name: ALLOWED_SENDERS
                  required: false
                  description: Comma-separated list of allowed sender email addresses
                - name: ALLOWED_DOMAINS
                  required: false
                  description: Comma-separated list of allowed sender domains
                - name: OWNER_EMAIL
                  required: false
                  description: Owner email address for forwarding or notifications
            links:
                repository: https://github.com/resend/resend-skills
                documentation: https://resend.com/docs/agent-email-inbox-skill
    inputs:
        - name: RESEND_API_KEY
          description: Resend API key for sending and receiving emails. Get yours at https://resend.com/api-keys
          required: true
        - name: RESEND_WEBHOOK_SECRET
          description: Webhook signing secret for verifying inbound email event payloads. Returned as `signing_secret` in the response when you create a webhook via the API.
          required: true
    references:
        - security-levels.md
        - webhook-setup.md
        - advanced-patterns.md
    ---
    
    # AI Agent Email Inbox
    
    ## Overview
    
    This skill covers setting up a secure email inbox that allows your application or AI agent to receive and respond to emails, with content safety measures in place.
    
    **Core principle:** An AI agent's inbox receives untrusted input. Security configuration is important to handle this safely.
    
    ### Why Webhook-Based Receiving?
    
    Resend uses webhooks for inbound email, meaning your agent is notified **instantly** when an email arrives. This is valuable for agents because:
    
    - **Real-time responsiveness** — React to emails within seconds, not minutes
    - **No polling overhead** — No cron jobs checking "any new mail?" repeatedly
    - **Event-driven architecture** — Your agent only wakes up when there's actually something to process
    - **Lower API costs** — No wasted calls checking empty inboxes
    
    ## Architecture
    
    ```
    Sender → Email → Resend (MX) → Webhook → Your Server → AI Agent
                                                  ↓
                                        Security Validation
                                                  ↓
                                        Process or Reject
    ```
    
    ## SDK Version Requirements
    
    This skill requires Resend SDK features for webhook verification (`webhooks.verify()`) and email receiving (`emails.receiving.get()`). Always install the latest SDK version. If the project already has a Resend SDK installed, check the version and upgrade if needed.
    
    | Language | Package | Min Version |
    |----------|---------|-------------|
    | Node.js | `resend` | >= 6.9.2 |
    | Python | `resend` | >= 2.21.0 |
    | Go | `resend-go/v3` | >= 3.1.0 |
    | Ruby | `resend` | >= 1.0.0 |
    | PHP | `resend/resend-php` | >= 1.1.0 |
    | Rust | `resend-rs` | >= 0.20.0 |
    | Java | `resend-java` | >= 4.11.0 |
    | .NET | `Resend` | >= 0.2.1 |
    
    Install the `resend` npm package: `npm install resend` (or the equivalent for your language). For full sending docs, install the `resend` skill.
    
    ## Quick Start
    
    1. **Ask the user for their email address** — You need a real email address to send test emails to. Ask the user and wait for their response before proceeding.
    2. **Choose your security level** — Decide how to validate incoming emails *before* any are processed
    3. **Set up receiving domain** — Configure MX records for the user's custom domain (see Domain Setup section)
    4. **Create webhook endpoint** — Handle `email.received` events with security built in from the start. **The webhook endpoint MUST be a POST route.**
    5. **Set up tunneling** (local dev) — Use Tailscale Funnel (recommended) or ngrok. See [references/webhook-setup.md](references/webhook-setup.md)
    6. **Create webhook via API** — Use the Resend Webhook API to register your endpoint programmatically. See [references/webhook-setup.md](references/webhook-setup.md)
    7. **Connect to agent** — Pass validated emails to your AI agent for processing
    
    ## Before You Start: Account & API Key Setup
    
    ### First Question: New or Existing Resend Account?
    
    Ask your human:
    - **New account just for the agent?** → Simpler setup, full account access is fine
    - **Existing account with other projects?** → Use domain-scoped API keys for sandboxing
    
    ### Creating API Keys Securely
    
    > Don't paste API keys in chat! They'll be in conversation history forever.
    
    **Safer options:**
    
    1. **Environment file method:** Human creates `.env` file directly: `echo "RESEND_API_KEY=re_xxx" >> .env`
    2. **Password manager / secrets manager:** Human stores key in 1Password, Vault, etc.
    3. **If key must be shared in chat:** Human should rotate the key immediately after setup
    
    ### Domain-Scoped API Keys (Recommended for Existing Accounts)
    
    If your human has an existing Resend account with other projects, create a **domain-scoped API key**:
    
    1. **Verify the agent's domain first** (Dashboard → Domains → Add Domain)
    2. **Create a scoped API key:** Dashboard → API Keys → Create API Key → "Sending access" → select only the agent's domain
    3. **Result:** Even if the key leaks, it can only send from one domain
    
    ## Domain Setup
    
    ### Option 1: Resend-Managed Domain (Recommended for Getting Started)
    
    Use your auto-generated address: `<anything>@<your-id>.resend.app`
    
    No DNS configuration needed. Find your address in Dashboard → Emails → Receiving → "Receiving address".
    
    ### Option 2: Custom Domain
    
    The user must enable receiving in the Resend dashboard: Domains page → toggle on "Enable Receiving".
    
    Then add an MX record:
    
    | Setting | Value |
    |---------|-------|
    | **Type** | MX |
    | **Host** | Your domain or subdomain (e.g., `agent.example.com`) |
    | **Value** | Provided in Resend dashboard |
    | **Priority** | 10 (must be lowest number to take precedence) |
    
    **Use a subdomain** (e.g., `agent.example.com`) to avoid disrupting existing email services.
    
    **Tip:** Verify DNS propagation at [dns.email](https://dns.email).
    
    > DNS Propagation: MX record changes can take up to 48 hours to propagate globally, though often complete within a few hours.
    
    ## Security Levels
    
    **Choose your security level before setting up the webhook endpoint.** An AI agent that processes emails without security is dangerous — anyone can email instructions that your agent will execute. The webhook code you write next should include your chosen security level from the start.
    
    Ask the user what level of security they want, and ensure that they understand what each level means.
    
    | Level | Name | When to Use | Trade-off |
    |-------|------|-------------|-----------|
    | **1** | Strict Allowlist | Most use cases — known, fixed set of senders | Maximum security, limited functionality |
    | **2** | Domain Allowlist | Organization-wide access from trusted domains | More flexible, anyone at domain can interact |
    | **3** | Content Filtering | Accept from anyone, filter unsafe patterns | Can receive from anyone, pattern matching not foolproof |
    | **4** | Sandboxed Processing | Process all emails with restricted agent capabilities | Maximum flexibility, complex to implement |
    | **5** | Human-in-the-Loop | Require human approval for untrusted actions | Maximum security, adds latency |
    
    For detailed implementation code for each level, see [references/security-levels.md](references/security-levels.md).
    
    ### Level 1: Strict Allowlist (Recommended)
    
    Only process emails from explicitly approved addresses. Reject everything else.
    
    ```typescript
    const ALLOWED_SENDERS = [
      'you@youremail.com',
      'notifications@github.com',
    ];
    
    async function processEmailForAgent(
      eventData: EmailReceivedEvent,
      emailContent: EmailContent
    ) {
      const sender = eventData.from.toLowerCase();
    
      if (!ALLOWED_SENDERS.some(allowed => sender === allowed.toLowerCase())) {
        console.log(`Rejected email from unauthorized sender: ${sender}`);
        await notifyOwnerOfRejectedEmail(eventData);
        return;
      }
    
      await agent.processEmail({
        from: eventData.from,
        subject: eventData.subject,
        body: emailContent.text || emailContent.html,
      });
    }
    ```
    
    ### Security Best Practices
    
    #### Always Do
    
    | Practice | Why |
    |----------|-----|
    | Verify webhook signatures | Prevents spoofed webhook events |
    | Log all rejected emails | Audit trail for security review |
    | Use allowlists where possible | Explicit trust is safer than filtering |
    | Rate limit email processing | Prevents excessive processing load |
    | Separate trusted/untrusted handling | Different risk levels need different treatment |
    
    #### Never Do
    
    | Anti-Pattern | Risk |
    |--------------|------|
    | Process emails without validation | Anyone can control your agent |
    | Trust email headers for authentication | Headers are trivially spoofed |
    | Execute code from email content | Untrusted input should never run as code |
    | Store email content in prompts verbatim | Untrusted input mixed into prompts can alter agent behavior |
    | Give untrusted emails full agent access | Scope capabilities to the minimum needed |
    
    ## Webhook Endpoint
    
    After choosing your security level and setting up your domain, create a webhook endpoint. **The webhook endpoint MUST be a POST route.** Resend sends all webhook events as POST requests.
    
    > **Critical: Use raw body for verification.** Webhook signature verification requires the raw request body.
    > - **Next.js App Router:** Use `req.text()` (not `req.json()`)
    > - **Express:** Use `express.raw({ type: 'application/json' })` on the webhook route
    
    ### Next.js App Router
    
    ```typescript
    // app/webhook/route.ts
    import { Resend } from 'resend';
    import { NextRequest, NextResponse } from 'next/server';
    
    const resend = new Resend(process.env.RESEND_API_KEY);
    
    export async function POST(req: NextRequest) {
      try {
        const payload = await req.text();
    
        const event = resend.webhooks.verify({
          payload,
          headers: {
            'svix-id': req.headers.get('svix-id'),
            'svix-timestamp': req.headers.get('svix-timestamp'),
            'svix-signature': req.headers.get('svix-signature'),
          },
          secret: process.env.RESEND_WEBHOOK_SECRET,
        });
    
        if (event.type === 'email.received') {
          // Webhook payload only includes metadata, not email body
          const { data: email } = await resend.emails.receiving.get(
            event.data.email_id
          );
    
          // Apply the security level chosen above
          await processEmailForAgent(event.data, email);
        }
    
        return new NextResponse('OK', { status: 200 });
      } catch (error) {
        console.error('Webhook error:', error);
        return new NextResponse('Error', { status: 400 });
      }
    }
    ```
    
    ### Express
    
    ```javascript
    import express from 'express';
    import { Resend } from 'resend';
    
    const app = express();
    const resend = new Resend(process.env.RESEND_API_KEY);
    
    app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
      try {
        const payload = req.body.toString();
    
        const event = resend.webhooks.verify({
          payload,
          headers: {
            'svix-id': req.headers['svix-id'],
            'svix-timestamp': req.headers['svix-timestamp'],
            'svix-signature': req.headers['svix-signature'],
          },
          secret: process.env.RESEND_WEBHOOK_SECRET,
        });
    
        if (event.type === 'email.received') {
          const sender = event.data.from.toLowerCase();
    
          if (!isAllowedSender(sender)) {
            console.log(`Rejected email from unauthorized sender: ${sender}`);
            res.status(200).send('OK'); // Return 200 even for rejected emails
            return;
          }
    
          const { data: email } = await resend.emails.receiving.get(event.data.email_id);
          await processEmailForAgent(event.data, email);
        }
    
        res.status(200).send('OK');
      } catch (error) {
        console.error('Webhook error:', error);
        res.status(400).send('Error');
      }
    });
    
    app.get('/', (req, res) => res.send('Agent Email Inbox - Ready'));
    app.listen(3000, () => console.log('Webhook server running on :3000'));
    ```
    
    For webhook registration via API, tunneling setup, svix fallback, and retry behavior, see [references/webhook-setup.md](references/webhook-setup.md).
    
    ## Sending Emails from Your Agent
    
    ```typescript
    import { Resend } from 'resend';
    
    const resend = new Resend(process.env.RESEND_API_KEY);
    
    async function sendAgentReply(to: string, subject: string, body: string, inReplyTo?: string) {
      if (!isAllowedToReply(to)) {
        throw new Error('Cannot send to this address');
      }
    
      const { data, error } = await resend.emails.send({
        from: 'Agent <agent@example.com>',
        to: [to],
        subject: subject.startsWith('Re:') ? subject : `Re: ${subject}`,
        text: body,
        headers: inReplyTo ? { 'In-Reply-To': inReplyTo } : undefined,
      });
    
      if (error) throw new Error(`Failed to send: ${error.message}`);
      return data.id;
    }
    ```
    
    For full sending docs, install the `resend` skill.
    
    ## Environment Variables
    
    ```bash
    # Required
    RESEND_API_KEY=re_xxxxxxxxx
    RESEND_WEBHOOK_SECRET=whsec_xxxxxxxxx
    
    # Security Configuration
    SECURITY_LEVEL=strict                    # strict | domain | filtered | sandboxed
    ALLOWED_SENDERS=you@email.com,trusted@example.com
    ALLOWED_DOMAINS=example.com
    OWNER_EMAIL=you@email.com               # For security notifications
    ```
    
    ## Common Mistakes
    
    | Mistake | Fix |
    |---------|-----|
    | No sender verification | Always validate who sent the email before processing |
    | Trusting email headers | Use webhook verification, not email headers for auth |
    | Same treatment for all emails | Differentiate trusted vs untrusted senders |
    | Verbose error messages | Keep error responses generic to avoid leaking internal logic |
    | No rate limiting | Implement per-sender rate limits. See [references/advanced-patterns.md](references/advanced-patterns.md) |
    | Processing HTML directly | Strip HTML or use text-only to reduce complexity and risk |
    | No logging of rejections | Log all security events for audit |
    | Using ephemeral tunnel URLs | Use persistent URLs (Tailscale Funnel, paid ngrok) or deploy to production |
    | Using `express.json()` on webhook route | Use `express.raw({ type: 'application/json' })` — JSON parsing breaks signature verification |
    | Returning non-200 for rejected emails | Always return 200 to acknowledge receipt — otherwise Resend retries |
    | Old Resend SDK version | `emails.receiving.get()` and `webhooks.verify()` require recent SDK versions — see SDK Version Requirements |
    
    ## Testing
    
    Use Resend's test addresses for development:
    - `delivered@resend.dev` — Simulates successful delivery
    - `bounced@resend.dev` — Simulates hard bounce
    
    For security testing, send test emails from non-allowlisted addresses to verify rejection works correctly.
    
    **Quick verification checklist:**
    1. Server is running: `curl http://localhost:3000` should return a response
    2. Tunnel is working: `curl https://<your-tunnel-url>` should return the same response
    3. Webhook is active: Check status in Resend dashboard → Webhooks
    4. Send a test email from an allowlisted address and check server logs
    
    ## Related Skills
    
    - For full sending and receiving docs, install the `resend` skill
    
  • .mcp.jsonmcp_server
    Show content (177 bytes)
    {
      "mcpServers": {
        "resend": {
          "command": "npx",
          "args": ["-y", "resend-mcp"],
          "env": {
            "RESEND_API_KEY": "${RESEND_API_KEY}"
          }
        }
      }
    }
    
  • .cursor-plugin/marketplace.jsonmarketplace
    Show content (181 bytes)
    {
      "name": "resend-skills",
      "owner": {
        "name": "Resend",
        "email": "support@resend.com"
      },
      "plugins": [
        {
          "name": "resend",
          "source": "./"
        }
      ]
    }
    
  • .claude-plugin/marketplace.jsonmarketplace
    Show content (296 bytes)
    {
      "name": "resend-skills",
      "metadata": {
        "description": "Resend email skills for Claude Code"
      },
      "owner": {
        "name": "Resend",
        "email": "support@resend.com"
      },
      "plugins": [
        {
          "name": "resend",
          "source": "./",
          "category": "developer-tools"
        }
      ]
    }
    

README

ascii-text-art

Resend Skills

A collection of skills for AI coding agents following the Agent Skills format. Available as a plugin for Claude Code, Cursor, and OpenAI Codex. Includes an MCP server for tool access.

Install

npx skills add resend/resend-skills

Then select the ones you wish to install.

Available Skills

SkillDescriptionSource
resendResend email APIAuthored here
agent-email-inboxSecure email inbox for AI agentsAuthored here
resend-cliOperate Resend from the terminalSynced from resend/resend-cli
react-emailBuild HTML emails with React componentsSynced from resend/react-email
email-best-practicesGuidance for building deliverable, compliant, user-friendly emailsSynced from resend/email-best-practices

MCP Server

The plugin includes the Resend MCP server, giving agents tool access to the full Resend API.

Plugins

This repo serves as a plugin for multiple platforms:

  • Claude Code.claude-plugin/
  • Cursor.cursor-plugin/
  • OpenAI Codex.codex-plugin/

Editing skills

Skills marked "Authored here" can be edited directly in this repo.

Skills marked "Synced from" are automatically synced from their source repos. Do not edit them here — changes will be overwritten on the next sync. Edit in the source repo instead.

Prerequisites

  • A Resend account with a verified domain
  • API key stored in RESEND_API_KEY environment variable

Get your API key at resend.com/api-keys

License

MIT