Tracking and unsubscribe
Opens, clicks and unsubscribes are counted by Runearc itself, with signed links and no third party. Settings are per workspace.
How it works
When tracking is on, Runearc adds a 1 pixel image to the HTML for opens and rewrites links to a signed redirect
for clicks. Both use tokens that identify the message and recipient; a forged token is rejected. Hits from link
scanners, mail security gateways and preview bots are stored with is_bot: true and a reason, and never
counted. The public routes are GET /t/o/{token} (the pixel), GET /t/c/{token} (a 302 to the original URL)
and GET, POST /t/u/{token} (the unsubscribe page and the one click endpoint). They need no key.
Marketing mail (campaigns, sequences, or an email sent with the tag
category set to marketing) also gets an unsubscribe link and a List-Unsubscribe header, is refused for
unsubscribed recipients with recipient_unsubscribed, and honours the frequency cap. Transactional mail is
never blocked by either.
Settings
curl -X PATCH https://api.runearc.com/v1/tracking/settings \
-H "Authorization: Bearer $RUNEARC_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "track_opens": true, "track_clicks": true,
"frequency_cap": { "count": 3, "hours": 24 } }'| Field | Type | Description |
|---|---|---|
| track_opens | boolean | Default true. |
| track_clicks | boolean | Default true. |
| unsubscribe_enabled | boolean | Default true. Adds the link and header to marketing mail. |
| strict_bot_filter | boolean | Default true. Flags more suspicious hits, such as several links clicked within seconds. |
| frequency_cap | object | null | { "count": 1..1000, "hours": 1..8760 }: at most that many marketing emails per recipient in the window. null removes it. Default none. |
GET /v1/tracking/settings reads them and PATCH /v1/tracking/settings changes the keys you send. Changing
needs a key or role that manages domains.
Tracking domain
Links use a shared Runearc host by default. To put them on your own host, set a subdomain such as
track.acme.com and add the CNAME it returns in cname_target, then verify.
| Request | Body | Returns |
|---|---|---|
| GET /v1/tracking/domain | none | hostname, status (pending or verified), cname_target, verified_at. |
| PUT /v1/tracking/domain | hostname | The domain, pending. |
| POST /v1/tracking/domain/verify | none | Checks the CNAME in live DNS. Verified, or tracking_cname_missing. |
| DELETE /v1/tracking/domain | none | 204. Back to the shared host. |
Read what was counted
| Request | Query | Returns |
|---|---|---|
| GET /v1/emails/{id}/tracking | none | opens, unique_opens, clicks, unique_clickers, unsubscribes, flagged_opens, flagged_clicks. |
| GET /v1/tracking/events | message_id, contact_id, kind (open, click, unsubscribe), include_bots, limit | data: id, message_id, contact_id, recipient, kind, url, is_bot, bot_reason, created_at. |
Totals over time, by campaign or by sequence are on Analytics.
CLI and MCP
runearc tracking show
runearc tracking set --opens on --clicks on --unsubscribe on --strict on --cap 3/24 # or --no-cap
runearc tracking domain-set track.acme.com
runearc tracking domain-verify
runearc tracking domain-delete
runearc tracking email eml_...MCP tools: get_tracking_settings, update_tracking_settings and get_email_tracking.
Errors
| Code | Status | Meaning and fix |
|---|---|---|
| invalid_frequency_cap | 400 | Use 1 to 1000 emails per 1 to 8760 hours, or null. |
| invalid_tracking_domain | 400 | Use a subdomain you control, such as track.acme.com, not the bare domain. |
| tracking_domain_taken | 409 | Another workspace uses that hostname. |
| tracking_cname_missing | 400 | The CNAME is not in DNS yet. Add it, wait, verify again. |
| recipient_unsubscribed | 400 | A recipient of a marketing email unsubscribed. Remove them, or send it as transactional. |
| frequency_cap_reached | 402 | A recipient reached the cap. Skip them or raise the cap. |