Open and Click Tracking
Open and click tracking is disabled by default for all domains. We suggest only tracking open rates for Broadcasts to ensure that inbox providers do not mistakenly identify your transactional emails as marketing emails.
You can enable tracking in the Resend Dashboard under the Configuration tab or programmatically when you create a new domain or update an existing domain.
Once verified, all tracked links in your emails will use your tracking subdomain (e.g., links.emails.example.com).
Go to the Domains page and click on the domain you want to configure.
In the Configuration tab, click Configure under Enable tracking metrics.

Provide a name for your tracking subdomain, enable open and/or click tracking, and click +Add domain.

Add the CNAME record to your DNS settings (e.g. Cloudflare, GoDaddy, etc.) to verify your tracking subdomain.

Click I've added the records to verify the domain.
Once verified, all tracked links in your emails will use your custom tracking subdomain.
You can add a tracking subdomain when creating a new domain by passing the tracking_subdomain field along with click_tracking and/or open_tracking.
import { Resend } from 'resend';
const resend = new Resend('re_xxxxxxxxx');
const { data, error } = await resend.domains.create({
name: 'example.com',
openTracking: true,
clickTracking: true,
trackingSubdomain: 'links',
});$resend = Resend::client('re_xxxxxxxxx');
$resend->domains->create([
'name' => 'example.com',
'open_tracking' => true,
'click_tracking' => true,
'tracking_subdomain' => 'links',
]);import resend
resend.api_key = "re_xxxxxxxxx"
params: resend.Domains.CreateParams = {
"name": "example.com",
"open_tracking": True,
"click_tracking": True,
"tracking_subdomain": "links",
}
resend.Domains.create(params)require "resend"
Resend.api_key = "re_xxxxxxxxx"
params = {
name: "example.com",
open_tracking: true,
click_tracking: true,
tracking_subdomain: "links",
}
Resend::Domains.create(params)package main
import "github.com/resend/resend-go/v4"
func main() {
client := resend.NewClient("re_xxxxxxxxx")
params := &resend.CreateDomainRequest{
Name: "example.com",
OpenTracking: resend.Bool(true),
ClickTracking: resend.Bool(true),
TrackingSubdomain: "links",
}
client.Domains.Create(params)
}use resend_rs::{types::CreateDomainOptions, Resend, Result};
#[tokio::main]
async fn main() -> Result<()> {
let resend = Resend::new("re_xxxxxxxxx");
let _domain = resend
.domains
.create(
CreateDomainOptions::new("example.com")
.with_open_tracking(true)
.with_click_tracking(true)
.with_tracking_subdomain("links"),
)
.await?;
Ok(())
}import com.resend.*;
import com.resend.services.domains.model.CreateDomainOptions;
Resend resend = new Resend("re_xxxxxxxxx");
CreateDomainOptions params = CreateDomainOptions.builder()
.name("example.com")
.openTracking(true)
.clickTracking(true)
.trackingSubdomain("links")
.build();
resend.domains().create(params);using Resend;
IResend resend = ResendClient.Create( "re_xxxxxxxxx" );
var resp = await resend.DomainAddAsync( new DomainAddData()
{
DomainName = "example.com",
OpenTracking = true,
ClickTracking = true,
TrackingSubdomain = "links",
} );curl -X POST 'https://api.resend.com/domains' \
-H 'Authorization: Bearer re_xxxxxxxxx' \
-H 'Content-Type: application/json' \
-d $'{
"name": "example.com",
"open_tracking": true,
"click_tracking": true,
"tracking_subdomain": "links"
}'If you already have a domain, you can call the update endpoint to add a tracking subdomain and enable tracking.
import { Resend } from 'resend';
const resend = new Resend('re_xxxxxxxxx');
const { data, error } = await resend.domains.update({
id: 'd91cd9bd-1176-453e-8fc1-35364d380206',
openTracking: true,
clickTracking: true,
trackingSubdomain: 'links',
});$resend = Resend::client('re_xxxxxxxxx');
$resend->domains->update(
'd91cd9bd-1176-453e-8fc1-35364d380206',
[
'open_tracking' => true,
'click_tracking' => true,
'tracking_subdomain' => 'links',
]
);import resend
resend.api_key = "re_xxxxxxxxx"
params: resend.Domains.UpdateParams = {
"id": "d91cd9bd-1176-453e-8fc1-35364d380206",
"open_tracking": True,
"click_tracking": True,
"tracking_subdomain": "links",
}
resend.Domains.update(params)require "resend"
Resend.api_key = "re_xxxxxxxxx"
Resend::Domains.update({
id: "d91cd9bd-1176-453e-8fc1-35364d380206",
open_tracking: true,
click_tracking: true,
tracking_subdomain: "links",
})package main
import "github.com/resend/resend-go/v4"
func main() {
client := resend.NewClient("re_xxxxxxxxx")
params := &resend.UpdateDomainRequest{
OpenTracking: true,
ClickTracking: true,
TrackingSubdomain: "links",
}
client.Domains.Update("d91cd9bd-1176-453e-8fc1-35364d380206", params)
}use resend_rs::{types::DomainChanges, Resend, Result};
#[tokio::main]
async fn main() -> Result<()> {
let resend = Resend::new("re_xxxxxxxxx");
let changes = DomainChanges::new()
.with_open_tracking(true)
.with_click_tracking(true)
.with_tracking_subdomain("links");
let _domain = resend
.domains
.update("d91cd9bd-1176-453e-8fc1-35364d380206", changes)
.await?;
Ok(())
}import com.resend.*;
import com.resend.services.domains.model.UpdateDomainOptions;
Resend resend = new Resend("re_xxxxxxxxx");
UpdateDomainOptions params = UpdateDomainOptions.builder()
.id("d91cd9bd-1176-453e-8fc1-35364d380206")
.openTracking(true)
.clickTracking(true)
.trackingSubdomain("links")
.build();
resend.domains().update(params);using Resend;
IResend resend = ResendClient.Create( "re_xxxxxxxxx" );
await resend.DomainUpdateAsync(
new Guid( "d91cd9bd-1176-453e-8fc1-35364d380206" ),
new DomainUpdateData()
{
TrackOpen = true,
TrackClicks = true,
TrackingSubdomain = "links",
}
);curl -X PATCH 'https://api.resend.com/domains/d91cd9bd-1176-453e-8fc1-35364d380206' \
-H 'Authorization: Bearer re_xxxxxxxxx' \
-H 'Content-Type: application/json' \
-d $'{
"open_tracking": true,
"click_tracking": true,
"tracking_subdomain": "links"
}'Whether creating or updating a domain, the API will return the required DNS records that need to be added to your domain provider's DNS settings. Note the Tracking record.
{ "id": "4dd369bc-aa82-4ff3-97de-514ae3000ee0", "name": "example.com", "created_at": "2026-03-28 17:12:02.059593+00", "status": "not_started", "open_tracking": true, "click_tracking": true, "tracking_subdomain": "links", "capabilities": { "sending": "enabled", "receiving": "disabled" }, "records": [ // ... other records ... { "record": "Tracking", "name": "links.example.com", "type": "CNAME", "value": "links1.resend-dns.com", "ttl": "Auto", "status": "not_started" } ], "region": "us-east-1"}Add the required CNAME record to your domain provider's DNS settings (e.g. Cloudflare, GoDaddy, etc.).
Once you've added the required DNS record(s), call the verify endpoint to verify the domain.
import { Resend } from 'resend';
const resend = new Resend('re_xxxxxxxxx');
const { data, error } = await resend.domains.verify(
'd91cd9bd-1176-453e-8fc1-35364d380206',
);$resend = Resend::client('re_xxxxxxxxx');
$resend->domains->verify('d91cd9bd-1176-453e-8fc1-35364d380206');import resend
resend.api_key = "re_xxxxxxxxx"
resend.Domains.verify(domain_id="d91cd9bd-1176-453e-8fc1-35364d380206")require "resend"
Resend.api_key = "re_xxxxxxxxx"
Resend::Domains.verify("d91cd9bd-1176-453e-8fc1-35364d380206")package main
import "github.com/resend/resend-go/v4"
func main() {
client := resend.NewClient("re_xxxxxxxxx")
client.Domains.Verify("d91cd9bd-1176-453e-8fc1-35364d380206")
}use resend_rs::{Resend, Result};
#[tokio::main]
async fn main() -> Result<()> {
let resend = Resend::new("re_xxxxxxxxx");
resend
.domains
.verify("d91cd9bd-1176-453e-8fc1-35364d380206")
.await?;
Ok(())
}import com.resend.*;
import com.resend.services.domains.model.VerifyDomainResponse;
Resend resend = new Resend("re_xxxxxxxxx");
VerifyDomainResponse verified = resend.domains().verify("d91cd9bd-1176-453e-8fc1-35364d380206");using Resend;
IResend resend = ResendClient.Create( "re_xxxxxxxxx" );
await resend.DomainVerifyAsync( new Guid( "d91cd9bd-1176-453e-8fc1-35364d380206" ) );curl -X POST 'https://api.resend.com/domains/d91cd9bd-1176-453e-8fc1-35364d380206/verify' \
-H 'Authorization: Bearer re_xxxxxxxxx' \
-H 'Content-Type: application/json'All tracked links in your emails will use your tracking subdomain.
View the API reference for more details.
How Open Tracking Works
Section titled “How Open Tracking Works”A 1x1 pixel transparent GIF image is inserted in each email and includes a unique reference to this image file. When the image is downloaded, Resend can tell exactly which message was opened and by whom.
How Click Tracking Works
Section titled “How Click Tracking Works”To track clicks, Resend modifies each link in the body of the HTML email to point to your tracking subdomain.
When recipients clicks a link, the request is redirected to your tracking subdomain, their click event is recorded, and they are redirected to the original URL.
Troubleshooting
Section titled “Troubleshooting”Multiple tracking CNAME records
Section titled “Multiple tracking CNAME records”When you change your tracking subdomain, you may see two CNAME records in the DNS Records table: one active and one inactive (shown dimmed). The inactive record is kept intentionally to avoid breaking links in emails that were already sent using the previous subdomain. You do not need to remove it.
Changing the Tracking Subdomain
Section titled “Changing the Tracking Subdomain”Because tracking subdomains are used in email links, they're handled differently than other records.
- Not removable: After your tracking subdomain has been created, it can only be changed, never removed. This behavior preserves any email links that may already be sent with the current tracking subdomain. For this reason, do not remove old tracking DNS records. (All previously used records remain active and are included in the response.)
- Requires verification: After changing the tracking subdomain, a new DNS record must be verified. Until then, the previous value is used.
Removing a Domain with a Tracking Subdomain
Section titled “Removing a Domain with a Tracking Subdomain”If you remove a domain with a configured tracking subdomain, the Resend provisioned proxy will also be removed, breaking any existing email links that use that subdomain.
To keep links working, set up your own proxy pointing to Resend’s tracking DNS records before removing the domain.
Unsupported Domains
Section titled “Unsupported Domains”Custom tracking subdomains require us to issue a TLS certificate. Some domains under regional restrictions are not supported by our certificate provider and cannot use custom tracking subdomains. Learn more in the certificate provider docs.