Skip to Content
Ai VisibilityAI Visibility — Architecture

AI Visibility — Architecture

Database Schema

Models

AIVisibilityPlatform — global registry of supported LLM platforms (seeded, not tenant-editable):

FieldTypeNotes
idString (cuid)
keyStringchatgpt | claude | gemini | perplexity
displayNameStringShown in UI
modelStringe.g. gpt-4.1, claude-sonnet-4-5
apiKeyEnvVarStringName of env var checked for key presence
isEnabledBooleanGlobal on/off (admin-controlled)

TenantAIVisibilityPlatform — per-tenant platform toggle:

FieldTypeNotes
tenantIdStringFK → Tenant
platformIdStringFK → AIVisibilityPlatform
isEnabledBooleanDefaults to true if no record exists

AIVisibilityPrompt — prompts monitored per tenant:

FieldTypeNotes
tenantIdString
promptStringFull prompt text sent to LLMs
promptTypeStringlocal | category | brand | competitor
isActiveBooleanOnly active prompts are sent on each run

AIVisibilitySnapshot — one row per prompt × platform per run:

FieldTypeNotes
tenantIdString
promptIdStringFK → AIVisibilityPrompt
platformStringPlatform key
isMentionedBooleanWhether brand name appears in LLM response
rawExcerptString?±150 chars around first brand mention
citedSourcesString[]URLs extracted from response
competitorsString[]Competitor names detected in response
runAtDateTimeWhen the check was executed

AIVisibilityNarrative — brand narrative generated after each run:

FieldTypeNotes
tenantIdString
periodStringlast_7_days | last_30_days
statusStringpending | generating | done | failed
narrativeString?Markdown text from brand-narrative-analyst

BullMQ Workers

agent__ai-visibility-monitor

File: packages/agents/src/workers/ai-visibility-monitor.worker.ts

Job data: AIVisibilityMonitorJobData{ tenantId, tenantName, promptIds? }

Flow:

  1. Load globally enabled platforms, filter by tenant preferences and apiKeyEnvVar presence
  2. Load active prompts for the tenant (all, or specific promptIds if provided)
  3. Reserve ai_visibility credits
  4. For each prompt × platform: call LLM, parse brand mentions + cited URLs + competitor names, write AIVisibilitySnapshot
  5. Consume credits if any snapshots were created; release if none
  6. Enqueue brand-narrative-analyst job after a successful run

LLM callers:

PlatformClientEnv vars
ChatGPTAzureOpenAI (openai SDK)AZURE_OPENAI_API_KEY, AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_DEPLOYMENT, AZURE_OPENAI_API_VERSION
ClaudeAnthropic SDKANTHROPIC_API_KEY
GeminiDirect fetch to generativelanguage.googleapis.comGOOGLE_GENERATIVE_AI_KEY
PerplexityDirect fetch to api.perplexity.aiPERPLEXITY_API_KEY

Nightly schedule: registered on worker startup, runs at 0 2 * * * UTC.

agent__brand-narrative-analyst

File: packages/agents/src/workers/brand-narrative-analyst.worker.ts

Auto-triggered after each monitor run. Reads the latest snapshots and produces a human-readable narrative (AIVisibilityNarrative) describing how the brand is perceived across AI platforms.


API Routes

All routes under /tenant/v1/ai-visibility in apps/api/src/routers/ai-visibility.ts. Require tenant JWT auth.

MethodPathDescription
GET/platformsList platforms with apiKeyPresent + tenant enabled state
PATCH/platforms/:platformIdToggle a platform on/off for this tenant
GET/promptsList tenant’s monitored prompts
POST/promptsCreate a new prompt
PATCH/prompts/:promptIdEdit/toggle a prompt
DELETE/prompts/:promptIdDelete a prompt
GET/snapshotsLatest snapshot per prompt × platform
GET/statsGEO score, per-platform mention rate, trend data
POST/runEnqueue an on-demand monitor run
GET/narrativeLatest brand narrative for the tenant
GET/historySnapshot history grouped by run date

apiKeyPresent Check

Both the API and the Next.js dashboard server component check process.env[p.apiKeyEnvVar] to determine whether to show “API key missing”. This means the keys must be present in both apps/api/.env and apps/dashboard/.env.local even though the actual LLM calls only happen in apps/servers/agents/.env.

Three files must have the keys:

FilePurpose
apps/servers/agents/.envActual LLM calls in the worker
apps/api/.envapiKeyPresent check in /platforms endpoint
apps/dashboard/.env.localapiKeyPresent check in page.tsx server component

Seed Data

packages/db/src/seed.tsseedAIVisibilityPlatforms() upserts the four platform records on every seed run. Re-run pnpm db:seed (from packages/db) after changing apiKeyEnvVar or model values.

Current seeded values:

keydisplayNamemodelapiKeyEnvVar
chatgptChatGPT (GPT-4.1)gpt-4.1AZURE_OPENAI_API_KEY
geminiGoogle Gemini 2.0gemini-2.0-flashGOOGLE_GENERATIVE_AI_KEY
perplexityPerplexityllama-3.1-sonar-small-128k-onlinePERPLEXITY_API_KEY
claudeClaude (Sonnet)claude-sonnet-4-5ANTHROPIC_API_KEY

© 2026 Leadmetrics — Internal use only