SegOps AIDocs

AI Visibility API

Base path: `/api/ai-reach/`

All endpoints require Authorization: Bearer <token>.


Queries#

List queries#

GET /api/ai-reach/queries/

Returns all active and paused queries for the authenticated tenant.

Response

json
[
  {
    "id": 1,
    "query_text": "best running shoes under $150",
    "category": "product discovery",
    "tags": [],
    "providers": ["chatgpt", "perplexity", "claude"],
    "is_active": true,
    "schedule": "daily",
    "last_run_at": "2026-05-09T10:00:00Z",
    "created_at": "2026-05-09T08:00:00Z",
    "updated_at": "2026-05-09T10:00:00Z"
  }
]

Create query#

POST /api/ai-reach/queries/

Request body

FieldTypeRequiredDescription
query_textstringyesNatural-language query, max 1000 chars. Must be unique per tenant.
categorystringnoGrouping label (brand awareness, product discovery, etc.)
tagsstring[]noFree-form tags
providersstring[]noSubset of chatgpt,perplexity,claude,gemini,grok. Empty = all.
is_activebooleannoDefault true
schedulestringnohourly, daily, or weekly. Default daily.

Response 201 Created — the created AIQuery object.


Get / Update / Delete query#

GET    /api/ai-reach/queries/<id>/
PATCH  /api/ai-reach/queries/<id>/
DELETE /api/ai-reach/queries/<id>/

PATCH supports all fields except id, created_at, updated_at.


Run query immediately#

POST /api/ai-reach/queries/<id>/run/

Enqueues run_ai_query Celery task immediately. Returns before execution completes.

Response 200

json
{ "status": "queued", "query_id": 1 }

Get query results#

GET /api/ai-reach/queries/<id>/results/

Returns the 50 most recent execution results for this query (all providers).

Response

json
[
  {
    "id": 42,
    "query": 1,
    "provider": "chatgpt",
    "raw_response": "When looking for running shoes...",
    "mentioned_brands": [
      { "name": "Nike", "position": 1, "sentiment": "positive" },
      { "name": "Acme Shoes", "position": 3, "sentiment": "neutral" }
    ],
    "mentioned_products": [
      { "name": "Acme Pro Runner", "sku": "APR-001", "position": 3 }
    ],
    "position": 3,
    "sentiment": "neutral",
    "is_simulated": false,
    "captured_at": "2026-05-09T10:00:00Z"
  }
]

Dashboard#

Get aggregated metrics#

GET /api/ai-reach/dashboard/

Returns rolled-up metrics across all queries for the authenticated tenant.

Response

json
{
  "mention_rate": 0.72,
  "avg_position": 2.4,
  "share_of_voice": 0.31,
  "sentiment_score": 0.68,
  "total_runs": 120,
  "by_provider": [
    {
      "provider": "chatgpt",
      "label": "ChatGPT",
      "mention_rate": 0.80,
      "avg_position": 2.1,
      "total_runs": 30
    }
  ],
  "timeline": [
    { "date": "2026-05-01", "total": 10, "mentioned": 7, "mention_rate": 0.70 },
    { "date": "2026-05-02", "total": 10, "mentioned": 8, "mention_rate": 0.80 }
  ]
}

When no results exist, all numeric fields return 0 or null and arrays are empty.


ClickHouse Table#

segmentation.ai_visibility_snapshots

ColumnTypeDescription
tenant_idStringTenant ID
query_idStringAIQuery PK (as string)
providerLowCardinality(String)Provider ID
mention_rateFloat320–1
avg_positionFloat321-based; 0 if not mentioned
share_of_voiceFloat320–1
sentiment_scoreFloat320–1
mentioned_skusStringJSON array of matched product SKUs
is_simulatedUInt81 if adapter returned stub data
captured_dateDateDate of execution

ReplacingMergeTree on captured_date. ORDER BY (tenant_id, query_id, provider, captured_date).


Competitors#

List competitors#

GET /api/ai-reach/competitors/

Response

json
{
  "count": 2,
  "results": [
    {
      "id": 1,
      "name": "Nike",
      "brand_aliases": ["Nike Inc", "Nike Group"],
      "domain": "nike.com",
      "is_active": true,
      "created_at": "2026-05-09T10:00:00Z",
      "updated_at": "2026-05-09T10:00:00Z"
    }
  ]
}

Create competitor#

POST /api/ai-reach/competitors/

Request body

FieldTypeRequiredDescription
namestringyesPrimary brand name. Must be unique per tenant.
brand_aliasesstring[]noAlternate names matched case-insensitively in AI responses.
domainstringnoBrand domain (display only).
is_activebooleannoDefault true. Inactive competitors are excluded from tracking.

Response 201 Created — the created Competitor object.

Get / Update / Delete competitor#

GET    /api/ai-reach/competitors/<id>/
PATCH  /api/ai-reach/competitors/<id>/
DELETE /api/ai-reach/competitors/<id>/

Competitor Comparison#

Cross-query position comparison#

GET /api/ai-reach/comparison/

For each query that has results, returns the tenant's average position alongside each registered competitor's average position.

Response

json
{
  "queries": [
    {
      "id": 1,
      "query_text": "best running shoes under $150",
      "category": "product discovery",
      "tenant_avg_position": 2.1,
      "total_results": 15,
      "competitors": [
        { "id": 1, "name": "Nike", "avg_position": 1.3, "mention_count": 12, "win_rate": 0.3 },
        { "id": 2, "name": "Adidas", "avg_position": null, "mention_count": 0, "win_rate": 1.0 }
      ]
    }
  ],
  "competitors": [/* Competitor objects */]
}

win_rate per query competitor entry: fraction of results where tenant was mentioned and outranked that competitor (or competitor was not mentioned).


Win rate summary#

GET /api/ai-reach/win-rate/

Aggregate win/loss/tie counts per competitor across all queries. Sorted by win rate descending.

Response

json
{
  "competitors": [
    {
      "id": 1,
      "name": "Nike",
      "domain": "nike.com",
      "wins": 45,
      "losses": 20,
      "ties": 5,
      "win_rate": 0.64,
      "total_compared": 70
    }
  ]
}

Win conditions:

  • Win — tenant mentioned and outranks competitor, or competitor not mentioned at all
  • Loss — competitor mentioned and outranks tenant, or tenant not mentioned
  • Tie — both at same position

Alert Rules#

List alerts#

GET /api/ai-reach/alerts/

Response — paginated list of CompetitorAlert objects.

Create alert#

POST /api/ai-reach/alerts/

Request body

FieldTypeRequiredDescription
alert_typestringyessov_jump, rank_drop, or new_competitor
competitorintegernoCompetitor ID. Required for sov_jump.
thresholdfloatnosov_jump: SOV delta (e.g. 0.1 = 10%). rank_drop: worst allowed position (e.g. 5). Default 0.1.
webhook_urlstringnoPOST target when alert fires. Payload is JSON with alert_type, message, and context fields.
is_activebooleannoDefault true.

Alert types:

TypeTriggerThreshold meaning
sov_jumpCompetitor's 7-day avg SOV increased vs prior 7 daysSOV delta (0–1)
rank_dropTenant's 3-day avg position exceeds thresholdWorst allowed position (integer)
new_competitorReserved for future detection

Get / Update / Delete alert#

GET    /api/ai-reach/alerts/<id>/
PATCH  /api/ai-reach/alerts/<id>/
DELETE /api/ai-reach/alerts/<id>/

ClickHouse Tables#

segmentation.ai_visibility_snapshots — tenant-level visibility per query/provider/date.

segmentation.competitor_snapshots — competitor-level metrics per query/provider/date.

ColumnTypeDescription
tenant_idStringTenant ID
query_idStringAIQuery PK
providerLowCardinality(String)Provider ID
competitor_nameStringCompetitor primary name
positionFloat321-based mention position; 0 if not mentioned
sovFloat32Share of voice for this competitor in this result
sentiment_scoreFloat320–1
is_mentionedUInt81 if found in AI response
captured_dateDateDate of execution

ReplacingMergeTree on captured_date. ORDER BY (tenant_id, query_id, provider, competitor_name, captured_date).