Developer tools should give developers power.
You should have control that's predictable and accessible from any surface you prefer: the dashboard, MCP, CLI, or the API. Humans and agents alike should have equal access with the developer as the operator.
Earlier this summer, we mapped several interactions that required the Resend Dashboard and started slowly closing the gap for each one.
Today, I'm excited to share how far we've come. Most of what you manage in the dashboard is now accessible through the MCP, CLI, or the API. That's the direction we're committed to, and it's what we mean by headless:
Everything the dashboard can do should also be available programmatically.
The initiative lets you:
We'll make the dashboard best-in-class, while also committing to full headless operation for every future entity. Use the dashboard when it makes sense. Build your own custom experiences when it doesn't.
While many on the team have contributed to get us to this point, three people in particular deserve a special call-out: Diel Duarte, Gabriel Miranda, and Felipe Freitag.
Here are the key changes we've released in the last several months.
The Email Metrics API returns deliverability and engagement data for your emails, from send volume to unsubscribe rate, grouped or filtered by period, domain, broadcast, or email. It powers:
A request with no parameters returns every metric over the past 7 days.
import { Resend } from 'resend';const resend = new Resend('re_xxxxxxxxx');const { data } = await resend.emails.metrics()
Learn more about the Email Metrics API or read the changelog.

Email Metrics API Changelog
Read the full changelog for the Email Metrics API.
resend.com/changelog/email-metrics-api
Everything the webhook detail page does is now available in the API, every SDK, the MCP server, and the CLI:
Together these let you list failed events and replay them in a loop, page your team when deliveries fail, rotate secrets from CI, or connect the MCP server and ask your agent what failed.
Start by listing the events delivered to a webhook. Each one carries a delivery status of success, failed, attempting, or pending.
import { Resend } from 'resend';const resend = new Resend('re_xxxxxxxxx');const { data, error } = await resend.webhooks.events.list({webhookId: '4dd369bc-aa82-4ff3-97de-514ae3000ee0',});
Learn more about the Headless Webhook API or read the changelog.

Headless Webhook API Changelog
Read the full changelog for the Headless Webhook API.
resend.com/changelog/headless-webhook-api
The Cancel Broadcast API allows you to cancel a Broadcast programmatically. You can use it to stop a Broadcast when it's still scheduled or during sending before it finishes.
To cancel a Broadcast, use the Broadcast's ID with the cancel endpoint.
import { Resend } from 'resend';const resend = new Resend('re_xxxxxxxxx');const { data, error } = await resend.broadcasts.cancel('559ac32e-9ef5-46fb-82a1-b76b840c0f7b',);
Learn more about the Cancel Broadcast API or read the changelog.

Cancel Broadcast API Changelog
Read the full changelog for the Cancel Broadcast API.
resend.com/changelog/cancel-broadcast-api
The Share Email API returns a link to a read-only view of any sent or received email, viewable by anyone with the link.
Use the ID of any sent or received email. The API returns the full share URL, and links expire after a duration you control. Pass any duration up to 48 hours, like 10m, 2 hours, or 1 day. It defaults to 48 hours.
import { Resend } from 'resend';const resend = new Resend('re_xxxxxxxxx');const { data, error } = await resend.emails.share('49a3999c-0ce1-4ea6-ab68-afcd6dc2e794',{ expiresIn: '2 hours' },);
Learn more about the Share Email API or read the changelog.

Share Email API Changelog
Read the full changelog for the Share Email API.
resend.com/changelog/share-email-api
The new duplicate Automation endpoint creates a copy of an existing Automation, named after the original with a (Copy) suffix. The copy starts as a disabled draft, so nothing runs until you review and publish it.
To duplicate an Automation, use the Automation's ID with the duplicate endpoint.
import { Resend } from 'resend';const resend = new Resend('re_xxxxxxxxx');const { data, error } = await resend.automations.duplicate('c9b16d4f-ba6c-4e2e-b044-6bf4404e57fd',);
Learn more about the Duplicate Automation API or read the changelog.

Duplicate Automation API Changelog
Read the full changelog for the Duplicate Automation API.
resend.com/changelog/duplicate-automation-api
You could already create, retrieve, list, and delete segments from the API. The new update endpoint completes the set, so you can rename a segment from the API, CLI, or any official SDK.
import { Resend } from 'resend';const resend = new Resend('re_xxxxxxxxx');const { data, error } = await resend.segments.update('78261eea-8f8b-4381-83c6-79fa7120f1cf',{name: 'Active Users',},);
Learn more about the Update Segment API or read the changelog.

Update Segment API Changelog
Read the full changelog for the Update Segment API.
resend.com/changelog/update-segment-api
The List Clicked Links API returns every link clicked in a Broadcast with its total and unique clicks, ranked by total clicks. Use it to feed your own dashboards, compare CTAs across campaigns, or surface your top-performing links to inform future emails. Click tracking must be enabled for your domain.
import { Resend } from 'resend';const resend = new Resend('re_xxxxxxxxx');const { data, error } = await resend.broadcasts.clickedLinks('559ac32e-9ef5-46fb-82a1-b76b840c0f7b',{ limit: 20 },);
Learn more about the List Clicked Links API or read the changelog.

List Clicked Links API Changelog
Read the full changelog for the List Clicked Links API.
resend.com/changelog/list-clicked-links-api
The Update API Key endpoint renames an API key in place. The token stays the same, and so do the key's permission and domain restriction, so you can keep key names in sync with a renamed project or clean up naming conventions from a script without rotating secrets.
import { Resend } from 'resend';const resend = new Resend('re_xxxxxxxxx');const { data, error } = await resend.apiKeys.update('b6d24b8e-af0b-4c3c-be0c-359bbd97381e',{ name: 'Production' },);
Learn more about the Update API Key Name endpoint or read the changelog.

Update API Key Name Changelog
Read the full changelog for the Update API Key Name endpoint.
resend.com/changelog/update-api-key-name
Most new Broadcasts start from one you've already sent. The new duplicate Broadcast endpoint creates a copy of any Broadcast, including ones that have already been sent. The copy keeps the segment, topic, sender, subject, reply-to, preview text, and content, and starts as a draft so nothing goes out until you review and send it.
To duplicate a Broadcast, use the Broadcast's ID with the duplicate endpoint.
import { Resend } from 'resend';const resend = new Resend('re_xxxxxxxxx');const { data, error } = await resend.broadcasts.duplicate('559ac32e-9ef5-46fb-82a1-b76b840c0f7b',);
Learn more about the Duplicate Broadcast API or read the changelog.

Duplicate Broadcast API Changelog
Read the full changelog for the Duplicate Broadcast API.
resend.com/changelog/duplicate-broadcast-api
Now that we've brought compatibility between all the different services, our next goal is to give you more precise control.
For everything from API key provisioning to webhook filtering and scoping, we're working hard to make Resend the best experience for developers and agents.
In the meantime, you can start building today: