Layers

Organic Scoring

How Layers ranks creatives 0–10 to decide what becomes ad-eligible. Generated content, UGC, and manual content each have their own scoring pool.

View as Markdown

Every piece of content (organic or generated) gets an organic_score from 0 to 10. The score determines whether the content is eligible to be promoted as an ad. The threshold for eligibility is organic_score >= 4.0 (ELIGIBILITY_THRESHOLD = 4.0).

Score is recomputed every 4 hours by the organic performance schedule, which runs per-project scoring workflows.

Three pools

PoolSourceDefault scoreDecay / freshness
generatedLayers AI generations7.0linear decay to floor 2.0 over 30 days
ugcCreator posts (via SIFT)0 (until views threshold met)90-day freshness window, up to +1.5 freshness bonus
manualManually uploaded creativesscored using the generated formula (default 7.0 with 30-day decay)linear decay to floor 2.0 over 30 days

Why three pools? Each kind of content has a different signal-to-noise ratio. A 14-day-old AI-generated video is rare and worth amplifying. A 14-day-old UGC video has accumulated real engagement metrics — you should score it on that, not on age alone.

Generated content scoring

score(t) = clamp(2.0, 10.0,
  7.0 - 5.0 * (ageDays / 30) + adBoost
)

adBoost = min(3.0, totalSpend30Days / 33.3)

In English:

  • Default at creation: 7.0.
  • Linear decay to a floor of 2.0 across 30 days. Without ad boost, a piece of generated content stays eligible (score >= 4.0) for ~18 days.
  • Ad boost of up to +3.0 if it accumulates ad spend (capped at $100 of spend in the trailing 30 days = max boost).
  • High-performing ads stay eligible indefinitely — floor 2.0 + max boost 3.0 = 5.0, which is above the 4.0 threshold.

UGC scoring

UGC scores are derived from real engagement. Inputs (combined with a base multiplier to produce the 0–10 output):

  • Quality (weighted 45%) — engagement-rate percentile within the project's UGC pool.
  • Reach (weighted 25%) — impressions percentile.
  • Outperformance (weighted 30%) — how much the post beats the project's UGC baseline.
  • Freshness bonus up to +1.5 within a 90-day window.
  • Min 50 views — posts with fewer views aren't scored.
  • Min 3 posts in the pool before scoring runs at all.

UGC below 4.0 is not ad-eligible. UGC that ages past 90 days loses the freshness bonus and typically falls below threshold.

Manual scoring

Content with scoring_pool = 'manual' is treated as generated content — it starts at 7.0 and decays linearly to 2.0 over 30 days, with the same ad-spend boost. If you want a manual creative to stay eligible regardless of auto-score, set project_ads_content.override = 'include'.

Overrides

Two columns on project_ads_content give you escape hatches:

  • override = 'include' — bypass the 4.0 threshold; this ad is always eligible.
  • override = 'exclude' — force ineligible regardless of score.
  • override = null — auto-score governs.

Use overrides sparingly. They are applied per project (so a single piece of ads_content can be eligible in one project, excluded in another).

Safety failures

If a piece of content fails safety review (safetyFailed = true), score is forced to 0 regardless of pool, decay, or boost. Safety failures cannot be overridden — you must regenerate the content.

Score drift across versions

The scoring algorithm is versioned. Each ads_content.scoring_version records which version produced its current score. When the algorithm changes (weighting tweaks, pool-formula updates), every row is rescored on the next cycle and scoring_version is bumped.

Where to see the score

Next

On this page