Skip to main content

Overview

This agent drafts replies to the email in a Resend Inboxes inbox and posts each draft to your Slack DMs for approval. Every email chain gets its own Slack thread. Click Send to reply from the inbox address, Don’t send to drop the draft, or reply in the thread (“shorter”, “say we’re closed over Christmas”) to get a new draft. Later emails in the same chain land in the same Slack thread. Tech stack: Features:
  • A person sends every reply: the model has no tools, so a hostile email can only change the words of a draft you read first.
  • Feedback in the thread: replying to a draft writes a new one that takes your note into account.
  • One run per email chain: a run waits days for your click without holding a machine or a concurrency slot.
  • Sender allowlist: ALLOWED_SENDER_DOMAINS drops unknown senders before they reach Slack or cost a model call.
  • Idempotent sends: each inbound email gets at most one reply, even when a run retries.

GitHub repo

View the Resend email agent repo

Click here to view the full code for this project in our examples repository on GitHub. You can fork it and use it as a starting point for your own project.

How it works

Each email chain gets one long-running email-thread run that sleeps on a wait token between events. The token is tagged with the email thread id and the Slack message timestamp, so a webhook from either side can find it and wake the run.

Hosted webhook endpoints

Two endpoints receive events, one for Resend and one for Slack. Trigger.dev checks each request’s signature before your code runs, and the Slack source answers Slack’s url_verification handshake.
src/trigger/endpoints.ts

Waiting for approval

After posting a draft, the run creates a wait token and waits on it. The token is created before the draft is posted, so a fast click always has something to complete. Each button carries the token id.
src/trigger/email-thread.ts

Waking the run

The webhook tasks find the waiting token by tag and complete it with what happened: a button click, a reply in the thread, or a new email. The run then sends the reply, drops it, or redrafts with your note, and waits again.
src/trigger/inbound.ts

Running the project

You need a Trigger.dev project, a Resend inbox on a verified domain, a Slack workspace where you can create apps, and a Vercel AI Gateway key.
1

Clone the example and install

Set TRIGGER_PROJECT_REF in .env to your project’s ref, from the project settings page in the dashboard. Then run pnpm dev, which adds two endpoints, resend and slack, to your dev environment’s Webhooks page.
2

Connect Resend

Add a Resend webhook pointing at the resend endpoint URL with the inbox.email.received event. Paste its signing secret into the resend endpoint, and set RESEND_API_KEY.
3

Connect Slack

Put the slack endpoint URL in slack/manifest.yaml and create the app from it. Paste its Signing Secret into the slack endpoint, click Verify in Event Subscriptions, install the app, and set SLACK_BOT_TOKEN.
4

Set who approves and who can email

Set SLACK_USER_ID to your Slack member ID, ALLOWED_SENDER_DOMAINS to the domains you accept email from, and AI_GATEWAY_API_KEY. Restart pnpm dev.
5

Send a test email

Email your inbox from an allowed address. A DM from the bot shows the email with a draft in its thread, and the dev terminal shows a resend-email-received run followed by an email-thread run that stays waiting.
To deploy, run npx trigger.dev@latest deploy. Production has its own resend and slack endpoints, with their own URLs and secrets:
  1. Slack: create a second Slack app from the manifest with the production slack URL. Paste its Signing Secret into the production slack endpoint, then click Verify in the app’s Event Subscriptions. Install the app and use its bot token as SLACK_BOT_TOKEN in production. A Slack app sends its clicks and replies to one URL, so dev and production each need their own app.
  2. Resend: point a Resend webhook at the production resend URL and paste its signing secret into the production resend endpoint. Disable or delete the dev webhook, or use a separate inbox for dev, so the same email doesn’t get drafted twice.
  3. Set the other environment variables on your production environment as in dev.
An endpoint without its secret shows “Needs secret” on the Webhooks page.

Relevant code

Learn more

Webhooks

Hosted endpoints that verify and deliver events to your tasks.

Wait for token

Pause a run until an external event completes a token.

Webhook sources

Every built-in source, including resend(), and how each is verified.

Slack

The Slack source and channel in @trigger.dev/slack.