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. Theidentifier 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 withadvertiser_ids.advertiser_ids— every offer of these advertisers. Ignored whenoffer_idsis set.affiliate_ids— the partners in scope.payout_revenue_id— narrows the rule to a single event of the one offer inoffer_ids. Leave it at0to 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.
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:
substitutionreplaces the payout and revenue of that incoming conversion.bonusrecords a separate bonus conversion and leaves the incoming one untouched. It needs anoutcome_trigger:instantgrants the bonus on the incoming conversion,end_of_cyclegrants it once when the cycle closes, aggregating the whole cycle.
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 aGET 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.