Skip to main content

Campaign is the tenancy boundary

A campaign represents one tracked brand. Nearly every other record hangs off it, and the dashboard is always scoped to the campaign currently selected in the session.
Campaigns also reference up to two GoogleAccount records — ga4_google_account_id and gsc_google_account_id — so Analytics and Search Console can be connected under different Google identities. isDomainFullyVerified() is true only when both ga4_domain_verified and gsc_domain_verified are set.

Prompts

Prompt is the unit of tracking. Its lifecycle is driven by three status constants: Useful scopes: active(), suggested(), approved(), status($status) and forCampaign($campaignId). updateLastRun() stamps the prompt after a batch.
PROMPTS_ACTIVE_LIMIT (default 10) caps how many prompts a campaign may have active at once, which is what bounds the per-campaign provider spend. PROMPTS_AUTO_SEED controls whether a new campaign gets a starter set automatically.

Prompt runs

PromptRun is one execution of one prompt against one provider. Runs are grouped by batch_idscopeByBatchId() is how a whole scheduled batch is retrieved. Visibility lives on the run:
  • hasVisibility() — whether the brand was detected in the answer
  • getVisibilityType() — how it appeared
  • scopeWithVisibility() / scopeWithoutVisibility() — filtering for analytics
  • screenshot_url accessor — resolves the stored SERP screenshot, captured by TakeSearchScreenshotJob via ScreenshotOne

Sentiment

SentimentRecord holds a scored topic result, with SentimentMention rows for the individual brand mentions behind the score. The BrandSentiment model namespace holds the supporting value objects used by the scoring pipeline.

Competitors

Three models cover the competitor flow:
  • DiscoveredCompetitor — domains found in answers, before classification
  • CompetitorAppearance — per-run appearances used for benchmarking
  • CompetitorSubdomain — subdomain grouping so blog.example.com and example.com count as one competitor
CompetitorClassifier decides which discovered domains are direct competitors. That verdict feeds non_competitor_penalty in the scoring formula, so misclassification directly moves visibility scores.

Supporting models