Tracking guide
Caps, rules and day-parting
Targeting rules and lists, traffic controls, caps with soft and hard behaviour, day-parting and scheduled actions.
These controls decide whether a click or conversion is accepted, rerouted or limited. They run on the edge for clicks and in ingest for conversions, from the same configuration snapshot, so a change applies within seconds.
Rule sets
A rule set attaches to an offer (also landing page, SmartLink entry, payout entry, cap, fraud rule, or the whole tenant for traffic controls). It has:
| Field | Meaning |
|---|---|
event_scope | click, impression, conversion, event or all. |
match | all (every rule must match) or any. |
rules[] | variable, operator, values[]. |
action | allow, deny, route_to, fail_traffic, mark. |
fail_traffic_target | Where denied traffic goes: offer, offer_url, offer_group or smartlink plus routing_value; with is_pay_affiliate and is_pass_through options and reason_categories (traffic_filter, already_converted, offer_expired, geotargeting, caps, other) that become the click's invalid reason. |
partner_ids, excluded_partner_ids | Narrow to or exempt partners. |
priority, date_valid_from/to, status | Evaluation order (higher first) and validity. |
Order of evaluation at click time: tenant traffic controls, then the offer's rule sets, then landing-page rules. The first deciding rule set wins.
Variables and operators
GET /rules/catalog returns the live catalogue; POST /rules/validate checks a rule set without saving.
- Variables: geography (
country,region,city,postal_code,dma), network (isp,carrier,connection_type,asn,ip,ip_range), device and software (device_type,device_brand,device_model,os,os_version,browser,browser_version,browser_language,user_agent), traffic (referer,source,sub1tosub10,creative_id,tracking_domain,link_style), app (app_name,app_id,app_version,sdk_version), ids (aid,gaid,idfa), conversion-side (txn_id,user_id,email,sale_amount,payout,revenue,goal,status,coupon_code,ctit_seconds), time (hour_of_day,day_of_week,date_rangein the tenant time zone), flags (is_proxy,is_vpn,is_datacenter,is_bot,fraud_score,cookie_consent) and entity ids. - Operators:
equals,not_equals,in,not_in,contains,not_contains,starts_with,ends_with,regex,gt,gte,lt,lte,between,empty,not_empty,cidr,not_cidr,day_of_week,hour_range,list_ref(in a list),not_in_list. Short aliases (eq,neq,starts,ends,exists,missing,in_list) are accepted.
Lists
Lists are reusable value sets referenced by list_ref: ip, isp, subid, domain, referrer, suppression, email_suppression, asn, device_id, custom. Fill them with values, upload a file (POST /lists/{id}/upload), or point source_url at a file and set sync_interval_minutes; a worker refreshes URL-backed lists (DT_LIST_SYNC_EVERY, default 1 minute poll) and POST /lists/{id}/sync forces a refresh. Fraud has its own deny lists, see fraud.
Conversion rules and holds
Rule sets with event_scope: conversion run when a conversion arrives, before caps and pricing: first the tenant's
(Traffic ▸ Conversion rules, scoped to all traffic, partners, offers or countries like traffic controls), then the
offer's (Offer ▸ Targeting ▸ Conversion rules), by priority. Actions:
| Action | Effect |
|---|---|
deny | The conversion is stored rejected with status source rule:<rule set id>. |
hold | The conversion is stored extended (reason hold) for hold_minutes (1 to 129600, 90 days) and then becomes hold_then (approved or rejected). Only conversions that would otherwise be approved or pending are held. |
allow | Stop evaluating conversion rules. |
mark | Record the rule set on the conversion (fraud context) and continue. |
Variables are the catalogue's conversion-side ones (sale_amount, payout, revenue, goal, ctit_seconds,
conv_ip, txn_id, coupon_code, email, user_id, status) plus the click's geo, device, flags and sub IDs;
GET /rules/conversion-catalog lists them. Templates on the page: hold every conversion for 7 days, hold sales over
500 for 3 days, reject installs under 10 seconds after the click, reject conversions from proxies.
Held conversions (Money ▸ Held conversions, GET /conversion-holds) lists every hold with a countdown. The
workers release due holds every 10 seconds (DT_HOLDS_EVERY) through the conversion status API with
expected_status: extended, so a hold never overrides a decision: approving, rejecting or reversing a held
conversion yourself cancels its hold, also when your decision and the timer cross. Release now
(POST /conversion-holds/{id}/release, needs the approve-conversions permission) applies the rule's decision at once.
During an ingest outage holds stay due and are released when it is back (after 20 failed attempts a hold is marked
failed and waits for a manual release). The conversion inspector in the logs shows the hold and its countdown.
Bookkeeping rows the timer keeps for decisions on conversions that no rule held are deleted after a day
(DT_HOLDS_PLACEHOLDER_TTL).
Caps
A cap limits an entity over a period.
| Field | Values |
|---|---|
entity_type, entity_id | offer, offer_group, advertiser, partner, smartlink, goal, landing_page, tenant. |
metric | clicks, unique_clicks, gross_clicks, impressions, conversions, approved_conversions, pending_conversions, installs, events, payout, pending_payout, revenue, pending_revenue, gross_revenue, net_revenue, sale_amount, budget. |
per_minute, hourly, daily, weekly, monthly, lifetime | The limits (decimal strings). Buckets follow the cap's timezone (default: the tenant's caps time zone); weeks run Monday to Sunday. |
partner_ids, partner_cap_mode | Scope to partners; each gives every partner its own counter, group shares one. |
goal_id, landing_page_ids, geo | Narrow what is counted. |
is_soft_cap | A soft cap lets in-flight conversions through and marks them pending with reason cap_reached; a hard cap rejects. |
on_reached | redirect_blank, fallback_link, fail_traffic, pause_entity, block, notify. |
alert_thresholds, notify_channels | For example [50, 80, 100] percent. |
visibility | public lets partners see their remaining cap. |
Behaviour to know:
- Click-metric caps are enforced at click time and a hard cap reached marks the click invalid with reason
caps. If the cap counter cannot be read at that moment (the cache did not answer within its budget), a hard click cap never lets the click through: the visitor gets the cap's fallback and the click is recorded invalid with reasoncaps_degraded(it still appears in reports as an invalid click). Soft caps only notify, so they let it through. Conversion and money caps are enforced in ingest when the conversion is created. - Counters live in Redis; a nightly job (
DT_CAP_RECONCILE_HOUR, default 03:00 UTC) reconciles them with ClickHouse and records the differences (GET /caps/reconciliations).GET /caps/statusandGET /caps/reportshow utilisation. - If Redis is unreachable, or has lost its counters, soft caps let traffic through (they only notify). Hard caps never over-admit: a click under a hard click cap — on the offer or on any other entity (partner, smartlink, landing page, advertiser, offer group) — gets the offer's fallback and is recorded invalid with reason
caps_degraded; a conversion under a hard conversion or money cap is queued and counted once the counters are readable again. - Several windows are checked together. A conversion that falls under, say, a daily and a weekly cap is admitted only if every window has room, and then all of them count it in one step. If one hard window is full, none of them is counted.
- Rejected conversions give their place back. When a conversion ends rejected or cancelled (cap rejection, scrub, a status change or a reversal) every window it took is released; if it is later approved again it is counted again. Payout and revenue caps move by the change in amount.
- Money caps count the final price. A payout cap counts the amount the partner is actually paid, after overrides, tiered commissions, partner-tier commissions, scrub and customer-value bonuses; a revenue cap counts the revenue actually booked. Conversion-count caps are checked first, money caps after pricing, and a conversion either fits every window or takes none.
- Each conversion counts once. A conversion's admission can run more than once — a slow reply that is retried, a conversion queued during a Redis hiccup and replayed, a retry after an ingest crash — but every window remembers which conversions it holds, so it is counted once and a full cap is never filled by repeats.
- Arrival order decides. For offers with hard conversion or money caps, tiered payouts or customer-value ladders, conversions are decided in the order they arrived, also when one of them had to be queued — but a later conversion waits only when its decision can depend on the queued one. A conversion-count cap that has room for every conversion queued ahead (or none even without them) decides at once; only a window the queued conversions could fill makes the later one wait behind them (answered
queued, reasonordered_behind_deferral). The same holds for conversion-count tiers (it waits only when the queued ones could move the counter across a tier boundary). Payout / revenue caps, revenue tiers and customer-value ladders wait when a queued conversion could move the same window (same partner, period and — for ladders — user), because its amount is not known yet. Everything else is decided at once. - The first copy wins. When an advertiser's retry arrives while the first copy of the same transaction is still queued, the retry waits for it: the first copy creates the conversion and the retry answers
duplicatewith its id. - Nobody waits more than a minute. If a conversion would wait longer than
DT_INGEST_ORDER_HOLD_MS(60 s) behind queued ones (a long Redis outage), it is decided with the counters as they are; such conversions carryorder_hold_expired(event annotation and the postback log reason). - Approved-conversion caps count approved conversions only. A conversion that arrives pending (must-approve, an advertiser status, a hold, a fraud flag) takes no place in an
approved_conversionscap; it takes one when it is approved, and gives it back if it is later rejected, cancelled or moved out of approved. - Today
unique_clickscaps are not counted at click time (duplicate detection runs after cap evaluation).
Day-parting
A schedule opens an offer, or a partner on an offer, only inside weekday and time windows, evaluated in a time zone with daylight-saving handled correctly.
{
"name": "Business hours EU",
"offer_id": "01J...",
"timezone": "Europe/Berlin",
"windows": [{ "days": [1, 2, 3, 4, 5], "start": "08:00", "end": "20:00" }],
"outside_action": "fail_traffic",
"fail_traffic_target": { "type": "offer_url", "routing_value": "https://example.com/closed" }
}
Rules on hour_of_day, day_of_week and date_range, payout-override hours and weekdays, and entry validity dates use the same clock: the offer schedule's time zone if the offer has one, otherwise the tenant's reporting time zone.
days are 0 (Sunday) to 6. A window whose end is earlier than its start wraps past midnight. A partner-specific schedule replaces the offer-level one for that partner. A schedule with no windows is always closed. outside_action is block or fail_traffic.
Scheduled actions
POST /scheduled-actions queues a change for later: set_status on an offer, partner, advertiser, SmartLink, cap, payout entry or partner-offer setting; set_cap; set_payout; set_access. They run once at run_at (polled every 15 seconds by default, DT_SCHEDULED_ACTIONS_EVERY), record a result and can be cancelled with POST /scheduled-actions/{id}/cancel. Use them for "pause this offer on Sunday night" or "raise the payout for the weekend". For repeating or metric-driven changes see automation rules in the automation reference.
Automation rules on a metric threshold use the report metrics (for example cr, epc, avg_fraud_score) plus invalid_click_rate, postback_failure_rate, postback_attempts and cap_utilization_pct. Set compare_to: previous_period to compare with the period before (epc_change_pct is accepted as a shorthand for epc compared with the previous period). A rule with a metric outside that list is refused when saved; an older rule that the reports refuse is paused and the reason is shown in its run history.
Per-partner overrides
Everything about a partner on an offer (access, payout overrides, caps, custom scrub, visibility) lives in the partner-offer setting, set with PUT /partners/{id}/offers/{offerId}.