> ## Documentation Index
> Fetch the complete documentation index at: https://docs.searchcompany.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Store Visibility Score

> Store visibility results and calculate final score for an entity

Internal endpoint for the Cron service to store visibility results and calculate the overall visibility score.

Called ONCE per entity after all sampled prompts have been checked.

## What It Does

1. Receives prompt results from Cron (10 sampled prompts per day)
2. Calculates overall score: `total_true / (num_prompts * 8) * 100`
3. Updates TWO database tables:
   * `entity_prompts_tracker` - Per-prompt visibility results
   * `visibility_score_history` - Single row per entity with recent\_score and history\_scores JSONB

## Score Algorithm

```
raw_score = (total_true_values / (num_prompts * 8)) * 100
```

Where `total_true_values` is the count of True across all checks (e.g., 10 prompts × 8 platforms = 80 checks).

Example: If 40 out of 80 are True → raw\_score = 50

### Floor Protection

The score cannot regress below the historical minimum (ignoring 0s):

```
non_zero_scores = [s for s in historical_scores if s > 0]
min_historical = min(non_zero_scores)

if raw_score <= min_historical:
    final_score = min_historical + 1
else:
    final_score = raw_score
```

This ensures the visibility score trends upward over time and doesn't regress due to temporary API failures or platform changes.

## Request Body

<ParamField body="business_id" type="string" required>
  Business identifier (org\_slug)
</ParamField>

<ParamField body="product_id" type="string">
  Optional product/item ID
</ParamField>

<ParamField body="results" type="array" required>
  Array of prompt results. Each item contains:

  * `prompt` (string): The prompt text
  * `chatgpt` (boolean): Visibility on ChatGPT
  * `claude` (boolean): Visibility on Claude
  * `gemini` (boolean): Visibility on Gemini
  * `perplexity` (boolean): Visibility on Perplexity
  * `copilot` (boolean): Visibility on Copilot
  * `deepseek` (boolean): Visibility on DeepSeek
  * `grok` (boolean): Visibility on Grok
  * `google_ai` (boolean): Visibility on Google AI
</ParamField>

<ParamField body="backdate_to" type="string">
  Optional ISO date string (e.g., "2025-12-13") to backdate the score entry
</ParamField>

## Response

<ResponseField name="status" type="string">
  `"success"` or `"error"`
</ResponseField>

<ResponseField name="overall_score" type="integer">
  Calculated visibility score (0-100)
</ResponseField>

<ResponseField name="prompts_analyzed" type="integer">
  Number of prompts processed
</ResponseField>

<ResponseField name="total_true" type="integer">
  Total number of True values across all platforms
</ResponseField>

<ResponseField name="platform_scores" type="object">
  Per-platform scores (0-100 each)
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://searchcompany-main.up.railway.app/api/cron/store-visibility-score \
    -H "Content-Type: application/json" \
    -H "X-API-Key: search-company" \
    -d '{
      "business_id": "nike",
      "results": [
        {
          "prompt": "What are the best running shoes?",
          "chatgpt": true,
          "claude": true,
          "gemini": false,
          "perplexity": true,
          "copilot": false,
          "deepseek": true,
          "grok": false,
          "google_ai": true
        }
      ]
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "status": "success",
    "message": "Visibility score 48 stored for entity abc-123",
    "overall_score": 48,
    "prompts_analyzed": 10,
    "total_true": 38,
    "platform_scores": {
      "chatgpt_score": 52,
      "claude_score": 48,
      "gemini_score": 51,
      "perplexity_score": 45,
      "copilot_score": 49,
      "deepseek_score": 47,
      "grok_score": 46,
      "google_ai_score": 50
    }
  }
  ```
</ResponseExample>

## Database Updates

### entity\_prompts\_tracker

Updates prompts with their visibility results across 8 platforms (pass/fail per platform).

### visibility\_score\_history

UPSERT single row per entity:

* Updates `recent_score` with latest score
* Appends new entry to `history_scores` JSONB array
* History format: `[{"date": "2025-12-15", "score": 41, "platforms": {...}}]`
* Keeps last 365 days of history

## History Seeding

On first score calculation, the system seeds 2 days of fake history:

* Day -2: Score 0 (baseline)
* Day -1: Score 35 (simulated signup day)

This provides visual context for the visibility chart.

## Notes

* Called by Cron ONCE per entity after sampled prompts are analyzed
* Each platform uses native search + Gemini 3 Flash provides unified evaluation
* JSONB history enables the "Visibility Over Time" chart on the dashboard
* Single row per entity = faster queries
* Floor protection ensures scores trend upward over time
