Affinity is the recommendation API behind both Lookout and Wave. Given an event, Affinity surfaces:
  • Taste clusters — segments of likely buyers for this event.
  • Suggested clusters for a given budget and goal — what to actually spend on.
  • Expected value — predicted tickets sold and revenue for any cluster selection.
  • Default campaign parameters — sensible budget / goal / cluster defaults so you don’t ask the user to start from scratch.

Taste clusters

A taste cluster is a learned segment for one specific event. It is not a generic audience — it is “people likely to buy a ticket to this event, grouped by why.” Clusters are versioned by tc_run_id.
GET /events/{eid}/clusters
{
  "tc_run_id": 42,
  "clusters": [
    {
      "tc":          "family_outings_munich",
      "label":       "Family outings, Munich area",
      "metric_pred": 0.83,
      "reach_estimate": 184000,
      "size_share": 0.21
    },
    ...
  ]
}
metric_pred is the model’s affinity score (higher = more likely to buy). size_share is the cluster’s share of the total reachable audience.

Suggested clusters at a budget

The Wave wizard doesn’t make the user pick clusters cold — it asks Affinity for the suggested mix at the user’s target budget and goal.
GET /events/{eid}/suggested_tcs?budget=5000&goal=tickets
[
  { "tc": "family_outings_munich",    "rank": 1, "expected_tickets": 412, "expected_revenue": 18540 },
  { "tc": "german_metal_diehards",    "rank": 2, "expected_tickets": 287, "expected_revenue": 12915 },
  { "tc": "casual_concertgoers",      "rank": 3, "expected_tickets": 198, "expected_revenue": 8910 }
]
Goals supported (subset — confirm in API Reference):
GoalWhat it optimises
ticketsNumber of tickets sold
revenueGross revenue
reachImpressions

Expected value

Once the user has a candidate cluster selection, get the predicted outcome:
GET /events/{eid}/campaigns_expected_value?goal=tickets&budget=5000
This is the same model output as suggested_tcs but at higher fidelity once a selection is locked in — use it to render the “we expect ~412 tickets at €18,540 revenue” preview in your UI.

Default campaign parameters

GET /events/{eid}/default_campaigns_parameters
{
  "total_budget": 5000,
  "goal":         "tickets",
  "tcs":          ["family_outings_munich", "german_metal_diehards", "casual_concertgoers"]
}
The Wave wizard pre-fills the budget input and pre-selects the clusters from this payload. Surface these as defaults the user can change, not hard constraints.

Budget min/max

GET /events/{eid}/budget_min_max?runtime_in_days=14
Returns the allowed budget range for the event at the given runtime. Use this to validate (and visualise) the budget slider.

CTA suggestions

For the campaign creative editor:
GET /setup_processes/call_to_action/?goal=tickets&has_pixel=true&lead_form_id=
Returns the list of allowed CTAs for the chosen goal. The reference webapp shows them as radio chips next to the creative form.

How recommendations interact with campaigns

A campaign is uniquely identified by (eid, tc, tc_run_id). When Affinity re-runs (because the event’s data changed materially), the new run produces new clusters with a new tc_run_id. Existing campaigns keep their old run — that’s intentional, so an active campaign isn’t disrupted by a model refresh. In your UI:
  1. Lookout — read clusters and show the latest run.
  2. Wave — when starting a new setup, take the latest run and lock the tc_run_id for the duration of the wizard. When the user edits a running campaign, keep its existing tc_run_id.

Geo radius helper (Meta targeting)

For partners using Meta as the ad platform, Affinity also provides a radius suggestion:
GET /fb_targeting/suggested_radius?location=Munich%2CDE&distance_unit=km
Returns { radius: <number> } — wire it into the location targeting widget so the user starts from a sensible default.

Caveats

There is no “global audience” Affinity endpoint partners can call independent of an event. Recommendations always live under /events/{eid}/.
Don’t hard-code “show top 5 clusters”. Some events have 3, some have 12. Render whatever the API returns and let the user pick within it.
Newly-ingested events without enough historical signal return clusters: [] until the platform finishes computing recommendations. The reference webapp shows an empty-state with a “still computing” message — mirror that.
A 0.83 on one event doesn’t mean the same thing as 0.83 on another. Don’t rank events by their clusters’ scores.