Affinity Targeting
Affinities are weighted preferences that tell the matching engine what your brand looks and feels like. Each campaign can define up to 50 affinities across 8 types. Affinities serve two purposes: they influence the match scoring (making your campaign more likely to win relevant scenes) and they drive visual pipeline bias (changing how the scene actually looks when your campaign wins).
Affinity Types at a Glance
| Type | Match Method | Visual Impact | Scoring Impact |
|---|---|---|---|
action | Substring in narrative text | None (scoring only) | Weight added to affinity_match |
location | Same compatibility group | Swaps scene location | Weight added to affinity_match |
theme | Exact match with theme_id | None (scoring only) | Weight added to affinity_match |
mood | Exact match in mood_tags | Indirect (influences lighting if no lighting affinity) | Weight added to affinity_match |
category | Exact match with game category | None (scoring only) | 1.5x category_affinity multiplier |
lighting | Always matches (visual-only) | Replaces scene lighting preset | Weight added to affinity_match |
effects | Always matches (visual-only) | Replaces scene effects preset | Weight added to affinity_match |
camera | Reserved | Reserved for future use | Not yet scored |
Weights
Each affinity has a weight field ranging from 0.1 to
3.0. The weight determines how much this affinity contributes to the
campaign's match score when it matches the scene context.
| Weight Range | Meaning | Use Case |
|---|---|---|
| 0.1 - 0.5 | Nice to have | Slight preference, low-priority aesthetic |
| 0.5 - 1.0 | Moderate preference | Standard targeting weight |
| 1.0 - 2.0 | Strong preference | Core brand aesthetic, key theme alignment |
| 2.0 - 3.0 | Critical | Brand-defining visual identity, must-have context |
The affinity_match component of the scoring formula is:
1.0 + (sum of matched weights / total affinities). Higher weights on
matching affinities directly increase your campaign's score.
action
Matches against the narrative text of the current scene using substring search.
The affinity_value is searched as a case-insensitive substring within
game_context.narrative.
Example values: "confrontation", "obtaining", "celebration", "negotiation", "crisis"
POST /api/ads/campaigns/{id}/affinities
{
"affinity_type": "action",
"affinity_value": "celebration",
"weight": 1.5
} This affinity matches if the narrative text contains the word "celebration" anywhere. Useful for brands that want to appear during specific narrative beats -- for example, a luxury brand targeting celebration scenes, or a security company targeting crisis moments.
location
Matches when the campaign's target location is in the same compatibility group as the scene's current location. If the campaign wins, the scene location is swapped to the affinity's location value -- but only if both locations belong to the same group. This preserves narrative coherence.
POST /api/ads/campaigns/{id}/affinities
{
"affinity_type": "location",
"affinity_value": "rooftop terrace",
"weight": 1.0
}
In this example, if the base scene is set in "city plaza" (urban group), the
affinity matches because "rooftop terrace" is also in the urban group. The scene
location would be swapped to "rooftop terrace". However, if the base scene were
"military bunker" (military group), this affinity would not match
-- the groups are different.
Location Compatibility Groups
outdoor_nature
- vast open landscape
- mountaintop overlook
- beach
- tropical resort
- forest clearing
- desert expanse
- lakeside
indoor_formal
- library or study
- boardroom
- oval office
- press briefing room
- courtroom
- embassy reception
- war room
urban
- modern urban street
- marketplace or vault
- futuristic cityscape
- rooftop terrace
- city plaza
- subway station
- neon-lit alley
liminal
- crossroads or threshold
- foggy street
- dimly lit basement
- abandoned building
- tunnel entrance
- deserted highway
military
- military bunker
- aircraft carrier deck
- control room
- pentagon corridor
- missile silo
- situation room
theme
Matches via exact match against game_context.theme_id.
This is purely a scoring affinity -- it does not alter any visual pipeline slot.
POST /api/ads/campaigns/{id}/affinities
{
"affinity_type": "theme",
"affinity_value": "2",
"weight": 2.0
}
Theme IDs correspond to the game's theme system (e.g., "1" = Watergate Redux,
"2" = Pandemic Crisis, "4" = Corporate Takeover). A pharmaceutical
company might weight the Pandemic Crisis theme highly, while a tech brand might target
Corporate Takeover.
mood
Matches via exact match against the game_context.mood_tags list.
If the scene's mood tags include the affinity value, the affinity matches.
POST /api/ads/campaigns/{id}/affinities
{
"affinity_type": "mood",
"affinity_value": "triumphant",
"weight": 1.5
} Common mood values:
"tense","kinetic""bittersweet","determined""reflective","weary""contemplative","eureka""triumphant","jubilant""paranoid","bitter""wonder","possibility""solemn","noble"
Mood affinities also have an indirect visual effect: if a campaign wins and has mood affinities but no explicit lighting affinity, the mood can influence the lighting choice in the biased pipeline.
category
Matches via exact match against the game's category. This affinity is unique
because it triggers the category_affinity multiplier in the scoring formula
(1.5x) rather than contributing to the affinity_match sum.
POST /api/ads/campaigns/{id}/affinities
{
"affinity_type": "category",
"affinity_value": "simulation",
"weight": 1.0
}
When this affinity matches, the entire campaign score is multiplied by 1.5x. This makes
category targeting one of the most impactful affinities for winning auctions. Common
category values: "simulation", "rpg", "strategy",
"adventure", "puzzle".
lighting
Controls the scene's lighting when the campaign wins. The affinity_value is a
preset key that maps to a descriptive lighting string used by the game's rendering pipeline.
| Preset Key | Rendered As |
|---|---|
warm | warm golden hour |
cool | cool moonlight |
dramatic | dramatic silhouette |
natural | soft natural daylight |
neon | neon-lit urban glow |
clinical | sterile fluorescent |
overcast | overcast diffused |
POST /api/ads/campaigns/{id}/affinities
{
"affinity_type": "lighting",
"affinity_value": "warm",
"weight": 2.0
}
When this campaign wins, the biased_pipeline.lighting field in the match
response will contain "warm golden hour", regardless of what the base scene's
lighting was. This is a direct override -- the lighting affinity always applies when the
campaign wins.
effects
Controls the scene's visual effects when the campaign wins. Like lighting, this is a direct override via preset keys.
| Preset Key | Rendered As |
|---|---|
cinematic | cinematic widescreen, film grain, shallow depth of field |
vintage | vintage film grain, muted tones, light leak |
clean | clean and modern, sharp focus, high contrast |
dreamy | soft focus, ethereal glow, pastel tones |
noir | high contrast, desaturated, heavy shadows |
documentary | handheld feel, naturalistic, sharp focus |
epic | sweeping scale, volumetric lighting, lens flare |
POST /api/ads/campaigns/{id}/affinities
{
"affinity_type": "effects",
"affinity_value": "cinematic",
"weight": 1.5
}
When this campaign wins, biased_pipeline.effects will be
"cinematic widescreen, film grain, shallow depth of field".
camera
Reserved for future use. This affinity type is defined in the schema but is not yet scored or applied to the visual pipeline. It will eventually control camera angles such as close-up, wide angle, dutch angle, tracking shot, etc.
Bulk API
You can set multiple affinities at once using the bulk endpoint. This replaces all existing affinities for the campaign.
POST /api/ads/campaigns/{id}/affinities/bulk
Authorization: Bearer sk_live_xxx
Content-Type: application/json
{
"affinities": [
{ "affinity_type": "lighting", "affinity_value": "warm", "weight": 2.0 },
{ "affinity_type": "effects", "affinity_value": "cinematic", "weight": 1.5 },
{ "affinity_type": "location", "affinity_value": "rooftop terrace", "weight": 1.0 },
{ "affinity_type": "mood", "affinity_value": "triumphant", "weight": 1.0 },
{ "affinity_type": "action", "affinity_value": "celebration", "weight": 1.5 },
{ "affinity_type": "category", "affinity_value": "simulation", "weight": 1.0 },
{ "affinity_type": "theme", "affinity_value": "4", "weight": 2.0 }
]
} Response:
{
"campaign_id": "camp_abc123",
"affinities_count": 7,
"affinities": [
{ "id": "aff_001", "affinity_type": "lighting", "affinity_value": "warm", "weight": 2.0 },
{ "id": "aff_002", "affinity_type": "effects", "affinity_value": "cinematic", "weight": 1.5 },
{ "id": "aff_003", "affinity_type": "location", "affinity_value": "rooftop terrace", "weight": 1.0 },
{ "id": "aff_004", "affinity_type": "mood", "affinity_value": "triumphant", "weight": 1.0 },
{ "id": "aff_005", "affinity_type": "action", "affinity_value": "celebration", "weight": 1.5 },
{ "id": "aff_006", "affinity_type": "category", "affinity_value": "simulation", "weight": 1.0 },
{ "id": "aff_007", "affinity_type": "theme", "affinity_value": "4", "weight": 2.0 }
]
} Example: Brand Profiles
Luxury Watch Brand
{
"affinities": [
{ "affinity_type": "lighting", "affinity_value": "warm", "weight": 2.5 },
{ "affinity_type": "effects", "affinity_value": "cinematic", "weight": 2.0 },
{ "affinity_type": "location", "affinity_value": "rooftop terrace", "weight": 1.5 },
{ "affinity_type": "mood", "affinity_value": "triumphant", "weight": 1.5 },
{ "affinity_type": "action", "affinity_value": "celebration", "weight": 1.0 }
]
} Cyberpunk Game Studio
{
"affinities": [
{ "affinity_type": "lighting", "affinity_value": "neon", "weight": 3.0 },
{ "affinity_type": "effects", "affinity_value": "noir", "weight": 2.0 },
{ "affinity_type": "location", "affinity_value": "neon-lit alley", "weight": 2.0 },
{ "affinity_type": "mood", "affinity_value": "paranoid", "weight": 1.5 },
{ "affinity_type": "action", "affinity_value": "confrontation", "weight": 1.0 }
]
} Documentary Streaming Service
{
"affinities": [
{ "affinity_type": "lighting", "affinity_value": "natural", "weight": 2.0 },
{ "affinity_type": "effects", "affinity_value": "documentary", "weight": 2.5 },
{ "affinity_type": "mood", "affinity_value": "contemplative", "weight": 1.5 },
{ "affinity_type": "action", "affinity_value": "revelation", "weight": 2.0 },
{ "affinity_type": "category", "affinity_value": "simulation", "weight": 1.0 }
]
} Related
- Ad Matching Pipeline -- how affinities fit into the 6-step pipeline
- Pipeline Bias -- visual before/after of biased scenes
- Placement Simulator -- try affinities interactively
- Analytics API -- track which affinities drive impressions