Skip to content
Docs

Tracking guide

Click flow

Exactly what the edge redirect does with a click, in order, and the redirect modes it can answer with.

The edge redirect (services/edge-redirect) serves every tracking link. It is built for the hot path: it reads a cached configuration snapshot (no database call per click), and a click is decided in about a millisecond of handler time (p99 under 1.5 ms in the repository benchmark, with an alert budget of 8 ms). This page is the order of decisions; each step can short-circuit with a recorded reason.

The steps

  1. Resolve the link. The Host header selects the tenant and its legacy style. The path and query are parsed with the link codec into an offer reference, a partner reference and canonical parameters (aliases). test=1 marks the click as a test.
  2. Look up entities in the snapshot: offer, partner, landing pages, SmartLink. Unknown references answer 404. Impression links (/i/...) record the impression and return a 1x1 GIF without routing.
  3. Enrich. Visitor IP (from the socket, or X-Forwarded-For/X-Real-IP when the edge trusts its proxy with DT_EDGE_TRUST_PROXY=1), geography from your CDN's geo headers (X-Geo-Country, CF-IPCountry, CloudFront-Viewer-Country, X-Geo-Region, X-Geo-City, X-Geo-ISP), device, OS, browser and bot detection from the user agent, language from Accept-Language, and Sec-GPC. Partners flagged to allow overrides can pass ip_override and ua_override for server-to-server clicks.
  4. Status and access. The click is marked invalid, with a reason, when the offer is not active (offer_inactive) or outside its start and end dates (offer_expired), the partner is not active (partner_inactive) or is blocked on this offer (blocked_partner), or the link format is not allowed for the offer (traffic_filter).
  5. Rules. Tenant-wide traffic controls run first, then the offer's rule sets in priority order. Actions are allow, deny, fail_traffic (send elsewhere) and route_to. A rule set can name its own fail-traffic target and reason category. Bots are marked bot. See caps, rules and day-parting.
  6. Extension hooks. Lists and targeting, day-parting, and fraud scoring run here and may deny, mark or reroute the click.
  7. Caps. Click-metric caps are counted in Redis; a hard cap reached makes the click invalid with reason caps.
  8. Deduplication. The offer's session_definition (cookie, ip, ip_user_agent, gaid, idfa, click_id_only) builds a session key; the first click per key in the window is unique (unique_click_session_hours, default 24), later ones are duplicates and, if the offer's duplicate_click_action is block or fail_traffic, invalid with reason duplicate. See deduplication.
  9. Route. A valid click picks a landing page: the one named by url_id (id or title), otherwise the offer's default landing page, otherwise the first active one (pages marked private are visible only to the partners listed on them). A landing page with a split list spreads traffic over its URLs by weight. With no landing page the offer's destination is used. Macros in the destination are expanded (macros). An allowed url= deep link replaces the destination. An invalid click goes to its fail-traffic target, then the offer's fallback URL, fallback offer, the advertiser's fallback, the tenant's fallback, or a blank 204.
  10. Respond and record. The response is written per redirect mode, with Cache-Control: no-store, and an X-Click-Id header. The click event, including the decision and reason, is published to NATS (dt.click.created.v1, stream DT_CLICKS) and lands in ClickHouse. Invalid clicks are stored too.

What a click records

Every click carries: click_id (a ULID), offer, partner, advertiser, landing page, creative, SmartLink, tracking domain and app, link style, redirect mode and final destination, all canonical parameters (sub1 to sub15, source, device ids, platform click ids, utm_*), geography, device, flags (is_unique, is_duplicate, is_bot, is_proxy, is_test), fraud score and reasons, matched rule ids, cap state, and handler latency. The schema is click.

Media cost on the click

A partner can pass what it paid for the click: cost=1.25 (alias media_cost=) and optionally cost_currency=EUR (default the offer currency). It counts only when the partner page has Media cost ▸ Accept cost= on clicks on (partners.tracking.enable_media_cost): the click stores the cost as sent (cost_status: accepted) and reports convert it to your currency at the click day's rate, which gives media cost and profit. From a partner without the permission the value is ignored (not_permitted); an unreadable value is invalid. Neither affects the redirect.

Redirect modes

Set per offer, overridable per landing page. Implemented by the edge:

ModeResponse
http302 (default)302 with Location.
http302_hide_referrer302 plus Referrer-Policy: no-referrer.
seo_friendly_301301, never cached (no-store).
http200_js200 page that redirects with JavaScript, with a meta-refresh fallback.
http200_js_hide_referrerSame, with no-referrer policy and meta tag.
meta_refresh_single200 page with a <meta http-equiv="refresh">.
meta_refresh_doubleTwo meta-refresh hops through /_hop.
direct_link, interstitial, app_store_smartCurrently answer 302 like http302; the click is still recorded.

Async JSON instead of a redirect

Add async=json (or format=json) to any tracking link and the edge answers 200 with JSON instead of redirecting:

json
{"click_id": "01JC9Z3Q4M8V2N6R7T1W5X0Y3Z", "redirect_url": "https://advertiser.example/?click_id=01JC9Z3Q4M8V2N6R7T1W5X0Y3Z"}

Use it from a landing page script, an app or a server that records the click and sends the visitor on itself. The click is decided and recorded exactly as the redirect would be: rules, caps and deduplication apply, an invalid click is stored with its reason and the answer carries invalid_reason plus the fallback destination as redirect_url. An unknown link answers 404 with {"error":{"code":"not_found"}}. The answer allows any origin (Access-Control-Allow-Origin: *, no credentials) and exposes the X-Click-Id header.

Redirects are stateless by default: no cookie is set. A cookie is set only when the offer's session_definition is cookie (name dt_<offer>, Secure, HttpOnly, SameSite=Lax, lifetime session_duration_hours).

Reliability

Click events are buffered and published asynchronously; if the bus is unavailable the edge keeps serving redirects and counts dropped events. Config changes reach the edge through Postgres LISTEN, the NATS dt.config.changed.v1 event and a 30-second poll, so a change is live within seconds. Each edge region exposes /healthz, /readyz and /metrics.