06 // Resend Email

The only library that receives email

Send mail, receive inbound replies, persist thread history, and query it fully typed. This full-stack library turns the Resend SDK up 10 notches.

The Fragno skill will guide your agent through the installation process:

npx skills add https://github.com/rejot-dev/fragno --skill fragno

Ask your agent

Tell your agent to "Integrate the Resend fragment into my application"

The Code

01_INITIALIZATION

Configure credentials, defaults, and callbacks once. Then start using it from your frontend immediately (see next tab).

01_INITIALIZATION
import { createResendFragment } from "@fragno-dev/resend-fragment";import { databaseAdapter } from "@/lib/db";export const resendFragment = createResendFragment(  {    apiKey: process.env.RESEND_API_KEY!,    webhookSecret: process.env.RESEND_WEBHOOK_SECRET!,    defaultFrom: "Support <support@example.com>",    onEmailReceived: async ({ threadId }) => {      console.log("Inbound →", threadId);    },  },  {    databaseAdapter,    mountRoute: "/api/resend",  },);

Features

Send email reliably

Trigger transactional email without blocking your synchronous request flow.

Webhooks handled

Signature verification, retries, and status syncing are packaged into the fragment.

Inbound email

Receive inbound mail and persist the actual message body and metadata locally.

Automatic threading

Resolve replies into thread timelines using reply tokens, headers, and subject heuristics.

Frontend hooks

Use typed hooks for domains, emails, threads, messages, and replies in your UI.

Canonical local store

Keep durable email and thread state in your own database instead of depending on provider retention.

Flow

The library integrates across every part of your stack: frontend, backend, and database. It handles communication with Resend for you. Instead of manually stitching together webhooks, retries, and local projections, you mount the fragment and simply use our hooks.

Inbound flow

  1. 1

    1. Resend receives an inbound email

    Resend posts an inbound webhook to the mounted webhook route.

  2. 2

    2. The fragment stores the inbound email in your database

    The fragment stores the inbound email in your database, then calls the onEmailReceived callback.

  3. 3

    3. Query from your frontend

    The frontend reads threads, messages, and delivery state through typed client hooks against your own backend.

BackendResend Routes/resend/webhook/email/received
FrontendClient-side hooksuseReceivedEmails()
Resend API
webhookEventsemailMessageDBWebhookEventsAPILayerResend integrationResend

Blueprint

The setup is deliberately small: create the server, mount the handlers, create a client, then build UI against typed thread and message hooks.

1. Create the fragment server

Configure credentials, defaults, and callbacks once.

import { createResendFragment } from "@fragno-dev/resend-fragment";import { databaseAdapter } from "@/lib/db";export const resendFragment = createResendFragment(  {    apiKey: process.env.RESEND_API_KEY!,    webhookSecret: process.env.RESEND_WEBHOOK_SECRET!,    defaultFrom: "Support <support@example.com>",    onEmailReceived: async ({ threadId }) => {      console.log("Inbound →", threadId);    },  },  {    databaseAdapter,    mountRoute: "/api/resend",  },);

2. Mount the routes

Expose the fragment through your framework once, then let the client call your app.

import { resendFragment } from "@/lib/resend";export const handlers = resendFragment.handlersFor("react-router");export const action = handlers.action;export const loader = handlers.loader;

3. Create the client

Point the client at the same mount route and get typed hooks back.

import { createResendFragmentClient } from "@fragno-dev/resend-fragment/react";export const resendClient = createResendFragmentClient({  mountRoute: "/api/resend",});

4. Build with thread hooks

Use local thread state instead of orchestrating raw webhook and provider APIs yourself.

function SupportInbox({ threadId }: { threadId: string }) {  const { data: threads } = resendClient.useThreads();  const { data: messages } = resendClient.useThreadMessages({    path: { threadId },  });  const { mutate: reply } = resendClient.useReplyToThread();  return (    <ThreadLayout threads={threads?.threads ?? []}>      <MessageTimeline messages={messages?.messages ?? []} />      <ReplyBox        onSend={(text) =>          reply({            path: { threadId },            body: { text },          })        }      />    </ThreadLayout>  );}

Interface

The library exposes a number of routes within your application. Some go to your own database, others go to Resend. The threading model is built into the library, so you can simply use the hooks to build your UI.

Route surface

// Used by Resend to post inbound webhooksPOST /webhook// Query domain readiness from ResendGET  /domainsGET  /domains/:domainId// Retrieve inbound and outbound emails from your own databaseGET  /emailsGET  /emails/:emailIdPOST /emails// Retrieve inbound emails directly from Resend (as long as retention allows)GET  /received-emailsGET  /received-emails/:emailId// Query threads and messages from your own databaseGET  /threadsPOST /threadsGET  /threads/:threadIdGET  /threads/:threadId/messagesPOST /threads/:threadId/reply
Fig. The Resend library integrated into our “Claw-like” backoffice.

Next steps

Ship email quickly, so you can focus on your business logic

Start with the quickstart, then move to the usage guide when you want to build inboxes, thread UIs, or callback-driven workflows. Or read the essay to see how and why we built this library.