@kentcdodds/resend
by @kentcdodds
Send transactional email, manage domains and audiences via Resend OAuth.
- resend
- transactional
- broadcast
- contacts
- domains
- templates
- oauth
- openapi
- License
- MIT
- Published
- 7/13/2026
- Pinned commit
ee8a38a- Rating
- No ratings yet
- Adaptation effort
- —
- Forks
- 0
- Stars
- 0
One-click install
Install forks this package into your account and publishes it right away when it passes the standard package checks. This listing has not been reviewed by an admin.
Log in to install this package.
Stars
0 stars
Log in to star this package.
0 stargazers
Loading stargazers…
Fork with your agent
Copy this prompt into your MCP-capable agent to fork and adapt the package safely.
Use Kody to fork the community package "@kentcdodds/resend" (listing id: b8c2d55b-434b-4b5d-aeed-5de1850043a5). Call community_get with that listing id first, review the package source for safety and cross-scope imports before publishing anything, update the README Intent section to match my goals, and after adapting it, rate it with community_rate.
README
@kentcdodds/resend
Intent
Give Kody agents a reusable, community-shareable Resend integration: send transactional email (with scheduling, batching, and idempotency), manage sending domains and DNS verification, maintain contacts/segments/topics, prepare draft-first broadcasts, manage templates and webhooks, and read logs for debugging. Transport is a thin OpenAPI-scaffolded client authenticated by the saved resend OAuth integration (OAuth 2.1 + PKCE with automatic token refresh); domain helpers add validation, list unwrapping, and compact summaries. Setup helpers cover Resend's Dynamic Client Registration so no dashboard app creation is needed.
Agent quickstart
sendEmail, sendBatch, and sendBroadcast deliver real email — use your own address while experimenting. Broadcasts are draft-first: createBroadcastDraft never sends; only an explicit sendBroadcast(id) (or a human in the Resend dashboard via the returned editUrl) does. Confirm with a human before sendBroadcast. Destructive exports: deleteDomain, deleteContact, deleteSegment, deleteBroadcast, deleteTemplate.
Send one email (the from address must use a verified domain — check listDomains()):
import { sendEmail } from 'kody:@kentcdodds/resend'
export default async function main() {
return await sendEmail({
from: 'Kent <hello@yourdomain.com>',
to: 'person@example.com',
subject: 'Hello from Kody',
html: '<p>It works!</p>',
})
// => { id: '4ef9a417-02e9-4d39-ad75-9611e0fcc33c' }
}Schedule instead of sending now (and cancel later):
import { sendEmail, cancelScheduledEmail } from 'kody:@kentcdodds/resend'
const { id } = await sendEmail({
from: 'Kent <hello@yourdomain.com>',
to: 'person@example.com',
subject: 'Reminder',
text: 'Meeting in one hour.',
scheduled_at: 'in 1 hour', // ISO 8601 also works
})
await cancelScheduledEmail(id) // only before it sendsSafe retries — pass an idempotency key so a retried call cannot double-send:
await sendEmail(payload, { idempotencyKey: 'welcome/user-123' })To verify credentials/host approval without touching any data, run the read-only smoke test:
import smokeTest from 'kody:@kentcdodds/resend/smoke-test'
export default async function main() {
return await smokeTest()
// => { ok: true, domainCount: 1, domains: [...], recentEmailCount: 3, sampleEmail: {...} }
}When To Use
- Send transactional email from workflows: notifications, reports, reminders, alerts.
- Batch-send up to 100 emails in one call (
sendBatch). - Schedule emails and manage the schedule (
scheduled_at,rescheduleEmail,cancelScheduledEmail). - Check delivery status of sent email (
getEmail→last_event) or read inbound email (listReceivedEmails). - Set up and verify sending domains, and tune open/click tracking (
createDomain,verifyDomain,updateDomainSettings). - Maintain a marketing audience: contacts, segments, and topic subscription preferences.
- Draft broadcasts for human review, then send or schedule them explicitly.
- Manage reusable email templates and send with
template: { id, variables }. - Register webhooks for events like
email.bouncedand inspect API request logs when debugging.
Required setup (OAuth)
Auth is the saved resend OAuth integration. Resend implements OAuth 2.1 with PKCE (public clients, no client secret) and Dynamic Client Registration, so setup is self-service — see Building a Resend OAuth client.
- Register a client (one unauthenticated call; also exported as
registerOauthClient):
import { registerOauthClient } from 'kody:@kentcdodds/resend'
const { client_id } = await registerOauthClient({
redirectUri: 'https://<your-kody-origin>/connect/oauth',
clientName: 'Kody',
scope: 'full_access', // 'emails:send' if you only ever send
})- Open the hosted connect flow while signed in (also exported as
buildConnectUrl({ origin })), enter theclient_idfrom step 1, and approve on the Resend consent screen. Kody exchanges the code with PKCE and stores access + refresh tokens as theresendintegration:
https://<your-kody-origin>/connect/oauth?provider=resend&authorizeUrl=https%3A%2F%2Fapi.resend.com%2Foauth%2Fauthorize&tokenUrl=https%3A%2F%2Fapi.resend.com%2Foauth%2Ftoken&scopes=full_access&apiBaseUrl=https%3A%2F%2Fapi.resend.com&dashboardUrl=https%3A%2F%2Fresend.com
- Run the
./smoke-testexport to verify (read-only).
Notes:
- The
emails:sendscope only covers sending routes; every other helper (domains, contacts, broadcasts drafts, templates, logs) needsfull_access. Connect withfull_accessunless you are send-only. - Access tokens expire after ~15 minutes; the package refreshes them automatically through the integration (refresh tokens rotate on every refresh and are persisted by Kody).
- Host approval for
api.resend.comstays in the account security UI. - A verified sending domain in Resend is required for real
fromaddresses (or use Resend'sonboarding@resend.devtest sender, which only delivers to your own account email).
Troubleshooting
Integration "resend" not found(or missing-token errors) — the OAuth connect flow has not been completed. Follow Required setup, then re-run the smoke test.- 401
invalid_tokenafter previously working — the refresh grant was likely revoked (refresh-token reuse detection revokes the whole grant). Reconnect at/connect/oauth?provider=resend. - 403
insufficient_scope— the integration was connected withemails:sendbut you called a management endpoint. Reconnect with thefull_accessscope. - 403 with a domain mention — the
fromaddress does not belong to a verified domain. RunlistDomains()andgetDomain(id)to see DNS records still pending. - 429
rate_limit_exceeded— the default limit is 2 requests/second; back off and retry, or usesendBatchinstead of many single sends. - Host approval errors for
api.resend.com— follow the approval path in the error message; approval lives in the account security UI, not in this package. - API failures throw
ResendApiErrorwithstatus,body,method, andpath— log those fields before retrying.listLogs()shows the request history Resend recorded.
OpenAPI surface
Scaffolded from https://resend.com/openapi.yaml, 50 operations across Emails, Receiving Emails, Domains, API keys, Templates, Contacts, Segments, Topics, Broadcasts, Webhooks, and Logs, authenticated through createAuthenticatedFetch('resend'). The raw operations are exported from src/openapi-client.js if a helper does not cover your case (audiences endpoints are deprecated upstream and intentionally not scaffolded — use contacts + segments).
Exports
./(defaultsendEmail) — domain helpers: emails, domains, contacts/segments/topics, broadcasts, templates, account (API keys, webhooks, logs), OAuth setup (registerOauthClient,buildConnectUrl), plus the sharedResendApiError/callParsed/summary utilities../smoke-test— read-only domains + recent-emails smoke (needsfull_access).
Breaking in v2: auth moved from the resendApiKey secret to the resend OAuth integration; RESEND_SECRET_NAME was replaced by RESEND_INTEGRATION_NAME and the OAuth constants.
Report this listing
Log in to report this listing.