Email feedback tracking

You need: email composed and provisioned.

Eight event types

The template model is what declares a link as trackable, the job model is the row events attach to, bounces and suppression is what a complaint becomes, and deliverability is what the numbers are telling you.

EventWritten by
sentThe send Workflow
openThe tracking pixel
clickA tracked link
unsubscribeThe opt-out callback
bounce, complaintThe inbound handler
suppressedA send that was skipped
failedA terminal send error

They live in your own database, keyed on the same normalized recipient the job is — so a job and its events agree on who the person is.

Per-campaign stats are one query

Pass a campaignId on any send and a single grouped query gives you the whole funnel — sent, opened, clicked, bounced, complained, unsubscribed.

Types with no events come back as zero rather than absent, derived from the enum so a new event type cannot be forgotten.

A template names the payload locations that hold URLs, each with a label, and the engine rewrites those to the tracking callback. The label is what lands on the click event for attribution.

A location is a top-level key — "url" — or a field on each element of an array — "articles[].link".

The callbacks keep a fixed prefix

/_pithy/email/c/:token · /_pithy/email/o/:token · /_pithy/email/u/:token

They keep /_pithy/email whatever basePath says, because a tracking URL is minted into mail already sitting in somebody’s inbox — and moving the route breaks every link ever sent.

All three are public, gated by the signature on the token in the path.

Unsubscribe answers both verbs

A one-click unsubscribe header sends a POST; a person clicking the link sends a GET.

Refusing either refuses somebody trying to leave.

The cross-environment limit

Links point at the sending environment’s Worker, so a production campaign’s opens, clicks and unsubscribes land in production’s database.

Bounces do not follow that rule. There is one inbound Worker per domain, and it is production’s — so feature and staging environments see their own opens, clicks and unsubscribes but not asynchronously-routed bounces.

A platform constraint stated rather than discovered.

Tracking defaults follow the category

category drives them — a marketing template and a transactional one do not want the same defaults, and the template says which it is.

It lands on an event row and it is queryable. A label is for attribution — "cta", "article-3" — not for anything about the recipient.

ESC