Appearance
Matching & Scoring
How the Brand Match App calculates match scores between creators and brands, and how matches are stored and managed.
Overview
Every creator–brand pair has a match score from 0–100, calculated by the V3 Python scorer (scripts/scoring_v3/scoring_v3.py). Scores are stored in the Neon matches table and served via ca-data. The app never recalculates scores live — it reads pre-computed values.
V3 Scoring Signals
The score is the sum of six weighted signals. Total possible: 100 points.
Signal 1 — Brand Behavioral (0–20 pts)
Measures how well this creator fits the brand's existing sponsorship behavior.
- View Fit (0–17 pts): Creator's average views vs. the average views of the brand's past sponsors. Closer to the brand's typical reach = higher score.
- Niche Overlap (0–3 pts): Whether the creator's niches overlap with the niches of other creators who have actually worked with this brand (sourced from
brand_creator_deal_history.json). Fuzzy-matched to strip parenthetical suffixes from deal names.
Signal 2 — Niche Precision (0–20 pts)
Two-component signal measuring how well the creator's content niche aligns with the brand.
- Component A — Roster Niche Overlap (0–12 pts): Creator niches vs. the niches of past CA roster partners for this brand. Scoring: 3+ overlapping niches = 12 pts, 2 niches = 9 pts, 1 niche = 4–7 pts, partial = 2 pts.
- Component B — Embedding Similarity (0–8 pts): Semantic similarity between the brand's description (augmented with past-partner niches) and the creator's profile using text embeddings. Rescaled to the actual corpus range (0.50–0.75).
Signal 3 — Competitive Intel (flat 20 pts)
Currently disabled. All creators receive the full 20/20. Will be re-enabled when competitive data is more complete.
Signal 4 — Price / Market Fit (0–20 pts)
Measures how well the creator's rate aligns with what this brand typically pays.
- Creator's rate vs. the brand's average deal rate
- Tighter alignment = higher score
Signal 5 — Audience Demographics (0–10 pts)
Scores the creator's audience characteristics against what the brand typically looks for:
- US audience percentage
- Male/female split
- Age distribution
Signal 6 — Inventory Momentum (0–10 pts)
Availability and frequency signal:
- How regularly the creator publishes sponsored content
- Whether they have open inventory slots
Score Color Coding
| Score | Color | Label |
|---|---|---|
| 90–100 | Green | Excellent fit |
| 80–89 | Lime | Strong fit |
| 70–79 | Gold | Good fit |
| 60–69 | Orange | Moderate fit |
| Below 60 | Gray | Weak fit |
Match Status (Pipeline)
Each match has a status field that tracks where it is in the agent's pipeline:
| Status | Description |
|---|---|
not_pitched | Default — not yet contacted |
reached_out | Agent has sent an initial pitch |
in_negotiation | Active discussion / deal in progress |
won | Deal closed |
not_a_fit | Agent has marked this as not worth pursuing |
Status is updated by the agent via the Pitch / Pass buttons in the UI, which call PATCH /api/matches/:id on ca-data.
Pitch Narrative
Each match record optionally includes a pitch_narrative — a one-paragraph pre-written angle explaining why this specific creator is a good fit for this specific brand. Narratives are generated in batch by scripts/generate_narratives_openai.py.
As of early 2026, ~55% of assigned matches have a narrative. The remaining ~45% show no narrative text in the UI.
Conflict Detection
The conflict_database table tracks creators with active sponsor detections for competing brands. If a creator in a match has a detected conflict with the brand being pitched, a warning badge appears on their match card.
Conflict data is synced separately from the sponsor detection pipeline.
Batch Rescoring
When scores need to be recalculated (e.g. after scoring model changes), the process is:
- Run
scripts/scoring_v3/run_v3_scoring.py— outputs 1,753 brand JSON files todata/matches_v3/ - Run
scripts/push_rescored_matches_to_neon.py— bulk UPDATE to Neon (2,000 rows per batch) - ca-data serves the updated scores immediately — no app redeploy needed
The last full rescore updated 107,307 rows to v3 scoring.