Skip to main content
Customer Value tracks what an individual customer has done over time and adjusts payout and revenue once that customer reaches a goal you define. Where a normal payout applies to a single conversion, a Customer Value rule looks at everything the customer has accumulated — conversions, events, sale amount, and your own custom attributes — and reacts when a threshold is crossed. Customers are identified by the user_id you pass on conversions. Conversions without a user_id are not tracked by Customer Value. There are two resources:

Data points

Custom attributes you send with conversions — subscription tier, deposit amount, product SKU. Rules read them as goal metrics and as payout or revenue sources.

Rules

The goals a customer must reach, the window they must reach them in, and the payout or revenue outcome applied once they do.

Data points

A data point declares an attribute you send with your conversions. The identifier is the key you send it under; it is lowercased on save and must be unique within the network. The type is required and determines what you can do with it. A value other than text or number is rejected rather than defaulted, because the type cannot be changed afterwards and a text data point can never satisfy a numeric goal. identifier and type are immutable once the data point exists, because rules and stored customer history reference them. Updates apply only name, status, and description — and omitting status leaves it as it is, so an inactive data point is not reactivated by a payload that simply does not mention it. Only active data points can be referenced by a rule. Set a data point to inactive to retire it while keeping the history already collected — deletion is blocked while any rule still references it.

Rules

A rule has five parts.

Scope

Scope decides which traffic the rule applies to. Leave a list empty to match everything.
  • offer_ids — the offers in scope. Mutually exclusive with advertiser_ids.
  • advertiser_ids — every offer of these advertisers. Ignored when offer_ids is set.
  • affiliate_ids — the partners in scope.
  • payout_revenue_id — narrows the rule to a single event of the one offer in offer_ids. Leave it at 0 to cover the offer’s base conversion and all of its events.
segmentation decides how the accumulated history is partitioned. shared keeps one history per customer across the whole scope. The other values (affiliate, offer, advertiser, affiliate_offer, affiliate_advertiser) keep a separate history per partner, per offer, per advertiser, or per combination — so the same customer progresses independently in each.

Timeframe and cycles

start_date is inclusive, end_date is exclusive, and both are yyyy-MM-dd interpreted in timezone_id (the network’s timezone by default). Leave start_date empty to start immediately and end_date empty to run indefinitely. Every metric and every data point accumulates toward the rule’s goals — conversions, events, payout, revenue, sale amount, and each data point the customer sends. cycle_type decides when those accumulated totals reset to zero:

Goals

goals are the conditions the customer’s accumulated totals must satisfy. All goals must be satisfied for the outcome to apply. Each goal picks a metric, an operator, and a value — always sent as a string, even for numeric comparisons. A data_point goal also needs metric_data_point_identifier and a logic that reduces the values collected over the cycle to a single one before comparison. A rule may be goalless only when cycle_type is from_first_conversion and outcome_type is substitution — every customer in scope then gets the outcome for the length of their window.

Progress settings

By default every accumulated total resets at the end of each cycle. progress_settings are the exceptions: each one names a single metric — or a single data point, via data_point_identifier — whose total carries over into the next cycle instead of resetting. Everything you don’t list keeps resetting. condition decides whether the carry-over applies at a given boundary:
  • any_value — always carries over. The total never resets.
  • negative_balance — carries over only when the total is negative at the boundary; a zero or positive total resets as usual. Use it so a customer whose refunds or chargebacks put them in the red starts the next cycle still in the red, rather than back at zero. Text data points have no balance and never carry over under this condition.
Each metric may appear at most once, and data_points once per data point. Progress settings only take effect where a cycle boundary exists, so they are meaningful on day, week, month, and quarter cycles. A rule_duration or from_first_conversion cycle never resets, so there is nothing for them to override.

Outcome

Goals are evaluated against the customer’s accumulated totals, not against a single conversion. Each incoming conversion is added to the history first, then the goals are checked — so the conversion being processed when the history first satisfies them is the one the outcome lands on. outcome_type decides what that outcome is:
  • substitution replaces the payout and revenue of that incoming conversion.
  • bonus records a separate bonus conversion and leaves the incoming one untouched. It needs an outcome_trigger: instant grants the bonus on the incoming conversion, end_of_cycle grants it once when the cycle closes, aggregating the whole cycle.
The amounts come from payout_method and revenue_method, which are configured independently. At least one of them must be enabled. For modifier, increase and decrease treat the value as a percentage while flat_increase and flat_decrease treat it as an absolute amount. For data_point, accumulated uses the total across the cycle and current uses the value on the incoming conversion.

Rule lifecycle

state is derived on read from the rule’s timeframe and configuration — you never set it. The state also controls what an update can change, so that a rule cannot be redefined out from under the history it has already accumulated:
Updates replace the whole rule — send the complete object. Fields you omit are treated as cleared, not preserved.
status is separate from state: an inactive rule never applies, whatever its state. Prefer setting status to inactive over deleting a rule you may want back.

Configuration constraints

Some combinations are rejected on create and update: A rule that becomes invalid after the fact — for example when its scoped event is made private — reports stopped_running rather than failing.

Reading after a write

Reads are served from a replica, so a GET by ID issued immediately after a create can return 404 for a second or two before the new record is visible. The write itself has already succeeded; only the read lags. If you are scripting against the API, use the object returned by the POST or PUT rather than re-fetching it — the response body is the saved record, including its assigned ID. Retry the read if you do need one.