TruIntel Platform Documentation
All the necessary documentation for the TruIntel services is found here.
Getting Started
What is TruIntel?
TruIntel is an AI Engine Optimization (AEO) platform for the era of AI-assisted search. As buyers shift from typing keywords into Google to asking ChatGPT, Claude, Gemini, Perplexity, and Google AI Overview, TruIntel measures and improves how those AI engines perceive, rank, and cite your brand. It pairs that AEO core with a full SEO toolkit, an AI-optimized CMS, traffic and lead intelligence, and automated weekly reporting.
Where traditional SEO optimizes for one ranking algorithm, AEO optimizes for the way large language models recommend brands inside conversational answers. TruIntel runs your real customer questions through all five AI engines on a schedule, parses every answer for whether you are recognized, where you rank, the sentiment around you, and whether a source links back to you, then turns that into a single Visibility Score and a queue of fixes.
AI Visibility Monitoring
Track how ChatGPT, Claude, Gemini, Perplexity, and Google AI Overview mention, rank, and cite your brand against competitors.
SEO Toolkit
Site Health, PageSpeed, on-page audits, crawler, keyword and backlink research, GSC sync, indexation, rank tracking, and CTR analysis.
AI Insights & Tasks
Weekly AI-generated insights and an actionable task queue, powered by a Gemini-driven analysis pipeline.
AI-Optimized CMS
Generate, score, optimize, schedule, and publish content engineered to be cited by AI engines.
Traffic Classification
Classify visitors as human, good agent, bad bot, or synthetic in real time using a four-factor model.
Lead Verification
Score and verify form submissions to filter fake leads and route only real prospects to sales.
ChatGPT
OpenAI gpt-5.4-mini via the Responses API with the web_search tool.
Claude
Anthropic claude-haiku-4-5 with server-side web search enabled.
Gemini
Google gemini-3.1-flash-lite with Google Search grounding.
Perplexity
Perplexity sonar, routed as perplexity/sonar through OpenRouter.
Google AI Overview
Google's own AI Overview, captured via SearchAPI.io (not a model TruIntel runs).
Where the product lives
The TruIntel app runs at app.truintel.ai. These docs live at truintel.ai/docs. Sign up, onboarding, and every feature described here happen inside the app.
Quick Start Guide
You can get your first AI visibility check running in a single sitting. The flow moves from account creation, through choosing a plan, into a guided brand-setup wizard that ends by firing your first full check across all five AI engines.
Create your account
Sign up at app.truintel.ai with email and password (verified by a 6-digit OTP) or with Google. Google accounts are auto-verified.
Name your organization
Create a workspace that holds your brands, team members, and billing. Each user can own exactly one organization.
Choose a plan
Pick a paid plan via Razorpay checkout, or activate the permanent Free plan. Plan selection sits between organization creation and brand setup.
Add your brand
Enter brand name, domain, and a 50-500 character description, pick a country, and optionally capture a Brand Voice profile.
Pick topics and prompts
Accept or edit AI-suggested topics, then expand each to load suggested prompts (the real questions customers ask AI engines about your space).
Confirm competitors
Review AI-discovered competitors (auto-selected up to your plan limit) and add your own. Their visibility is benchmarked against yours.
Connect Google Search Console (paid plans, optional)
Link GSC for real keyword, click, and position data. Free users see this step locked; everyone can skip it.
Launch
Hit Start Visibility Check on the Review step. TruIntel fires your first full AEO check plus a full SEO refresh and on-page audit in the background.
Not sure what to track?
TruIntel suggests topics, prompts, and competitors for you using an AI suggestion engine tuned to your brand and industry. You only need to review and confirm.
How long the first check takes
The first full visibility check runs across all five AI engines and takes roughly 5 minutes to populate. PageSpeed is not run automatically at launch — trigger it manually from the SEO section when you want it.
Authentication & Account Security
TruIntel uses JWT-based authentication with a short-lived access token and a rotating refresh token. You can sign up with email and password or with Google, and invited teammates join automatically when they verify or sign in for the first time.
| Method | How it works | Notes |
|---|---|---|
| Email & Password | Register, then verify a 6-digit OTP emailed to you. Login is blocked until your email is verified. | OTP valid 10 minutes, max 5 attempts; an unverified login re-sends a fresh code. |
| Google OAuth | Full-page redirect to Google consent, then back to the app with tokens. Not a popup. | Scope is openid email profile; Google accounts are auto-verified and linked to a matching email account. |
| Team invitation | An invited email auto-joins the organization on first verify or Google signup. | The first organization joined becomes the active workspace. |
| Password recovery | Forgot-password emails an OTP; reset-password takes the email, OTP, and new password. | Reset also marks the email verified. Google-only accounts cannot change a password. |
Password requirements
- 8 to 100 characters long.
- At least one uppercase letter, one lowercase letter, and one digit (no symbol is required).
- Stored hashed with bcrypt; passwords are never persisted in plain text.
OTP lifecycle
- Six numeric digits, delivered by email via Resend.
- Valid for 10 minutes; a new request resets the attempt counter.
- Up to 5 wrong attempts — on the fifth the code is invalidated and you must request a new one.
- Used for both email verification at signup and password reset.
Token security
The access token (JWT, HS256) expires after 30 minutes and is held in an in-memory variable mirrored to localStorage so a refresh restores your session. The refresh token lasts 7 days, is SHA-256 hashed in the database, and rotates on every use. A single in-flight refresh is shared to avoid races, refresh fires about 2 minutes before expiry, and the refresh token is also set as an HttpOnly SSO cookie on .truintel.ai for cross-subdomain login.
reCAPTCHA never locks you out
Auth forms are protected by reCAPTCHA in fail-open mode: a missing token or an unreachable verification service is soft-passed and logged, so a broken widget will not block a real login. Only a supplied-but-forged token is rejected.
Login
/loginEmail/password or Google sign-in.
Register
/registerCreate a new account.
Verify Email
/verify-emailEnter the 6-digit OTP.
OAuth Callback
/auth/callbackLands here after Google consent with tokens.
Forgot Password
/forgot-passwordRequest a reset OTP.
Reset Password
/reset-passwordSet a new password with email + OTP.
Account Ready
/account-readyPost-setup confirmation screen.
Organizations & Multi-Workspace
An organization is the top-level workspace that owns your brands, team members, plan, and billing. Understanding how organizations resolve and switch is key for anyone who belongs to more than one.
- Each user can OWN exactly one organization — creating a second returns an error. The creator becomes the OWNER.
- A user can BELONG to many organizations through invitations, even though they can only own one.
- A newly created organization starts with no plan, so plan selection is the immediate next step.
- Switching organizations clears the selected-brand and query caches so you always see the right workspace data.
How your current organization is resolved
When you load the app, TruIntel picks your active workspace using a single deterministic rule (never an arbitrary first-membership pick), which prevents the intermittent plan-bounce issues common to multi-org accounts.
Explicit active choice
The membership matching your saved active organization is used first.
An organization you own
If no active choice is set, an organization you own is selected next.
Oldest membership
Otherwise the oldest membership (by creation date) wins, keeping resolution stable across sessions.
Switching workspaces
If you belong to multiple organizations, use the workspace switcher to set a new active organization. The switch updates your account preference and reloads brands and data for the chosen workspace.
Onboarding Walkthrough
Onboarding is a guided wizard that takes you from a brand-new account to a live, monitored brand. The organization step is skipped automatically if you already have a workspace, and progress is saved so you can resume where you left off.
Organization
Name your workspace. This creates (or updates) the organization and routes you to plan selection. Skipped if an organization already exists.
Choose Plan
Pick a paid plan via Razorpay or activate the one-time Free plan. The Start Free button is disabled once the Free plan has been used by the organization.
Brand
Enter brand name, domain (required and validated), a 50-500 character description, and country. Optionally capture a Brand Voice profile. Saving loads AI topic suggestions.
Topics & Prompts
Accept AI-suggested topics (capped by plan), expand each to lazily load suggested prompts, and select your set. Paid plans also see an AI search-volume column per prompt.
Competitors
AI-discovered competitors are auto-selected up to your plan limit; add or remove as needed before saving.
Google Search Console
Optional, paid-plans only. A popup OAuth lets you pick a GSC property. Free users see a locked card; anyone can skip.
Review & Launch
Start Visibility Check bulk-creates your queries, completes onboarding, and navigates to the Overview while the first full check runs in the background.
What launch actually triggers
Submitting the Review step fires the first full AEO visibility check across all five AI engines, bulk-creates your competitors, and kicks off a full SEO refresh plus an on-page check. This takes about 5 minutes to populate. PageSpeed is not part of launch and is run manually later.
Brand Voice (optional)
Capturing a Brand Voice — tone (educational, conversational, or authoritative), audience, and preferences like em-dash and CTA usage — tunes AI content generation in the CMS. You can set or edit it later under Settings, Brand Voice.
Description length is enforced
The onboarding form requires a brand description between 50 and 500 characters and a valid domain, even though the backend itself only requires a name. This richer context improves AI mention detection.
Brands & Limits
A brand is the unit TruIntel monitors: it carries the queries, competitors, sources, SEO data, content, and reports tracked for one website. Your plan determines how many brands an organization can run at once.
- Brand fields include name, domain, description, industry, country, a default country code (used to localize AI prompts), logo, and the optional Brand Voice profile.
- The active-brand count is checked against your plan limit on every create — exceeding it returns a brand-limit error prompting an upgrade.
- A brand stays in onboarding until you complete it; until then the app routes you back into the wizard so setup is never left half-done.
- Completing onboarding on a paid plan also generates a one-time onboarding brand report.
| Plan | Brands | Tracking queries / brand | Total tracked queries | Competitors |
|---|---|---|---|---|
| Free | 1 | 5 | 5 | 1 |
| Starter | 1 | 10 | 10 | 1 |
| Lite | 2 | 25 | 50 | 3 |
| Pro | 3 | 40 | 120 | 5 |
| Enterprise | 4 | 50 | 200 | 5 |
All five engines on every plan
ChatGPT, Claude, Gemini, Perplexity, and Google AI Overview are available on every plan, including Free. Plans differ on how many brands, queries, and competitors you can track — not on which AI engines you can monitor.
Plans & Billing Basics
TruIntel offers a permanent Free tier plus four paid plans. Pricing is monthly and billed in USD through Razorpay. The Free plan is a real, hard-limited tier — not a time-limited trial.
Free
$0/mo
- 1 brand
- 5 queries / brand
- 1 competitor
- 1 team member
- All 5 AI engines
- 0 AI credits
Starter
$59/mo
- 1 brand
- 10 queries / brand
- 1 competitor
- 1 team member
- 50 AI credits / mo
- Insights, CMS, Reports
Lite
$139/mo
- 2 brands
- 25 queries / brand
- 3 competitors
- 2 team members
- 150 AI credits / mo
- All Starter features
Pro
$479/mo
- 3 brands
- 40 queries / brand
- 5 competitors
- 3 team members
- 500 AI credits / mo
- Rank Tracking, daily checks
Enterprise is a custom plan (contact sales) with 4 brands, 50 queries per brand, 5 competitors, up to 10 team members, 1000 AI credits per month, and the highest daily-priority check allowance.
| Plan | Team members | AI credits / mo | Daily-priority AEO queries |
|---|---|---|---|
| Free | 1 | 0 | 0 |
| Starter | 1 | 50 | 0 |
| Lite | 2 | 150 | 0 |
| Pro | 3 | 500 | 5 / brand |
| Enterprise | 10 | 1000 | 10 / brand |
No time-based free trial
The Free plan is permanent and can be activated once per organization. There is no 7-day trial system — any "free trial" marketing copy is misleading. Marketing also shows struck-through regular prices ($79 / $189 / $599) and an annual "Save 20%" toggle, but both are display-only: billing is monthly. In India, 18% GST is added on top of the listed USD price.
AI credits and top-ups
Paid plans include a monthly AI-credit allowance that resets on the 1st, plus any one-time top-up credits that never expire. Top-up packs: Starter Pack (50 credits, $10), Growth Pack (150 credits, $25), Pro Pack (400 credits, $50). Credits power CMS/content generation (1 credit standard, 3 premium), content repurposing (1 credit), and Prompt-Volumes page lookups (5 credits each, after the daily/monthly cap). Brief generation is free.
Teams & Roles
Organizations support multiple users with three roles: owner, admin, and member. Role checks gate only organization-management actions — they do not restrict which brand features a teammate can use. Feature access is gated by your plan, not by role.
| Action | Owner | Admin | Member |
|---|---|---|---|
| Update organization settings | Yes | Yes | No |
| Invite members | Yes | Yes | No |
| View / cancel pending invitations | Yes | Yes | No |
| Change a member’s role | Yes | No | No |
| Remove a member | Yes (anyone) | Yes (members only) | No |
| Use brand features (AEO/SEO/CMS/reports) | Yes | Yes | Yes |
Role nuances to know
Only owners can change roles — admins cannot. Admins can remove only members, never owners or other admins. The last remaining owner cannot be removed or demoted, so an organization always has at least one owner. There is no per-brand RBAC: a member is not read-only and has the same feature access as everyone else on the plan.
Inviting teammates
Send the invite
An owner or admin invites by email. If the person already has a TruIntel account they are added immediately as a member with an email notification.
Pending invitation
If they have no account, a pending invitation is created with a secure token that expires after 7 days, and an invite email is sent.
Acceptance
New users auto-join when they verify their OTP or sign up with Google. Existing users accept via the invitation token, which must match the invited email.
Member limits per plan
Team size is capped by plan: Free 1, Starter 1, Lite 2, Pro 3, Enterprise 10. Manage your team from Settings, where you can invite, view pending invitations, change roles, and remove members.
AI Visibility Monitoring
Overview & Visibility Score
AI Visibility Monitoring is TruIntel's core feature. For every brand it runs your tracking queries (prompts) against 5 AI answer engines — ChatGPT, Claude, Gemini, Perplexity, and Google AI Overview — and measures whether, where, and how each engine surfaces your brand. Results are stored as time-series so you can watch trends, get alerts, and prove progress.
Each check produces a composite AI Visibility Score (0–100), a query x platform performance matrix, per-platform scores, brand perception (attributes + sentiment), Share of Voice vs competitors, and cited-source tracking. The score is computed by deterministic Python text-parsing — there is no LLM at scoring time, so the same responses always produce the same number.
AI Visibility Score
| Component | Weight | What it measures | Exact behavior |
|---|---|---|---|
| Recognition Rate | 40% | Share of query x platform pairs where AI mentions your brand | mentions / total pairs x 100; denominator excludes pairs where Google AI Overview was unavailable |
| Position Strength | 30% | How high your brand ranks within the response | pos 1 = 100, then -15 per rank (pos 7+ = 0); mentioned but not in a ranked list defaults to 50; averaged over mentions |
| Sentiment | 20% | Whether AI describes your brand positively or negatively | raw sentiment [-1,1] mapped to [0,100] via (s+1)x50, averaged; defaults to 50 when there are no mentions |
| Citation Rate | 10% | Whether AI links to a source for the mention | mentions carrying a citation URL / total mentions x 100 |
Score interpretation (3-band gauge)
The Visibility gauge uses three bands: 75–100 Excellent (emerald), 50–74 Good (amber), and below 50 Poor (red). There is no separate "Fair" band on the gauge. A different 60/40 threshold colours the Recognition Rate and Citation Rate metric cells (60+ green, 40+ amber) — do not confuse the two.
Zero-state defaults
On a brand's very first check (or when no mentions exist yet), overall / recognition / position / citation read 0, but Sentiment defaults to 50. The first check is also flagged as a "Baseline" so trend deltas only appear from the second check onward.
The 5 AI Platforms
TruIntel queries 5 AI answer engines concurrently on every check. The exact production models are listed below and are visible in each mention's "Run details" panel. The first three (ChatGPT / Claude / Gemini) are environment-overridable; Perplexity and Google AI Overview routing are fixed. The same models run on every plan, including Free — models are global, not plan-tiered.
ChatGPT
gpt-5.4-mini via the OpenAI Responses API with the web_search tool (default search context "low"). The most widely used AI assistant.
Claude
claude-haiku-4-5 (Haiku 4.5) via the Anthropic Messages API with the web_search_20250305 server tool (max 5 searches per query).
Gemini
gemini-3.1-flash-lite via the google-genai SDK with Google Search grounding; falls back to non-grounded mode on a billing or quota error.
Perplexity
perplexity/sonar routed through OpenRouter (not Perplexity's own API), returning built-in source citations from real-time web access.
Google AI Overview
Google's own AI-generated search summary, scraped via SearchAPI.io in a two-step google -> AI-Overview flow. Many queries simply have no AI Overview — a normal, non-failing result.
How each platform is queried
| Platform | Model / source | Web access | Notes |
|---|---|---|---|
| ChatGPT | gpt-5.4-mini | web_search tool, depth "low" | OpenAI Responses API; single attempt, country + date localized |
| Claude | claude-haiku-4-5 | web_search_20250305, max 5 uses | Anthropic Messages API; fails fast if credits are too low |
| Gemini | gemini-3.1-flash-lite | Google Search grounding | Non-grounded fallback on quota/billing error; 1-hour quota cooldown circuit-breaker |
| Perplexity | perplexity/sonar (via OpenRouter) | Built-in real-time search | Returns citations; no recency filter |
| Google AI Overview | Google AIO (SearchAPI.io) | Google's live AIO | Two-step fetch; aio_available flag marks "no AIO for this query" |
Response intelligence is a separate, non-blocking pass
After parsing, a Kimi K2.5 model (moonshotai/kimi-k2.5 via OpenRouter) enriches each mention with an LLM-derived rank_position, an optional mention-type override (recommended/discouraged), and up to 3 brand attributes. It never blocks the check and never changes the numeric Visibility Score — that is computed entirely by deterministic text-parsing.
What Each Check Extracts
For every query x platform pair, the pipeline parses the AI response with pure text/regex analysis (no LLM, under ~50 ms) and stores a structured mention record. Competitors are parsed first, so they are captured even when your brand is not mentioned.
Mentioned (y/n)
Word-boundary match against brand-name variants: lowercase name, CamelCase split, hyphenated, no-space, full domain, and domain without TLD.
List position
Rank within a numbered or bulleted list (1. / - / #1). 0 (stored null) if mentioned but not in a list. Drives matrix colour and the Position Score.
Rank position (LLM)
A second, Kimi-K2.5-derived rank stored separately for display only. Non-blocking; never affects the score.
Mention type
recommended / alternative / compared / mentioned / not_mentioned, from keyword scanning; Kimi may override to recommended or discouraged.
Sentiment
positive / neutral / negative plus a -1…1 score, from keyword counting in a +/-100-char window around the first mention.
Snippet & full response
A ~200-char sentence-trimmed snippet around the mention, plus the complete AI response text for review.
Citations & sources
A single best citation URL (brand-domain preferred) plus every unique URL in the response, each stored as a classified CitedSource.
Competitor mentions
Each tracked competitor found, with its own list position and sentiment, stored as CompetitorMention rows.
Run telemetry
Model used, tokens, latency, web-search call count, Live/Cached state, and per-check cost breakdown in response_metadata.
Query intent taxonomy
Every query is auto-classified into one of 5 intents at creation time (via Kimi K2.5, defaulting to informational if unavailable). The intent automatically sets the query's business value (1–5) — this is derived, not manually assigned.
| Query intent | Auto business value | Example |
|---|---|---|
| transactional | 5 | "best CRM to buy for a small agency" |
| comparison | 4 | "TruIntel vs competitor X" |
| recommendation | 4 | "recommend an AEO tool" |
| brand | 3 | "is TruIntel any good" |
| informational | 2 | "what is answer engine optimization" |
Brand-name alias gap
Matching only covers name/domain morphologies — there are no custom aliases and no bare-first-word matching. A brand whose AI-surfaced name is a short or product name (e.g. "River Mobility" surfaced as "River Indie") can be recorded as not-mentioned even when it is explicitly recommended. This is a known limitation.
Query Performance Matrix
The Query Performance Matrix shows how each tracking query performs across all 5 platforms. Rows are queries, columns are the 5 platforms plus an "Avg #" column, and each cell is a status pill (not just a shaded square). The AI Visibility page shows the first 15 active queries (newest first) with a "View all N queries" link to Prompt Tracking.
| Cell state | Appearance | Meaning |
|---|---|---|
| Mentioned, position 1–3 | Green #N pill | Brand ranked in the top 3 of a list |
| Mentioned, position 4–6 | Amber #N pill | Brand ranked mid-list |
| Mentioned, position 7+ | Red #N pill | Brand ranked low in the list |
| Mentioned, no list position | Green check pill | Brand named but not inside a ranked list |
| Retryable failure | Amber, clickable retry icon | Credits / rate-limit / timeout / no-response — click to re-run that one cell |
| Non-retryable failure | Red warning icon | Configuration / invalid request — a retry cannot fix it |
| Auto-retry in flight | Amber spinner ("still loading") | Queued in a bulk sweep or an in-flight retry — not a failure |
| AI Overview unavailable / not mentioned | Grey dash | No Google AI Overview for that query, or brand simply absent |
Hovering a mentioned cell shows position, mention type, sentiment, and Share of Voice. Clicking a row opens the Query Detail view — platform tabs, the full AI response with your brand highlighted, the citation list, competitors found on that platform, and run telemetry (model, latency, tokens, Live/Cached).
Where filters live
The matrix on the AI Visibility page does not expose sort or intent filters. Full filtering — topic, status (all/active/suggested/inactive), country, priority-only, search, and sort — lives on the Prompt Tracking page at /queries. There is no UI filter by "business value" or "query intent".
Brand Perception & Sentiment
TruIntel surfaces the attributes and sentiment that AI engines associate with your brand — effectively the keywords AI uses to describe you. The Brand Perception table is built from BrandAttribute rows from the latest full visibility check only, grouped by attribute + sentiment.
- Brand attributes — up to 3 per response (from Kimi K2.5), each with its sentiment, an occurrence count, and which platforms produced it.
- A per-platform dropdown (All / ChatGPT / Claude / Gemini / Perplexity / Google AIO) filters the table client-side.
- Sentiment distribution — positive / neutral / negative mention counts, available both overall and per platform.
- Mention types — recommended / alternative / compared / mentioned / not_mentioned, refined by the Kimi enrichment pass.
Use perception to steer content
If AI consistently associates the wrong attributes with your brand — or attaches negative sentiment — that is a direct signal to adjust the content and sources AI reads. Negative attributes also drive the negative-mention alert.
Citation & Source Tracking
Every URL in every response is extracted as a CitedSource and aggregated into a SourceDomain with citation count, usage %, and first/last-seen dates. This shows which websites AI relies on in your industry and whether your own site is among them.
Source domain types
Each cited domain is classified into one of 8 types. Classification runs in order: your own domain -> corporate; a tracked competitor -> competitor; hardcoded lists and institutional suffixes (.gov/.edu) for editorial / reference / ugc / marketplace / institutional; otherwise "other", which a background Kimi K2.5 pass later reclassifies. Domain types are cached for 7 days.
| Type | Meaning |
|---|---|
| corporate | Your own / owned brand domain |
| competitor | A tracked competitor's domain |
| editorial | News, magazines, and editorial publications |
| reference | Reference and knowledge sites |
| ugc | User-generated content (forums, Reddit, Q&A) |
| institutional | Government, education, and other institutions (.gov/.edu) |
| marketplace | Retail and marketplace listings |
| other | Unclassified domains (reclassified in the background by Kimi K2.5) |
Gap analysis
Source Gap Analysis finds domains cited in responses where your brand was NOT mentioned but a competitor WAS — high-value outreach targets. If none are found, it falls back to your own low-citation domains. These results feed Off-Page Outreach.
Earn citations to lift visibility
Being cited by high-authority sources directly improves AI visibility. Use the gap analysis to find sources that cite competitors but not you, then create content or run PR outreach toward those domains.
Check Frequency, Types & Plan Limits
How many queries, brands, and competitors you can track — and how often checks run automatically — depends on your plan. All 5 platforms are available on every plan, including Free. Free runs exactly one onboarding visibility check and has no recurring schedule.
| Plan | Brands | Queries / brand | Total queries | Competitors | Daily priority / brand |
|---|---|---|---|---|---|
| Free ($0) | 1 | 5 | 5 | 1 | 0 |
| Starter ($59) | 1 | 10 | 10 | 1 | 0 |
| Lite ($139) | 2 | 25 | 50 | 3 | 0 |
| Pro ($479) | 3 | 40 | 120 | 5 | 5 |
| Enterprise (custom) | 4 | 50 | 200 | 5 | 10 |
| Check type | When (Asia/Kolkata IST) | What runs | Plans |
|---|---|---|---|
| Weekly full check | Monday 02:00 IST | ALL active queries x ALL 5 platforms | All paid plans |
| Daily priority check | Tue–Sun 02:00 IST | Priority queries only | Pro (5/brand) & Enterprise (10/brand) |
| Manual full check | On-demand (button) | All queries x 5 platforms, bypasses the 24h cache | All paid; Free = one check total |
| Single-cell / single-query recheck | On-demand (matrix retry, Query Detail) | 1 query x 1 or all platforms | All paid plans |
| Auto on prompt-add | Immediately after adding a prompt | That query x 5 platforms | Capped at 10 immediate runs / org / day |
Free tier: one check only
On the Free plan you get 5 queries, 1 brand, and exactly one visibility check (the onboarding run). The page shows "No recurring checks on free plan" and further check requests return a 403. Recurring scheduled checks require a paid plan.
24-hour response cache
AI responses are cached for 24 hours (keyed on the original query text + country). Scheduled checks reuse the cache; manual checks set skip_cache to force fresh provider calls (then write the fresh result back). This is why a manual re-run can differ from the weekly snapshot. Retries always run fresh.
Localized to your country
Every check is anchored to the brand's country and the current local date, with a "prefer local brands, retailers, and sources" directive injected into the system prompt and query. This mirrors what a real in-country user would see (20 countries supported). It is why you may see local rather than global brands in results.
Post-Check Processing & Alerts
Each visibility check runs a fixed processing pipeline. The score itself is pure deterministic text-parsing; the Kimi K2.5 enrichment and domain reclassification are non-blocking add-ons that never change the number.
Fan-out
Every active query x 5 platforms runs concurrently (semaphore-limited), using the 24h cache for scheduled checks or fresh calls for manual checks.
Text parse
Pure text/regex extraction of mentioned, list position, mention type, sentiment, snippet, citation URL, and all citations — no LLM.
Score
Recognition / Position / Sentiment / Citation rolled into the overall and per-platform scores; AI-Overview-unavailable pairs are excluded from the denominators.
Persist
VisibilityCheck, QueryMention, PlatformScore, CitedSource, and CompetitorMention rows are written, with a per-check cost breakdown.
Response intelligence
Kimi K2.5 (non-blocking) updates rank_position, may override mention type, and extracts up to 3 brand attributes.
Domain classification
Cited domains are typed by rules now, with a background Kimi reclassification for "other" domains; SourceDomain usage % is recomputed.
Trends & alerts
Score / recognition deltas vs the previous full check are computed, then alerts are generated and HIGH/CRITICAL ones emailed.
Alert types
Five alert types are generated after a check. Only HIGH/CRITICAL alerts trigger an email to members.
| Alert | Trigger | Severity |
|---|---|---|
| visibility_drop | Score dropped >= 5.0 points vs the previous check | HIGH if >= 10, else MEDIUM |
| score_milestone | Score crossed 50 / 75 / 90 upward | LOW |
| competitor_overtake | A competitor's mentions exceeded your brand's | MEDIUM |
| negative_mention | New negative-sentiment mentions vs the previous check | HIGH if >= 3, else MEDIUM |
| integration_error | Transient/retryable platform failures (points to Retry) | MEDIUM |
Weekly Insights are separate
The 4-step AI Insights pipeline (Trend -> Competitor -> Strategy -> Tasks, powered by gemini-3.1-flash-lite) is a separate weekly scheduled task, not part of every visibility check. See the Insights section.
Failure Detection & Retry
Platform clients make a single attempt; all persistent retry is owned by a dedicated retry service so that "failed" means the same thing everywhere — in the matrix pills, query detail, and retry counts. Silent empty-200 responses are caught and surfaced honestly instead of showing an infinite spinner.
| Failure bucket | Retryable? | Example |
|---|---|---|
| rate_limit / timeout / temporary | Auto-retry | 429, 503, overloaded, connection reset |
| no_response (empty 200) | Manual retry only | Provider returned an empty response |
| credits | Non-retryable | Credit balance too low, insufficient quota, 402 |
| configuration | Non-retryable | Missing API key / unsupported country |
| invalid_request | Non-retryable | Invalid value / unsupported parameter |
Auto-retry fires immediately after a check for transient failures, then backs off across 30s / 2m / 5m / 15m / 1h for up to 6 attempts before marking the cell "exhausted". A 60-second beat sweeper re-runs any brand whose next retry is due. Each retry inserts a new mention row, so history is preserved and the original aggregate score is not mutated.
- Per-cell retry — click the amber retry pill on a matrix cell to re-run that one (query, platform) inline; a manual single-cell retry resets the chain so 6 fresh auto-retries can follow.
- "Retry all failed" button — sweeps every manually-recoverable failure (exhausted, in-backoff, credits-depleted, silent-empty); only configuration and invalid_request are excluded because a retry cannot fix them.
- The "Retry N failed" count equals exactly what the button re-runs (pending + stuck), so it never lies.
- An in-flight Redis lock collapses double-clicks and overlapping sweeps into a single provider call, and the matrix spinner is server-derived so it survives page navigation and device switches.
Credit-exhaustion is intentionally non-retryable
Errors that mean "out of money" (insufficient quota, credit balance too low, payment required, 402) are classified non-retryable so doomed retries do not burn the backoff window or show an endless spinner. Top up the relevant provider, then use "Retry all failed".
AI Crawlability & Site Audit
The AI Crawlability / Site Audit view (at /visibility/site-audit) checks how well AI crawlers can reach and read your site — a prerequisite for being cited at all. It combines site-health signals with per-vendor LLM-bot access derived from your robots.txt.
Site health gauge
Composite health signal alongside Domain Authority, backlinks, and referring domains.
LLM bot access
Per-vendor allow/block status (e.g. GPTBot, ClaudeBot, Google-Extended) parsed from robots.txt.
Top cited URLs
The brand URLs most frequently cited by AI engines, so you know which pages are working.
Spam score
Domain spam-risk signal (Pro+ only) to flag toxicity that can suppress citations.
Blocked bots = invisible brand
If your robots.txt blocks AI crawlers, those engines cannot read your pages and will struggle to cite you. The audit makes per-vendor access explicit so you can open the right bots.
Pages & API Reference
AI Visibility spans several pages in the AI Visibility navigation section. Free can reach AI Visibility, AI Crawlability/Site Audit, Prompt Tracking, Prompt Research, Sources, and Competitors; richer surfaces such as Prompt Volumes are paid-only.
AI Visibility
/visibilityScore gauge, query performance matrix, brand perception, Share of Voice, topics performance, and trends.
Prompt Tracking
/queriesManage tracking queries with full filters (topic, status, country, priority, search) and sort.
Query Detail
/queries/:queryIdPer-query deep dive: platform tabs, full responses, citations, competitors, and run telemetry.
AI Crawlability / Site Audit
/visibility/site-auditSite health, LLM-bot access from robots.txt, and top brand URLs cited by AI.
Sources
/sourcesCited domains, source-type breakdown, URL-level tracking, and source gap analysis.
Competitors
/competitorsCompetitor mention counts, positions, and head-to-head Share of Voice.
Prompt Research
/prompt-researchDiscover and add new high-value tracking queries.
Key API endpoints
# Run and watch a check
POST /api/v1/visibility/check
GET /api/v1/visibility/check/{task_id}/status
GET /api/v1/visibility/check/{task_id}/stream # SSE live progress
# Scores, history, mentions
GET /api/v1/visibility/score/{brand_id}
GET /api/v1/visibility/history/{brand_id}
GET /api/v1/visibility/checks/{brand_id}
GET /api/v1/visibility/checks/{check_id}/details
GET /api/v1/visibility/mentions/{brand_id}
GET /api/v1/visibility/mentions/{brand_id}/{query_id}
GET /api/v1/visibility/response/{mention_id}
GET /api/v1/visibility/{brand_id}/site-audit
# Matrix, retries, sources (dashboard + sources routers)
GET /api/v1/dashboard/{brand_id}/queries # performance matrix
POST /api/v1/dashboard/{brand_id}/queries/{query_id}/regenerate # single cell
POST /api/v1/dashboard/{brand_id}/queries/retry-failed # manual sweep
GET /api/v1/dashboard/{brand_id}/retry-status # badge polling
GET /api/v1/sources/{brand_id}/type-stats
GET /api/v1/sources/{brand_id}/gapLive progress stream
A running check exposes a Server-Sent Events stream (/visibility/check/{task_id}/stream) so the UI can show "checking query X of N" in real time rather than a blank loading state.
Prompts & Queries
What a Tracking Query Is
A tracking query (also called a "prompt") is a buyer-style question that TruIntel runs across the five AI platforms on a schedule to measure your brand visibility. Each query represents something a potential customer might genuinely ask an AI assistant about your category — for example "What is the best CRM for small teams?" rather than a keyword.
Every query stores its text, country/locale (default India), tags, status, intent, an auto-derived business value, and an optional topic. Queries are managed on the Prompt Tracking page at /queries; each query has a detail view at /queries/:id showing its visibility trend over time.
Buyer-Intent Phrasing
Queries are written as natural questions a customer would ask an AI assistant — never as raw keywords.
Localized
Each query carries a country/locale (default India). The country and current date are injected into the AEO system prompt so results reflect the right market.
Tagged & Grouped
Queries carry free-form tags and belong to one topic, so you can slice visibility by theme across the whole dashboard.
Trend-Tracked
Each query keeps a history of scores. The detail page charts how its visibility moves week over week.
Status & Check Cadence
Every query has one of three statuses, and the status plus the priority flag together decide when (and whether) the query is checked across the AI platforms.
| Status | Meaning | Counts toward plan limit? | Checked? |
|---|---|---|---|
| active | Being tracked on schedule | Yes | Yes |
| suggested | An AI recommendation not yet accepted | No | No (until accepted) |
| inactive | Paused by you | No | No |
Your plan limit counts only non-inactive queries (active + suggested), so pausing a query frees a slot. The check cadence then depends on whether the query is flagged as priority:
| Cadence | Applies to | When it runs (Asia/Kolkata IST) | UI label |
|---|---|---|---|
| Weekly full check | Every active query | Monday 02:00 IST | Mon |
| Daily priority check | Priority queries only | Tue-Sun 02:00 IST (plus Monday's full check) | Daily |
| Not checked | Inactive queries | Never | — |
Priority is a Pro+ feature
Daily priority slots are 0 on Free, Starter, and Lite. Pro gives 5 priority queries per brand and Enterprise gives 10. On the lower plans the priority toggle hard-fails with "Priority query limit reached" — every query is simply checked in the Monday full pass.
Intent & Business Value
When a query is created, TruIntel classifies its intent exactly once and caches the result on the row. Business value is then auto-derived from that intent — it is never a number you set yourself. The classifier runs on Kimi K2.5 (moonshotai/kimi-k2.5) via OpenRouter and falls back to informational / value 2 if classification ever fails.
| Intent | What it captures | Business value |
|---|---|---|
| transactional | Ready-to-buy / "near me" / pricing questions | 5 |
| comparison | "X vs Y" head-to-head questions | 4 |
| recommendation | "Best / top / recommended" questions | 4 |
| brand | Questions that name your brand directly | 3 |
| informational | General "how / what / why" research questions | 2 |
The effective business-value range is therefore 2-5 (never a user-assigned 1-5). In the UI, a QueryIntentBadge shows the intent and a "$" / "$$" badge marks high-value queries (value >= 4 shows $, value 5 shows $$).
High-value queries can auto-promote to priority
If a new query classifies at business value >= 4 and your plan still has a free priority slot, TruIntel auto-flags it as priority at creation so the most commercially valuable questions get the daily cadence first.
Bulk-created queries (from onboarding) are classified in the background a few seconds after they are saved, so their intent badges fill in shortly after the queries appear.
Adding & Editing Queries
The "Add Prompt" modal has two tabs: Manual (type a query yourself) and AI (generate suggestions). There is no CSV upload — bulk creation happens through the AI-generation and onboarding flows.
Open Add Prompt
From the Prompt Tracking page, choose the Manual or AI tab. Manual takes one query at a time; AI proposes a batch you can accept.
Plan limit + duplicate checks run first
Creation is blocked at your per-brand query cap (PLAN_LIMIT_EXCEEDED). Case-insensitive duplicate detection returns the existing query instead of creating a second copy.
Intent is classified
The new query is sent to the intent classifier, which sets its intent, business value, and possibly an auto-priority flag.
An immediate check may kick off
So you don't have to wait for the next cycle, TruIntel can run a single instant AEO check on the new query and show a "Running now…" state while it processes.
Instant-check quota
Immediate single-query checks are capped at 10 per organization per IST day. It is a soft limit: once exhausted the prompt is still created, the instant check is just skipped and the query waits for its next scheduled run.
Editing the text resets the trend
If you materially change a query's wording, TruIntel clears the cached intent/business value (re-derived on the next run) and returns a history-reset warning. Existing history rows are kept, not deleted, but the change only takes effect on the next scheduled check — edits never trigger an immediate run.
Topics & Organization
Topics group related queries so you can slice visibility by theme across the dashboard. Each query belongs to exactly one topic, and topics carry a denormalized query count.
- Topics are unlimited on every plan — there is no per-plan topic cap.
- Create, rename, and delete topics from the Topics Management modal; duplicate names are blocked case-insensitively.
- Deleting a topic does not delete its queries — their topic is simply set to none (they keep all history, they just lose the grouping).
- Filter visibility data by topic anywhere the topic filter appears to focus on one area of your business.
- Topics also help the AI Insights engine group related findings.
AI Query Suggestions
TruIntel can generate topics and prompts for you using its provider-agnostic utility-LLM chain (Anthropic-first, falling back to Gemini then OpenAI). These are an on-demand onboarding flow — every "suggest" call is preview-only and saves nothing until you accept selections through the bulk endpoints. They are not a continuously refreshed feed.
- Topics-with-prompts: one shot that returns N topics each pre-filled with K prompts (the current onboarding step).
- Topics only, then prompts-per-topic: the legacy two-step flow for expanding one topic at a time.
- Generate: produces queries for topics you have already saved, distributed across your plan's per-brand query budget.
- Generated prompts are deliberately brand-agnostic (your brand name is forbidden inside the query text) and localized to your brand's country.
How many topics and prompts you can generate depends on your plan:
| Plan | Topics generated | Selectable | Prompts / topic | Regenerations |
|---|---|---|---|---|
| Free | 4 | 3 | 2 | 1 |
| Starter | 5 | 3 | 3 | 1 |
| Lite | 6 | 4 | 3 | 2 |
| Pro | 8 | 5 | 3 | 2 |
| Enterprise | 10 | 6 | 5 | 3 |
Best practice
Start with 10-20 high-intent queries that map directly to your product, then expand into broader category questions. Queries where you have a realistic chance of being mentioned move your score; very generic questions tend to have low brand recognition.
Prompt Research
Prompt Research (/prompt-research) is an ideation tool that turns keywords into ready-to-track prompt ideas. You enter a list of keywords and TruIntel returns up to 15 natural-language prompt ideas, each with suggested tags and an intent label, that you can add straight into tracking with one click.
What it does NOT do
Prompt Research does not query any AI platform and does not analyze responses. It generates prompt ideas only — it computes no mentions, sources, or sentiment. To actually measure visibility, add an idea to tracking and let the scheduled AEO checks run it.
- Input is a set of keywords (not a single seed topic).
- Output is 15 flat prompt ideas — there are no topic clusters.
- Each idea carries up to 3 tags and an intent of transactional, comparison, recommendation, or informational.
- Generation runs on the utility-LLM chain (Anthropic-first fallback) and is stateless apart from saving a history row.
- Past runs are saved: browse the research history and open any past run to see its full prompt list.
Costs 1 credit per run — paid plans only
Each Prompt Research generation deducts 1 AI credit up front (insufficient credits return HTTP 402). Because Free includes 0 monthly credits, Prompt Research is effectively a paid-plan feature, metered by credits rather than unlimited.
Plan Limits & Routes
Query capacity scales with your plan. Limits below are per brand — both the per-brand query cap and the daily priority slots are counted per brand.
| Plan | Queries / brand | Total tracked queries | Daily priority queries |
|---|---|---|---|
| Free | 5 | 5 | 0 |
| Starter ($59) | 10 | 10 | 0 |
| Lite ($139) | 25 | 50 | 0 |
| Pro ($479) | 40 | 120 | 5 |
| Enterprise | 50 | 200 | 10 |
Prompt Tracking
/queriesList, add, filter, and prioritize tracking queries; manage topics.
Query Detail
/queries/:idPer-query visibility trend, platform breakdown, and history.
Prompt Research
/prompt-researchGenerate prompt ideas from keywords and add them to tracking.
Sources & Citations
What Source Tracking Captures
When an AI platform answers one of your tracked queries, it often cites source domains and URLs. Source Tracking (/sources) records every one of those citations so you can see exactly which sites AI engines lean on when they describe your category — and which ones mention you versus your competitors.
Per-Citation Records
Each individual citation is stored with its domain, URL, title, and type, tied back to the exact AI mention it came from.
Aggregated Domain Stats
Per domain, TruIntel rolls up total citations, usage percentage, average citations, and first/last seen timestamps.
Typed & Filterable
Every domain is classified into one of eight source types, so you can filter earned media from owned, competitor, or marketplace sources.
Sources are a byproduct of visibility checks
There is no separate "scan sources" action. Citation data accumulates automatically as your scheduled AEO checks run, so coverage deepens with your query count and check cadence. The page is viewable on every plan, including Free.
Source Types
TruIntel classifies every cited domain into one of eight stored source types. A domain is typed once and the result is cached, so each domain is classified only a single time.
| Source Type | Description | Examples |
|---|---|---|
| corporate | Company / brand official sites (including your own) | yoursite.com, vendor homepages, about pages |
| editorial | News outlets and publications | Forbes, TechCrunch, Reuters |
| ugc | User-generated content platforms | Reddit, Quora, Stack Overflow |
| institutional | Government, academic, and non-profit sites | .gov, .edu, standards bodies |
| reference | Reference and knowledge bases | Wikipedia, documentation hubs |
| competitor | A tracked competitor's domain (matched, not LLM-guessed) | rival.com |
| marketplace | E-commerce and listing platforms | Amazon, Product Hunt, app stores |
| other | Anything that does not fit the above | Misc. blogs and aggregators |
Classification follows a fixed order: (1) if the domain matches your brand it becomes corporate, and if it matches a tracked competitor it becomes competitor; (2) hardcoded lists / first-seen typing handle well-known hosts; (3) anything still "other" is sent to a Kimi K2.5 (via OpenRouter) classifier, whose output is cached. The LLM can return any type except competitor (that one is reserved for domain matching).
Earned vs Owned are filter aggregates, not stored types
The Sources page lets you filter by "Earned Media" and "Owned-Corporate". These are frontend roll-ups, not database types: Owned = corporate, and Earned = editorial + ugc + reference + institutional + marketplace + other. Use them for a quick split, and the eight underlying types for precision.
Sources Page
The Sources page opens with a five-cell stat strip, then a distribution donut, a gap-opportunity table, and the full citation table.
- Stat strip: Total sources, Earned Media, Corporate-Owned, Competitor, and Gap Opportunities.
- Source Type Distribution donut: citations broken down by type (interactive donut).
- "Sources You Should Target": the gap table, with a per-row "Draft outreach" button that hands the domain to the Outreach workflow.
- "All Citation Sources": the full table with domain search, a type dropdown (All / Earned Media / Owned-Corporate / Editorial / UGC / Reference / Institutional / Marketplace / Competitor), a platform filter, and sorting by Most cited, Last seen, First seen, or Highest usage %.
Click any row to open the Source Detail modal, which lists that domain's top cited URLs, which AI platforms cite each one, and the first/last seen dates.
Source Gap Analysis
Gap Analysis surfaces high-authority domains that AI cites in responses where your competitors appear but your brand does not. These are your highest-leverage outreach targets — places AI already trusts in your category that have not yet picked you up.
Find your blind spots
TruIntel collects the AI responses where your brand was not mentioned for a tracked query.
Check who got cited instead
It identifies which active competitors were mentioned in those same responses and aggregates the domains cited there (top 30).
Score the opportunity
Each domain gets an opportunity score from 0-100, weighted by how often competitors are cited there, the size of the gap versus your own citations, and how many distinct competitors it favors. The top 20 are returned.
Act
Each gap row shows the domain, its type, competitor vs brand citations, which competitors cite it, the platforms involved, a representative query, and a plain-language opportunity note — and a "Draft outreach" button to start a pitch.
Fallback when there's no competitor data yet
If TruIntel doesn't yet have competitor-cited responses to compare against, it instead surfaces your own low-citation domains (3 or fewer cites) as "room for growth", excluding your own host and a blocklist of non-targetable mega-platforms (Google, Bing, YouTube, Facebook, X, LinkedIn, Reddit, Quora, Medium, Wikipedia, Amazon, CDNs, and AI-infra hosts).
Actionable insight
If a domain is frequently cited by AI in your industry but never references your brand, getting featured there (through PR, guest posts, or partnerships) can meaningfully lift your visibility score.
Routes
Sources
/sourcesCitation tracking: stat strip, type donut, gap targets, and the full citation table with per-source detail modals.
Competitor Tracking
How Competitor Tracking Works
Competitor Tracking (/competitors) measures how your rivals show up in AI answers alongside you. You add competitors (manually or from AI discovery) and TruIntel records every time one of them is mentioned in a response to your tracked queries — position, sentiment, snippet, and platform.
Competitor data appears only after the next full check
Competitor mentions are generated solely as a byproduct of your brand's weekly full visibility check. There is no competitor-only refresh, and adding or removing a competitor dispatches no task — it is just a database row. A newly added competitor shows "tracking starts at next check" and has no metrics until that Monday 02:00 IST full pass runs.
Discovery & Lifecycle
Competitors move through a suggested → active → rejected lifecycle. AI discovery proposes candidates; you decide which to track.
Discover
AI discovery (run at onboarding and on demand) uses the utility-LLM chain to propose competitors from your brand profile, returning name, domain, description, and a confidence score, then dedupes against what you already have. Discovered entries get an "Auto-discovered" badge.
Triage
Suggested competitors can be tracked (promoted to active, subject to your plan limit) or rejected. You can also manually add competitors directly as active.
Maintain
Edit a competitor's name, domain, or description at any time — edits are free and never count against your limit. Delete removes it entirely.
The limit applies to active competitors only
Your plan cap is enforced at add / track / bulk time and counts only competitors with status active. Suggested and rejected entries are free, and edits are always exempt.
Comparison Metrics
The Visibility Comparison table shows you side by side with each tracked competitor across the latest full check window. A status PillTabs filter lets you view suggested, active, or rejected competitors.
| Metric | What it shows |
|---|---|
| Visibility Score | AI visibility (0-100) for the brand and each competitor |
| Recognition Rate | How often each brand is mentioned in AI responses |
| Average Position | Where each brand appears in AI answers (1st, 2nd, 3rd, …) |
| Sentiment | Dominant tone (positive / neutral / negative) per competitor |
| Citation Count | How often each brand's own domain is cited as a source |
| Top Topics | The top 3 topics each brand shows up in |
| Platform Breakdown | Which platforms favor which competitors |
The competitor score uses a different formula
Your brand's visibility score weighs Recognition 0.40 + Position 0.30 + Sentiment 0.20 + Citation 0.10. The per-competitor score uses Recognition 0.40 + Position 0.30 + Presence 0.30, with no sentiment or citation term. The two are broadly comparable but not computed identically — read competitor scores as a directional benchmark, not a like-for-like number.
Analysis Surfaces
Beyond the comparison table, the Competitors page offers four deeper analysis surfaces.
AEO Competitor Matrix
A grid of your brand versus each competitor, showing per-platform mention presence at a glance.
Top Opportunities
"Missing" prompts where a competitor is mentioned but you are not, and "Weak" prompts where you are mentioned but a competitor outranks you — each with the query, topic, competitor, and the mention/position deltas.
Prompt Overlap (Venn)
Per competitor, how your tracked prompts split into brand-only, competitor-only, both, and neither across every query.
Mentions History
A time series of mention counts for your brand and each active competitor, in day or month buckets over 1-365 days, with an optional platform filter.
Plan Limits & Routes
How many competitors you can actively track depends on your plan. Unknown plans fall back to Starter limits.
| Plan | Tracked competitors |
|---|---|
| Free | 1 |
| Starter | 1 |
| Lite | 3 |
| Pro | 5 |
| Enterprise | 5 |
Competitors
/competitorsDiscovery, suggested/active/rejected workflow, comparison table, AEO matrix, opportunities, prompt overlap, and mentions history.
Prompt Research
Overview
Prompt Research is an AI prompt-idea generator. You type 1-20 seed keywords and an LLM returns up to 15 natural-language search prompts — the kinds of questions real people ask AI assistants like ChatGPT, Claude, Gemini and Perplexity in your industry. Each prompt comes with topic tags and an intent label, and any prompt can be promoted into a tracked AI Visibility query with one click.
What it is — and what it is NOT
Prompt Research only writes question ideas. It does NOT call ChatGPT/Claude/Gemini/Perplexity/Google AI Overview, and it does NOT analyze brand mentions, citations, or sentiment. The actual cross-platform analysis happens later in AI Visibility, after you add a prompt as a tracking query.
Idea generation
Turn a handful of seed keywords into up to 15 ready-to-use, natural-language AI search prompts tailored to your brand industry.
Tags + intent
Every prompt carries up to 3 topic tags and one of 4 intent categories so you can sort comparison, recommendation, transactional and informational questions.
Add to Queries
Promote any generated prompt into a tracked AI Visibility query — with an optional Topic, a Country, and the tags carried over.
Saved history
Every generation is saved per brand as a Recent Search you can reload — keywords and full prompt list included.
Best practice
Use Prompt Research to build your tracking query set. Generate prompt ideas for a topic, pick the questions that matter most to your audience, then "Add to Queries" so AI Visibility starts measuring whether your brand shows up in those answers across all five platforms.
How It Works
The flow is a single round-trip: you supply keywords, TruIntel injects your brand context, an LLM drafts the prompts, and the result is sanitized and saved. No AI platforms are queried at this stage.
Add seed keywords
Enter 1-20 keyword chips via Enter or the "Add" button. The Generate button stays disabled until at least one keyword is present.
Brand context is injected
The selected brand name, industry and description are silently added as context — but the brand name is deliberately kept OUT of every generated prompt so the questions read like neutral user queries.
Generate (costs 1 AI credit)
One AI credit is deducted up front before generation. If you have no credits the request returns a 402 "Insufficient credits" error and nothing is generated.
LLM drafts the prompts
The utility-LLM chain is asked for exactly 15 prompts — a mix of comparison, recommendation, how-to, informational and buying-intent questions, each with 1-3 tags and one intent category.
Sanitize + return
Empty prompts are dropped, tags are capped at 3, and any invalid intent is forced to "informational". You receive up to 15 clean prompts (not always a fixed 15).
Saved to history
The keywords and prompt list are stored as a Recent Search row scoped to the brand, returning a history_id you can reload later.
Up to 15 — not 20-30, and no clusters
The model is asked for exactly 15 prompts; after sanitization you get 15 or fewer. There are no topic clusters. Each prompt only carries tags and an intent label — nothing else.
Output & Intent Categories
Each generated prompt is a small object: the prompt text, up to 3 topic tags, and exactly one intent category. Intents render as colored pills so you can scan the mix of question types at a glance.
| Field | Type | Notes |
|---|---|---|
| text | string | The natural-language search prompt. Never contains your brand name. |
| tags | string[] | Up to 3 topic tags. Carried over when you Add to Queries. |
| intent | enum | One of 4 categories (see below); invalid values default to informational. |
The 4 intent categories
Transactional
Buying-intent questions — pricing, where to buy, "best deal" style prompts close to a purchase decision.
Comparison
Head-to-head questions comparing products, vendors or approaches ("X vs Y", "alternatives to...").
Recommendation
Ask-for-a-pick questions ("best tools for...", "what should I use to...") where AI suggests options.
Informational
How-to and explainer questions. Also the fallback category for any prompt with an unrecognized intent.
Add to Queries
Add to Queries is the real payoff. It converts a generated prompt idea into a tracked AI Visibility query, so TruIntel begins measuring whether your brand appears in AI answers to that question on every platform.
Click "Add to Queries"
Each result row has an Add to Queries button that opens a short modal.
Pick or create a Topic (optional)
Group the query under an existing topic, or create a new topic on the spot. Topics organize your tracked queries.
Choose a Country
Set the market for the query. The default is India (IN); change it to localize AI answers to the right region.
Save the query
The prompt tags carry over automatically. Once added, the result row switches to an "Added" state so you do not double-add.
This is what feeds tracking
A prompt only starts producing visibility data once you Add to Queries. From there it is picked up by the normal AI Visibility cadence — the weekly full check (Mondays 02:00 IST) and, on Pro/Enterprise, the daily-priority checks.
History (Recent Searches)
Every generation is persisted per brand so you can revisit and reload past ideas without spending another credit. The idle screen surfaces recent searches as clickable pills.
- Each Recent Search stores the keywords used, the full prompt list, and the prompt count.
- Clicking a recent-search pill reloads its keywords and prompts instantly — no new generation, no credit spent.
- History is scoped to the brand and organization and is paginated (default 20 per page, up to 100).
- Reloading a past entry lets you Add to Queries from older ideas at any time.
Reloading is free
Only generating a fresh set of prompts costs 1 AI credit. Opening a saved Recent Search to re-read or promote those prompts to queries is free.
Credits, Plans & Availability
There is no route-level plan gate on Prompt Research — access is governed purely by your AI-credit balance. Each generation costs exactly 1 AI credit, drawn from your monthly plan_credits pool first, then any never-expiring extra_credits top-ups. Because the Free plan gets 0 credits, Prompt Research is effectively a paid-plan feature.
| Plan | Monthly AI credits | Prompt Research | Approx. generations/month |
|---|---|---|---|
| Free | 0 | Blocked (every run 402s) | 0 |
| Starter | 50 | Available | ~50 (shared) |
| Lite | 150 | Available | ~150 (shared) |
| Pro | 500 | Available | ~500 (shared) |
| Enterprise | 1000 | Available | ~1000 (shared) |
Credits are shared
AI credits are a single shared pool. Prompt Research generations compete with CMS/Plus content drafts, repurposing, and Prompt-Volumes page lookups. There is no per-day or per-session cap beyond your credit balance — heavy generation will draw down credits you also need elsewhere.
- plan_credits reset on the 1st of each month; extra_credits top-up packs never expire.
- Top-up packs (one-time): Starter Pack 50 credits $10, Growth Pack 150 credits $25, Pro Pack 400 credits $50.
- The page itself unlocks only after the brand's first AI Visibility check completes — until then it shows a loading gate.
How Prompts Are Generated (Model)
Prompt Research does not use a single fixed model. It runs through TruIntel's provider-agnostic utility-LLM chain: the first provider that returns valid JSON wins, and the chain falls through on any error, a non-JSON response, or a 60-second timeout. This keeps generation resilient when one provider is rate-limited or down.
| Order | Provider | Model |
|---|---|---|
| 1 | Anthropic | claude-haiku-4-5 |
| 2 | Gemini | gemini-3.1-flash-lite |
| 3 | OpenAI | gpt-5.4-mini (JSON mode) |
| 4 | OpenRouter (backstop) | moonshotai/kimi-k2.5 |
No platform analysis here
This utility chain only drafts prompt text. It is separate from the AEO platforms that AI Visibility actually queries, and separate from the deterministic Python scoring used to compute your Visibility Score.
Routes & API Reference
Prompt Research lives under the AI Visibility section in the navigation, and is backed by three endpoints under the /api/v1/prompt-research prefix.
Prompt Research
/prompt-researchThe generator page — keyword input, results with tags + intent, Add to Queries, and Recent Searches. Found under AI Visibility in the nav.
Backend endpoints
| Method | Endpoint | Purpose |
|---|---|---|
| POST | /api/v1/prompt-research/generate | Generate prompts (costs 1 AI credit); saves a history row and returns history_id. |
| GET | /api/v1/prompt-research/{brand_id}/history | List past generations (paginated: limit 1-100, default 20; offset). |
| GET | /api/v1/prompt-research/{brand_id}/history/{history_id} | Fetch a single past generation with its full prompt list for reloading. |
Prompt Volumes
NewWhat Prompt Volumes Is
Prompt Volumes is a single-keyword "AI search volume" research tool. You type one keyword or topic and it estimates how many times people are asking AI engines about it, broken down across five AI platforms, plus a 12-month trend, the kinds of prompts users actually ask, the top cited source domains, co-mentioned brands, and topic categories. Think of it as keyword research for the AI-answer era.
It lives under the AI Visibility group at the route /prompt-volumes and replaced the retired "AI Mentions" feature (the old /ai-mentions route permanently redirects here). The page is a single scrolling layout — there are no tabs.
Total AI Search Volume
A headline volume number summed across all five platforms, with a period-over-period delta so you can see whether interest is rising or falling.
12-Month Trend
A multi-line chart of monthly volume per platform, with client-side period re-slicing (1 / 3 / 6 / 12 months) that recomputes totals instantly without a new lookup.
Relevant Prompts
AI-generated, buyer-style example questions for the topic, each with its own search keyword and an asynchronously filled volume column.
Sources & Brands
The top 10 cited source domains, the top 10 brands co-mentioned for the topic, and the top 5 topic categories.
Intent Classification
A rule-based badge labelling the keyword as informational, commercial, transactional, or navigational, with a confidence score.
Watchlist
Star any keyword to save it (with its region and language) to the active brand's watchlist so you can re-run it later.
Brand context required
You must have a brand selected in the top-nav brand picker to use the page — lookups and watchlist saves are scoped to that brand. The keyword you research itself is brand-agnostic; the brand only provides scope for saving and history.
Powered by DataForSEO + Gemini
Real volume comes from the DataForSEO "LLM Mentions" dataset (for ChatGPT and Google AI Overview). The relevant-prompts list is generated by Gemini (gemini-3.1-flash-lite). The three remaining platforms are modelled from the ChatGPT number — see the next subsection for the honest breakdown.
The Five Platforms: Real vs Estimated
The page always shows five platforms, but they are not all measured the same way. This is the single most important nuance to understand: only ChatGPT and Google AI Overview carry real measured data. Gemini, Claude, and Perplexity volumes are statistically modelled from the ChatGPT number using fixed market-share multipliers.
ChatGPT
REAL data. Volume is derived from Google "People Also Ask" — DataForSEO's official proxy for ChatGPT prompt volume, since no public ChatGPT-usage API exists.
Google AI Overview
REAL data. Sourced from DataForSEO Google AI Overview impressions and Google Search Volume data, using your actual selected region.
Gemini
ESTIMATED. Modelled as ChatGPT volume x 0.171 market-share multiplier, with a confidence band.
Claude
ESTIMATED. Modelled as ChatGPT volume x 0.043 market-share multiplier, with a confidence band.
Perplexity
ESTIMATED. Modelled as ChatGPT volume x 0.071 market-share multiplier, with a confidence band.
| Platform | Source | How the number is produced | Confidence band |
|---|---|---|---|
| ChatGPT | Real | DataForSEO PAA-proxy volume (Google People Also Ask) | Measured |
| Google AI Overview | Real | DataForSEO AI Overview impressions / Google Search Volume | Measured |
| Gemini | Estimated | ChatGPT volume x 0.171 | -0.85 / +1.20 |
| Claude | Estimated | ChatGPT volume x 0.043 | -0.80 / +1.30 |
| Perplexity | Estimated | ChatGPT volume x 0.071 | -0.80 / +1.25 |
Each estimated platform row carries an "[Estimated]" badge with a tooltip explaining the methodology, and a confidence range (low / high) derived from the per-platform band multipliers above. The Total Volume in the hero is the sum of all five platform values (two real plus three estimated).
Estimates never fabricate volume
If ChatGPT volume is 0, all three estimated platforms are 0 too — the model never invents non-zero estimates. The market-share weights are a hardcoded set "as of 2026-04-01", refreshed quarterly from Statcounter / Similarweb / OpenAI-Anthropic disclosures.
ChatGPT is US-only at the source
DataForSEO's ChatGPT dataset is hard-pinned to the US (location code 2840, language English) regardless of the region you select. For non-US regions, TruIntel applies a silent regional multiplier to the US ChatGPT number so the displayed value reflects that region's relative scale. Google AI Overview, by contrast, uses your actual selected region directly with no weighting.
| Region | ChatGPT multiplier | Region | ChatGPT multiplier |
|---|---|---|---|
| United States | 1.00 | Germany | 0.14 |
| India | 0.55 | France | 0.11 |
| United Kingdom | 0.18 | Canada | 0.08 |
| Japan | 0.18 | Spain | 0.07 |
| Brazil | 0.20 | Australia | 0.05 |
Unlisted regions
Any region not in the table above uses a multiplier of 1.00, meaning ChatGPT volume is shown at its raw US value for that region.
How a Lookup Works
A standard page lookup fans out four parallel DataForSEO calls plus one Gemini call, then assembles the trend, deltas, intent, sources, brands, and categories from the merged results.
1. Real volume (2 aggregate calls)
Aggregated-metrics calls for Google AI Overview and for ChatGPT return the real headline volumes for the keyword.
2. Trend + hierarchy data (2 search calls)
Search/live calls for Google and ChatGPT return monthly search history (for the 12-month trend) and fan-out queries.
3. Relevant prompts (1 Gemini call)
Gemini (gemini-3.1-flash-lite) generates the buyer-style example prompts for the topic.
4. Model + assemble
The backend models the three estimated platform volumes, builds the 12-month trend, computes period-over-period deltas, classifies intent, and merges top sources, brand entities, and categories.
5. Async prompt volumes
The frontend then fires a follow-up call to fill the per-prompt volume column; until it returns, that column shows a skeleton.
| Lookup type | Calls fired | Approx. cost | What you get |
|---|---|---|---|
| Page (default) | 4 DataForSEO + 1 Gemini | ~$0.42 fresh | Full data: volume, trend, prompts, sources, brands, categories |
| Onboarding | 2 DataForSEO (aggregate only) | ~$0.20 fresh | Total-volume number only; prompts/sources/brands/categories empty |
The onboarding variant (used during brand onboarding) skips the two search/live calls, so it returns only the headline volume — relevant prompts, sources, brands, categories, and hierarchy come back empty by design.
24-hour caching
Every lookup is cached for 24 hours, keyed by a hash of the keyword, region, language, and purpose. An identical lookup within 24 hours is served free and marked as cached — it does not cost money and does not consume your lookup quota.
- A "force refresh" action bypasses the cache and re-bills (exposed as a secondary action in the UI).
- Page lookups and onboarding lookups never share a cache row (different purpose in the key).
- Self-heal: if a cached row has volume but an empty prompts list (e.g. Gemini was down at capture time), only the cheap Gemini call re-runs — not the full ~$0.42 bundle.
- Stale-cache fallback: if every upstream DataForSEO call fails but a past row exists for the exact keyword, the stale row is served rather than an error.
Lookup outcome statuses
| Status | Meaning | Quota / billing |
|---|---|---|
| ok | Full data returned | Consumes one lookup (if fresh) |
| partial | Some upstream calls failed; partial data shown | Consumes one lookup (if fresh) |
| insufficient_data | Keyword too obscure — both ChatGPT and Google volume are 0 | Persisted so the same keyword does not re-bill within 24h; shows "Not enough AI search activity" with broader-term suggestions |
| service_unavailable | All upstream calls failed (outage / out of credit) | Refunded — does NOT consume quota; shows "Service temporarily unavailable, try again" |
Reading the Results
The results page renders as a single scroll. Here is what each panel shows and how to read it.
Search Hero
Keyword input, the intent badge, the save-to-watchlist star, and your recent searches.
Prompt Volume Chart
"Across 5 platforms" total + delta hero, a filters bar (period, region, platform toggles), the 12-month multi-line chart, and a per-platform breakdown panel you can toggle on or off.
Relevant Prompts
A table of AI-generated example prompts with an asynchronously filled volume column.
Top Sources & Brands
The top 10 cited source domains and the top 10 brands co-mentioned for the topic, shown side by side.
Topic Categories
The top 5 topic categories associated with the keyword.
Trend and period filter
The 12-month trend is built from monthly search history summed across all returned items so the line matches the headline magnitude. The two real series (ChatGPT and Google AI Overview) are trimmed to a common end month because Google publishes AI Overview data on a slower cadence than ChatGPT. The estimated series are the ChatGPT spine scaled by each platform's weight.
The period selector is free
The API always returns the full 12 months. Switching between 1 / 3 / 6 / 12 months re-slices and recomputes totals and deltas entirely in your browser — no new lookup is fired and no quota is consumed. The default period is 12 months.
Intent badge
A rule-based classifier (no LLM) labels the keyword by matching keyword prefixes. Transactional terms are checked first, then commercial, then informational; anything unmatched defaults to informational at low confidence.
| Intent | Triggered by terms like | Example |
|---|---|---|
| Transactional | buy, price, coupon, deal | "buy crm software" |
| Commercial | best, vs, review, top | "best crm for startups" |
| Informational | what is, how to, guide | "what is a crm" |
| Navigational | brand / product navigation terms | "salesforce login" |
Relevant Prompts (AI-generated, not captured)
These are example prompts, not real user prompts
The Relevant Prompts table shows buyer-style questions that Gemini invents for the topic — they are AI-generated examples, NOT captured real user prompts. Each carries a summary, a full question, an intent tag, and a short search keyword that Gemini extracts (2-4 word noun phrase).
A page lookup returns five relevant prompts by default. The volume column for each is filled in asynchronously: the frontend sends each prompt's search keyword to a follow-up endpoint that runs DataForSEO cross-aggregated calls (Google AI Overview + regionally weighted ChatGPT) and returns a per-keyword AI search volume, cached for 24 hours. Onboarding lookups produce no relevant prompts.
Plan Gating & Quotas
Prompt Volumes is a paid feature — the Free plan has no access and sees an upgrade gate. There are three separate, independently metered quotas related to Prompt Volumes, plus a hard monthly spend wall that sits above all of them.
Free plan: locked
The Free tier ($0, permanent) cannot run Prompt Volumes lookups at all. All quotas below are 0 for Free.
(a) Page lookups — daily vs monthly cadence by plan
The main page-lookup cap uses a monthly cadence on the lower tiers and a daily cadence on Pro and Enterprise. Monthly counters reset on the 1st of the next month; daily counters reset at midnight.
| Plan | Price | Page lookup cap | Cadence |
|---|---|---|---|
| Free | $0 | Locked (no access) | — |
| Starter | $59/mo | 2 lookups | per month |
| Lite | $139/mo | 5 lookups | per month |
| Pro | $479/mo | 25 lookups | per day |
| Enterprise | Custom | 200 lookups | per day |
Cache hits and outages are refunded
The quota counter increments up front and is decremented again if the lookup did not fire a fresh charge — so a cached result and a service_unavailable result both cost you nothing against your cap.
(b) Onboarding AI Search Volume — separate daily budget
The lighter onboarding lookup (used in brand onboarding Step 3) has its own per-day budget so onboarding bursts never drain your page-lookup allowance.
| Plan | Onboarding lookups / day |
|---|---|
| Free | 0 |
| Starter | 5 |
| Lite | 6 |
| Pro | 30 |
| Enterprise | 60 |
(c) Topics Performance "AI Volume" column — Pro+ only
The AI Overview page's Topics Performance card has an "AI Volume" column that batches up to 10 keywords per call. It is locked below Pro and metered as batches per day. A batch only counts when at least one keyword in it is uncached.
| Plan | Topics Performance batches / day |
|---|---|
| Free / Starter / Lite | 0 (locked) |
| Pro | 20 |
| Enterprise | 100 |
Spending credits past the cap
When a paid-plan user exhausts the page-lookup cap, they can spend AI credits to keep going at a rate of 5 credits = 1 lookup.
- Available only on the page surface, never on onboarding.
- Credits are checked up front but deducted only after a confirmed fresh DataForSEO charge — cache hits and upstream failures never burn credits.
- Deducts from your monthly plan_credits first, then from never-expiring extra_credits; you need at least 5 available.
- The Free plan can never redeem (no Prompt Volumes access at all).
The real ceiling is the monthly spend wall
Independent of the lookup caps, all Prompt Volumes spend is bounded by a per-plan monthly USD budget: Free $0 (disabled), Starter $20, Lite $40, Pro $75, Enterprise unlimited. When exceeded, a lookup is denied (and the quota is refunded). The lookup cap is the soft throttle; this budget is the hard wall.
Watchlist & History
Save the keywords you care about so you can re-run them later without retyping. The watchlist is a per-brand list of saved keywords, each remembering its region and language.
Save a keyword
Click the star on the search bar to save the current keyword (with its region and language) to the active brand's watchlist.
Re-run anytime
Re-running a saved keyword within 24 hours hits the cache, so it returns instantly and for free.
Remove
Delete a watchlist item when it is no longer relevant; the watchlist cascade-deletes with its brand.
No automatic refresh
The watchlist is a manual saved-keyword list — there is no background task that refreshes it. Uniqueness is enforced per brand + keyword + region + language, so saving the same combination twice is rejected as a duplicate.
A "recent searches" list also surfaces your most recent lookups (newest first) so you can jump back to a previous result. The page URL (carrying the keyword, region, and period) is the source of truth, which makes a result link shareable and refresh-safe.
Page states
| State | When it appears | What you see |
|---|---|---|
| Loading | A fresh lookup is running | "Crunching the numbers..." |
| Insufficient data | Keyword too obscure (no AI activity) | "Not enough AI search activity" + suggested broader terms |
| Service unavailable | All upstream calls failed | "Service temporarily unavailable, try again" (no quota consumed) |
| Select a brand | No brand chosen in the picker | A prompt to pick a brand first |
| Upgrade gate | Free plan | An upgrade prompt — Prompt Volumes requires a paid plan |
Not currently shown
Earlier "Demographics" and "Keyword Hierarchy" tabs were removed (Demographics had no trusted data source; Hierarchy added little beyond Topic Categories). The backend still computes hierarchy data internally, but it is not surfaced in the UI.
Routes & API Reference
Prompt Volumes is reached from the AI Visibility group in the sidebar. Below is the page route and the backend endpoints that power it (all under /api/v1, all requiring an active subscription).
Prompt Volumes
/prompt-volumesThe main research page (AI Visibility group). The old /ai-mentions route redirects here.
API endpoints
| Method | Path | Purpose |
|---|---|---|
| POST | /prompt-volumes/lookup | Run or serve a keyword lookup (keyword, region, language, period, brand, purpose, force_refresh, use_credits) |
| GET | /prompt-volumes/quota | Lookup quota status (used / max / remaining, cadence, credits available) — no DataForSEO cost |
| POST | /prompt-volumes/prompts-volume | Async per-prompt volume fill (1-10 keywords per call) |
| GET | /prompt-volumes/recent | Recent lookups for the org, newest first |
| GET | /brands/{brand_id}/prompt-volumes/watchlist | List a brand's saved keywords |
| POST | /brands/{brand_id}/prompt-volumes/watchlist | Save a keyword to the brand watchlist |
| DELETE | /prompt-volumes/watchlist/{item_id} | Remove a watchlist item |
Error codes
| HTTP | Code | Meaning |
|---|---|---|
| 403 | PROMPT_VOLUMES_PLAN_REQUIRED | Plan does not include page lookups (e.g. Free) |
| 403 | ONBOARDING_PV_PLAN_REQUIRED | Plan does not include onboarding lookups |
| 403 | TOPICS_PERFORMANCE_PLAN_REQUIRED | Topics Performance AI Volume requires Pro+ |
| 429 | PROMPT_VOLUMES_DAILY_LIMIT | Daily page-lookup cap reached (Pro / Enterprise) |
| 429 | PROMPT_VOLUMES_MONTHLY_LIMIT | Monthly page-lookup cap reached (Starter / Lite) |
| 429 | ONBOARDING_PV_DAILY_LIMIT | Onboarding lookup daily cap reached |
| 429 | TOPICS_PERFORMANCE_DAILY_LIMIT | Topics Performance batch daily cap reached |
| 402 | INSUFFICIENT_CREDITS | Fewer than 5 credits available to redeem a lookup |
| 402 | PV_REDEMPTION_NOT_ALLOWED | Credit redemption not allowed on this surface (e.g. onboarding) |
Default lookup parameters
Lookups default to region United States (location code 2840) and language English. The UI exposes region, language, and period (1 / 3 / 6 / 12 months) selectors; the trend periods are derived client-side from a single 12-month payload.
SEO Module
SEO Module Overview
TruIntel's SEO module pairs traditional search optimization with the AEO engine. It runs on two engines: free homepage-level checks powered entirely by free Google APIs and self-hosted parsing, and paid domain-level metrics from DataForSEO plus a self-hosted site crawler. Keyword data is Google Search Console first, with DataForSEO only enriching volume and difficulty.
Free Checks Engine
Homepage-level analysis using Google PageSpeed Insights, Chrome UX Report, SSL, meta, robots, structured data, readability and content quality. No paid vendor, runs for every brand starting at onboarding.
Paid Checks Engine
Domain Authority, backlink profile, referring domains and competitor DA from DataForSEO. Gated by a $5/brand/month SEO budget; degrades gracefully when funds run low.
Self-Hosted Crawler
A breadth-first httpx + BeautifulSoup site crawler that powers the on-page technical audit, crawl visualization and image audit on a dedicated queue.
GSC-First Keywords
Tracked keyword positions, clicks, impressions and CTR come from Google Search Console. DataForSEO Labs only enriches volume/difficulty and supplies optional top-organic keywords.
No single SEO score
There is no one composite backend SEO score. The Site Health gauge is computed client-side as a plain average of four components (see SEO Dashboard). Other distinct scores include the On-Page SEO Checker grade, per-page crawler penalty scores and content-quality buckets.
SEO Dashboard
/seoSite Health gauge, search performance, backlinks, competitors and quick wins.
PageSpeed
/seo/pagespeedMobile + desktop Lighthouse scores, Core Web Vitals and per-page scans.
Backlinks
/seo/backlinksBacklink profile, toxic detection and disavow file generation.
Keywords
/seo/keywordsGSC-first keyword rankings, position changes and alerts.
Site Audit
/seo/auditSite-wide BFS crawler technical audit results.
Competitors
/seo/competitorsCompetitor DA, trust, backlinks and keyword overlap.
Images
/seo/imagesImage audit: alt text, file size, format and indexability.
Keyword Suggestions
/seo/keyword-suggestionsDataForSEO Labs suggestions from a seed keyword (1 credit each).
Keyword Gap
/seo/keyword-gapCompetitor keyword opportunity buckets (Pro+).
Backlink Gap
/seo/backlink-gapDomains linking to competitors but not you (Pro+).
On-Page Checker
/seo/onpage-checkerOn-demand single-URL audit, ~90 checks with A-F grade.
Crawl Visualization
/seo/visualizationInteractive React Flow graph of your internal link structure.
SEO Dashboard
The SEO Dashboard aggregates PageSpeed, keyword rankings, backlink analysis and on-page audit data into a single view headed by the Site Health gauge. It is the landing page for the SEO module and is route-accessible on every plan, though Free brands only ever see data from their single onboarding audit.
Site Health Score
A client-side average of PageSpeed mobile, PageSpeed desktop, Domain Authority and SSL status. Shows last-checked and next-update timestamps.
Domain Authority & Trust
DA (0-100) and Trust Score tracked over time with a competitor comparison bar chart.
Page Speed
Mobile and desktop Lighthouse scores side-by-side with a Core Web Vitals breakdown.
Backlinks & Referring Domains
Total backlinks and referring-domain counts with change indicators versus the previous report.
- Site Health stats bar — pages crawled, issues found, average response time, average page score and broken links
- Search Performance card — GSC clicks, impressions, CTR and average position over time on an interactive line chart
- Top Pages by Inbound Links — pages with the most internal links pointing to them
- Quick Wins section — dynamic alerts for PageSpeed under 50, SSL expiration, broken links and image issues
- Images preview — table of images missing alt text with an inline fix button
- Competitor SEO card — top competitors with DA, trust score, backlinks and referring domains
- DA & Trust vs Competitors — horizontal bar chart comparing your domain against tracked competitors
Site Health Score Formula
The Site Health Score is computed entirely on the client as a simple average of whichever of four components are present. It is the single source of truth shared by the Overview "SEO Health Score" card and the SEO Dashboard "Site Health" gauge. There is no separate backend aggregate, and there is no "technical health" term.
Site Health Score (average of available components)
- SSL is scored valid = 100, expiring = 60, any other status = 0
- Only the components that are present are averaged, then rounded; the score is null when none are present
- This matches the backend Site-Audit "Site Health" definition used by the visibility site-audit service
Why your score may show fewer components
On Free plans, or when DataForSEO funds are exhausted, Domain Authority may be missing and the average is taken over the remaining components. A degraded or estimated DA can shift the score, so always check the underlying tiles.
Free SEO Checks
Every brand gets a suite of free, homepage-level SEO checks that require no paid vendor. Nine checks run in parallel, with mobile-friendliness derived from the PageSpeed mobile score and an HTTP security-headers analysis added from already-captured response headers. Each check has its own timeout and fails gracefully without blocking the others.
- PageSpeed Insights (Google PSI v5) — mobile and desktop, with performance / SEO / accessibility / best-practices category scores (30s timeout, retries on 429/500/502/503)
- Core Web Vitals — LCP, INP and CLS field data from the Chrome UX Report (CrUX)
- SSL certificate — validity, issuer and expiry via an HTTPS HEAD request
- Structured data — JSON-LD schema detection with types and details
- Meta-tag audit — title, description, Open Graph, Twitter cards, hreflang and canonical
- Robots.txt & Sitemap — validity, sitemap URL count and blocking rules
- Readability — Flesch Reading Ease and Gunning Fog index
- Content quality — heading hierarchy, links, image alt text and paragraphs (0-100 buckets)
- Technology detection — CMS, frameworks, analytics and CDN identification
- Mobile-friendly (derived) — from the PSI mobile score: 90+ good, 50+ needs improvement, under 50 not friendly
- HTTP headers (derived) — security-header analysis from the captured response headers
Core Web Vitals measure INP, not FID
TruIntel reads LCP, INP (Interaction to Next Paint) and CLS from the Chrome UX Report. First Input Delay (FID) is deprecated and is not measured anywhere in the product.
Free plan: one audit total
On the Free plan the free checks run exactly once — the onboarding audit. A second refresh returns FREE_PLAN_SEO_LIMIT. Paid plans re-run them on a schedule and on demand.
PageSpeed & Core Web Vitals
PageSpeed combines lab data (Lighthouse via PSI v5, mobile and desktop) with field data (Chrome UX Report). Lab data yields detailed metrics and category scores plus prioritized performance opportunities; field data reflects real users at the 75th percentile.
| Metric | What It Measures | Good Target | Poor Threshold |
|---|---|---|---|
| LCP (Largest Contentful Paint) | Loading — time until the largest visible element renders | <= 2.5 s | > 4.0 s |
| INP (Interaction to Next Paint) | Interactivity — responsiveness across all interactions | <= 200 ms | > 500 ms |
| CLS (Cumulative Layout Shift) | Visual stability — how much the layout shifts while loading | <= 0.1 | > 0.25 |
| FCP (First Contentful Paint) | Time until the first content appears | < 1.8 s | > 3.0 s |
| TTI (Interactive) | Time until the page is fully interactive | < 3.8 s | > 7.3 s |
| TBT (Total Blocking Time) | Total time the main thread was blocked | < 200 ms | > 600 ms |
| Speed Index | How quickly content is visually displayed | < 3.4 s | > 5.8 s |
| TTFB (Server Response Time) | Time to the first byte from the server | < 800 ms | > 1.8 s |
- Category scores — performance, SEO, accessibility and best-practices are captured for both mobile and desktop
- Performance opportunities — estimated ms/byte savings for render-blocking resources, unused CSS/JS, modern image formats, responsive images, text compression, image encoding and long cache TTL
- Diagnostics — server response time, DOM size, total byte weight, main-thread work breakdown, bootup time and third-party count
- Per-page PageSpeed scanning — scan multiple pages, track progress and rescan individual pages from the PageSpeed page
PageSpeed quota per refresh
Each SEO refresh consumes one of your monthly PageSpeed checks: Free 1, Starter 5, Lite 5, Pro 15, Enterprise 30 per month. Color-coded statuses (green good, amber needs improvement, red poor) and trend lines track the impact of optimizations over time.
Paid Checks — Domain Authority & Backlinks
Paid checks pull domain-level metrics from DataForSEO, running sequentially over a shared session: Domain Authority, then the backlink profile, then competitor comparison. If Domain Authority or backlinks return insufficient funds, the remaining calls are skipped and the report is marked PARTIAL.
Domain Authority & Trust
- DA is derived from the DataForSEO raw rank (0-1000): DA = min(100, round(rank / 10)), scaled to 0-100 for the UI while the raw domain_rank is preserved
- Trust Score = round(dofollow referring domains / referring domains x 100) — the share of referring domains that link dofollow
- Also returns total backlinks, referring domains, referring IPs and referring subnets
- da_change is the current DA minus the previous report DA
- When the Backlinks subscription is inactive the source degrades to estimated_fallback (a whois-based estimate) and the UI warns
Backlink profile & competitor DA
- Rich backlink data: broken links, new/lost counts, referring IPs/subnets and the dofollow/nofollow split
- New and lost backlinks are tracked by comparing referring domains against the previous report
- Anchor-text distribution (non-critical)
- Competitor DA comparison fetches DA, rank, trust, backlinks and referring domains for each active competitor with a domain, capped at your competitor plan limit
- Optional top-organic keywords pull the brand domain ranking keywords from DataForSEO (not run inside the main paid checks — keywords stay GSC-first)
DataForSEO budget cap
Paid checks run under a $5 per brand per month SEO/SERP budget. When funds are exhausted, paid tiles may show "—" and the report is marked PARTIAL. This is expected behavior, not an error.
Keyword Rankings & Google Search Console
Keyword tracking is Google Search Console first. Connecting GSC gives you real positions, clicks, impressions, CTR and average position per query, with DataForSEO only enriching volume and difficulty. Without GSC, ranking coverage is limited to enriched and top-organic data.
- Position distribution — breakdown of keywords in Top 3, Top 10, Page 2 and below
- Position changes — which keywords improved or declined, with delta indicators
- Search volume & difficulty — estimated monthly searches and competition level
- Filter tabs — Improved, Declined, Top 10 and All keywords
- Keyword history chart — position trends for individual keywords over time
- Position alerts — notifications when important keywords move significantly
| GSC Property | Detail |
|---|---|
| OAuth scope | Read-only; tokens are Fernet-encrypted at rest |
| Initial sync | Last 90 days of search performance |
| Daily sync | Rolling last 28 days, refreshed daily at 05:00 IST |
| Data lag | 3-day lag on the end date (Google publishes data with a delay) |
| Position change window | Compared against records at least 7 days old |
| Connect flow | Settings -> Integrations -> Connect GSC (popup OAuth), then select a property |
Connect GSC for the full picture
GSC provides real click-through rate, actual impressions and the search queries you rank for, including long-tail terms. Without it, TruIntel relies on third-party enrichment that will not include every query.
Backlinks, Toxic Detection & Disavow
Monitor your full backlink profile to understand site authority. TruIntel tracks totals and changes over time, flags potentially toxic links and provides a complete disavow workflow that exports a Google-compatible file.
- Total backlinks and referring domains with trends over time
- New and lost backlinks tracked against the previous report
- Dofollow vs nofollow distribution
- Anchor-text distribution and referring IPs/subnets
- Toxic backlinks — spam score, toxic count, broken backlinks and nofollow breakdown (Pro+)
Disavow workflow (Pro+)
Identify toxic links
Review flagged backlinks with their spam scores; add individual entries or bulk-add domains to your disavow list.
Generate the disavow file
Export a Google-compatible disavow .txt file ready to upload to Google Search Console.
Mark as submitted
Record that you submitted the file to Google so the entry is tracked.
Track history
Review your full submission history and manage entries (add, bulk-add, delete) over time.
Pro feature
Toxic backlink detection and the disavow workflow are available only on Pro and Enterprise plans. Lower tiers see an upgrade prompt instead of toxic data.
On-Page Technical Audit (Crawler)
TruIntel's self-hosted breadth-first crawler (httpx + BeautifulSoup, concurrency 3, with optional JS-link recovery) scans your whole site and stores a detailed record per page. It runs on a dedicated crawl queue with a 30-minute time limit, separate from the inline paid checks.
| Stored per page | Examples |
|---|---|
| Meta & canonical | Title, meta description, canonical URL, html lang, noindex/nofollow flags |
| Structure | H1/H2/H3 counts, word count, image count, images without alt |
| Links | Internal, external and broken link counts |
| Response | Status code, response time (ms), page size (bytes), crawl depth, content hash |
| Scoring | issues[], overall_score and extended_data |
Per-page score
Each page starts at 100 and loses points for each issue by severity: critical -20, high -15, warning -10, medium -8, low -3, info -2, floored at 0.
- Crawl summary carries the site graph (nodes + edges), crawl budget, sitemap stats, security headers and computed site warnings
- Per-brand page cap: Free 500, Starter 10,000, Lite 50,000, Pro 100,000, Enterprise 200,000 — enforced by a post-run truncation
- Image optimization is a byproduct of the crawl: every <img> is extracted and HEAD-fetched for file size, alt text, format, dimensions, indexability and loading attribute
On-Page SEO Checker (Single URL)
The On-Page SEO Checker is an on-demand tool that audits any single public URL in under 45 seconds and returns roughly 90 checks across 10 weighted categories, each with recommendations and quick wins. It is distinct from the site-wide crawler audit — use the Checker for a fast deep-dive on one page.
| Category | Weight |
|---|---|
| Meta | 14 |
| Content | 12 |
| Headings | 10 |
| Images | 10 |
| Links | 10 |
| Security | 10 |
| Performance | 10 |
| Schema | 8 |
| Mobile | 8 |
| Accessibility | 8 |
- Each category score = (passed x 1.0 + warning x 0.5) / checks x 100; the overall is the weighted average
- Letter grade: A >= 90, B >= 80, C >= 70, D >= 50, F < 50
- Render modes: auto, browser (Playwright fallback for SPAs) or http
- Works on any public URL — the brand only owns the scan for quota and history purposes
Competitor SEO Comparison
Compare your SEO metrics against tracked competitors to see how your domain authority, backlinks and keyword footprint stack up. Competitor metrics come from the paid-checks competitor comparison run.
- Domain Authority, trust score, backlinks and referring domains per competitor
- Competitor top keywords with a dropdown selector
- Keyword overlap analysis — shared vs unique keywords
- DA & Trust vs Competitors comparison chart
- Track multiple competitors simultaneously, up to your plan limit
Competitor limits
Tracked competitors per brand: Free 1, Starter 1, Lite 3, Pro 5, Enterprise 5. Competitor SEO data only refreshes as a byproduct of a full paid-checks run.
Keyword Suggestions
Keyword Suggestions returns up to 50 related keywords from the DataForSEO Labs keyword-suggestions API for a seed keyword you enter, scoped to your country. Each suggestion includes search volume and difficulty so you can prioritize, and history is saved for reuse.
- Enter a seed keyword to get up to 50 country-scoped suggestions
- Each suggestion shows search volume and keyword difficulty
- One-click add to your tracking keyword list
- Suggestion history is saved per brand
Not AI-powered — costs 1 credit per request
Suggestions come from DataForSEO Labs seeded by your keyword, not from an AI model. Each request costs 1 AI credit from your monthly pool (Starter 50, Lite 150, Pro 500, Enterprise 1000 credits/mo). A paid plan is required; there is no separate monthly suggestion cap.
Image Optimization
The image audit is a byproduct of the site crawl. Every image on crawled pages is extracted, HEAD-fetched for its file size, and analyzed for SEO best practices including alt text, format, dimensions and indexability.
- Detect oversized images that slow down page load (real file sizes via HEAD fetch)
- Flag missing or poor alt text and decorative images
- Recommend modern format conversions (e.g. PNG to WebP)
- Identify images without specified dimensions or proper loading attributes
- Track indexability per image alongside the page it appears on
Backfill sizes
A backfill-sizes action re-fetches file sizes for images that were missing size data, so the audit can flag heavy images even after the initial crawl.
Keyword Gap & Backlink Gap
Gap analyses surface competitive opportunities by comparing your footprint against competitors. Both are Pro and Enterprise only and power content and link-building prioritization, including outreach targets.
Keyword Gap buckets
| Bucket | Meaning |
|---|---|
| Missing | A competitor ranks for the keyword and you do not |
| Weak | A competitor outranks you for the keyword |
| Strong | You outrank the competitor |
| Untapped | Low-competition keyword neither of you targets |
| Shared | You and the competitor both rank for the keyword |
Keyword Gap supports filters, sort and search with an opportunity-volume summary. Backlink Gap (a DataForSEO domain intersection) lists domains that link to your competitors but not to you, feeding the outreach link-building pipeline.
Pro feature
Keyword Gap and Backlink Gap are available on Pro and Enterprise plans. Lower tiers see an upgrade prompt.
Crawl Visualization
Crawl Visualization renders your site's internal link structure as an interactive directed graph built from the crawler's forward links. It uses React Flow to draw pages as nodes and internal links as edges, helping you spot orphan pages, overly deep pages and crawl issues.
- Interactive graph with zoom, pan and node selection
- Detail panel showing page title, path, status, depth, score, issues, response time, word count, noindex flag and link counts
- Orphan page detection — pages with no internal links pointing to them
- Crawl-depth view — how far each page sits from the homepage
- Graph metadata: total nodes/edges, max depth and root URL
SEO Monitoring Schedule
SEO checks run on the Celery beat scheduler in Asia/Kolkata (IST). All times below are genuine IST. On-demand refreshes are also available, subject to per-plan quotas and a rate limit.
| Task | When (IST) | Plans |
|---|---|---|
| Monthly full SEO (free + paid + crawl) | 1st of month, 04:00 | All paid |
| Weekly SEO (DA, backlinks, competitors, crawl) | Monday 04:00 | Pro + Enterprise only |
| Daily GSC keyword sync | Daily 05:00 | Paid, GSC connected |
| Daily indexation check | Daily 05:30 | Pro+ with GSC |
| Weekly backlink verification | Wednesday 04:00 | All paid |
| SEO report cleanup (delete > 180 days) | Quarterly | All |
On-demand refresh
- Default refresh runs free checks only (homepage PageSpeed mobile + desktop, SSL, meta, robots/sitemap, structured data, readability, content quality, tech)
- A full refresh also queues paid checks (DA/backlinks/competitors) and a site crawl — paid plans only; on Free these are skipped
- Rate limit: 1 refresh per hour per brand (the first-time onboarding full check is exempt)
- Each refresh consumes 1 of the monthly PageSpeed quota
Weekly SEO is Pro+ only
The weekly DA/backlinks/competitors/crawl pass runs Monday at 04:00 IST and is limited to Pro and Enterprise. Starter and Lite get the monthly full pass only.
SEO Plan Limits & Gating
SEO capabilities scale with your plan. Dashboard, PageSpeed, Site Audit, On-Page Checker and Crawl Visualization pages are route-accessible on every plan, but Free brands only ever see data from their single onboarding audit and paid tiles show "—" or an upgrade prompt.
| Capability | Free | Starter | Lite | Pro | Enterprise |
|---|---|---|---|---|---|
| Free checks (PSI/CWV/SSL/meta) | 1 audit total | Yes | Yes | Yes | Yes |
| Scheduled SEO refresh | Onboarding only | Monthly | Monthly | Weekly + Monthly | Weekly + Monthly |
| PageSpeed checks / month | 1 | 5 | 5 | 15 | 30 |
| Tracked SEO keywords | 0 | 750 | 2,000 | 5,000 | 10,000 |
| Top-organic keywords | 50 | 50 | 100 | 200 | 500 |
| Crawl pages | 500 | 10,000 | 50,000 | 100,000 | 200,000 |
| Competitors | 1 | 1 | 3 | 5 | 5 |
| Toxic backlinks + disavow | No | No | No | Yes | Yes |
| Keyword Gap / Backlink Gap | No | No | No | Yes | Yes |
| Keyword Suggestions | No | 1 credit each | 1 credit each | 1 credit each | 1 credit each |
| Rank Tracking | No | No | No | Yes | Yes |
| Indexation Monitoring | No | No | No | Yes | Yes |
Adjacent "Analyze" SEO features
Internal Linking (Free), Indexation Monitoring (Pro+, via GSC URL Inspection), CTR Optimization (Starter+, from GSC performance data) and Rank Tracking (Pro+, DataForSEO SERP) live in the SEO Analyze nav group and are documented in their own sections.
SEO Analyze Tools
NewOverview
The SEO Analyze tools are four on-page and ranking utilities that turn your existing crawl, Google Search Console, and SERP data into specific, actionable fixes. They live under the SEO section nav in two groups: "On-Page & Indexing" (Internal Linking, Indexation Monitoring, CTR Optimization) and "Keywords & Rankings" (Rank Tracking).
Internal Linking
On-demand crawl that surfaces orphan pages, suggested internal-link opportunities with anchor + context sentence, and a PageRank-style authority map. Starter+.
Indexation Monitoring
Tracks which pages Google has indexed via the GSC URL Inspection API, alerts on deindexation, and charts coverage over time. Pro+ with a connected GSC.
CTR Optimization
Finds high-impression / low-CTR pages from your synced GSC data and generates query-aware AI title + meta rewrites with a before/after preview. Starter+.
Rank Tracking
Brand-scoped Google SERP-position tracking via DataForSEO, organised into keyword projects with movement, distribution, and history. Pro+, paid, manual runs.
Where each tool gets its data
| Tool | Data source | Needs GSC? | Extra cost? |
|---|---|---|---|
| Internal Linking | On-demand site crawler (own crawler) | No | No |
| Indexation Monitoring | GSC URL Inspection API (webmasters.readonly scope) | Yes | No (free Google API) |
| CTR Optimization | GSC Search Analytics performance data (already synced) | Yes | No |
| Rank Tracking | DataForSEO Google organic SERP | No | Yes (metered) |
GSC dependency, in one line
Indexation Monitoring and CTR Optimization both ride your existing Google Search Console connection (no re-consent, no new vendor). The account-wide token can analyze any verified property. Internal Linking and Rank Tracking do not need GSC at all.
Plan gates at a glance
| Tool | Minimum plan | Cadence |
|---|---|---|
| Internal Linking | Starter | Manual / on-demand |
| CTR Optimization | Starter | Manual / on-demand |
| Indexation Monitoring | Pro | Daily 05:30 IST + on-demand |
| Rank Tracking | Pro | Manual / on-demand only |
Reachable vs. usable
Internal Linking and Indexation pages are reachable on any plan, but below the paid gate the API returns an in-page upgrade state ("Internal Linking is a paid feature" / "Indexation Monitoring is a Pro feature"). CTR Optimization and Rank Tracking are gated at the route level (minimum Starter and Pro respectively).
Internal Linking
Internal Linking runs an on-demand scan of any public website (defaulting to your brand domain) and returns three things: orphan pages with no inbound internal links, internal-linking opportunities (a source page that already talks about a target topic but does not yet link to it), and a PageRank-style link-authority distribution that flags important pages starved of internal links.
Plan gate
Starter and up. Monthly scan quota: Starter 5, Lite 15, Pro 50, Enterprise 200. The quota resets on the 1st of each month. Free returns an in-page "paid feature" upgrade state.
What a scan finds
Orphan pages
Pages with zero inbound internal links. Search engines and users struggle to discover these, so they typically under-perform.
Linking opportunities
A source page that mentions a target page topic but does not link to it. Each opportunity includes a suggested 2-5 word anchor and the verbatim existing sentence to place the link in.
Link authority
A PageRank-style 0-100 distribution across pages, plus an "under-linked important pages" list (300+ words with only 1-2 inbound links).
How it works
Each scan runs a fresh same-site crawl (it does not read a cached content index), then builds the page corpus and link graph in memory. The only AI step validates candidate opportunities and writes the anchor text.
| Parameter | Value |
|---|---|
| Crawl type | HTML-only, same-site BFS, robots-respecting |
| Max depth / concurrency | depth 4 / 6 concurrent requests |
| Pages per scan | Default 100, configurable 5-300 |
| Orphan definition | 0 inbound internal links |
| Under-linked definition | 300+ words AND only 1-2 inbound links |
| Targets analysed | Top 15 (orphans first, then under-linked) |
| Candidate ranking | TF-IDF cosine + keyword overlap (title/H1 up-weighted), top 5 per target |
| PageRank | Damping 0.85, 40 iterations, scaled 0-100 |
AI is optional, not load-bearing
The LLM (via the provider-agnostic chain) confirms relevance, proposes the anchor, and returns the context sentence, dropping anything below 40 relevance. If the LLM is unavailable, a deterministic fallback keeps the top 2 candidates and clamps relevance to the 40-80 range so scans never fail outright.
Applying a suggestion
CMS-authored page
Apply inserts the link into the content and republishes the page to the edge automatically.
Any non-CMS page
TruIntel returns a "manual insertion required" response and the UI shows a Copy button that copies the ready-made <a href="target">anchor</a> for you to paste.
The page
The landing hero invites a scan; once a scan completes you get a stat strip (Orphan pages, Opportunities, Pages analysed, Avg links / page) and sticky pill tabs.
- Opportunities tab: a table of "Source to Target" rows with the italic context sentence, a suggested-anchor pill, a relevance score, and Apply / Copy / Dismiss actions.
- Orphan Pages tab: the list of pages with no inbound links.
- Authority tab: the link-authority distribution plus the under-linked important pages table.
- States covered: empty ("No linking opportunities found" / "No orphan pages"), loading ("Discovering internal links..."), error, scan-incomplete, and the paid-feature upgrade gate.
Manual only
Internal Linking scans are on-demand only. There is no scheduled job, and a scan is guarded so you cannot launch a second one while one is already running for the same brand and target.
Internal Linking page
/internal-linksRun scans and review orphans, opportunities, and authority.
Queue a scan
POST /api/v1/seo/{brand_id}/internal-links/scanBody { target_url?, max_pages? }. Consumes 1 monthly scan slot.
Get results
GET /api/v1/seo/{brand_id}/internal-linksOrphans + opportunities + authority; filter=active|pending|applied|dismissed.
Scan status
GET /api/v1/seo/{brand_id}/internal-links/statusLive scan progress (pending/running/complete/failed/idle).
Apply / Dismiss
POST .../internal-links/{suggestion_id}/apply | /dismissInsert the link (CMS republish) or dismiss the suggestion.
Indexation Monitoring
Indexation Monitoring tracks whether Google has indexed each of your pages using the Google Search Console URL Inspection API. It buckets every URL as indexed, not-indexed, excluded, or error, detects deindexation transitions (was indexed, now not), emails an alert when that happens, and charts coverage over time.
Plan gate + requirement
Pro and Enterprise only, and it requires a connected Google Search Console. It rides the existing webmasters.readonly OAuth scope, so there is no re-consent and no new vendor.
TruIntel never force-indexes
There is no API to force a page into Google index. A "re-check" only re-inspects the URL and the UI never promises indexing. Indexation Monitoring is observability, not a submission tool.
The coverage buckets
| Bucket | Meaning |
|---|---|
| Indexed | Verdict PASS / "Submitted and indexed". The only bucket where is_indexed is true (deindexation alerts key off it). |
| Not indexed | Crawled or discovered but not currently indexed. |
| Excluded | Deliberately excluded: noindex, canonical, duplicate, redirect, or robots-disallowed. |
| Error | Fetch, robots, or server failure including 404, soft-404, and server errors. |
| Unknown | No coverage signal returned. |
Rule order matters
Robots/meta blocking is evaluated BEFORE the PASS check, so a page reported as "indexed though blocked by robots.txt" is bucketed as Excluded and can surface as a deindexation event. This ordering is deliberate.
Daily budget and pacing
Google caps URL Inspection at roughly 2,000 calls/day and 600/min per property. TruIntel enforces a per-brand, per-property daily counter capped at min(plan limit, 2000) and paces requests at about 6/second to stay under the per-minute ceiling.
| Plan | URL inspections / day / property |
|---|---|
| Pro | 1,000 |
| Enterprise | 2,000 |
The list of URLs to inspect is built on demand by crawling the chosen property (sitemap + links, hard-capped at 1,000 pages, cached 6h). Within the daily budget it prioritises never-checked URLs first, then the least-recently-checked, rotating the long tail across days so coverage stays fresh.
Schedule and alerts
- A daily scheduled indexation check runs at 05:30 AM IST (staggered just after the 05:00 GSC sync) for Pro+ brands with a connected GSC.
- You can also run a check on demand ("Check now") or re-check a single URL.
- A single concise deindexation email is sent after a run commits, only for confirmed indexed-to-not transitions, never for a transient error.
- A daily IndexationSnapshot row records the indexed / not-indexed / excluded / error counts that power the trend chart.
The page
- Stat cards: Indexed, Not indexed, Excluded, Errors.
- A coverage donut (Indexed / Not indexed / Excluded / Errors) plus an "Indexed over time" trend line.
- A URL table with columns URL, Status, "Google's reason" (coverage state), Last crawled, and a per-row Re-check action.
- A Recent checks history with expandable check detail.
- States: no-brand, error, the "Indexation Monitoring is a Pro feature" upgrade gate, "Connect Google Search Console", "This site isn't in your Search Console account" (reconnect), and "Run first check" / "Try again".
Indexation page
/indexationCoverage summary, donut, trend, and per-URL status.
Summary + URLs
GET /api/v1/seo/{brand_id}/indexationCounts + paginated URL list; filter, search, property_url, budget snapshot.
Coverage trend
GET /api/v1/seo/{brand_id}/indexation/trendIndexationSnapshot series by day and property.
Run a check
POST /api/v1/seo/{brand_id}/indexation/checkQueue a full property check; respects the daily budget (429 when exhausted).
Re-check one URL
POST /api/v1/seo/{brand_id}/indexation/recheckRe-inspect a single URL on demand.
Check history
GET /api/v1/seo/{brand_id}/indexation/checksRecent-check list and per-check detail.
CTR Optimization
CTR Optimization finds pages that already earn impressions in Google but get clicked far less than their position should deliver, then writes query-aware AI title and meta-description rewrites for the biggest opportunities, with a before/after Google preview and one-click apply (CMS pages) or copy (anything else).
Plan gate + requirement
Starter and up. It uses the GSC performance data TruIntel already syncs, so there is no extra cost, but it does require a connected Google Search Console. Monthly suggestion quota: Starter 5, Lite 15, Pro 50, Enterprise 200, resetting on the 1st.
How an opportunity is found
TruIntel pulls GSC Search Analytics on the page dimension over a default 28-day lookback that ends 3 days back (to dodge GSC data lag), then compares each page's actual CTR against an expected-CTR-by-position curve.
| Setting | Value |
|---|---|
| Default lookback | 28 days, ending 3 days back (configurable 7-90) |
| Minimum impressions | 50 (configurable 10-10000) |
| Position range | Positions 1-30 qualify |
| Gap threshold | Actual CTR below 0.7x expected CTR |
| Potential clicks | round(impressions x (expected - actual)), must be at least 1 |
| AI rewrites generated | Top 12 opportunities per scan, by potential clicks |
| Title / meta limits | Title up to 60 chars, meta up to 155 chars |
Site-calibrated curve avoids false positives
Rather than only using a static benchmark curve (pos1 ~28%, pos2 ~15%, decaying to ~0.4% at pos30), TruIntel builds a site-specific curve from the median actual CTR in each position bucket (positions with at least 5 qualifying rows). This stops a brand or geo query already earning ~50% CTR at position 1 from being wrongly flagged against the generic 28% benchmark; sparse buckets fall back to the static curve.
Two ways to run it
Full "Analyze CTR" scan
Set Property, Lookback, and Min impressions, then run a scan. It creates a queued analysis, ranks opportunities, and generates AI rewrites for the top 12. Gated on the monthly suggestion limit but not run-metered.
Inline single-page suggestion
Generate an AI title/meta for one page on demand. This consumes 1 monthly slot, and the slot is refunded if generation fails.
Applying a suggestion mirrors Internal Linking: a CMS-managed page is updated and republished to the edge, while any other page falls back to manual copy buttons.
The page
- Landing hero "Find your CTR wins" with Property + Lookback + Min impressions inputs and an "Analyze CTR" button, plus Recent Analyses pills.
- Scan detail: a stats header (Opportunities count, Potential clicks +N, Window) and an Opportunities table (Page, impressions, CTR, position, potential clicks).
- Optimize modal: a before/after Google SERP preview with the AI title/meta and apply or copy actions.
- States: GSC-required, the Free-plan "Upgrade to Connect" gate, and empty "No CTR opportunities".
Manual only
CTR Optimization is on-demand only. There is no scheduled job; you trigger every analysis and suggestion yourself.
CTR Optimization page
/ctr-optimizationRun analyses and review CTR opportunities.
Opportunities
GET /api/v1/seo/{brand_id}/ctr/opportunitiesRanked opportunities + summary for a property.
Suggest (1 slot)
POST /api/v1/seo/{brand_id}/ctr/suggestGenerate AI title/meta for one page; refunded on failure.
Apply / Dismiss
POST .../ctr/apply | /ctr/dismissApply title/meta (CMS republish or manual copy) or dismiss.
Analyze CTR scan
POST /api/v1/seo/{brand_id}/ctr/scansQueue a scan { property_url?, lookback_days, min_impressions, max_opportunities }.
Scan history / triage
GET .../ctr/scans · PATCH .../scans/{id}/opportunities/{id}Recent analyses and open/dismissed/done triage.
Rank Tracking
Rank Tracking monitors your keywords' Google organic SERP positions via DataForSEO. You group a domain or URL plus a set of keywords into a "project" (with a chosen country, device, and language), and each on-demand run checks every active keyword's position, diffs it against the previous run, and stores the movement.
Pro+, paid, manual only
Rank Tracking uses DataForSEO, which is metered paid spend, so it is Pro and Enterprise only, runs on-demand only (no scheduled job), and is protected by a triple budget guard. An unfunded account fails a run loudly rather than silently storing "all not ranking".
Project configuration
| Setting | Value / default |
|---|---|
| Country | country_code, default "us" |
| Device | desktop or mobile |
| Depth | SERP depth 10-100, default 100 |
| Language | language_code, default "en" |
| Status | active or paused |
The triple spend guard
| Guard | Pro | Enterprise |
|---|---|---|
| Keywords per project | 25 | 100 |
| Runs per month (per brand) | 30 | 120 |
| Monthly USD budget (per brand) | $10 | $40 |
A run is blocked if the estimated spend would push the brand over its monthly USD budget. The run-count and budget counters both reset on the 1st of the month.
Cost model
A live Google-organic SERP check at depth 100 costs about $0.0155 per keyword (scaled by depth, floor $0.0015/kw). Search-volume metric refreshes cost about $0.05 per request (covering up to 1,000 keywords). The actual charge is recorded exactly once per run from DataForSEO's own cost field in a finalize step, so it is never double-counted.
What a run records
- Each keyword's current organic position and the change versus the previous run.
- Movement summary: Avg position, Moved up, Moved down, Unchanged.
- A distribution donut bucketed Top 3 / 4-10 / 11-100 / Not ranking.
- An average-position-over-time line and a per-keyword history sparkline.
- Per-keyword SERP competitors for the tracked query.
The page
- An optional brand-wide spend strip, then a 2-column split: selectable project cards on the left (name, domain, keyword count, last run, latest avg position) and the ProjectPanel on the right.
- ProjectPanel has a movement StatRow, the distribution donut + avg-position line, and Overview / Keywords / History tabs.
- The Keywords table shows Keyword, Position, Change, Volume, and URL, with expandable per-keyword history sparkline and SERP competitors.
- The "Run check" button shows a live cost estimate ("Run check ~ $X") before you spend.
- Run status pills: Complete / Running / Queued / Failed, plus a run history list.
Rank Tracking page
/rank-trackingProjects, keywords, runs, and spend.
Spend + caps
GET /api/v1/seo/{brand_id}/rank-tracking/spendBrand spend with plan budget / runs / keyword caps.
Projects
GET/POST /api/v1/seo/{brand_id}/rank-tracking/projectsList or create a project with initial keywords.
Keywords
POST/DELETE .../projects/{id}/keywordsAdd (capped) or soft-delete keywords; refresh-metrics enriches volume/CPC/competition.
Run estimate
GET .../projects/{id}/run-estimateEstimated cost + remaining budget before running.
Queue a run
POST .../projects/{id}/runsTriple-guarded rank check; queued to the seo worker.
Limits, Quotas & Resets
A consolidated reference for every plan limit, monthly reset, and route across the four Analyze tools.
Per-plan limits
| Limit | Free | Starter | Lite | Pro | Enterprise |
|---|---|---|---|---|---|
| Internal-link scans / month | 0 | 5 | 15 | 50 | 200 |
| CTR suggestions / month | 0 | 5 | 15 | 50 | 200 |
| Indexation URL inspections / day | 0 | 0 | 0 | 1,000 | 2,000 |
| Rank Tracking keywords / project | 0 | 0 | 0 | 25 | 100 |
| Rank Tracking runs / month | 0 | 0 | 0 | 30 | 120 |
| Rank Tracking budget / month | $0 | $0 | $0 | $10 | $40 |
Everything resets on the 1st
Internal Linking scan quota, CTR suggestion quota, and the Rank Tracking run-count and USD budget all reset on the 1st of the month. Indexation's URL-inspection budget is separate — it is a per-day cap that resets every day, not monthly.
Routes
Internal Linking
/internal-linksStarter+. Reachable on any plan; shows an upgrade state below the gate.
Indexation Monitoring
/indexationPro+ with GSC. Reachable on any plan; shows an upgrade state below the gate.
CTR Optimization
/ctr-optimizationRoute-gated at minimum Starter; requires GSC.
Rank Tracking
/rank-trackingRoute-gated at minimum Pro; paid DataForSEO spend.
Where they live in the nav
All four sit under the SEO section: Internal Linking, Indexation, and CTR Optimization in the "On-Page & Indexing" group, and Rank Tracking in the "Keywords & Rankings" group. All backend endpoints mount under /api/v1/seo.
Google Search Console
Overview
Connecting Google Search Console (GSC) links TruIntel directly to Google’s own Search Analytics and URL Inspection APIs, so every clicks / impressions / CTR / position number you see is pulled straight from Google — no third-party estimation or enrichment. The connection powers three distinct capabilities for each brand.
Search Performance dashboard
A live clicks / impressions / CTR / average-position dashboard that proxies the GSC Search Analytics API on demand, with a 15-minute cache for speed.
Daily keyword-ranking sync
Every day at 05:00 IST TruIntel stores GSC rows as KeywordRanking history records that feed the rest of the SEO surfaces.
Indexation Monitoring (Pro+)
Reuses the same read-only token to call the GSC URL Inspection API and track per-page index status.
Authorization uses a popup-based OAuth flow with a read-only scope, and the resulting tokens are encrypted at rest. Because the Google token is account-wide, one connected Google account can read every verified property in that account.
Two data paths, not one
The Search Performance page does NOT read the daily sync — it queries Google live per request (15-min cached). The daily 05:00 IST sync is a separate path that writes keyword-ranking history used by other SEO pages. Knowing which is which explains why the dashboard can be fresher than stored history.
Plan Availability
GSC connection and the Search Performance dashboard are available on every paid plan (Starter and up). The Free plan is explicitly blocked — the connect modal and the Search Performance page both render an upgrade prompt for Free accounts. Indexation Monitoring is gated higher, to Pro and Enterprise only.
| Capability | Minimum plan |
|---|---|
| Connect GSC / OAuth / manage property | Starter (any paid plan) |
| Search Performance dashboard (summary / chart / table) | Starter (any paid plan) |
| Search Performance page (/seo/keywords) | Starter (any paid plan) |
| Immediate per-brand sync on connect | Starter (Free brands rejected) |
| Daily 05:00 IST keyword sync | Starter (any paid plan) |
| Indexation Monitoring (URL Inspection) | Pro and Enterprise only |
Free plan cannot connect GSC
GSC requires a paid plan. Free accounts see an upgrade state instead of the connect button, and any immediate-sync request from a Free brand is rejected on the backend.
Connecting Your Account
You can start the connection from two places: Settings → Integrations and the empty state of the Search Performance page. Both open the same shared connect modal.
Open the connect flow
Go to Settings → Integrations and click Connect Google Search Console, or use the Connect prompt on the Search Performance page empty state.
Authorize in the Google popup
A 600×700 OAuth popup opens. Sign in and grant read-only access. TruIntel requests offline access with a consent prompt so Google always returns a refresh token.
Pick a property (optional)
Choose the verified GSC property to track from the list. This step is now optional — if you skip it, TruIntel auto-selects the property matching your brand domain (or the only / first verified property).
Sync begins automatically
TruIntel queues an immediate keyword sync, then keeps it fresh with the daily 05:00 IST sync. The first sync pulls 90 days of history; each daily run refreshes the trailing 28 days.
Property selection is optional
If you close the modal before picking a property, TruIntel self-heals: it auto-resolves the property whose host matches your brand domain (preferring a sc-domain: Domain property), otherwise the sole or first verified property, and persists it. You no longer get silently-empty GSC data from skipping this step.
OAuth & Security
The connection is read-only and hardened against CSRF and token theft. TruIntel never requests write access to your Search Console.
- Scope is webmasters.readonly — read-only access only, no ability to change anything in your Search Console.
- The OAuth request uses access_type=offline and prompt=consent so Google always returns a refresh token for long-lived sync.
- The OAuth state parameter is a signed JWT containing brand and user IDs plus a nonce, with a 10-minute expiry, protecting against CSRF.
- The callback returns a tiny popup-close HTML page that postMessages the result back to the opener; the frontend validates the message origin before acting on it.
- Both access and refresh tokens are Fernet-encrypted at rest (AES-128-CBC + HMAC-SHA256) using a server-side key.
- Tokens auto-refresh proactively whenever they are within 5 minutes of expiry.
Revocation handling
If you revoke Google access, the next sync sees an invalid_grant error. TruIntel deactivates the integration, records the sync error, and raises a HIGH-severity in-app alert ("Google Search Console disconnected — reconnect required"). Performance requests then return a TOKEN_REVOKED error until you reconnect.
Disconnect is a hard delete
Disconnecting from Settings → Integrations deletes the stored integration row outright (tokens included) rather than just disabling it. Reconnecting starts a fresh OAuth flow.
Search Performance Dashboard
The Search Performance page lives at /seo/keywords (the URL says "keywords" but the page is titled Search Performance). It is a live mirror of your GSC Search Analytics data, queried per request with a 15-minute Redis cache, not a read of the daily sync.
Controls
| Control | Options |
|---|---|
| Date presets | 24h, 7d, 28d, 3 months (default), 6 months, 12 months, 16 months (16 months is the hard maximum range) |
| Search type | Web (default), Image, Video, News (backend also accepts Discover and Google News) |
| Filter dimension | query, page, country, device |
| Filter operator | contains, notContains, equals, notEquals |
| Metric toggles | clicks, impressions, CTR, position (all on by default) |
Data table tabs
The results table has six dimension tabs, each sortable by clicks, impressions, CTR, or position, with page sizes of 10 / 25 / 50.
- Queries — the search terms surfacing your site
- Pages — per-URL performance
- Countries — performance by geography
- Devices — desktop / mobile / tablet split
- Search Appearance — rich-result and feature breakdown
- Days — the date-dimension time series
Data is 2–3 days behind Google
Google’s Search Analytics data is not real-time. Every TruIntel query ends its range at today minus 3 days, matching how fresh Google’s own data actually is. If the latest days look empty, that is expected.
Sync & Data Mechanics
Separate from the live dashboard, the daily keyword sync writes ranking history that other SEO surfaces read. Understanding its windows explains the position-change deltas you see elsewhere.
- First sync (never synced before) pulls the last 90 days; every subsequent daily sync re-pulls the trailing 28 days.
- Rows are fetched on the query and page dimensions, paginated up to 25,000 rows per page.
- Results store one KeywordRanking row per query, aggregated across pages: the best page (lowest position) wins, while clicks and impressions are summed. Queries longer than 500 characters are skipped.
- Position change is computed against the most recent prior GSC record that is at least 7 days old, so the overlapping 28-day windows still yield a meaningful delta.
- Clicks, impressions, CTR, and average position come directly from Google — there is no DataForSEO or other third-party volume enrichment (a previously stored search_volume is simply carried over if it already exists).
- Every sync writes an SEOReport record and is guarded by a Redis lock so global and per-brand syncs never overlap.
- All GSC calls use a 3-try retry ladder with exponential backoff that respects Retry-After on 429s and retries 5xx / timeout errors.
DataForSEO enrichment was removed
Earlier docs and some UI copy mention "search volume enrichment via DataForSEO" — that path no longer exists. GSC metrics are served directly from Google. Treat any DataForSEO-volume claim about GSC as stale.
Connection status
The status surface exposes whether the integration is connected and active, whether a sync is currently running, the connected property URL, last sync time and status, any sync error, and the available data date range. The UI polls this every 10 seconds while a sync is in progress.
Indexation Monitoring (Pro+)
Indexation Monitoring is a Pro and Enterprise feature that rides the exact same read-only OAuth scope — no re-consent needed. It calls Google’s URL Inspection API to report the real index status of your pages and alerts you when pages fall out of the index.
- Classifies each page as indexed, not_indexed, excluded, error, or neutral, using Google’s own inspection verdict.
- The URL list is built on demand by a site crawler (sitemap + links); never-checked and least-recently-checked URLs are prioritized so the long tail rotates daily.
- Google caps URL Inspection at ~2,000 inspections/day and 600/min per property. TruIntel enforces a per-(brand, property) daily budget of min(plan limit, 2,000) and paces requests at roughly 6/sec.
- When a previously indexed page becomes not_indexed or excluded, a deindexation alert is raised.
- The daily indexation check runs at 05:30 IST, staggered just after the 05:00 keyword sync.
Account-wide token
Because the Google token is account-wide, Indexation can analyze any verified property in your connected account — even though each brand stores only one selected property for the Search Performance dashboard.
Schedule
All GSC-related background jobs run on the Asia/Kolkata (IST) timezone. Times below are genuine IST.
| Task | Time (IST) | Applies to |
|---|---|---|
| Daily keyword-ranking sync (all active integrations) | 05:00 daily | All paid plans with GSC connected |
| Daily indexation check (URL Inspection) | 05:30 daily | Pro+ with GSC connected |
| Immediate per-brand sync | On connect | Starter and up |
Routes & Endpoints
Where GSC appears in the app, and the backend endpoints that power it.
Search Performance
/seo/keywordsThe live GSC clicks / impressions / CTR / position dashboard with 6 tabs, date presets, search-type switch, filters, and metric toggles.
Settings → Integrations
/settingsConnect, view status, or disconnect GSC via the connection card and shared connect modal.
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /integrations/gsc/authorize | Build the Google OAuth consent URL |
| GET | /integrations/gsc/callback | OAuth callback (no auth; returns popup-close HTML) |
| GET | /integrations/gsc/properties | List verified GSC properties (Google sites.list) |
| POST | /integrations/gsc/connect | Link a chosen property to a brand |
| DELETE | /integrations/gsc/{brand_id}/disconnect | Delete the integration (hard delete) |
| GET | /integrations/gsc/{brand_id}/status | Connection status + sync freshness |
| GET | /seo/{brand_id}/gsc/summary | Aggregate totals (clicks / impressions / CTR / position) |
| GET | /seo/{brand_id}/gsc/chart | Time series on the date dimension |
| GET | /seo/{brand_id}/gsc/table | Dimension table, paginated |
One property per brand
Each brand stores exactly one GSC property for its dashboard and daily sync. To track a different property, reconnect and select it (or let auto-resolution pick it).
AI Insights & Tasks
Overview
AI Insights is the brain that turns a brand's week of raw visibility, SEO, traffic and lead data into a ranked set of strategic Findings and concrete Tasks. Each brand gets a weekly Insight Report produced by a 4-step Gemini pipeline, then a closed-loop layer measures which completed tasks actually moved the needle and feeds that memory back into the next report.
Findings
Data-backed strategic observations with a priority, category, 0-100 impact score and supporting evidence — ranked so you focus on what matters most.
Tasks
Concrete actions derived from findings, split into Owned (your content/SEO) vs Earned (PR/UGC/outreach), each with category, effort, due date and impact tracking.
Outcome Attribution
A monthly closed loop that measures which completed tasks moved visibility and records a "what worked" memory per brand.
Opportunity Agent
Fuses AEO gaps with keyword/SERP gaps into ranked, evidence-backed content opportunities that feed the content calendar.
Powered by gemini-3.1-flash-lite
The full insights pipeline runs on gemini-3.1-flash-lite. If the primary Gemini key fails, generation falls over to the multi-provider llm_json chain (Anthropic, then a Gemini pool, OpenAI and OpenRouter) so a report still gets produced.
Where to find it
Insights live under the Insights nav section: /insights (Key Findings), /insights/:findingId (Finding detail) and /tasks (Task board). The closed-loop surfaces — Opportunities, Calendar and Accountability — live in Content Studio.
How the Pipeline Works
Every report is produced by a 4-step sequential Gemini pipeline where each step feeds the next. The pipeline first aggregates a rich week-over-week snapshot of the brand, then runs trend analysis, competitor analysis, strategy (findings) and task generation — finishing with a deterministic grounding pass that drops or hides anything the live data contradicts.
Step 1 — Trend Analysis
Reads the brand's week of data and writes a free-form narrative on what is changing: visibility score movement, platform shifts, emerging queries and sentiment changes.
Step 2 — Competitor Analysis
Examines what competitors are doing differently — which platforms mention them more, where they gain ground, and the source domains they win that you do not.
Step 3 — Strategy (Findings)
Emits a JSON array of 5-10 findings with priority, category, impact score and evidence. Runs at temperature 0.1 with structured JSON output for reliability.
Step 4 — Task Generation
Converts findings into a JSON array of 5-15 concrete tasks with category, effort, due date and target platform. Each task is heuristically linked to its best-match finding.
After the four steps, two final passes run before the report is marked complete:
- Grounding pass (no LLM): deterministically DROPS findings/tasks that the live data hard-contradicts (e.g. a "robots.txt blocks AI crawlers" claim when the live crawler check says all bots are allowed) and FLAGS unverifiable ones (grounded=false), which are hidden from the default view.
- Task-to-finding linking + query resolution: each task is matched to at most one source finding, and its affected queries are resolved to canonical tracked-query text so the attribution loop can join them later.
Anti-fabrication by design
A standing grounding/anti-fabrication system instruction is injected on every step, reinforced by per-step in-prompt grounding rules and a today's-date context so analysis stays current. The deterministic grounding pass is the final backstop — findings the data cannot support never reach you.
What the Pipeline Reads
The differentiator is the breadth of input. Before any LLM call, the aggregator builds a week-over-week snapshot (current period vs the prior period) from full visibility checks plus every other connected data channel. The richer the input, the sharper and more grounded the findings.
AEO Visibility
Overall + per-platform + per-query results from full visibility checks, with week-over-week deltas, query intent distribution and high-value queries.
Competitors
Per-competitor mention data and how it changed this week, driving competitive findings and earned-media tasks.
Sources (Earned vs Owned)
Citation source domains split into brand-citing vs competitor-only — the split that powers earned-media task targeting.
Brand Attributes
The attributes AI associates with the brand, including which were newly gained or lost this week.
SEO Detail
Domain Authority, keywords, backlinks, on-page issues with specific URLs, PageSpeed and Core Web Vitals.
Traffic Classification
Human / agent / bot split plus the AI-crawler breakdown from the traffic engine.
Lead Verification
Verified / review / rejected counts and AI-referred lead share.
Live AI-Crawler Access
A live robots.txt evaluation per AI vendor (the same source as the AI Crawlability page) so crawler findings reflect reality.
Period windows
Scheduled weekly runs analyse the previous calendar week (Mon-Sun). A manual generation uses the window from the previous Monday through today, so today's fresh check is included. Brand-new brands with no prior data get a "baseline" framing instead of an error.
Findings & Evidence
Each report contains roughly 5-10 findings (the grounding pass may drop or hide some, so a report can show fewer). Findings are ordered by impact score, descending, so the most consequential observations sit at the top.
Every finding carries the following fields:
| Field | Values / Range | Notes |
|---|---|---|
| Title & Description | Free text | A clear observation plus its explanation. |
| Priority | critical / high / medium / low | Drives ordering and visual emphasis. |
| Category | performance / competitive / content / technical | The four UI filter categories. |
| Impact Score | 0-100 (float) | Rendered to a whole number with score color. >80 ~ critical, >60 ~ high. |
| Finding Type | visibility_drop, platform_gap, competitor_threat, content_opportunity, citation_gap, and more | A finer descriptor than Category. |
| Evidence | JSON + metric name/value/change | The specific data points backing the finding. |
| Affected scope | queries / platforms / competitors | Which queries, platforms and rivals the finding touches. |
| Grounded | true / false (+ notes) | Grounding verdict; flagged (false) findings are hidden by default. |
Category vs Finding Type
Category is the four-value enum (performance, competitive, content, technical) used by the UI filters. Finding Type is a separate, finer descriptor (visibility_drop, platform_gap, citation_gap, seo_issue, keyword_gap, lead_quality_insight, and more). They are different fields — do not confuse them.
The Insights page (/insights) shows a stats row (Visibility Score and change, Findings count, Critical/High, Tasks Generated, Mentions) above a Key Findings table with columns for Finding, Priority, Category, Impact, Platforms, Tasks count and Date. Search and Priority/Category filters narrow the list, and a week selector lets you browse report history. By default the page scopes to the latest completed report and shows grounded findings only. Clicking a row opens /insights/:findingId for the full evidence breakdown.
Tasks & Lifecycle
Tasks are the concrete actions derived from findings. Each report generates 5-15 tasks, and every task maps to at most one source finding via a heuristic match (it may be unlinked). A task is either Owned or Earned — a distinction that defines how you act on it.
Owned tasks
Actions on assets you control — your own content, SEO and technical fixes.
Earned tasks
Actions targeting sources AI engines already cite — PR, UGC and outreach to win earned media.
Tasks move through a simple three-state lifecycle:
| Status | Meaning | Transition rules |
|---|---|---|
| todo | New task awaiting action (default) | Review and start work. |
| done | Completed | Stamps completed_at and optional completion notes; eligible for impact tracking. |
| dismissed | Not applicable / not actionable | Requires a dismissal reason. Reverting to todo clears completion and dismissal fields. |
Beyond status, each task exposes a rich set of fields you can use to plan and prioritise:
- Category — content / seo / pr / technical / social
- Priority — critical / high / medium / low
- Content type — listicle, how-to guide, article, comparison, category page, profile, UGC (reddit/youtube/quora/forum), outreach, technical SEO
- Targeting — target source and target platform (chatgpt / claude / gemini / perplexity / google_ai_overview / all)
- Planning — estimated effort hours, suggested due date, success metrics, expected impact
- Impact tracking — actual_impact and impact_measured_at, auto-stamped by attribution (prefixed "Auto:") or set manually on done tasks
Turn tasks into drafts
Any task with a content type can be auto-generated into a draft via the content tooling — the Tasks page surfaces "N tasks can be auto-generated" so you can go from insight to draft in one step.
The Task Board
The Tasks page (/tasks) offers two ways to work, plus summary analytics and quick filters. By default it scopes to the latest completed report, ordered by priority then date.
Kanban Board (default)
Three columns — To Do, Done, Dismissed — with drag-and-drop to change a task's status.
Split View
Two columns side by side — Owned Content vs Earned Media — so you can balance both motions at a glance.
Three independent filter axes plus search let you slice the board:
| Filter axis | Values |
|---|---|
| Status | todo / done / dismissed |
| Type (task group) | earned / owned |
| Category | content / seo / pr / technical / social |
A stat row across the top (Total, To Do, Urgent = critical+high, Done, Dismissed) doubles as a click-to-filter control. Summary cards show a status donut, priority bars and a progress card with completion rate, earned/owned counts and per-category breakdowns. Completing a task opens a modal for notes and optional impact; dismissing requires a reason. The completion rate is computed as done divided by (total minus dismissed).
Outcome Attribution — What Worked
Outcome Attribution is the closed loop that proves which completed tasks actually moved visibility. It runs period-over-period — primarily monthly, folded into the monthly brand-report task — rather than as a strict next-week check.
For each task completed in the period, attribution joins the task's affected queries to how those queries actually moved. A query only counts as a creditable win when one of these is true:
- It flipped from not-mentioned to mentioned ("query won")
- Its rank position improved
- It gained a citation
Conservative crediting
Attribution is deliberately strict: it requires a genuine prior baseline (a brand-new query is not a "win"), enforces a reverse-causality guard (the movement must post-date the task's completion), and scopes by platform (a ChatGPT-targeted task cannot claim a Gemini-only win). Credited tasks get an "Auto:" actual_impact stamp, but a human-written note is never overwritten.
Each period writes a BrandPerformanceSnapshot (upserted per brand and period) capturing tasks completed/total, overall score and change, queries won, net position change, citations gained, attributed outcomes and a what_worked map (category to measured-impact weight). That what_worked memory then biases both the next report's strategy and task prompts and the Opportunity Agent's scoring — closing the loop. It surfaces at Content Studio's Accountability page (/content-studio/accountability) and via the performance-history and accountability endpoints.
Opportunity Agent & Content Opportunities
The Opportunity Agent turns gaps into ranked, evidence-backed content opportunities — the rows that populate the content calendar (there is no separate calendar table). It fuses three signals into a single opportunity_value score from 0-100.
Opportunity scoring
- AEO query gaps — tracked queries with one or fewer platform mentions
- Keyword/backlink gaps — DataForSEO Labs gaps vs top-3 competitors (budget-guarded)
- What-worked bias — a +/-0.20 nudge from the brand's proven-impact categories
Evidence-gated (anti-slop)
Every opportunity carries an evidence object — no evidence, no opportunity. Each row also has opportunity_value (0-100), sub-scores (aeo_gap_score, keyword_gap_score), search volume, difficulty, a source (aeo_gap / keyword_gap / serp_gap / combined) and a lifecycle status (suggested, scheduled, in_progress, published, dismissed). Off-topic and competitor-branded keywords are dropped, and duplicates are merged by canonical key.
DataForSEO budget guard
Each keyword-gap fan-out is bounded by the per-brand SEO/DataForSEO budget (default $5/brand/month). On exhaustion the agent degrades gracefully to AEO-only opportunities.
Opportunities refresh weekly for paid brands (Sundays 09:00 IST) and surface in Content Studio at /content-studio/opportunities and the publishing /content-studio/calendar. Although these pages sit under the Insights nav section, they are Content Studio surfaces of the same closed loop.
Generation, Schedules & States
Reports are generated automatically each week and can also be triggered manually. Manual generation runs asynchronously: the report row is pre-created in a "generating" state so the UI can poll for completion every few seconds.
| Job | Schedule (IST) | Scope |
|---|---|---|
| Weekly insight generation | Monday 06:00 | Paid brands with a full visibility check this week |
| Monthly outcome attribution | 2nd of month 06:00 | Writes the BrandPerformanceSnapshot / what_worked |
| Opportunity refresh | Sunday 09:00 | Paid brands (Opportunity Agent) |
| Cleanup old reports | Quarterly, 1st 06:00 | Deletes reports older than 90 days |
All times are IST
Celery runs on the Asia/Kolkata timezone, so "Monday 06:00" is genuinely 6:00 AM IST. (A stale code docstring claiming 6:00 AM UTC is wrong.)
The Insights and Tasks pages handle every report state explicitly:
| State | What you see |
|---|---|
| Generating | A live loader; the page polls every 3 seconds until the report completes. |
| Completed | The full Key Findings table and task board. |
| Failed | An explicit error with a retry action (the Celery task also auto-retries). |
| First week | A baseline framing for brands with no prior period of data. |
| Empty | A "Generate First Report" call to action. |
How long does it take?
Generation makes four Gemini calls (each capped at 90–120s; the competitor step at 90s, the others at 120s) under a ~10-minute task limit, so a report usually completes in under a couple of minutes.
Plan Gating & Access
AI Insights is effectively a Starter-and-above feature. The Insights and Tasks pages are gated to Starter+, and generating a report requires a paid plan. The AI models themselves are global, not plan-tiered.
| Capability | Required plan |
|---|---|
| View /insights and /tasks pages | Starter+ (Free sees an upgrade gate) |
| Generate / regenerate a report | Paid only (Starter+) |
| Weekly auto-generation | Paid brands only |
| Read reports/findings/tasks via API | Any valid plan with an active org |
| Opportunity Agent keyword-gap fan-out | Bounded by per-brand SEO budget ($5/brand/mo default) |
Free plan
On the Free plan the Insights and Tasks pages show an upgrade gate, and weekly auto-generation does not run. Upgrade to Starter or above to unlock the full insights loop.
Routes & Endpoints
The product surfaces for AI Insights and Tasks, plus the Content Studio surfaces of the closed loop:
Insights
/insightsStats row + Key Findings table with search, filters and a week selector.
Finding Detail
/insights/:findingIdFull evidence and metrics for a single finding.
Tasks
/tasksKanban / Split View task board with status, type and category filters.
Opportunities
/content-studio/opportunitiesRanked, evidence-backed content opportunities from the Opportunity Agent.
Calendar
/content-studio/calendarThe publishing calendar built on scheduled content opportunities.
Accountability
/content-studio/accountabilityMonthly performance snapshot and what-worked history.
Key backend endpoints under /api/v2/insights:
- GET /{brand_id}/reports, /reports/latest, /reports/{report_id} — list and fetch reports
- POST /{brand_id}/generate (async by default), POST /{brand_id}/generate-tasks — generate a report or regenerate tasks
- GET /{brand_id}/findings, /findings/{finding_id} — read findings (latest report + grounded-only by default)
- GET /{brand_id}/tasks, /tasks/summary, /tasks/{task_id} — read tasks and summary counts
- PATCH /{brand_id}/tasks/{task_id}/status, /tasks/{task_id}/impact — change status or set impact
- GET /{brand_id}/performance-history, /accountability?period=YYYY-MM — the closed-loop data
Legacy v1 insights
An older /api/v1/insights rule-based engine still exists in the backend but is not used by the live product UI — only the v2 pipeline described here is surfaced.
Reports
Overview
Brand Reports are pre-computed, point-in-time snapshots that consolidate your AI Visibility (AEO), SEO, traffic, and lead data into a single executive-ready document. Each report bundles headline metrics, an LLM-written executive summary, and linked findings and tasks, so a stakeholder can understand a brand's standing without opening five different dashboards.
Reports are computed once and stored as a record — they do not re-query live data when you open them. This makes them fast to load and gives you a stable historical archive you can compare period over period. You can browse them at /reports and open any single report at /reports/:reportId.
Consolidated snapshot
AEO, SEO, traffic, and lead data captured together for one period, plus denormalized headline metrics (overall score, score change, recognition rate, total mentions).
AI executive summary
A short, data-grounded narrative written by gemini-3.1-flash-lite, with a deterministic fallback so a summary always exists even if the model is unavailable.
Findings and tasks
Each report links to overlapping AI insights and surfaces a task summary, turning the snapshot into an actionable to-do list.
PDF export
Any completed report can be exported to a clean, multi-page A4 PDF for sharing with clients or leadership.
Reports are a paid feature
The Free plan does not receive any reports — every scheduler skips Free brands, and onboarding and instant reports are paid-gated. Viewing reports requires an active subscription; manually generating one requires Starter or above.
Report Types
TruIntel produces three distinct report types. All three share the same underlying data model and detail layout — they differ in the window they cover, how they are triggered, and how their executive summary is framed.
| Type | Period covered | How it is created | Notes |
|---|---|---|---|
| Weekly | Previous calendar Mon-Sun (scheduled) or a single "today" (instant) | Scheduled Monday 10:00 IST, instant on every full AEO check, or manual | The most common type; cadence rows show in the list, instant single-day rows are hidden |
| Monthly | The full previous calendar month | Scheduled on the 2nd at 06:00 IST, or manual | Adds month-over-month deltas and an accountability narrative; also emails org members |
| Onboarding | A single day (the day onboarding finished) | Auto-generated once, when onboarding completes and the first AEO + SEO checks are done | Exactly one per brand, ever; appears in the Reports list as a baseline |
The onboarding report is your baseline
Generated automatically the first time a brand finishes onboarding, the onboarding report establishes a "day one" baseline with no week-over-week comparison. Every brand gets exactly one, and it cannot be regenerated.
Brand Reports are separate from AI Insight Reports. When an insight report overlaps a brand report's window, the brand report optionally links to it so its findings appear inside the report detail page.
What Is Inside a Report
The report detail page renders its data as a sequence of section cards (around 13 — the three brand-narrative sections, How AI Describes Your Brand / New This Period / Earned Media Opportunities, share a single three-card row), opening with a hero that shows the overall visibility score, recognition rate, total mentions, the five AI platforms, and a button to jump to the linked insight report. Underneath, the sections walk through every dimension of the brand's performance.
- Executive Summary — the AI-written narrative for the period
- AI Visibility Overview — a stat row of the headline AEO metrics
- Platform Breakdown — per-platform performance across ChatGPT, Claude, Gemini, Perplexity, and Google AI Overview
- Query Performance — how individual tracked queries surfaced the brand
- Competitor Landscape — a comparison table against tracked competitors
- Brand Perception — sentiment distribution for the period
- How AI Describes Your Brand — extracted brand attributes
- New This Period — what changed since the last snapshot
- Earned Media Opportunities — citation and outreach openings
- Citation Sources — the domains AI engines cited
- SEO Health — domain authority, PageSpeed, backlinks, keywords, and crawl issues
- Top Keywords — the brand's strongest keyword positions
- Traffic Intelligence — a visitor-classification donut plus agent breakdown
- Lead Verification — verified, review, and rejected lead counts
- Findings & Actions — the task summary plus linked findings
Under the hood, a report stores four JSONB snapshots — AEO, SEO, traffic, and lead — built by reusing the same aggregation used elsewhere, so generating a report does not re-query the source tables. The SEO, traffic, and lead snapshots are null when a brand has no data for them, which is common on lower tiers where that underlying data is feature-gated. The AEO snapshot is the richest, carrying current metrics, changes, platforms, competitors, sources, attributes, queries by intent, query highlights, sentiment distribution, totals, a task summary, and (monthly only) accountability data.
Lower tiers may see partial reports
A report always includes the AEO snapshot. SEO, traffic, and lead sections appear only when that data exists — brands on plans that do not unlock those features will see those cards omitted rather than empty.
The AI Executive Summary
Every report opens with a short executive summary written by gemini-3.1-flash-lite at a low temperature (0.3) for consistency. The model receives the aggregated metrics for the period and returns a tight, data-grounded narrative — never marketing fluff.
| Report type | Summary style | Length budget |
|---|---|---|
| Weekly | A 3-4 sentence data summary of the week | 300 tokens |
| Monthly | A two-part accountability narrative driven by the attribution payload (what was promised vs. what happened) | 400 tokens |
| Onboarding | A welcoming "here is your baseline" framing with no week-over-week comparison | 300 tokens |
A summary always exists
Each of the three summary prompts has a deterministic fallback. If the model is unavailable or fails, TruIntel writes the narrative from the raw numbers so a report is never published without a summary.
Generation Schedules
Reports are generated automatically on the platform's Celery beat schedule. All times are genuine India Standard Time (Asia/Kolkata) — the scheduler runs in IST, so no timezone conversion is needed.
| Job | When (IST) | Scope |
|---|---|---|
| Weekly brand report | Monday 10:00 | All paid brands with a visibility check in the last 14 days |
| Monthly brand report | 2nd of month 06:00 | All paid brands with a visibility check in the last 14 days |
| Weekly visibility email | Monday 08:00 | Org members of active paid brands (built from VisibilityChecks, not a report) |
| Instant "State of Your Brand" | On every full AEO check | Auto-dispatched ~20s after a full visibility check completes |
| Onboarding report | At onboarding completion | Once per brand, after first AEO + SEO checks finish |
The weekly email fires before the weekly report
The Monday 08:00 IST visibility email is built from your two most recent VisibilityChecks, not from a brand report — and it runs two hours before the 10:00 IST brand-report generation. The two are independent jobs. The weekly brand-report task itself sends no email.
Both scheduled generators only process paid brands that have a visibility check within the past 14 days. The monthly job does extra work: it also regenerates content opportunities and fans out a monthly accountability email to every org member.
Instant and Onboarding Reports
Beyond the scheduled cadence, two automatic mechanisms keep a fresh report available without you asking.
Instant "State of Your Brand"
Every time a full visibility check completes, TruIntel auto-dispatches a weekly-type report scoped to a single day (today), about 20 seconds later. It is idempotent per day, so repeated checks do not pile up duplicate rows. This instant report is hidden from the historical list — it powers the "current state" view rather than the archive. The frontend polls for it every 4 seconds while it builds.
Onboarding report coordinator
When onboarding finishes, TruIntel creates a placeholder onboarding report and dispatches a coordinator task. Because AEO and SEO checks finish at different times, the coordinator polls readiness and re-dispatches itself every 45 seconds until both are ready, then generates the report.
Placeholder created
A paid-gated onboarding report row is created for today the moment onboarding completes.
Coordinator dispatched
A coordinator task starts after a 60-second countdown and begins polling readiness.
Readiness polling
AEO is ready once a full check completes; SEO is ready when a Redis flag or any SEO report exists. The coordinator re-dispatches itself every 45 seconds.
Generate or force-finalize
When both are ready it generates. After up to 50 attempts (~37 minutes) it force-generates or marks the report failed so the loading state always resolves.
Two different poll intervals
The 4-second interval is the frontend polling a generating report so the UI updates. The 45-second interval is the backend onboarding coordinator polling whether AEO and SEO data are ready. They are unrelated.
Weekly Email Report
Separately from the in-app brand reports, TruIntel emails a weekly visibility digest every Monday at 08:00 IST to each org member of an active paid brand. It is built directly from your two most recent VisibilityChecks — not from a stored brand report — so it can go out before the brand report is generated.
- A circular visibility-score gauge with the week-over-week delta
- A Week-over-Week Movement table
- Top Insights for the week (up to 3)
- A list of open tasks (top 5)
- A "Full Report" link back to /overview
Delivery via Resend
Weekly emails are sent through Resend. If no Resend API key is configured the job skips gracefully rather than failing. The monthly accountability email is a separate job that fans out to every org member.
There is no email-frequency setting
Older docs pointed to "Settings -> Notifications" to configure email frequency. That section is an in-app alert feed — it does not control report-email cadence. The weekly/monthly email schedule is fixed.
Exporting to PDF
Any completed report can be exported as a clean, multi-page A4 PDF for sharing with clients or leadership. The exporter uses your browser's native print pipeline rather than a screenshot library, so text stays crisp and selectable.
Open a completed report
Navigate to /reports and open a report whose status is completed — only completed reports are downloadable.
Click the download icon
This opens the report with a ?download=1 flag, which prepares the print-optimized layout.
Save as PDF
TruIntel injects print CSS (A4 page size, a cover page, a repeating header, and page-break control) and triggers the browser print dialog automatically. Choose "Save as PDF".
Native print, not a screenshot
Export is powered by the browser print dialog (window.print) with injected @media print styles — there is no html2canvas or jsPDF involved, so tables and text export as real, selectable content.
Reports Page and States
The Reports area lives under the REPORT group in the sidebar. The list page at /reports gives you a filterable archive, and each report opens at /reports/:reportId.
List page (/reports)
- Tabs to filter by All / Onboarding / Weekly / Monthly
- A table paginated at 20 rows per page
- Type badges: weekly (info), monthly (default), onboarding (success)
- An empty state that explains the automatic onboarding report and the weekly/monthly cadence
- Instant single-day weekly rows are hidden; onboarding rows do appear
Detail page (/reports/:reportId)
- A hero with the score, recognition rate, mentions, "5 AI platforms", and a linked-insight button
- The ~13 section cards described above
- A generating guard with a spinner (e.g. "Building your onboarding report") while a report is still computing
- A failed state with a "Retry generation" action
- A developer-only Raw JSON view
Live polling while generating
The list, the detail page, and the instant report all poll every 4 seconds while a report is in the generating state, so the UI flips to the finished report automatically without a manual refresh.
API Reference
Report endpoints live under the /api/v1/reports prefix. The router requires an active subscription for all reads; manual generation additionally requires a paid plan (Starter or above).
List reports
GET /api/v1/reports/{brand_id}Paginated list (limit 1-50, default 20) with an optional type filter.
Latest report
GET /api/v1/reports/{brand_id}/latestThe most recent completed report for the brand.
Instant report
GET /api/v1/reports/{brand_id}/instantToday's instant report in any status (generating, completed, or failed).
Kick instant report
POST /api/v1/reports/{brand_id}/instantSelf-kicks generation of the instant "State of Your Brand" report.
Single report
GET /api/v1/reports/{brand_id}/{report_id}The full record for one report, including all four snapshots.
Generate report
POST /api/v1/reports/{brand_id}/generateManually generate a report. Body { report_type } accepts weekly, monthly, or onboarding. Requires a paid plan; returns a task_id.
Idempotent per window
A unique constraint on (brand, type, period_start, period_end) means re-running generation for the same window updates the existing report rather than creating a duplicate.
Plan Availability
Reports are entirely a paid capability. The table below summarizes what each plan can do with reports.
| Capability | Free | Starter | Lite | Pro / Enterprise |
|---|---|---|---|---|
| Scheduled weekly/monthly reports | No | Yes | Yes | Yes |
| Onboarding + instant reports | No | Yes | Yes | Yes |
| View reports | No | Yes | Yes | Yes |
| Manually generate a report | No | Yes | Yes | Yes |
| PDF export | No | Yes | Yes | Yes |
| Weekly + monthly email | No | Yes | Yes | Yes |
Free plan receives no reports
Every report scheduler filters out Free brands, the onboarding and instant flows are paid-gated, and the email loops exclude Free. Upgrade to Starter or above to unlock reports.
There is no further per-tier gating on reports themselves — a Starter report and a Pro report are structurally identical. However, lower tiers may see null SEO, traffic, or lead sections because that underlying data is feature-gated elsewhere in the product.
Content Studio & Opportunities
NewOverview
Content Studio is TruIntel's in-app editorial production hub. It turns your measurement signals — AI-answer gaps, keyword and backlink gaps, and SERP gaps — into a ranked plan of what to write, a research-grounded staged draft flow, a publishing calendar that auto-publishes real articles to your website, a unified drafts library, and a monthly accountability view that proves the work moved the needle.
It lives under the Insights section of the left navigation, and every page sits under the /content-studio/* route family. Content Studio is the v2.1 successor to the older standalone "Plus" express generator: the frontend feature key and plan gate are still labelled "plus" / "TruIntel Plus" internally, but the user-facing product is Content Studio.
Opportunities
A ranked, evidence-backed list of what to write next, fused from AEO mention gaps, keyword/backlink gaps, and SERP gaps and scored 0-100.
Staged Create
A resumable wizard — Brief, Keywords, Outline, Draft, CMS — that grounds every decision in real research before spending a credit.
Publishing Calendar
A month grid that schedules real CMS articles and auto-publishes them to your connected website at the chosen time.
Library
One unified view of every draft across both the Plus generation store and the CMS, with source badges and per-row actions.
Accountability
A monthly value-proof surface: tasks completed, queries won, rank movement, citations gained, and per-task attributed outcomes.
Paid plans only (Starter and up)
Every Content Studio page is gated to paid plans. The backend require_paid_plan dependency returns 403 PAID_PLAN_REQUIRED for Free or no-plan organizations, and the frontend wraps each route in a PlanGatedRoute with a Starter minimum. Free accounts do not see Content Studio.
Opportunities
/content-studio/opportunitiesFront door — ranked table of content opportunities (also where /content-studio bare redirects).
Staged Create
/content-studio/createThe Brief to CMS wizard. Always opportunity-seeded — entered from an opportunity's Create button, not a top-level nav item.
Resume Create
/content-studio/create/:briefIdResume an in-progress brief by its id.
Calendar
/content-studio/calendarPublishing calendar over real CMS articles.
Library
/content-studio/libraryUnified drafts across Plus generations and CMS content.
Accountability
/content-studio/accountabilityMonthly value-proof and looking-forward opportunities.
Opportunities
The Opportunities page is the front door of Content Studio. It presents a ranked table of content ideas, each one carrying the evidence behind it — search volume, difficulty, the target AI query, ranking competitors, and the specific gap that was found. Rows are sorted by opportunity value (0-100) descending so the highest-impact ideas surface first.
Three signals fused into one ranked plan
Each opportunity is fused from up to three independent gap signals. A candidate that appears in more than one signal is merged into a single combined opportunity rather than duplicated.
AEO query gaps
Active tracked queries where your brand has zero or low mentions across AI platforms. Sub-score scales with how few mentions you have and the query's business value (1-5).
Keyword / backlink gaps
Keywords your top-mentioned competitors rank for but you do not (missing / weak / untapped), pulled from DataForSEO Labs and filtered for relevance.
SERP gaps
Search-result opportunities folded into the same ranking, contributing evidence such as competing pages and median content depth.
Combined opportunity value (when both AEO and keyword signals are present)
When only one signal is present, that sub-score is used directly. The final value is then biased up to plus or minus 20% by the latest performance snapshot's "what worked" weighting — keyword-gap ideas lean to the "seo" category, everything else to "content" — so the plan adapts to what has actually been moving your numbers.
Anti-slop by construction
A candidate with no evidence metric — no query, keyword, competitor, search volume, or SERP data — is dropped before it is ever produced. Every opportunity you see is backed by a verifiable signal, and re-running the generator dedupes against your existing non-dismissed ideas so the list never piles up duplicates.
Status lifecycle
| Status | Meaning |
|---|---|
| suggested | Freshly generated and ranked, awaiting your decision. |
| scheduled | You set a target date for it (sets scheduled_date). |
| in_progress | A brief or draft is being built from it. |
| published | The resulting article went live. |
| dismissed | You discarded it; dismissed ideas are excluded from dedup so they stay gone. |
Page actions and states
- Per-row Create — opens the staged Create wizard seeded with that opportunity (?opportunity_id=).
- Per-row Schedule — a date picker that sets scheduled_date and flips status to scheduled. This action is hidden until a CMS domain is connected.
- Header Refresh — re-derives opportunities on demand.
- Filters — server-side status filter (suggested / scheduled / in_progress / published / dismissed) plus a client-side title search.
- Summary StatRow — total count, top value, high-impact count (value 75 or higher), and scheduled count.
- States — no-brand, loading skeleton, error with Retry, empty (a setup checklist prompting you to run your first scan when none exists, otherwise a Refresh CTA), and the ranked table.
How Opportunities Appear
You rarely have to ask for opportunities — TruIntel generates them for you. There are four paths that populate the list, ranging from a one-time back-fill when you onboard to a weekly scheduled refresh.
Auto back-fill at onboarding
After your first visibility check completes, a one-time task back-fills Opportunities (alongside Off-Page Outreach and Directory) — but only when the table is still empty for the brand. It runs roughly 45 seconds after source-gap data settles, on paid plans only, behind a per-brand lock.
Weekly refresh
Every Sunday at 09:00 IST, a scheduled task regenerates and commits opportunities for every active paid brand, spaced out to stay within budget.
Manual refresh
The header Refresh button (POST /content/opportunities/refresh) re-derives and returns a fresh list any time you want.
Report-driven
Opportunity generation also runs as part of brand-report runs, tagging each idea with the report it came from for provenance.
Kill switch
The onboarding auto back-fill is governed by the GROWTH_AUTOGEN_ENABLED flag (default on). It skips Free / no-plan organizations entirely and only fills tables that are empty, so it never overwrites work you have already started.
What powers the keyword signal
The keyword/backlink gap signal diffs your domain against your top three most-mentioned active competitors that have a domain, using DataForSEO Labs. It requires a connected domain, at least one qualifying competitor, and an available per-brand SEO budget.
Graceful degradation
Each per-competitor lookup is budget-gated before the call and records real spend after. If the budget is exhausted, or you have no domain or no competitor with a domain, the keyword signal simply returns nothing and the plan degrades to AEO-only — it never errors out.
Staged Create Wizard
Staged Create is a resumable five-step wizard that grounds every editorial decision in real research before you spend a single credit. You always enter it from an opportunity (or with an explicit topic and seed keyword); it is intentionally not a standalone nav item. Every checkpoint surfaces the evidence behind its recommendation.
Brief
TruIntel assembles a research brief from the seed: keyword research, SERP-derived word count, a merged table of contents, and People-Also-Ask questions. This step is free.
Keywords
Review the primary keyword and the secondary cluster (each tagged with its source). Confirm or edit.
Outline
Review the LLM-merged table of contents drawn from the top-ranking pages and PAA. Confirm or adjust.
Draft
Generate the full article. This charges credits (standard 1, premium 3).
CMS
Send the finished draft to your CMS, ready to schedule and publish.
Express mode and resuming
Express mode skips the Keywords and Outline checkpoints and takes the built brief straight to Draft. Briefs are persisted and resumable — return to any in-progress brief via /content-studio/create/:briefId, and a fresh build can be seeded by an opportunity_id or by an explicit topic plus seed keyword.
What goes into a brief (free)
Building a brief is free — the credit charge only happens at the Draft step. Before any paid call, a budget gate runs; if the per-brand SEO budget is exhausted the request returns 429 PLAN_LIMIT_REACHED. The research that goes into a brief:
- Keyword research — DataForSEO Labs keyword suggestions (up to 50) plus search volume for the seed keyword.
- GSC demand — your top 25 Google Search Console query rows over a 90-day lookback, where impressions represent real demand (degrades to DataForSEO-only when GSC is not connected).
- Secondary cluster — Labs suggestions ranked by volume with GSC terms folded in, deduped by token-set, capped at 8 keywords, each evidence-tagged with its source (dataforseo or gsc).
- SERP analysis — one paid advanced live-SERP call (depth 10) returning organic URLs and PAA; the top pages are fetched and analyzed to compute an outlier-filtered median word count (with a 120-word floor).
- Merged outline — an LLM-merged table of contents (up to 14 sections) built from the analyzed pages, with a deterministic frequency-merge fallback if the LLM is unavailable.
- H1 synthesis — a small utility-LLM call proposes the H1 (opportunity titles are used as-is).
Never raises
The SERP analysis is designed to never fail the brief — if a page fetch or LLM call fails, it degrades to a partial result rather than blocking you. Every keyword in the brief carries an evidence object, including bare keywords you type yourself (wrapped with a "user" source).
Editing a brief (free)
You can patch any field on a brief without charge — topic, H1, primary and secondary keywords, target location, audience, tone, word count, table of contents, PAA, and status. Briefs move through their own status lifecycle: draft, keywords_confirmed, toc_confirmed, drafted, archived.
Drafting, Repurposing & Credits
The Draft step is the only part of the staged flow that costs credits. TruIntel deducts the credits up front (under a row lock to prevent double-charging), creates a placeholder generation, marks the brief drafted, and dispatches a background task that runs the full content-generation pipeline. If the dispatch fails, the credits are automatically refunded and the brief is reset.
| Action | Credit cost | Model used |
|---|---|---|
| Build brief | Free | DataForSEO research + utility-LLM (Anthropic-first) for TOC merge and H1 |
| Edit brief | Free | No model — direct field patch |
| Draft (standard tier) | 1 credit | gemini-3.1-flash-lite |
| Draft (premium tier) | 3 credits | claude-haiku-4-5 |
| Repurpose (each format) | 1 credit | gemini-3.1-flash-lite |
Premium is Haiku, not Sonnet or GPT
The 3-credit premium draft tier pins the Anthropic-first utility chain, which resolves to claude-haiku-4-5. The standard 1-credit tier uses gemini-3.1-flash-lite. Research grounding (keywords, SERP, PAA) always comes from DataForSEO.
Repurpose one brief into many surfaces
The primary blog draft comes from the Draft step. Repurpose takes the same brief and produces the other fast, grounded answer surfaces from it — each becomes its own generation that you poll until ready. The brief's status and its opportunity link are left untouched.
FAQ page
A question-and-answer page derived from the brief. 1 credit.
Structured answer
A concise, citation-ready answer block tuned for AI engines. 1 credit.
Meta descriptions
Search-snippet meta descriptions for the topic. 1 credit.
Credit pools and monthly allowances
Credits come from two live pools: plan_credits, your monthly allowance that resets on the 1st, and extra_credits, top-up packs that never expire. The discontinued legacy "Plus" credit pool is no longer spendable.
Starter
$59/mo
- 50 AI credits / month
- Full Content Studio
- Standard + premium drafts
Lite
$139/mo
- 150 AI credits / month
- Full Content Studio
- 2 brands
Pro
$479/mo
- 500 AI credits / month
- Full Content Studio
- 3 brands
Enterprise
Custom
- 1000 AI credits / month
- Full Content Studio
- 4 brands
Top-up packs (one-time, never expire)
Need more credits mid-month? Starter Pack — 50 credits for $10; Growth Pack — 150 credits for $25; Pro Pack — 400 credits for $50. Free plan has 0 monthly credits, so drafting requires a paid plan or a top-up.
Publishing Calendar
The Calendar page is a publishing calendar over your real CMS articles — not over opportunities. The month grid shows scheduled, publishing, published, and failed articles on the day they go live, and a side Planner drawer lets you queue new work.
Two different calendars
Do not confuse the opportunity-level scheduled_date (an internal planning field, served by the legacy /content/calendar endpoint) with this Calendar page, which schedules and publishes real CMS articles via /cms/content/{id}/schedule. The Calendar page is a true publishing calendar.
The Planner drawer
Ready tab
Draft articles you can drag onto a day (or click Schedule) to publish.
Ideas tab
Content opportunities you can turn into an article without leaving the calendar.
How scheduling and auto-publishing work
Drop a draft on a day
A date-time picker opens. You pick a time, which must be in the future. The article moves to status scheduled and any previous publish error is cleared.
The every-minute poller fires
A beat task (cms.run_due_scheduled_publishes) runs every minute. It atomically claims due scheduled rows, flips them to publishing, pushes the article to your website, and sets the final status. This poller is the single source of truth — there is deliberately no per-item timer, which would be unreliable and could double-publish.
Reschedule or unschedule
Drag a scheduled chip to another day to move its fire time. Unschedule returns an article to draft and clears its scheduled time.
| Status | Chip behavior |
|---|---|
| scheduled (violet) | Draggable to reschedule; can be unscheduled back to draft. |
| publishing (amber) | Locked while it publishes (409 if you try to move it). |
| published | Anchored and immutable — it has already gone live. |
| failed | Retryable — re-schedule it to try again. |
| draft | Lives in the Ready tab until you schedule it. |
| generating | Locked while content is still being produced. |
| archived | Locked. |
Clicking any chip opens that article in the CMS editor. The calendar reads from GET /cms/content/calendar?month= and uses POST /cms/content/{id}/schedule, POST /cms/content/{id}/unschedule, and GET /cms/content/{id}/publish-status.
Library
The Library is a single unified "my drafts" view that merges both content stores behind one table, each row carrying a source badge so you always know where a draft came from.
TruIntel Plus generations
Publishable drafts produced by the staged Create flow and repurpose actions. Rows offer Preview, Send-to-CMS, Schedule, and status actions.
CMS content
Articles already in your CMS — landing pages, comparisons, how-tos, listicles. Rows offer Open-in-CMS and View-live.
CMS is additive
The CMS feed is layered on top of the Plus drafts. If the CMS source errors, the Library still renders your Plus drafts rather than failing the whole page.
- Source and status filters plus a title search.
- A freshness stamp showing when the list was last loaded.
- States — no-brand, loading skeleton, error with Retry, empty (a generate CTA), and the merged table.
Accountability
Accountability is the "did the work move the needle?" surface. It answers, month by month, whether the content and SEO work TruIntel recommended actually changed your numbers — and what to do next. It has two halves.
Looking back
Defaulting to the latest monthly snapshot (or any period you pick), the looking-back view proves impact with concrete, attributed evidence rather than vanity charts.
Task completion
A ring showing how many recommended tasks were completed in the period.
Queries won
AI queries where your brand started getting recognized.
Rank movement
How your overall score and positions shifted across the period.
Citations gained
New citation URLs your brand earned in AI answers.
Attributed outcomes
Which specific task moved which metric — anti-slop proof, not correlation.
Performance trend
A history of overall_score over time from the performance-history endpoint.
Looking forward
The second half lists that period's top-ranked content opportunities, each with evidence pills that link straight into the staged Create flow — closing the loop from proof back into the next round of work.
Monthly accountability email
On the monthly brand-report run (the 2nd of the month at 06:00 IST), a monthly accountability email — tasks done, what moved, and the top forward opportunities — is sent to every member of the organization.
Data comes from GET /api/v2/insights/{brand_id}/accountability?period=YYYY-MM and GET /api/v2/insights/{brand_id}/performance-history.
Plan Gating & Reference
All of Content Studio is a paid-plan feature. The capability-level gates and the full route reference are below.
| Capability | Gate |
|---|---|
| All Content Studio pages (Opportunities, Create, Calendar, Library, Accountability) | Paid only — Starter and up |
| Auto-generate opportunities at onboarding | Skips Free/no-plan; GROWTH_AUTOGEN_ENABLED (default on) |
| Build / edit a brief | Free step, still Starter+ |
| Draft from a brief | Charges credits — standard 1, premium 3 |
| Repurpose a format | 1 credit each |
| Keyword-gap opportunity signal | Needs DataForSEO + at least one competitor with a domain + available per-brand SEO budget |
Frontend routes
Bare redirect
/content-studioRedirects to /content-studio/opportunities.
Opportunities
/content-studio/opportunitiesRanked opportunities table.
Create
/content-studio/createStaged Create wizard (opportunity- or topic-seeded).
Resume Create
/content-studio/create/:briefIdResume a saved brief.
Calendar
/content-studio/calendarPublishing calendar over CMS articles.
Library
/content-studio/libraryUnified drafts (Plus + CMS).
Accountability
/content-studio/accountabilityMonthly value-proof and forward opportunities.
Backend endpoints
Build brief (free)
POST /content/briefAssembles a research-grounded brief.
Fetch / edit brief
GET, PATCH /content/brief/{id}Read or patch brief fields (editing is free).
Draft from brief
POST /content/brief/{id}/draftKicks the draft — charges credits (tier standard/premium).
Repurpose brief
POST /content/brief/{id}/repurposeProduces faq_page / structured_answer / meta_descriptions (1 credit each).
List opportunities
GET /content/opportunitiesRanked list, filterable by status.
Refresh opportunities
POST /content/opportunities/refreshRegenerates opportunities on demand.
Schedule opportunity
PATCH /content/opportunities/{id}Sets scheduled_date and/or status.
CMS publishing calendar
GET /cms/content/calendar, POST /cms/content/{id}/schedule, /unscheduleSchedule and publish real CMS articles.
Accountability
GET /api/v2/insights/{brand_id}/accountability, /performance-historyLooking-back proof and the score trend.
Key schedules (IST, Asia/Kolkata)
Weekly opportunity refresh runs Sunday 09:00. The CMS scheduled-publish poller runs every minute. The monthly accountability email goes out on the 2nd at 06:00 with the monthly brand report. AI credits reset on the 1st at 00:00.
TruIntel Plus (AI Content)
Overview
TruIntel Plus (branded "TruPlus" in the app) is a credit-metered AI content generator. It turns your brand's AEO and SEO visibility data into ready-to-use marketing assets — SEO blog posts, FAQ pages, structured answers, schema markup, and metadata/social tags — that are pre-optimized to win mentions and citations across AI engines.
Every generation deducts credits from your organization's credit ledger. Output can be copied as Markdown or HTML, downloaded as a .md file, marked Published or Archived, or sent straight to the CMS as an editable draft.
Paid plans only
TruPlus is unavailable on the Free plan. The entire /api/v1/plus router is gated by require_paid_plan, so Free / no-plan organizations receive an upgrade lock screen on every Plus page. Starter and above unlock it.
Grounded in your data
Generators are seeded with your brand's real visibility, competitor, and source data — not generic prompts — so output reflects how AI engines actually describe your space.
Credit-metered
Each express generation costs 1 credit drawn from your monthly plan pool, then a never-expiring extra-credit pool. Briefs and CMS hand-off are free.
CMS-ready output
Blog, FAQ, and Structured Answer drafts can be sent to the CMS for free, where you edit, score, schedule, and publish them.
Fast inline generation
The live UI runs generation inline (synchronous), typically 30-60 seconds, via the provider-agnostic LLM fallback chain.
The Create Catalog
The create surface offers 5 grounded generators across 2 tiers. (The old "Backlink Strategy" tier — directory listings, outreach templates, citation plans — was retired from Plus and moved to the dedicated Outreach workspace at /outreach.)
Content tier
| Generator | Output | What it produces |
|---|---|---|
| Blog Post | HTML | A ~2000-word SEO article with an FAQ section, routed through the unified CMS 5-step HTML pipeline — including AEO answer blocks, schema markup, meta title/description, OpenGraph tags, and quality + AEO-citation scores. |
| FAQ Page | Markdown | 8-12 question/answer pairs grounded in visibility data and an optional target query / People-Also-Ask, structured for JSON-LD FAQPage schema. |
| Structured Answer | Markdown | A citation-friendly, definitive 150-300 word answer written for AI engines to quote directly. |
Technical SEO tier
| Generator | Output | What it produces |
|---|---|---|
| Schema Markup | Markdown + JSON-LD | Ready-to-paste Organization, Product/Service, FAQPage, and Article structured-data blocks. |
| Metadata & Social Tags | Markdown + HTML | A merged generator: an SEO title/meta-description table for 8-10 pages, plus OpenGraph and Twitter-card markup for the top 5-7 pages. |
Blog Post is the differentiator
Unlike the other express types, Blog Post runs the full unified CMS HTML pipeline. That means it ships with structured AEO answer blocks, schema markup, meta and OpenGraph tags, and computed quality + AEO-citation scores baked in — not just plain prose.
Where each generator lives
Blog Post, FAQ Page, Structured Answer, and Metadata & Social Tags appear in the generic wizard's step-0 grid. Schema Markup is reachable only via its own landing card / route (/plus/schema-markup). All five also have dedicated single-type pages.
Generating Content
You can generate from a dedicated single-type page (a single form) or from the generic 4-step wizard at /plus/generate. Both call the same backend and both cost 1 credit.
Pick a generator
Choose a surface from the TruPlus hub cards, a dedicated type page, or the wizard's Select step.
Enter a Target Query
A Target Query is required — it anchors the generation to a real search intent. Additional Context is optional and lets you steer tone, audience, or specifics.
Generate
The form submits POST /api/v1/plus/generate (synchronous by default). The system checks your credit balance, validates the brand belongs to your org, runs the generator, stores a PlusGeneration row, and deducts 1 credit.
Review & act on the preview
The preview screen offers Copy Markdown, Copy HTML, Download .md, Send to CMS (eligible types), and Publish / Archive.
Typical generation time
The live UI runs generation inline (synchronously), usually 30-60 seconds. A failed generation automatically refunds the credit to your extra-credit pool.
Which model runs it
Plus labels every generation gemini-3.1-flash-lite in its metadata, but execution actually routes through the provider-agnostic llm_json fallback chain, which is Anthropic-first (Claude Haiku 4.5) and only falls back to Gemini, OpenAI, or OpenRouter on failure. The displayed model name is nominal, not necessarily the provider that produced the text.
Output, CMS Hand-off & Lifecycle
Once generated, content lives as a PlusGeneration record you can export, push to the CMS, or move through a simple publish lifecycle.
Export actions
- Copy Markdown — copy the raw Markdown to your clipboard.
- Copy HTML — copy the rendered HTML to your clipboard.
- Download .md — save the generation as a Markdown file.
Send to CMS
Eligible types (Blog Post, FAQ Page, Structured Answer) can be pushed into the CMS with one click. The bridge converts Markdown to HTML, parses frontmatter, runs a free Gemini SEO-optimization pass, and creates a CMSContent draft linked back to the generation. It is free, charges no credit, and is idempotent (re-sending updates the same draft). Structured Answers map to the CMS blog_post type.
Free publish bridge
Send to CMS never costs a credit. Once in the CMS, the draft can be edited, AI-SEO scored, scheduled, and published using the full Content Studio toolset.
Lifecycle & statuses
| Status | Meaning |
|---|---|
| generated | Default state for a new generation. |
| published | Marked published by you (sets published_at). Use Mark Published from the preview or history row. |
| archived | Hidden from the active list via Archive. |
| processing | In-flight marker shown while a staged/async generation runs ("Generating"). |
| failed | A staged/async generation failed; the credit is refunded. |
Delete permanently removes a generation row. Publish and Archive are reversible status changes via PATCH /plus/generations/{brand}/{id}/status.
Generation History
The /plus/history page is a searchable, filterable record of everything you have generated for a brand.
Search & filter
Search by title and filter by generator type and status. Results are paginated 10 per page.
Per-row actions
View, Send to CMS, Mark Published, Archive, and Delete directly from each row.
CMS indicator
Rows that have been pushed to the CMS show a "Sent to CMS" indicator so you can track what has crossed over.
Reopen any generation
View a saved generation in the wizard viewer via /plus/generate?view=<id>&brand_id=<id>.
Credit System
Plus content consumes AI credits. Credits are drawn from two live pools in a fixed order, and unused express generations are refunded on failure.
| Pool | Source | Reset | Spend order |
|---|---|---|---|
| Plan Credits | Included with your subscription plan | Resets to the plan allowance on the 1st of each month (00:00 IST) | Spent first |
| Extra Credits | One-time top-up purchases | Never reset, never expire | Spent after plan credits are exhausted |
No more "Plus credits" pool
The old plus_credits pool (the discontinued "TruIntel Plus $99/mo" subscription) is unspendable and always 0. Only the Plan Credits and Extra Credits pools are live, and there is no sellable Plus subscription.
Monthly plan allowance
| Plan | Monthly Credits | Per-plan extra-credit price |
|---|---|---|
| Free | 0 (Plus locked) | — |
| Starter | 50 | $2.00 / credit |
| Lite | 150 | $1.75 / credit |
| Pro | 500 | $1.50 / credit |
| Enterprise | 1,000 | $1.00 / credit |
What each action costs
| Action | Credit cost |
|---|---|
| Express generation (any of the 5 surfaces, incl. Blog Post) | 1 credit |
| Staged draft — standard quality | 1 credit |
| Staged draft — premium quality | 3 credits |
| Repurpose a brief into another format | 1 credit per format |
| Brief generation | Free |
| Send to CMS | Free |
| Internal-link suggestions | Free |
| Failed generation | Refunded to extra-credit pool |
Prompt Volumes redemption
On paid plans, once your daily/monthly Prompt-Volumes cap is exhausted, 5 credits can be redeemed for one additional Prompt-Volumes page lookup.
Buying Extra Credits
When your plan credits run low, the TruPlus hub surfaces a low-credits banner and a "Buy Extra Credits" modal. The live UI sells flat top-up packs purchased via Razorpay — one-time, and the credits never expire.
| Pack | Credits | Price | Per credit |
|---|---|---|---|
| Starter Pack | 50 | $10 | $0.20 |
| Growth Pack | 150 | $25 | $0.167 |
| Pro Pack | 400 | $50 | $0.125 |
Two purchase paths exist
The Plus "Buy Extra Credits" modal sells the flat packs above. A separate per-plan endpoint also lets you buy 5-100 credits at your plan's per-credit rate ($2.00 Starter down to $1.00 Enterprise). Both land in the same never-expiring extra-credit pool; the flat packs are the in-app default.
Your live credit balance and usage are shown on the TruPlus hub (low-credits banner + balance widget) and the Buy-Credits modal, and are also surfaced in Settings → Billing. For India, 18% GST is added on top of the listed USD price.
Advanced: Briefs, Repurpose & Internal Links
These Plus-service capabilities share the same credit pool and PlusGeneration table but are surfaced through the Content Studio brief flow rather than the core Plus pages.
Draft from brief
Build a content brief for free, then generate a draft. Standard quality costs 1 credit; premium quality costs 3 credits. The async worker fills the draft and refunds credits on failure.
Repurpose a brief
Turn one brief into many grounded formats — FAQ Page, Structured Answer, and Metadata & Social Tags — at 1 credit per format.
Internal-link suggestions
Free and read-only. Given a finished generation plus your brand's crawled content index, it proposes grounded anchor-to-URL internal links where the anchor text appears verbatim and the target is a real indexed page.
Premium tier
Premium-tier drafts pin the Anthropic-first chain (Claude Haiku 4.5) for higher quality at 3 credits. Brief generation itself is always free — you only pay at draft time.
Routes & Endpoints
TruPlus pages and the dedicated single-type generators in the product app:
TruPlus Hub
/plusGenerator cards (5 surfaces), Off-page pointer to /outreach, Recent Generations, and the Buy Credits modal.
Generate Wizard
/plus/generate4-step wizard (Select → Configure → Generate → Preview); also the viewer for a saved generation.
Generation History
/plus/historySearchable, filterable table of all generations with per-row actions.
Blog Post
/plus/blog-postDedicated ~2000-word HTML SEO article generator (CMS pipeline).
FAQ Page
/plus/faq-pageDedicated 8-12 pair FAQ generator.
Structured Answer
/plus/structured-answerDedicated citation-friendly definitive-answer generator.
Schema Markup
/plus/schema-markupDedicated JSON-LD structured-data generator.
Metadata & Social Tags
/plus/meta-tagsDedicated merged meta + OpenGraph/Twitter generator.
Key backend endpoints (all under /api/v1/plus, gated by require_paid_plan):
| Method & path | Purpose |
|---|---|
| GET /plus/credits | Current credit balance and usage. |
| POST /plus/generate | Generate content (1 credit); inline or via the plus worker. |
| GET /plus/generations/{brand_id} | Paginated list with type/status filters. |
| GET /plus/generations/{brand_id}/{id} | Full content of one generation. |
| PATCH /plus/generations/{brand_id}/{id}/status | Publish or archive. |
| POST /plus/generations/{brand_id}/{id}/send-to-cms | Free CMS draft bridge. |
| POST /plus/generations/{brand_id}/{id}/internal-link-suggestions | Free internal-link proposals. |
| DELETE /plus/generations/{brand_id}/{id} | Delete a generation. |
| POST /plus/credits/purchase + /verify | Per-plan variable credit purchase and verification. |
| GET /plus/templates | Template catalog with estimated token counts. |
Off-page work moved out of Plus
Directory listings, outreach emails, and citation recommendations are no longer generated in Plus. Those live in the Off-page Outreach workspace at /outreach. Invoking the retired types returns a guidance error pointing you there.
CMS & Publishing
Starter+CMS Overview
The TruIntel CMS is an in-app content module mounted under /cms/* in the product app (app.truintel.ai) — not a separate cms.truintel.ai portal. It lets a brand scan its own website, generate AEO-optimized articles with AI, edit them in a rich-text editor with a live AI SEO score, and publish them to Cloudflare’s global edge so the page looks like it lives on the brand’s own site.
Scan
BFS-crawl up to 200 pages to build a content index, extract a reusable brand template, detect page templates, and surface SEO issues + content gaps.
Generate
Produce AEO-optimized articles through a 5-step AI pipeline grounded in your real visibility gaps and site facts.
Edit
A TipTap rich-text editor with a live AI SEO/AEO score, free AI-assist (rewrite/expand/shorten), and editable meta + schema.
Publish
Push to Cloudflare KV + Workers via a multi-tier renderer; publish now, schedule for later, or auto-refresh monthly.
Optimize
AI fix-preview, batch apply, and undo on existing pages plus live-page editing (Visual Page Editor, robots.txt, sitemap).
Schedule
Drag-to-schedule publishing over a calendar, backed by a durable every-minute poller (no dropped jobs).
Plan gate
The entire CMS module (all 16 pages) is gated to Starter and above. The Free plan cannot access CMS because CMS spend is funded by the monthly AI credit allowance, which is 0 on Free.
The module is 16 pages / 16 routes, every one wrapped in a CMS protected route plus the Starter+ plan gate. It reuses your org-level AI credit pools rather than a separate CMS wallet.
5-Step Content Pipeline
Each new article is produced by a 5-step AI pipeline. Before step 1 runs, a pre-step gathers grounding data: your brand’s latest full AEO visibility check (overall/recognition/citation scores, per-platform scores, the queries where your brand is NOT mentioned, and your top 5 competitors), real product/pricing/feature snippets from the content index, relevant existing pages for internal linking, and your scanned brand tone. The pipeline grounds content in your actual AEO gaps and site facts — it does not crawl the live web.
Research
Analyzes the AEO landscape and your visibility gaps. Outputs key points, audience, intent, citation analysis, internal-link targets, a suggested word count, and direct-answer queries for FAQ seeds.
Outline
Builds a citation-optimized structure: title, quick answer, sections, key takeaways, FAQ questions, and internal links.
Full Draft
Writes the complete article as HTML. This is the only hard-fail step — if drafting fails the pipeline aborts and held credits are refunded.
SEO Optimization
Produces the meta title, meta description, Open Graph tags, and schema objects (see schema note below).
Quality Check
Returns a quality score, an AEO citation score, issues, strengths, AEO elements present, and a citation prediction.
Every step is a separate LLM call and has a deterministic fallback, so a transient model outage degrades quality rather than crashing the job. Output is stored as HTML plus a TipTap-compatible JSON wrapper. Generation progress is pushed live so the New page can show each step completing in real time.
Which model writes your content
The generation pipeline runs through TruIntel’s provider-agnostic fallback chain (default order Anthropic → Gemini → OpenAI → OpenRouter), so the primary model is Anthropic claude-haiku-4-5, then gemini-3.1-flash-lite, then gpt-5.4-mini, then OpenRouter moonshotai/kimi-k2.5. Gemini Flash is only the primary model for the site scan and the monthly content refresh.
Schema generated
The SEO Optimization step emits FAQPage, Article, HowTo, and a Speakable spec, combined into a single schema_markup array. There is no generic WebPage schema.
| Content type | Use |
|---|---|
| blog_post | Standard editorial / blog article |
| faq_page | Question-and-answer page optimized for direct-answer citation |
| landing_page | Conversion-focused landing page |
| comparison | Brand vs competitor / X vs Y comparison |
| how_to_guide | Step-by-step instructional content |
| listicle | List-format article |
Generation Entry Points & Credits
There are two ways to kick off generation: a quick Express path from the CMS New page, and a staged, brief-driven path from Content Studio. They differ in how much research happens up front and in credit cost.
Express (New page)
Give a target keyword, content type, and context, and the full pipeline runs end to end. Costs 2 credits.
Staged / brief-driven (Content Studio)
First build a free ContentBrief (keyword research + SERP analysis + merged TOC + People Also Ask), confirm it, then draft. Standard tier costs 1 credit; premium costs 3.
The premium brief tier biases the chain Anthropic-first and adds a premium editorial note. Despite an internal “Sonnet-first” label, premium still resolves to claude-haiku-4-5 — there is no Sonnet model wired in.
| CMS action | Credits |
|---|---|
| Express content generation (New page) | 2 |
| Staged brief draft — standard tier | 1 |
| Staged brief draft — premium tier | 3 |
| Brief generation (research / SERP / outline / PAA) | 0 (free) |
| Repurpose existing content | 1 per format |
| AI-assist (rewrite / expand / shorten) | 0 (free) |
| AI SEO/AEO score | 0 (free) |
| Auto page-optimization fix (per auto-fixable issue) | 1 each |
| Monthly auto-refresh (per piece) | 1 |
| Manual (non-auto-fixable) optimization guidance | 0 (free) |
CMS spends from your org-level AI credit pools in the order plan_credits (monthly allowance, resets on the 1st) then extra_credits (top-ups that never expire). Monthly allowance by plan: Free 0, Starter 50, Lite 150, Pro 500, Enterprise 1000. One-time top-up packs never expire: 50 credits for $10, 150 credits for $25, 400 credits for $50.
Credits are refunded on failure
Credits are pre-deducted at the start of a job and refunded on any definitive failure. A cleanup task marks anything stuck in the generating state for over 15 minutes as failed and refunds the held credits, so a dead job never silently burns credits.
Content Editor & AI SEO Score
Generated content opens in a full TipTap rich-text editor with rich text, images (with alt text), code blocks, links, an SEO sidebar, AI-assist, and editable schema.
Rich Text Editing
Headings, bold, italic, lists, blockquotes, and links with a familiar toolbar.
Image Support
Insert images with alt text via drag-and-drop or paste from the clipboard.
Code Blocks
Syntax-highlighted code blocks for technical content.
SEO Sidebar
Edit meta title and description with live character counts.
AI Assist runs on selected text with three actions — rewrite, expand, or shorten (up to 5,000 characters of input). It is free (0 credits) and runs through the multi-provider LLM chain, so it survives a single provider outage.
AI SEO/AEO Score
The editor scores your draft against a 5-dimension rubric summing to 100. Scoring is LLM-judged (gemini-3.1-flash-lite via the chain), free, and cached on the content’s publish metadata keyed by a SHA-256 fingerprint of title, meta title, meta description, slug, schema, and content HTML — so re-running skips the LLM unless the content meaningfully changed.
| Dimension | Max | Covers |
|---|---|---|
| Content Quality | 25 | Depth, originality, expertise, factual specificity |
| Readability & Structure | 20 | Heading hierarchy, paragraph length, scannability |
| SEO Optimization | 20 | Meta craft, keyword integration, slug, internal linking |
| AI Engine Readiness (AEO) | 20 | Citation-worthy facts, direct answers, FAQ, schema, listicles |
| Engagement & Hook | 15 | Opening hook, examples, CTAs, value-prop clarity |
Ratings: excellent at 85% or above, good 65–84%, needs work 40–64%, and poor below 40% of each dimension’s max. Note that the page title is rendered as the page H1 by the publish template, so the scorer counts the title as the H1 and will not penalize the body for a “missing H1.”
Multi-Tier Edge Publishing
TruIntel publishes content to Cloudflare’s global edge (KV + Workers) using a multi-tier renderer that makes the published page blend into your existing site. The system picks the highest tier it can, falling back gracefully.
| Tier | Method | Best for |
|---|---|---|
| reference_clone (Tier 1) | Fetch a real reference page from your site, clone its full HTML, and replace only the content area | Sites with consistent templates — nav, sidebar, and footer are preserved so the page looks identical to your existing pages |
| main_replacement (Tier 1.5) | Clone the full page and replace the <main> element (used when you pick a template but the content selector is not found) | Template chosen but content selector missing; a missing footer is re-injected from the brand template |
| clone_wrap (Tier 2) | Wrap the content in your extracted brand header, footer, and CSS | Sites where no usable reference page exists but a brand template was captured |
| standalone (Tier 3) | A built-in template using your brand colors and fonts | Final fallback when site extraction failed |
All HTML passes through a single sanitization choke point before serving. After render, the page is written to Cloudflare KV, sitemap.xml is regenerated, the published URL is built (preferring your real domain), search engines are pinged (Bing IndexNow plus a best-effort Google sitemap ping), and the edge cache is purged for the slug and sitemap. If the publish satisfied a linked task, that task is auto-marked done.
Content lifecycle statuses
- draft — created or generated, not yet published
- generating — the AI pipeline is running
- failed — generation failed (credits refunded)
- scheduled — queued for a future publish time
- publishing — actively being rendered and pushed to the edge
- published — live on the edge
- publish_failed — the publish step failed
- archived — retired from active management
You can unpublish a live piece at any time, and a publish-preview endpoint lets you see the rendered page before it goes out.
Scheduled Publishing
Instead of publishing immediately, you can schedule a piece for a future date and time. Scheduling is surfaced through the Content Studio publishing calendar, where you drag content onto a date to set its publish time.
Schedule
Set a future ISO 8601 publish time on a piece that has HTML. Its status flips to scheduled.
Wait for the poller
A beat task runs every minute, atomically claims due scheduled rows in batches, flips them to publishing, and publishes them.
Reschedule or unschedule
Change the time, or unschedule to send the piece back to draft.
Durable by design
The every-minute poller is a deliberate replacement for per-item delayed jobs, which Railway’s broker can silently drop. Because the poller re-checks due rows continuously, a scheduled publish will fire even if a worker was restarting at the exact target minute.
- Content that is published, publishing, generating, or archived cannot be (re)scheduled.
- A piece must have HTML before it can be scheduled.
- The scheduled time must be in the future.
Domain Setup & Edge Onboarding
To publish on your own website you connect a custom domain. TruIntel supports two connection modes depending on how much of your DNS you want to delegate.
| Mode | How it works | Coverage |
|---|---|---|
| custom_hostname (default) | You add a CNAME for www (or a subdomain) pointing to TruIntel’s Cloudflare proxy; we provision a custom hostname with automatic SSL. Your apex stays on your existing host. | www / subdomain only |
| full_zone | You delegate your nameservers; we create a dedicated Cloudflare zone, import your DNS, set up proxy topology, origin pinning, the Worker route, and SSL mode. | Both apex and www proxy through the Worker |
Add your domain
Enter the domain where you want to publish (e.g. blog.yoursite.com or yoursite.com).
Point DNS
For custom_hostname mode, add the CNAME for www to TruIntel’s proxy. DNS and SSL status are tracked from pending to connected.
Brand template extraction
TruIntel crawls your site to capture header, footer, CSS, colors, fonts, and logo so published pages match your look and feel.
Start publishing
Once DNS status is connected, content is served from Cloudflare’s edge worldwide under your real domain.
Hostinger apex → www SSL footgun
The connect flow only CNAMEs www to us; your apex stays on Hostinger and must 301-redirect to www. Use Hostinger’s website Redirects tool (or .htaccess with an https:// target) — NOT “Domains → Redirect your domain,” which has no SSL and breaks https:// visitors. Do not touch the www CNAME or the apex A record.
Helper endpoints validate and diagnose the connection: detect-origin, validate-origin, check-canonical, dns-status, kv-status, and disconnect-info. You can also re-extract the brand template at any time if your site’s design changes.
Site Scanning & Brand Template Extraction
The site scanner BFS-crawls up to 200 pages (concurrency 3, max depth 10, with a 20-minute crawl cap) to build a content index and capture everything needed for on-brand publishing. Per-page AI analysis, content-gap analysis, and recommendations use gemini-3.1-flash-lite directly — so for the scan, Gemini Flash is correct.
BFS crawl
Breadth-first crawl that respects robots.txt and builds the content index (up to 200 pages).
Brand template extraction
Captures header, footer, CSS, colors, fonts, logo, and favicon — with a Playwright headless fallback for JS-rendered (React/Next/Astro) sites and CSS inlining to survive redeploys.
Layout archetype detection
Identifies layout archetypes that power Tier-1 reference cloning.
Page template detection
Detects up to 20 page templates and marks the best blog template as recommended.
Per-page AI analysis
Gemini analyzes each page for topics, summary, and tone (capped at 5 concurrent AI calls; auto-skips remaining AI if over 60% fail).
Content gap analysis
Surfaces missing topics and gaps relative to competitors.
Recommendations
Generates prioritized recommendations for content creation and optimization.
Historical comparison
Compares against the previous scan; the content index is swapped atomically and fix markers carry across re-scans.
Brand template captures more than colors
The extracted brand template stores header HTML, footer HTML, external + inlined CSS, primary/secondary colors, fonts, logo, favicon, background color, a separate content background color (pulled from a real blog page to fix homepage-vs-blog theme mismatch), nav links, raw head tags, and layout archetypes. This is the engine behind Tier-1 reference cloning.
A cleanup task marks any scan stuck for more than 30 minutes as failed so the scan UI never hangs indefinitely.
Page Optimization & Fix Chain
The Optimization page turns the crawler’s detected issues into actionable fixes. Each issue carries severity, a credit cost, and whether it is auto-fixable. Auto-fixable issues are applied as edge modifications to KV; non-auto-fixable issues come with manual guidance only.
| Issue type | Cost | Examples |
|---|---|---|
| Auto-fixable | 1 credit each | missing/duplicate/too-long/too-short title, missing or bad meta description, duplicate meta, missing schema, missing Open Graph tags |
| Manual guidance only | 0 credits | broken page, redirect loop, missing/multiple H1, thin content, missing canonical, slow/large page, images without alt, noindex, internal broken links |
Scan & detect
The crawler detects issues across meta tags, headings, content, and structured data.
Preview the fix
Each fix preview is generated via the multi-provider LLM chain and cached for an hour, so you see exactly what will change before applying.
Apply (single or batch)
Apply one fix, or use fix-all to pre-generate every preview in parallel then apply them sequentially.
Undo
Revert any applied fix to its original state.
A connected domain is required to deploy fixes
Because fixes deploy as edge modifications to KV, the batch aborts with a no-domain-connected error unless you have a domain whose DNS status is connected. Connect and verify a domain before applying optimization fixes.
Content Refresh
TruIntel keeps published content current by refreshing it on a monthly schedule. Refresh rewrites only the outdated sections, updates meta, and adds new internal links — it is not a full rewrite. The refresh model is gemini-3.1-flash-lite.
- Runs monthly on the 1st at 03:00 IST.
- A piece is eligible when it is over 3 months old, has new internal-link opportunities (pages crawled after its last refresh), or was published over 30 days ago and never refreshed.
- Refresh updates only outdated sections plus meta and internal links.
Refresh costs 1 credit per piece
Content refresh is automatic in scheduling, but it is not free. It deducts 1 credit per refreshed piece, and a piece is skipped if your org has fewer than 1 credit remaining. Keep credits available if you want monthly refresh to run.
CMS Pages & Live-Site Tools
Beyond generation, editing, and publishing, the CMS includes a set of live-site management pages for a connected domain. All 16 CMS pages share the single Starter+ gate.
Dashboard
/cms/dashboardCMS home with scan status and a content overview.
Content Library
/cms/contentList of all generated content with its current status.
New Content
/cms/content/newExpress generation entry point (target keyword + content type + context).
Content Editor
/cms/content/:id/editTipTap editor with the live AI SEO score, AI-assist, and editable meta + schema.
Optimization
/cms/optimizationDetected issues with fix-preview, batch apply, and undo.
Onboarding
/cms/onboardingInitial CMS / domain setup flow.
Visual Page Editor
/cms/edit-pageSidebar-only live-page editor (Elements / SEO / History / Pages / Settings); edits deploy to KV and auto-publish.
Edit Pages
/cms/edit-pagesList of live pages on a connected domain that can be edited.
Robots.txt Editor
/cms/robots-txtEdit the robots.txt stored and served from edge KV.
Sitemap Editor
/cms/sitemapManage the auto-generated sitemap URL list.
CMS Competitors
/cms/competitorsCompetitor view scoped to the CMS module.
CMS Tasks
/cms/tasksContent tasks tied to insights and optimization.
Backlink Fixes
/cms/backlink-fixesBroken-backlink redirect manager that deploys redirect maps to Worker KV.
Credit History
/cms/creditsPer-action credit usage ledger.
Settings
/cms/settingsDomain and brand-template settings.
Upgrade
/cms/upgradePlan upgrade prompt for unlocking more credits/capacity.
Backend support comes from roughly 60 endpoints under /api/v1/cms (scan, content index, credits, content CRUD + calendar, SEO analysis, domains, publish + schedule, optimization, AI-assist, brand template, page editor, page SEO, robots.txt, and sitemap) plus the Content Studio brief flow under /api/v1/content (brief create/get/patch, paid draft, repurpose, opportunities, and calendar).
Outreach
NewWhat Off-Page Outreach Is
Off-Page Outreach is a human-in-the-loop backlink and earned-media workspace. It finds high-value link and AI-citation prospects, drafts one finished, ready-to-send email per prospect signed with your real identity, and tracks every prospect through an honest 5-stage CRM funnel. It does not auto-send email — sending is a one-click handoff to your own email client.
Surface prospects
Fuses three off-page signals — backlink gaps, AI-citation gaps, and broken links — into one ranked, evidence-tagged prospect list.
Draft one finished email
AI writes a complete, placeholder-free email per prospect, grounded in that prospect's evidence and signed with your real sender identity.
Track an honest funnel
Move each prospect through prospect to drafted to sent to replied to linked, with every stage clearly labelled "auto" or "you mark".
Auto-find contacts
A free background site-scrape finds a public contact email for each prospect, ranking role inboxes (editor, press, partnerships) highest.
Auto-confirm wins
A sent or replied prospect is auto-promoted to "linked" on the next refresh, once its backlink appears in your tracked backlink table.
Send from your inbox
A mailto handoff opens your own email client with the subject and body pre-filled, plus a "Copy all" button. No connected mailbox, no server-side send.
A manual CRM, not an auto-sender
Outreach is deliberately honest about its limits. Only the "drafted" and "linked" stages advance automatically; you mark "sent" and "replied" yourself. Connected-mailbox auto-send is planned for a future phase — until then, Outreach is a tracker with a mailto handoff.
Plans, Access & Cost
Off-Page Outreach is available on the Pro and Enterprise plans. Drafting and regenerating emails is completely free — it never spends AI credits. The only metered cost is the paid backlink-gap lookup during a refresh, which draws on your brand's SEO budget.
Free / Starter / Lite
Not available
- Outreach page is plan-gated
- Prospects still auto-generate at onboarding for paid brands only
- Upgrade to Pro to plan and track outreach
Pro
$479/mo
- Full Off-Page Outreach access
- Up to 3 brands, 5 competitors per brand
- AI-drafted emails (free, unlimited)
- SEO budget $5/brand/month for paid lookups
Enterprise
Custom
- Full Off-Page Outreach access
- Up to 4 brands, 5 competitors per brand
- AI-drafted emails (free, unlimited)
- Contact sales for custom limits
Drafting is free
Generating and regenerating an outreach email does NOT charge AI credits. It calls the utility-LLM text generator directly rather than the credit-metered content path, so you can rewrite a draft as many times as you like at no cost.
India GST
For accounts billed in India, 18% GST is added on top of the listed USD plan price.
Where It Lives & Routes
Outreach lives in the left sidebar under the Insights section, in the "Off-Page" group as "Off-Page Outreach". It is a two-page experience: a ranked prospect table and a single-prospect composer.
Off-Page Outreach (list)
/outreachRanked prospect table with source, evidence, score, funnel stage and a per-row action. "Find prospects" triggers a refresh.
Prospect composer (detail)
/outreach/:prospectIdFull-width single-column composer: sender identity, tone and angle controls, the AI email, the funnel stepper and the mailto send bar.
Deep-link from Sources
Opening /outreach?domain=<gap-domain> (for example from a "Draft outreach" button on the Sources gap table) finds the matching prospect and jumps straight to its composer. If that prospect has not been materialized yet, Outreach kicks one refresh and opens it as soon as it appears.
The Three Prospect Sources
A refresh fuses three off-page signals into one ranked list. Each prospect carries an evidence object — there is no prospect without a concrete grounding signal, which keeps the list (and the emails written from it) free of generic filler.
Backlink gap
A referring domain that links to a tracked competitor but not to you. Found via a paid DataForSEO backlink-gap lookup fanned across your competitors.
AI-citation gap
A domain that AI assistants cite in answers mentioning competitors but not you. A free read of your source-gap analysis — no paid lookup.
Broken-link reclaim
An external site that links to a 404 / broken page on YOUR site. High-intent: they already link to you, so it is an easy fix-and-reclaim ask.
| Source | UI label | Score basis |
|---|---|---|
| Backlink gap | Backlink gap (blue) | Estimated Domain Authority (or rank/10, capped at 100), plus +8 for each additional competitor the domain also links to |
| AI-citation gap | AI-citation gap (violet) | Opportunity score from the source-gap analysis |
| Broken-link reclaim | Broken link (orange) | Fixed 75.0 — high intent because they already link to your site |
Unlinked mentions are not surfaced yet
A fourth "unlinked mention" source (a site that names your brand without linking) exists in the data model and email angles but is not generated today — there is no collector for it. Only backlink-gap, AI-citation-gap, and broken-link prospects appear in your list.
Merge, rank and cap
When the same domain shows up from more than one signal, the highest-scoring source becomes the primary and the other signals are folded into the evidence as "also seen". The final list is ranked by score (highest first) and capped at 100 prospects per refresh.
| Fan-out cap | Value | Why |
|---|---|---|
| Competitors used for the gap | 5 | Top competitors by mention count |
| Domains pulled per competitor | 30 | Keeps the paid lookup affordable |
| Broken-link prospects | 25 | Caps the reclaim list |
| Total prospects per refresh | 100 | Hard cap on the merged, ranked list |
Who Never Gets Pitched
Outreach filters out domains that should never receive a pitch, both when building the list and again defensively in the UI. This keeps you from emailing yourself, your competitors, or unreachable infrastructure hosts.
- Non-targetable AI-infrastructure, search and CDN hosts (a denylist including vertexaisearch.cloud.google.com, google.com, amazonaws.com and similar) — flagged "Not targetable" with no email offered.
- Your own brand domain and its name-slug variants (.com / .in / .co.in / .net / .org / .ai / .io for slugs of four or more characters).
- Your tracked competitor domains, and any prospect whose evidence marks it as a competitor.
- Stale, untouched prospects that are now excluded are deleted on the next refresh — but manually-advanced prospects (anything past "prospect") are left untouched.
Manual progress is preserved
A refresh only deletes prospects still in the "prospect" stage that have become excluded. Once you advance a prospect (draft, mark sent, and so on), refreshes never overwrite or discard your work.
Surfacing Prospects: Onboarding & Refresh
Prospects arrive two ways: a one-time back-fill at onboarding so the page is never empty, and an on-demand "Find prospects" refresh you trigger yourself.
Onboarding back-fill
When your first AEO visibility check completes, a one-time job back-fills Outreach (alongside Opportunities and Directory) for paid brands only. It runs "empty-only" — it generates a surface just once, so later refreshes are a cheap no-op.
On-demand refresh
Click "Find prospects" on the list page to regenerate and upsert the prospect list. The list re-ranks, excluded stale prospects are cleaned up, and won backlinks are auto-promoted.
Contact enrichment
Two seconds after a refresh, a background task auto-finds public contact emails for prospects missing one. It never overwrites an address you typed.
Refresh cost & budget
Only the backlink-gap part of a refresh costs money (a paid DataForSEO lookup). It is checked against your SEO/SERP per-brand monthly budget — $5 per brand per month by default — BEFORE running. The AI-citation gap (a DB read) and broken-link reclaim (cached data) are free. A refresh is also skipped entirely when a brand has no competitors or no domain set.
Budget exhausted? 429
If the backlink-gap lookup would exceed your brand's monthly SEO budget, the refresh returns a 429 ("plan limit reached") and no paid work runs. The AI-citation and broken-link prospects still refresh because they are free.
The AI-Drafted Email
Outreach writes one complete, ready-to-send email per prospect — not a template menu. The draft is grounded in that specific prospect's domain, gap reason and the competitor it links to, and is signed with your real identity. There are no placeholders or square brackets to fill in.
One finished email
A real subject and body, 110-190 words, value-first and warm. Send it as-is; the subject is never repeated inside the body.
Per-prospect grounding
The prompt is built from that prospect's evidence (the gap reason, the competitor, the citing query), not your generic brand pitch — so every email is specific.
Signed with your identity
Pulls your name, job title, phone, email and custom signature from your profile. A verbatim custom signature wins; otherwise a name/title/email/phone block; otherwise a "The {brand} Team" fallback.
No placeholders, ever
A hard rule forbids [Name], [Company], [DATE], [LINK] and the like. The recipient greeting is inferred from the domain (surferseo.com to "Surfer SEO").
Tone & angle controls
| Control | Options |
|---|---|
| Tone (5) | Professional (default), Friendly, Concise, Persuasive, Casual |
| Angle (6) | Broken-link replacement, Add to resource page, Convert unlinked mention, Guest-post pitch, Better than competitor, Cite our data / study |
The angle is auto-seeded from the prospect's source — backlink-gap defaults to "add to resource page", AI-citation gap to "cite our data", and broken link to "broken-link replacement". Regenerating re-runs the draft with your chosen tone and angle, keeping the existing draft visible while the new one is written.
Resilient model chain — and free
Drafts use a provider-agnostic fallback chain — Anthropic (claude-haiku-4-5), then Gemini (gemini-3.1-flash-lite), then OpenAI (gpt-5.4-mini), then OpenRouter (moonshotai/kimi-k2.5) — so a single provider outage never blocks drafting. Generation never charges credits.
Your Sender Identity
Because every email is signed with a real person, you set up your sender identity once and it is reused for every draft across all of your brands. Generation is blocked until you provide at least a name and job title.
| Field | Used for |
|---|---|
| Name | The sign-off and the greeting from-line (required) |
| Job title | The sign-off block (required before you can generate) |
| Phone | Optional contact line in the signature |
| Reply-to identity in the signature | |
| Email signature | Optional verbatim custom sign-off that overrides the composed block |
Set it once, manage it in Settings
The composer shows an inline form to capture your name, job title and phone the first time. These are saved to your user profile and shown as a "Sending as {name} · {job title} · {email}" summary with an Edit toggle. You can change them anytime under Settings, Profile — the change applies to every brand.
Generate is blocked when
You have not added your name and job title ("Add your sender details first"), or the prospect is a non-targetable infrastructure host (no email is offered).
The Honest CRM Funnel
Each prospect moves through a five-stage funnel (plus a dismissed state). The UI labels every stage "auto" or "you mark" so you always know what the system tracks versus what you record yourself.
| Stage | Mode | How it advances |
|---|---|---|
| Prospect | start | Initial state when the prospect is created |
| Drafted | auto | Set automatically the moment you click Generate |
| Sent | you mark | "Mark sent" — requires a contact email, stamps the sent time, and opens your email client via the mailto handoff |
| Replied | you mark | "Mark replied" — you record it when the recipient responds |
| Linked | auto | Auto-detected when the backlink goes live; you can also "Mark linked" manually |
| Dismissed | — | "Dismiss" / "Restore" — removed from the active funnel, never miscounted as a live prospect |
Why "you mark" exists
Without a connected mailbox, TruIntel cannot see that you actually sent or got a reply, so it asks you to mark those stages honestly rather than faking automation. When mailbox connection ships in a future phase, "sent" and "replied" will graduate to automatic.
Sending the email
Sending is a mailto handoff: "Open in email" launches your own mail client with the recipient, subject and body pre-filled, and "Copy all" puts the subject and body on your clipboard. Nothing is sent from TruIntel's servers — you stay in full control of your own outbox.
Auto-Promotion When the Link Goes Live
The one funnel transition that can be detected without a connected mailbox is the win itself. When a sent or replied prospect's backlink later appears in your tracked backlinks table, Outreach auto-promotes it to "linked" on the next refresh.
- Runs on every refresh as a best-effort step — a failure never discards the prospect upsert.
- Matches on brand and normalized domain: exact host first, then a registrable-parent fallback so a bare-host prospect can claim a subdomain backlink.
- Guards against mis-matches (for example notjoyalukkas.com does not claim joyalukkas.com).
- Records the matched backlink id on the prospect so the won link is tracked alongside your Backlinks data.
Ties into the Backlinks tracker
Because auto-promotion reads from the same indexed-backlinks table that powers the Backlinks feature, a link you earn through outreach is automatically reflected as a "linked" win without you marking it.
Contact-Email Auto-Find
A free, best-effort background scrape tries to find a public contact email for each prospect so you do not have to. It uses TruIntel's own crawler — no paid vendor — and a missing email is a normal outcome you can simply type in.
Scrape a few pages
Checks the cited URL first, then common contact paths (/contact, /contact-us, /about, /about-us, /get-in-touch, /write-for-us). Capped at 4 pages, 4-second timeout each.
Extract addresses
Pulls emails from mailto: links and plain-text regex on each page.
Rank candidates
Same-site domain +4, role inbox (editor, press, pr, media, contact, hello, info, partnerships, outreach) +3, free mailbox (gmail, yahoo, outlook) -1. Rejects noreply, postmaster, abuse, unsubscribe, disposable and placeholder addresses. Ties break to the shorter local-part; exits early at score 6+.
When and how much it runs
The enrichment task runs after every refresh (and after onboarding back-fill), filling up to 60 prospects at a time, 8 at once. It only touches prospects missing an email in the "prospect" or "drafted" stage — it never overwrites an address you entered or touches sent / replied / linked prospects.
Always editable
The composer keeps the contact email in sync if auto-find lands one later, and the helper text reminds you: "We try to auto-find it from their contact page — edit if needed."
Pages, Columns & States
Both Outreach pages design every render state — no brand, loading, error, empty and content — so the experience is clear whether your list is full or still being built.
List page
| Column | Shows |
|---|---|
| Prospect | Favicon plus the linked domain |
| Source | Colored source pill (backlink gap / AI-citation gap / broken link) |
| Why | The top evidence line explaining the opportunity |
| Score | Opportunity score pill (0-100) |
| Stage | Funnel stage pill, with a "Draft ready" badge when an email exists |
| Action | Adapts to funnel position: Generate draft / Review draft / Track / View / "Not targetable" (disabled) |
The table auto-paginates at 15 rows per page, defaults to score-descending sort, and shows a "Updated {relative time}" freshness label. "Find prospects" triggers a refresh; the empty state offers the same CTA.
Detail (composer) page
A full-width single-column composer: header (favicon, linked domain, source and score pills), the honest funnel stepper, and then either a "Set up your email" card (sender identity, tone, angle, contact email and Generate) for un-drafted prospects, or the AI email itself (subject and body with copy links, inline regenerate, the mailto send bar and the funnel action buttons) once drafted. States cover no-brand, skeleton, error, not-found and content.
API Reference
All Outreach endpoints sit under /api/v1/backlinks/{brand_id}/outreach and require a paid plan. There is no single-prospect GET — the composer reads its prospect from the cached list.
| Method & path | Purpose |
|---|---|
| GET /prospects | List ranked prospects (score desc). Params: status, limit (1-500, default 100), offset (>=0). Returns data, total, limit, offset. |
| POST /refresh | Regenerate and upsert the list, then dispatch contact enrichment. 429 when the SEO budget is exhausted. |
| POST /{prospect_id}/draft | AI-draft the email. Body: tone, campaign_angle. 502 on generation failure, 404 if the prospect is missing. |
| PATCH /{prospect_id} | Update funnel status (and optional contact email). 400 on an invalid status, 404 if the prospect is missing. |
Side effects of a status update
Marking "sent" stamps the sent time (if unset) and stores the contact email; marking "linked" best-effort links the matching tracked backlink; any status update stores a supplied contact email.
Data model
Each prospect (the outreach_prospects table) stores its domain, url, source, score (0-100), a JSON evidence object, contact email, draft subject and body, status, an optional linked-backlink id, the sent time and timestamps — indexed by (brand, status) and (brand, score desc).
Directory Submissions
NewOverview
Directory & Citation Submissions is the earned half of off-page AEO. It surfaces a ranked, evidence-backed list of directory, review, and reference sites your brand should get listed on so AI answer engines — ChatGPT, Claude, Gemini, Perplexity, and Google AI Overview — start citing you in their answers. Each target explains why it matters, scores the opportunity, and can produce paste-ready listing copy on demand.
In the app the page is titled "Directory & Citations" and lives at /directory, inside the Insights nav section under the "Off-Page" group. Its sibling feature is Off-Page Outreach (the earned link / PR half) — Directory is the "get-listed-where-AI-looks" half. Both back-fill automatically once after your first AEO visibility check.
It is a manual CRM-style tracker
TruIntel finds the directories and drafts the copy, but you submit the listing on the directory's own site and then mark the status by hand. There is no automatic detection that a listing went live — advancing a target through the funnel only updates your tracking status, it does not submit anything for you.
Grounded targets
The primary source is your AEO source gap — high-authority sites where AI cites your competitors but not you. Every target is backed by real visibility evidence, not a generic list.
Opportunity score
Each directory carries a 0–100 opportunity score so you work the highest-leverage citation gaps first. Grounded gap entries always outrank cold-start catalog suggestions.
AI-drafted listing copy
Generate paste-ready submission copy for any directory — title, best-fit category, short and full descriptions, plus approval tips. Free, no credits charged.
Listing funnel
Track each target through not listed → submitted → pending → live. Refreshing the list preserves any status you have already advanced by hand.
Plan Access & Limits
In the product, the /directory page is a Pro+ feature — the route is gated to Pro and Enterprise. There is no per-plan quota on directory submissions beyond a hard cap of 100 targets per brand, and the feature carries no DataForSEO or credit cost: both refreshing the list and generating listing copy are completely free.
| Surface | Plans allowed | Notes |
|---|---|---|
| Directory page (/directory) | Pro, Enterprise | The UI route is gated to Pro+, blocking Starter and Lite even though the raw API allows them. |
| Directory API endpoints | Any paid plan | The backend requires a paid plan; Free / no-plan organizations receive a 403 PAID_PLAN_REQUIRED. |
| Auto-generate at onboarding | Any paid plan | The one-time back-fill runs for every non-Free plan; Free is skipped. |
No credits, no DataForSEO spend
Refreshing the directory list reads your existing AEO source-gap data (a free database read) plus a best-effort utility-LLM cold-start catalog. Listing-copy generation is also uncharged — it is not a credit-metered TruPlus generation. Nothing here touches your AI credit balance or any paid data vendor.
| Limit | Value |
|---|---|
| Max targets per brand | 100 rows (highest-scoring kept after each refresh) |
| Max cold-start catalog entries | Up to 20 per refresh, when the source gap is thin |
| Per-plan quota | None — only the 100-target cap applies |
| Cost | Free (no AI credits, no DataForSEO) |
How Targets Are Found
When you click "Find directories" (or when onboarding back-fills the page), TruIntel blends two sources into one ranked, de-duplicated list. The grounded source-gap data always wins, with the cold-start catalog filling in when your gap data is thin.
Source A — AEO source gap (primary, grounded)
This is the trustworthy source. It reuses the source-gap analysis from your AEO visibility check: high-authority domains that AI platforms cite in answers where your competitors are mentioned but your brand is not. Only earned-type sites are kept — editorial, community (UGC), reference, institutional, marketplace, and other — while corporate and competitor domains are dropped. The opportunity score for each target is the gap's own score (capped at 100), and the evidence records the surfacing query, which competitors are cited there, citation counts, and which platforms cite it.
Always present at onboarding
Source-gap data is written by the AEO visibility check, so by the time directories are generated after your first check, the grounded targets are guaranteed to be available.
Source B — Industry catalog (cold-start, best-effort)
When the source gap is thin or empty, a utility LLM is asked for up to 20 well-known directory, review, and reference sites for your brand's industry and country. The prompt steers a deliberate mix: a general business profile (e.g. Google Business Profile), a consumer or local review site (e.g. Yelp or the local equivalent), software / industry review sites (e.g. G2, Capterra, Trustpilot — only when relevant), reference and knowledge bases (e.g. Wikipedia, Wikidata), and vertical directories specific to your niche.
- Catalog entries get a fixed opportunity score of 40, so grounded source-gap entries always rank above them.
- The LLM call runs through the provider-agnostic fallback chain (Anthropic-first), so a single provider outage does not break the refresh.
- The whole catalog step is wrapped so that even a total LLM outage never fails the refresh — you still get your grounded targets.
Merge, rank, and upsert
Merge
Grounded source-gap targets are merged first and win any same-domain collision. The losing entry's reason is folded under evidence so no grounding is lost, and a real submission URL from the loser is preserved.
Exclude
Non-targetable hosts, your own domain, and your tracked competitors' domains are removed (see Why some sites never appear).
Flag already-cited
Domains that already cite you are marked with an "Already cited" flag — informational only, they are not excluded.
Rank and cap
Targets are sorted by opportunity score (descending) and capped at the top 100 per brand.
Upsert
Targets are de-duplicated by domain and saved without disturbing your tracking progress (see Refresh behavior).
Category Taxonomy
Every target is tagged with a category derived from its domain type. These categories appear in the Category column and detail modal so you can tell at a glance what kind of site you are dealing with.
| Category label | What it covers |
|---|---|
| Editorial | Editorial and publisher sites that publish reviews, roundups, or coverage. |
| Reference | Reference and knowledge bases such as Wikipedia or Wikidata. |
| Marketplace | Marketplaces and software review platforms (e.g. G2, Capterra). |
| Institutional | Institutional, governmental, or association directories. |
| Community | User-generated-content and community sites (forums, Q&A, social communities). |
| Corporate | Corporate sites (rarely shown as a target; corporate-type gap rows are dropped). |
| Directory | The default label for general or uncategorized directory sites. |
AI-Drafted Listing Copy
For any target you can generate paste-ready listing copy tailored to that one directory's submission form. The draft is grounded in your brand profile, voice, the directory's category, the surfacing query, and only the competitors cited on that specific directory. It is plain text, output in a fixed order so you can copy each field straight into the directory's form.
- Listing title — your business name plus an optional 3–6 word descriptor.
- Best-fit category — the single best category to pick on that directory.
- Short description — a summary / tagline field, kept to 160 characters or fewer.
- Full description — 70–110 words written in your brand's voice.
- Submission tips — 2–3 practical tips to improve approval and citation odds.
Free and anti-hallucination guarded
Generating listing copy is free — it does not deduct any AI credits. The prompt forbids inventing facts, metrics, addresses, phone numbers, prices, or awards, so the draft stays grounded in your real brand data. You can pass an optional tone or extra context, and regenerate as many times as you like.
The generated copy is stored on the target itself (alongside a generated-at timestamp), so once drafted it persists. In the detail modal you can Copy it, Get it for the first time, or Regenerate. If the LLM call fails, the request returns a 502 GENERATION_FAILED and nothing is saved.
The Listing Funnel
Each target moves through a four-stage manual funnel. You advance it by hand as you actually submit the listing on the directory's own site — no scheduled job ever changes the status for you.
| Status | Meaning | Side effect |
|---|---|---|
| Not listed | The default — you have not submitted a listing here yet. | None. |
| Submitted | You have filled out and submitted the listing on the directory. | Stamps a submitted-at timestamp the first time. |
| Pending | The directory is reviewing or has not yet published your listing. | None. |
| Live | Your listing is published and live on the directory. | Stamps an approved-at timestamp and stores the live listing URL when you supply one. |
You submit; TruIntel tracks
Advancing a target to "Live" does not publish anything — it only records your tracking status. TruIntel never auto-submits a listing and never auto-detects that a listing went live; you mark each step yourself after acting on the directory's own site.
In the UI the funnel is reinforced by a stat strip with a count per status (Not listed / Submitted / Pending / Live) that you can click to filter, plus colored status pills and "Mark submitted / Mark pending / Mark live" buttons on each row and in the detail modal. Passing an invalid status returns a 400 VALIDATION_ERROR.
Working With the Page
The Directory & Citations page is built around a single ranked opportunities table with a detail modal. Here is what each part does.
Find directories
The primary header button re-derives the list (blending source gap + catalog, then re-ranking). An "Updated … ago" label shows freshness.
Status filters
A stat strip and filter chips let you slice the table by funnel status. Counts update live and clicking a count filters the table.
Opportunities table
Columns: Directory (favicon, name, domain link), Category, AEO signal, Opportunity score (sortable), Competitors, Status (sortable), and Actions. Paginates at 20 rows.
Detail modal
"Why this directory" (reason, surfacing query, platforms cited, competitors listed, whether you are already cited), submit / listing links, the listing-copy block, and funnel buttons.
The AEO signal pill
The AEO signal column tells you how strong the evidence is for each target: "Already cited" means AI already cites you there, "Cited by AI·N" means N competitors are cited there, and "Recommended" marks a cold-start catalog suggestion with no direct gap evidence yet.
Render states
- Loading skeleton while the list is being fetched.
- Error state with a Retry button if the fetch fails.
- Empty state ("No directories yet") with a "Find directories" call to action.
- Content state with the ranked table.
- Filtered-empty state when a status filter matches nothing.
Score color coding
Opportunity scores are color-coded: 70 and above render as success (green), 40–69 as warning (amber), and below 40 as neutral. Cold-start catalog entries sit at exactly 40, so they appear amber until grounded evidence raises them.
Why Some Sites Never Appear
Several classes of domain are filtered out of the target list on every refresh. This is intentional — listing on these would not earn you AI citations, or would be impossible, so they are removed to keep the list actionable.
| Excluded | Why |
|---|---|
| Mega-platforms & AI infrastructure | Hosts like Google, Bing, DuckDuckGo, Yahoo, YouTube, and Facebook (plus AI-infra hosts and their subdomains) are not targetable directory listings. |
| Your own domain | Your brand's domain and close name-domain variants are removed so you do not "submit" to yourself. |
| Your tracked competitors | Domains owned by your active tracked competitors are dropped — you are looking for neutral citation sites, not competitor properties. |
| Competitor-type gap rows | Any source-gap entry whose domain type is "competitor" is excluded. |
Stale targets are cleaned up
On each refresh, any not-listed target that has since become excluded (for example a domain you just added as a competitor) is purged from the list. Targets you have already advanced in the funnel are never purged.
Auto-Population & Refresh Behavior
You rarely have to think about populating this page — it back-fills itself once after onboarding, and every refresh afterward is careful never to overwrite your manual progress.
One-time back-fill at onboarding
After your first successful AEO visibility check, a background task (growth.generate_initial_surfaces) back-fills the three off-page growth surfaces at once — Content Opportunities, Off-Page Outreach, and Directory & Citations — so they arrive populated instead of empty. It is dispatched roughly 45 seconds after the visibility check completes (to let source-gap writes settle).
- Empty-only: each surface generates only if the brand has no rows yet, so it runs once at onboarding and is a cheap no-op afterward.
- Paid plans only: Free / no-plan organizations are skipped.
- Isolated: each surface runs in its own try/except, so one failure never affects the others.
- Controlled by a kill switch (GROWTH_AUTOGEN_ENABLED, on by default) and a per-brand lock that prevents double-generation.
What a manual refresh preserves
| Row state at refresh | What happens |
|---|---|
| New target | Inserted as Not listed with its score and evidence. |
| Existing, still Not listed | Score, evidence, name, and URLs refreshed in place. |
| Manually advanced (Submitted / Pending / Live) | Status is kept untouched — only the evidence is refreshed. |
| Now excluded & still Not listed | Purged from the list. |
Refresh is safe to run anytime
Because manually-advanced rows keep their status and only their evidence is refreshed, you can click "Find directories" whenever you want fresh targets without losing track of where you are in the funnel.
Routes & Endpoints
The feature is a single front-end page backed by four brand-scoped API endpoints. All endpoints require a paid plan and verify that the brand belongs to your organization (returning 404 if not).
Directory & Citations
/directoryThe product page (Insights → Off-Page). Pro+ in the UI.
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /backlinks/{brand_id}/directories/submissions | List ranked targets (filter by status, paginate; ordered by opportunity score then recency). |
| POST | /backlinks/{brand_id}/directories/refresh | Re-derive the list (blend source gap + catalog, exclude, rank, upsert) and return it. |
| POST | /backlinks/{brand_id}/directories/{id}/generate-listing-copy | AI-draft listing copy for one target (optional tone / extra context). |
| PATCH | /backlinks/{brand_id}/directories/{id} | Advance the funnel status and optionally set the live listing URL. |
Related monitoring surfaces
Directory & Citations is the action surface for closing citation gaps. To see where you are already cited, use Citation & Source Tracking and the Sources view under AI Visibility — those monitor your citations, while Directory & Citations helps you earn new ones.
Traffic Classification
Overview
TruIntel's Traffic Classification system analyzes every visitor to your website and classifies them into one of four categories. This helps you understand your real traffic, identify AI agent visits, and filter malicious bots.
| Category | Description | Examples |
|---|---|---|
| HUMAN | Real human visitors browsing your website | Chrome/Safari users, mobile visitors |
| GOOD_AGENT | Legitimate AI agents and crawlers fetching content | ChatGPT browser, Claude, Perplexity, Googlebot, Bingbot |
| BAD_BOT | Malicious or unwanted automated traffic | Scrapers, spam bots, credential stuffers |
| SYNTHETIC | Traffic from automated tools that mimics human behavior | Headless browsers, automated testing, click fraud |
Classification Score
Classification runs through a deterministic, pure-Python pipeline (no LLM, no external API, evaluated in-process on every beacon). A visitor is matched against a sequence of layers, and only the visitors that fall through every rule-based layer reach the weighted Classification Score. The score (0–100) combines four signals:
Classification Score
- Behavior Analysis (40%) — mouse/touch events, natural-mouse and natural-scroll patterns, scroll/click/key activity, and time-on-page tiers (>5s / >15s / >30s). Passive readers stay human thanks to a forgiving base score; zero time-on-page and impossible event rates (>50 events/sec) are penalized.
- Browser Fingerprint (30%) — realistic screen dimensions per device type, valid color depth, timezone, language(s), WebGL vendor/renderer (real GPU vendors), hardware concurrency, and device memory.
- User Agent (20%) — browser signature validity, platform consistency, realistic version numbers, and penalties for missing or suspicious tokens (bot, crawler, spider, curl, python, wget, axios, …).
- Network Analysis (10%) — presence and validity of a referrer (search-engine and social referrers earn bonuses) and a valid connection type.
Score bands and confidence
The weighted total maps to a classification band. Note that score and confidence are different numbers: a real human only needs a score of 60 or above, while confidence (how sure the engine is) rises separately toward 95.
| Total score | Classification | Action | Confidence |
|---|---|---|---|
| 60–100 | HUMAN | ALLOWED | min(95, 70 + (total − 60)) |
| 40–59 (borderline) | SYNTHETIC if ≥3 synthetic indicators, else HUMAN | FLAGGED / ALLOWED | 75 / 65 |
| 20–39 | SYNTHETIC | FLAGGED | 80 |
| Below 20 | BAD_BOT | BLOCKED | 85 |
Synthetic indicators (any 3 flip a borderline 40–59 score to SYNTHETIC): excessive event rate (>30/sec), default headless resolution (exactly 800×600), headless fingerprint (no WebGL + no device memory + hardware concurrency ≤ 1), no interaction despite >30s on page, cookies disabled with no Do Not Track signal, and webdriver detected in the user-agent.
Labels are advisory, never enforcement
Actions like BLOCKED and FLAGGED are labels recorded in your traffic log — TruIntel never blocks or alters a visitor request. The optional server middleware always passes the request through (it calls next() immediately), and custom blacklist rules only change the recorded classification label. Use them for visibility and reporting, not as a firewall.
Classification Pipeline
Before any visitor reaches the weighted score above, the classifier evaluates a sequence of rule-based layers. Layers are checked in order and the first match wins, so the deterministic checks (your own rules, then the known-agent and known-bot catalogs) take precedence over heuristic scoring.
| Layer | Trigger | Result | Action | Confidence |
|---|---|---|---|---|
| 0 — Custom whitelist | Your brand whitelist regex matches the user-agent | GOOD_AGENT | WHITELISTED | 98 |
| 1 — Custom blacklist | Your brand blacklist regex matches the user-agent | BAD_BOT | BLOCKED | 98 |
| 2a — Known AI agent | UA matches an AI training / search / browsing / shopping agent | GOOD_AGENT | WHITELISTED | 95 |
| 2b — Other known agent | UA matches a search engine, social-preview, or monitoring agent | GOOD_AGENT | ALLOWED | 90 |
| 3 — Known bad bot | UA matches a bad-bot pattern | BAD_BOT | BLOCKED or FLAGGED | 90 |
| 4 — Suspicious pattern | Headless / automation signature detected | SYNTHETIC | FLAGGED | 85 |
| 5 — Score-based | None of the above — falls through to the weighted score | HUMAN / SYNTHETIC / BAD_BOT | Varies | 65–95 |
In-app browser guard
When a full real browser is used by a social-preview agent (for example a LinkedIn or WhatsApp in-app browser opening your page), the agent match is skipped and the visit falls through to score-based classification — so it is treated as a human, not a bot. Search-engine and monitoring agents (Googlebot WRS, Lighthouse) are deliberately NOT skipped because they legitimately render pages with browser-like user-agents.
Pattern Catalog
The known-agent and known-bot catalogs power layers 2 and 3, and you can browse them in the dashboard Pattern Catalog (paid plans). They are bundled in the product — there is no external lookup at classification time.
| Catalog | Entries | Categories | Examples |
|---|---|---|---|
| Known AI agents | 93 | 7 (ai_training, ai_search, ai_browsing, shopping_agent, search_engine, social_preview, monitoring) | GPTBot, ClaudeBot, PerplexityBot, Gemini-DeepResearch, Googlebot, Bingbot, Slackbot, Twitterbot, Lighthouse |
| Known bad bots | 55 | 6 (scraper, scanner, attack_tool, http_client, spam, fake) | SemrushBot, AhrefsBot, Scrapy, Nikto, sqlmap, python-requests, XRumer, HeadlessChrome |
Each bad bot carries a severity (low / medium / high / critical) and a default action of BLOCK or FLAG — both surfaced in the Threat Analysis view. GOOD_AGENT is broader than just AI assistants: it also covers traditional search engines (Googlebot, Bingbot), social-preview bots (Slackbot, Twitterbot, LinkedInBot), and uptime monitoring (Lighthouse).
Custom rules
Paid plans can add per-brand whitelist and blacklist rules from the Rules Manager. Each rule is a regex/substring pattern matched against the user-agent; a whitelist hit forces GOOD_AGENT / WHITELISTED and a blacklist hit forces BAD_BOT / BLOCKED, both at confidence 98 (the highest, ahead of the bundled catalogs). Saving rules replaces the full rule set for the brand. As with all actions, blacklist rules only relabel a visit — they do not stop traffic on your site.
Installation
TruIntel uses a two-part tracking system for complete traffic intelligence. The client-side script runs in the browser to detect real humans, headless browsers, and browser-based bots. The server-side middleware intercepts AI crawlers (GPTBot, ClaudeBot, PerplexityBot, etc.) that never execute JavaScript. Deploy both for the most accurate classification.
| Client-Side Script | Server-Side Middleware | |
|---|---|---|
| Runs in | Browser (visitor's device) | Your server (every HTTP request) |
| Detects | Humans, headless browsers, browser bots | AI crawlers, search bots, social preview bots |
| Method | Behavior analysis + browser fingerprint | User-Agent string matching + heuristics |
| Payload size | ~4 KB gzipped, async | Zero client impact (server-only) |
| Required? | Yes | Strongly recommended |
| Endpoint | Handled by tracker.js automatically | POST /api/v1/i/e (fire-and-forget) |
Find your Brand ID
Open your TruIntel dashboard → Settings → Integrations. Copy the Brand ID shown under your brand. Each brand has a unique ID.
Install the client-side script
Choose your framework below and paste the snippet into your site. The config object (window.TruIntel) must load before the tracker script. Works on every page automatically.
Add server-side middleware (strongly recommended)
Install middleware to catch AI crawlers that skip JavaScript. Without this, visits from GPTBot, ClaudeBot, PerplexityBot, and 50+ other AI agents will be invisible.
Verify installation
Visit your website in a browser, then check the Traffic page in TruIntel. Your visit should appear within 10 seconds. Test server-side with the curl command in the Verification section below.
Replace YOUR_BRAND_ID
Every code snippet on this page uses YOUR_BRAND_ID as a placeholder. Replace it with your actual Brand ID from Settings → Integrations before deploying. Using the wrong ID means traffic will not appear in your dashboard.
Client-Side Script
The client-side script classifies visitors using behavior analysis (mouse movements, scroll patterns, click timing), browser fingerprinting (canvas, WebGL, fonts, timezone), and user-agent parsing. It loads asynchronously, never blocks rendering, and sends one beacon per session in fire-and-forget mode. It sets two first-party cookies (no cross-site tracking), honors Do Not Track as a hard opt-out, and the traffic classifier itself collects no personally identifiable information. Note that the same script also includes optional lead-capture, which does read submitted contact-form fields — see Privacy & Performance below.
| Signal Collected | Purpose | PII? |
|---|---|---|
| Mouse movement patterns | Distinguish humans from headless browsers | No |
| Scroll depth & timing | Detect automated scroll behavior | No |
| Session ID (random UUID) | Group pageviews into sessions | No |
| Visitor ID (random UUID) | Identify returning visitors (no cross-site tracking) | No |
| Browser fingerprint (canvas hash) | Detect headless/spoofed browsers | No |
| Page URL & referrer | Attribute traffic sources | No |
| Screen resolution & timezone | Detect datacenter-based bots | No |
| User-agent string | Identify browser/OS and known bots | No |
Performance
The tracker script is ~4 KB gzipped, loads with async (non-blocking), and the collect endpoint rate-limits to 30 requests/minute per IP. It has zero impact on your Core Web Vitals (LCP, FID, CLS). It sets only first-party cookies on your own domain (a 365-day visitor ID and a ~30-minute session ID) — no third-party or cross-site tracking.
HTML
Add this snippet inside the <head> tag on every page you want to track. Both script tags are required — the first sets the config, the second loads the tracker.
<!-- TruIntel Traffic Tracking -->
<script>
window.TruIntel = {
brandId: 'YOUR_BRAND_ID',
apiUrl: 'https://api.truintel.ai/api/v1'
};
</script>
<script async src="https://api.truintel.ai/api/v1/traffic/tracker.js"></script>React
Create a tracker component and render it once at the root of your app (App.tsx or main layout). The cleanup function removes the script on unmount to prevent duplicates in development mode.
import { useEffect } from 'react';
export function TruIntelTracker() {
useEffect(() => {
window.TruIntel = {
brandId: 'YOUR_BRAND_ID',
apiUrl: 'https://api.truintel.ai/api/v1'
};
const s = document.createElement('script');
s.async = true;
s.src = 'https://api.truintel.ai/api/v1/traffic/tracker.js';
document.head.appendChild(s);
return () => { document.head.removeChild(s); };
}, []);
return null;
}
// Usage in App.tsx:
// import { TruIntelTracker } from './TruIntelTracker';
// function App() { return <><TruIntelTracker /><Router>...</Router></>; }Next.js
Use the Next.js Script component for optimized loading. The config must use beforeInteractive to ensure it loads before the tracker. Works with both App Router (app/layout.tsx) and Pages Router (_app.tsx).
import Script from 'next/script';
export default function RootLayout({ children }) {
return (
<html>
<head>
{/* Config MUST load before tracker.js */}
<Script id="truintel-config" strategy="beforeInteractive"
dangerouslySetInnerHTML={{
__html: `window.TruIntel={brandId:'YOUR_BRAND_ID',apiUrl:'https://api.truintel.ai/api/v1'};`,
}} />
<Script src="https://api.truintel.ai/api/v1/traffic/tracker.js"
strategy="afterInteractive" />
</head>
<body>{children}</body>
</html>
);
}Vue
Use lifecycle hooks to inject the script in your root App.vue or main layout component. The script is cleaned up on component unmount.
<script setup>
import { onMounted, onUnmounted } from 'vue';
let script = null;
onMounted(() => {
window.TruIntel = {
brandId: 'YOUR_BRAND_ID',
apiUrl: 'https://api.truintel.ai/api/v1'
};
script = document.createElement('script');
script.async = true;
script.src = 'https://api.truintel.ai/api/v1/traffic/tracker.js';
document.head.appendChild(script);
});
onUnmounted(() => {
if (script) document.head.removeChild(script);
});
</script>Nuxt
Add to your nuxt.config.ts to inject the script globally on every page. Nuxt handles the script loading order automatically.
export default defineNuxtConfig({
app: {
head: {
script: [
{
innerHTML: "window.TruIntel={brandId:'YOUR_BRAND_ID',apiUrl:'https://api.truintel.ai/api/v1'};",
},
{
src: 'https://api.truintel.ai/api/v1/traffic/tracker.js',
async: true,
},
],
},
},
});Svelte / SvelteKit
For SvelteKit, add to src/app.html inside the <head> tag. Alternatively, use <svelte:head> in your root +layout.svelte for dynamic injection.
<svelte:head>
{@html `<script>window.TruIntel={brandId:'YOUR_BRAND_ID',apiUrl:'https://api.truintel.ai/api/v1'};</script>`}
{@html `<script async src="https://api.truintel.ai/api/v1/traffic/tracker.js"></script>`}
</svelte:head>Angular
Add the script tags directly to your src/index.html inside the <head> tag. This is the simplest approach and works for all Angular versions.
<!-- Inside <head> -->
<script>
window.TruIntel = {
brandId: 'YOUR_BRAND_ID',
apiUrl: 'https://api.truintel.ai/api/v1'
};
</script>
<script async src="https://api.truintel.ai/api/v1/traffic/tracker.js"></script>WordPress
Add to your theme's functions.php file. If you use a child theme, add it there instead so updates don't overwrite it. Alternatively, use a plugin like "Insert Headers and Footers" or "WPCode" to inject without editing theme files.
// Add to your theme's functions.php
function truintel_tracking_script() {
echo '<!-- TruIntel Traffic Tracking -->
<script>
window.TruIntel = {
brandId: "YOUR_BRAND_ID",
apiUrl: "https://api.truintel.ai/api/v1"
};
</script>
<script async src="https://api.truintel.ai/api/v1/traffic/tracker.js"></script>';
}
add_action('wp_head', 'truintel_tracking_script');Script Load Order
The config script (window.TruIntel = {...}) MUST load before tracker.js. If the tracker loads first, it will not find the config and silently fail. In frameworks like Next.js, use strategy="beforeInteractive" for the config and strategy="afterInteractive" for the tracker.
Single-Page Apps (SPA)
The tracker script automatically detects client-side route changes (pushState/replaceState and popstate events). No extra configuration is needed for React Router, Vue Router, Next.js App Router, or any other SPA routing library. Each route change is tracked as a new pageview.
Server-Side Middleware
AI crawlers like GPTBot, ClaudeBot, and PerplexityBot typically don't execute JavaScript — they fetch raw HTML and parse it directly. The client-side script alone cannot detect them. Server-side middleware inspects the User-Agent header on every incoming request and forwards matching bot traffic to TruIntel's classification API. The API call is fire-and-forget: it runs asynchronously and never blocks or slows down the response to the visitor.
Best practice: use both
The client-side script handles humans and browser-based bots (scoring via behavior + fingerprint). The server-side middleware catches headless AI crawlers (scoring via user-agent + network). Together they give you complete visibility. Without middleware, 30-60% of AI agent traffic is invisible.
Next.js Middleware
Create middleware.ts at the root of your Next.js project (next to package.json, not inside src/). The matcher config excludes static assets, images, and API routes from bot detection.
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
const BRAND_ID = 'YOUR_BRAND_ID';
const TRUINTEL_API = 'https://api.truintel.ai/api/v1/i/e';
// AI crawlers — expand from Pattern Catalog for 90+ patterns
const AI_CRAWLERS = [
'GPTBot','ChatGPT-User','OAI-SearchBot','ChatGPT-Agent',
'ClaudeBot','Claude-Web','Claude-SearchBot','anthropic-ai',
'Google-Extended','GoogleOther','Gemini-Deep-Research',
'bingbot','BingPreview','Copilot','AzureAI-SearchBot',
'PerplexityBot','Perplexity-User',
'Meta-ExternalAgent','Meta-ExternalFetcher',
'Applebot','Applebot-Extended',
'GrokBot','xAI-Grok','Bytespider',
'DeepseekBot','MistralAI-User','Amazonbot',
'CCBot','Diffbot','YouBot','FirecrawlAgent',
];
const OTHER_BOTS = [
'Googlebot','DuckDuckBot','YandexBot','Baiduspider',
'Slackbot','Twitterbot','LinkedInBot',
'facebookexternalhit','WhatsApp','Discordbot',
];
const ALL = [...AI_CRAWLERS, ...OTHER_BOTS];
function isBot(ua: string): boolean {
if (!ua) return true;
const lower = ua.toLowerCase();
if (ALL.some(p => lower.includes(p.toLowerCase()))) return true;
if (lower.includes('bot/') || lower.includes('spider/')
|| lower.includes('crawler/')) return true;
if (lower.includes('mozilla/5.0') && lower.includes('applewebkit')) return false;
return true;
}
export function middleware(req: NextRequest) {
const ua = req.headers.get('user-agent') || '';
if (isBot(ua)) {
const ip = req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() || '';
// Fire-and-forget — never blocks or slows the response
fetch(TRUINTEL_API, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-Forwarded-For': ip },
body: JSON.stringify({
brand_id: BRAND_ID,
page: req.nextUrl.pathname + req.nextUrl.search,
user_agent: ua,
referrer: req.headers.get('referer') || null,
is_new_session: true,
is_new_visitor: true,
}),
}).catch(() => {});
}
return NextResponse.next();
}
// Exclude static assets, images, and API routes
export const config = {
matcher: [
'/((?!_next/static|_next/image|favicon.ico|api/|.*\\.(svg|png|jpg|jpeg|gif|webp|ico|css|js|woff|woff2)$).*)',
],
};Express.js
Create a middleware file and register it with app.use(). Place it early in your middleware chain (before route handlers) so every request is checked.
const BRAND_ID = 'YOUR_BRAND_ID';
const TRUINTEL_API = 'https://api.truintel.ai/api/v1/i/e';
const AI_CRAWLERS = [
'GPTBot','ChatGPT-User','OAI-SearchBot','ChatGPT-Agent',
'ClaudeBot','Claude-Web','Claude-SearchBot','anthropic-ai',
'Google-Extended','GoogleOther','Gemini-Deep-Research',
'bingbot','BingPreview','Copilot',
'PerplexityBot','Meta-ExternalAgent','Applebot',
'GrokBot','Bytespider','DeepseekBot','Amazonbot',
'CCBot','Diffbot','YouBot','FirecrawlAgent',
];
const OTHER_BOTS = [
'Googlebot','DuckDuckBot','YandexBot','Slackbot',
'Twitterbot','LinkedInBot','facebookexternalhit','Discordbot',
];
const ALL = [...AI_CRAWLERS, ...OTHER_BOTS];
function isBot(ua) {
if (!ua) return true;
const lower = ua.toLowerCase();
if (ALL.some(p => lower.includes(p.toLowerCase()))) return true;
if (lower.includes('bot/') || lower.includes('spider/')
|| lower.includes('crawler/')) return true;
if (lower.includes('mozilla/5.0') && lower.includes('applewebkit')) return false;
return true;
}
function truintelMiddleware(req, res, next) {
const ua = req.headers['user-agent'] || '';
if (isBot(ua)) {
const ip = (req.headers['x-forwarded-for'] || '').split(',')[0].trim()
|| req.socket?.remoteAddress || '';
fetch(TRUINTEL_API, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-Forwarded-For': ip },
body: JSON.stringify({
brand_id: BRAND_ID,
page: req.originalUrl || req.url,
user_agent: ua,
referrer: req.headers['referer'] || null,
is_new_session: true,
is_new_visitor: true,
}),
}).catch(() => {});
}
next();
}
module.exports = truintelMiddleware;
// Usage: const truintel = require('./truintel-middleware');
// app.use(truintel);Django
Create a middleware class and add it to your MIDDLEWARE list in settings.py. The API call runs in a background thread so it never blocks request processing.
import threading, json, urllib.request
BRAND_ID = 'YOUR_BRAND_ID'
TRUINTEL_API = 'https://api.truintel.ai/api/v1/i/e'
AI_CRAWLERS = [
'GPTBot','ChatGPT-User','OAI-SearchBot','ChatGPT-Agent',
'ClaudeBot','Claude-Web','Claude-SearchBot','anthropic-ai',
'Google-Extended','GoogleOther','Gemini-Deep-Research',
'bingbot','BingPreview','Copilot',
'PerplexityBot','Meta-ExternalAgent','Applebot',
'GrokBot','Bytespider','DeepseekBot','Amazonbot',
'CCBot','Diffbot','YouBot','FirecrawlAgent',
]
OTHER_BOTS = [
'Googlebot','DuckDuckBot','YandexBot','Slackbot',
'Twitterbot','LinkedInBot','facebookexternalhit','Discordbot',
]
ALL_PATTERNS = AI_CRAWLERS + OTHER_BOTS
def is_bot(ua):
if not ua:
return True
lower = ua.lower()
if any(p.lower() in lower for p in ALL_PATTERNS):
return True
if any(x in lower for x in ['bot/', 'spider/', 'crawler/']):
return True
if 'mozilla/5.0' in lower and 'applewebkit' in lower:
return False
return True
def _send(data, ip):
try:
req = urllib.request.Request(
TRUINTEL_API, json.dumps(data).encode(),
headers={'Content-Type': 'application/json', 'X-Forwarded-For': ip},
method='POST'
)
urllib.request.urlopen(req, timeout=5)
except Exception:
pass
class TruIntelCrawlerMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
ua = request.META.get('HTTP_USER_AGENT', '')
if is_bot(ua):
forwarded = request.META.get('HTTP_X_FORWARDED_FOR', '')
ip = forwarded.split(',')[0].strip() if forwarded else request.META.get('REMOTE_ADDR', '')
data = {
'brand_id': BRAND_ID,
'page': request.get_full_path(),
'user_agent': ua,
'referrer': request.META.get('HTTP_REFERER'),
'is_new_session': True,
'is_new_visitor': True,
}
threading.Thread(target=_send, args=(data, ip), daemon=True).start()
return self.get_response(request)
# settings.py:
# MIDDLEWARE = ['yourapp.truintel_middleware.TruIntelCrawlerMiddleware', ...]FastAPI
Add middleware using Starlette's BaseHTTPMiddleware. The API call runs in a background thread to keep the async event loop unblocked.
import threading, json, urllib.request
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
BRAND_ID = 'YOUR_BRAND_ID'
TRUINTEL_API = 'https://api.truintel.ai/api/v1/i/e'
AI_CRAWLERS = [
'GPTBot','ChatGPT-User','OAI-SearchBot','ChatGPT-Agent',
'ClaudeBot','Claude-Web','Claude-SearchBot','anthropic-ai',
'Google-Extended','GoogleOther','Gemini-Deep-Research',
'PerplexityBot','Meta-ExternalAgent','Applebot',
'GrokBot','Bytespider','DeepseekBot','Amazonbot','CCBot',
]
OTHER_BOTS = [
'Googlebot','DuckDuckBot','YandexBot','Slackbot',
'Twitterbot','LinkedInBot','facebookexternalhit','Discordbot',
]
ALL_PATTERNS = AI_CRAWLERS + OTHER_BOTS
def _is_bot(ua):
if not ua:
return True
lower = ua.lower()
if any(p.lower() in lower for p in ALL_PATTERNS):
return True
if any(x in lower for x in ['bot/', 'spider/', 'crawler/']):
return True
if 'mozilla/5.0' in lower and 'applewebkit' in lower:
return False
return True
def _send(data, ip):
try:
req = urllib.request.Request(
TRUINTEL_API, json.dumps(data).encode(),
headers={'Content-Type': 'application/json', 'X-Forwarded-For': ip},
method='POST'
)
urllib.request.urlopen(req, timeout=5)
except Exception:
pass
class TruIntelMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
ua = request.headers.get('user-agent', '')
if _is_bot(ua):
forwarded = request.headers.get('x-forwarded-for', '')
ip = forwarded.split(',')[0].strip() if forwarded else (str(request.client.host) if request.client else '')
data = {
'brand_id': BRAND_ID,
'page': str(request.url.path) + ('?' + str(request.url.query) if request.url.query else ''),
'user_agent': ua,
'referrer': request.headers.get('referer'),
'is_new_session': True,
'is_new_visitor': True,
}
threading.Thread(target=_send, args=(data, ip), daemon=True).start()
return await call_next(request)
# main.py:
# from truintel_middleware import TruIntelMiddleware
# app.add_middleware(TruIntelMiddleware)Flask
Use Flask's before_request hook to check every incoming request. Import and call init_truintel(app) in your main app file.
import threading, json, urllib.request
from flask import request
BRAND_ID = 'YOUR_BRAND_ID'
TRUINTEL_API = 'https://api.truintel.ai/api/v1/i/e'
AI_CRAWLERS = [
'GPTBot','ChatGPT-User','OAI-SearchBot','ChatGPT-Agent',
'ClaudeBot','Claude-Web','Claude-SearchBot','anthropic-ai',
'Google-Extended','GoogleOther','Gemini-Deep-Research',
'PerplexityBot','Meta-ExternalAgent','Applebot',
'GrokBot','Bytespider','DeepseekBot','Amazonbot','CCBot',
]
OTHER_BOTS = [
'Googlebot','DuckDuckBot','YandexBot','Slackbot',
'Twitterbot','LinkedInBot','facebookexternalhit','Discordbot',
]
ALL_PATTERNS = AI_CRAWLERS + OTHER_BOTS
def _is_bot(ua):
if not ua:
return True
lower = ua.lower()
if any(p.lower() in lower for p in ALL_PATTERNS):
return True
if any(x in lower for x in ['bot/', 'spider/', 'crawler/']):
return True
if 'mozilla/5.0' in lower and 'applewebkit' in lower:
return False
return True
def _send(data, ip):
try:
req = urllib.request.Request(
TRUINTEL_API, json.dumps(data).encode(),
headers={'Content-Type': 'application/json', 'X-Forwarded-For': ip},
method='POST'
)
urllib.request.urlopen(req, timeout=5)
except Exception:
pass
def init_truintel(app):
@app.before_request
def track_bots():
ua = request.headers.get('User-Agent', '')
if _is_bot(ua):
forwarded = request.headers.get('X-Forwarded-For', '')
ip = forwarded.split(',')[0].strip() if forwarded else (request.remote_addr or '')
data = {
'brand_id': BRAND_ID,
'page': request.full_path,
'user_agent': ua,
'referrer': request.referrer,
'is_new_session': True,
'is_new_visitor': True,
}
threading.Thread(target=_send, args=(data, ip), daemon=True).start()
# app.py:
# from truintel_middleware import init_truintel
# init_truintel(app)Go (net/http)
Create an HTTP middleware handler. The API call runs in a goroutine so it never blocks the response. Wrap your router with TruIntelMiddleware(router).
package main
import (
"bytes"
"encoding/json"
"net/http"
"strings"
)
const brandID = "YOUR_BRAND_ID"
const truintelAPI = "https://api.truintel.ai/api/v1/i/e"
var aiCrawlers = []string{
"GPTBot","ChatGPT-User","OAI-SearchBot","ChatGPT-Agent",
"ClaudeBot","Claude-Web","Claude-SearchBot","anthropic-ai",
"Google-Extended","GoogleOther","Gemini-Deep-Research",
"PerplexityBot","Meta-ExternalAgent","Applebot",
"GrokBot","Bytespider","DeepseekBot","Amazonbot","CCBot",
}
var otherBots = []string{
"Googlebot","DuckDuckBot","YandexBot","Slackbot",
"Twitterbot","LinkedInBot","facebookexternalhit","Discordbot",
}
func isBot(ua string) bool {
if ua == "" { return true }
lower := strings.ToLower(ua)
for _, p := range append(aiCrawlers, otherBots...) {
if strings.Contains(lower, strings.ToLower(p)) { return true }
}
for _, x := range []string{"bot/", "spider/", "crawler/"} {
if strings.Contains(lower, x) { return true }
}
if strings.Contains(lower, "mozilla/5.0") && strings.Contains(lower, "applewebkit") {
return false
}
return true
}
func TruIntelMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ua := r.UserAgent()
if isBot(ua) {
ip := strings.Split(r.Header.Get("X-Forwarded-For"), ",")[0]
if ip == "" { ip = strings.Split(r.RemoteAddr, ":")[0] }
go func() {
body, _ := json.Marshal(map[string]interface{}{
"brand_id": brandID,
"page": r.URL.RequestURI(),
"user_agent": ua,
"referrer": r.Referer(),
"is_new_session": true, "is_new_visitor": true,
})
req, _ := http.NewRequest("POST", truintelAPI, bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Forwarded-For", ip)
http.DefaultClient.Do(req)
}()
}
next.ServeHTTP(w, r)
})
}
// Usage: http.ListenAndServe(":8080", TruIntelMiddleware(router))AI Crawler Coverage — 93 agents + 55 bad bots
The examples above show the most common patterns. TruIntel's Pattern Catalog includes 93 known-agent signatures (across OpenAI, Anthropic, Google, Microsoft, Meta, Apple, xAI/Grok, ByteDance, Perplexity, DeepSeek, Mistral, Amazon, Cohere, Common Crawl, dozens of AI search engines, traditional search engines and social-preview bots) plus 55 known bad bots. For the complete and always-updated list, use the one-click copy in your TruIntel dashboard (Traffic → Install Script) or browse the Pattern Catalog.
Existing middleware?
If you already have middleware (auth, CORS, logging), just add the TruIntel middleware to your chain. It calls next() immediately after the fire-and-forget API call — it never modifies the request or response. Order doesn't matter since it's non-blocking.
Verification & Testing
After installing both scripts, verify that traffic data is flowing correctly into your TruIntel dashboard.
Verify client-side
Open your website in a browser. Open DevTools → Network tab and filter by "tracker.js". You should see the script load successfully (200 status). Then filter by "/i/" to see the tracking beacon requests.
Check the dashboard
Go to TruIntel → Traffic. Your visit should appear in the Live Traffic Feed within 10 seconds, classified as HUMAN with a high confidence score.
Test server-side with curl
Run the curl command below from your terminal. This simulates a GPTBot visit. Check the Traffic page — you should see it classified as GOOD_AGENT within a few seconds.
Test edge cases
Visit from a mobile device, try incognito mode, and verify that each visit appears correctly classified. Check that static assets (images, CSS) are NOT being tracked.
# Simulate GPTBot visiting your homepage
curl -s https://yoursite.com/ \
-H "User-Agent: Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) GPTBot/1.0"
# Simulate ClaudeBot
curl -s https://yoursite.com/ \
-H "User-Agent: ClaudeBot/1.0 (https://anthropic.com)"
# If your middleware is working, both visits appear in TruIntel
# within seconds, classified as GOOD_AGENTWhat to expect
After successful installation: real browser visits are classified as HUMAN (score 60 or above, reported at high confidence), known AI agents appear as GOOD_AGENT, headless browsers appear as SYNTHETIC (score 20–39, or a borderline 40–59 with 3+ synthetic indicators), and scrapers/spam bots with the lowest scores appear as BAD_BOT. It typically takes 24-48 hours to build a meaningful traffic profile.
Content Security Policy (CSP)
If your site uses Content Security Policy headers, you need to allow the TruIntel tracker script and its API endpoint. Add these directives to your CSP header:
Content-Security-Policy:
script-src 'self' https://api.truintel.ai;
connect-src 'self' https://api.truintel.ai;CSP and nonce-based policies
If you use nonce-based CSP (script-src 'nonce-xxx'), you need to add the nonce attribute to both TruIntel script tags. The async tracker script must also be whitelisted. Alternatively, add https://api.truintel.ai to your script-src allowlist.
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| No data appears in dashboard | Wrong Brand ID or script not loading | Check DevTools → Console for errors. Verify Brand ID matches Settings → Integrations. Ensure both script tags are present. |
| Script loads but no beacon sent | window.TruIntel config missing or loads after tracker.js | The config script must be placed BEFORE the tracker script tag. In Next.js, use beforeInteractive for config. |
| CSP error in console | Content Security Policy blocks tracker script or API calls | Add https://api.truintel.ai to script-src and connect-src in your CSP header. |
| Ad blocker blocks tracker | Browser ad blocker or privacy extension blocks third-party scripts | Expected behavior — ad-blocked visitors won't be tracked client-side. Server-side middleware still catches bots. Human traffic from ad-block users is typically 10-15%. |
| Double-counted pageviews | Script loaded twice (e.g., in both layout and page component) | Ensure the tracker script is loaded only ONCE at the root layout level. Check React StrictMode (dev only — not an issue in production). |
| Static assets appearing as visits | Server-side middleware runs on image/CSS/JS requests | Add path exclusions to your middleware. In Next.js, use the matcher config. In Express, check req.path for file extensions before calling isBot(). |
| Server-side events not appearing | Middleware not registered or API endpoint wrong | Verify the middleware is in your middleware chain. The endpoint is /api/v1/i/e (not /api/v1/traffic/middleware). Test with the curl command above. |
| All traffic shows as BOT | Server behind a proxy that strips User-Agent headers | Ensure your reverse proxy (Nginx, Cloudflare) forwards the original User-Agent header. Check X-Forwarded-For is set for correct IP attribution. |
| CORS errors in console | Browser blocks cross-origin API request | The tracker uses fire-and-forget beacons — CORS should not apply. If you see CORS errors, verify you're using the async script tag, not a direct fetch() call. |
| Rate limit errors (429) | More than 30 requests/minute from a single IP | This usually indicates a bot loop or misconfigured middleware. The collect endpoint allows 30 requests/minute per IP and returns Retry-After and X-RateLimit-* headers. Check for recursive middleware calls or scripts that re-inject themselves. |
Edge Cases
| Scenario | Solution |
|---|---|
| Single-page app (SPA) with client-side routing | The tracker automatically detects pushState/popstate route changes. No extra config needed for React Router, Vue Router, Next.js, etc. |
| Server-side rendering (SSR) / Static site generation (SSG) | The client-side script runs only in the browser — SSR-rendered pages won't cause double tracking. Server-side middleware catches bots during SSR requests. |
| Behind Cloudflare / CDN / reverse proxy | Ensure the proxy forwards the original User-Agent and X-Forwarded-For headers. Cloudflare does this by default. Cached CDN responses won't trigger middleware, which is correct behavior. |
| Behind Vercel / Netlify / similar platform | Next.js middleware runs on the edge (before cache). Vercel automatically provides the real IP via x-forwarded-for. No extra config needed. |
| Multiple brands on one domain (e.g., /brand-a, /brand-b) | Use different Brand IDs for each section. Set window.TruIntel.brandId dynamically based on the URL path. For middleware, use the URL path to determine which Brand ID to send. |
| Subdomains (blog.yoursite.com, app.yoursite.com) | Each subdomain can use the same or different Brand ID. Install the script separately on each subdomain. Middleware runs per-deployment, so deploy it on each subdomain's server. |
| TypeScript strict mode (no window.TruIntel) | Add a type declaration: declare global { interface Window { TruIntel?: { brandId: string; apiUrl: string } } } in a .d.ts file or at the top of your component. |
| Google Tag Manager (GTM) | Add both script tags as a Custom HTML tag in GTM. Set the trigger to "All Pages" with firing priority higher than other tags. Alternatively, paste directly in your HTML for more reliable loading. |
| Iframes / embedded content | The tracker runs in the parent page context. Content inside iframes on a different origin will not be tracked. Same-origin iframes may be tracked — add data-ti-ignore to the iframe to exclude it. |
| Testing in development (localhost) | The tracker works on localhost. Traffic from localhost appears in your dashboard labeled with the localhost origin. Use this to verify your installation before deploying to production. |
Privacy & Performance
| Data Category | Collected | NOT Collected |
|---|---|---|
| Identity | Random session/visitor UUIDs (stored in first-party cookies) | Names, emails, phone numbers, login credentials (by the traffic classifier) |
| Browsing | Page path + query on your domain, referrer domain | Browsing history, cross-site tracking |
| Device | Browser type, OS, screen resolution, timezone, device type, country (geo of IP) | Precise geolocation; IP is used for geo/rate-limit, not stored on the log as a long-term identity |
| Behavior | Mouse/touch movement patterns, scroll depth, click timing | Keystrokes, passwords, clipboard content, screenshots |
| Network | User-agent string, connection type | Cross-site cookies, third-party storage, session tokens |
Cookies, form data & lead capture — read this
The tracker is NOT cookieless. It sets two first-party cookies on your own domain: _ti_vid (a 365-day visitor identifier) and _ti_sid (a ~30-minute session identifier), plus localStorage/sessionStorage dedup keys. Separately, the same script bundles optional lead-capture that listens to contact-form submissions and reads submitted values (email, name, company, phone, message) to power Lead Verification — email is PII. It SKIPS auth/login/password forms and any form marked data-ti-ignore, and never blocks the user submit. The traffic classification feature on its own collects no PII; the lead-capture does. Disable lead-capture or mark forms with data-ti-ignore if you do not want this.
- Script size: ~4 KB gzipped (loads async, non-blocking)
- Beacon size: ~200 bytes per event (fire-and-forget POST), one beacon per session — fired at 30s or on page exit
- Rate limit: 30 requests/minute per IP on the collect endpoint (returns Retry-After + X-RateLimit-* headers)
- Do Not Track is a hard opt-out: if the browser sets DNT=1, the tracker sends nothing at all
- First-party cookies only (_ti_vid 365 days, _ti_sid ~30 min) — no third-party or cross-site tracking
- No impact on Core Web Vitals (LCP, FID, CLS) — verified via Lighthouse
- Server-side middleware adds 0ms latency — API call runs asynchronously in background
GDPR & Compliance
Visitor IDs are randomly generated UUIDs that cannot be traced back to individuals, and Do Not Track is honored as a hard opt-out (DNT=1 means nothing is sent). However, the tracker DOES set first-party cookies (_ti_vid is a persistent 365-day identifier) and the bundled lead-capture reads contact-form values such as email, so this is not a strictly cookieless or PII-free deployment. Treat it like first-party analytics: depending on your jurisdiction you may need cookie consent and a privacy-policy disclosure. If your legal team requires explicit consent, wrap the script tags in your consent management flow and use data-ti-ignore on forms you do not want read.
Dashboard & Analytics
The Traffic Intelligence dashboard (GROW ▸ Traffic Analysis, route /traffic) gives you real-time visibility into who visits your site. A time-range selector at the top switches every card between 24 Hours, 7 Days, and 30 Days; a Refresh button re-pulls the data and an Install Script button opens the multi-framework install modal.
Plan gating
The Traffic Intelligence dashboard, custom rules, and the Pattern Catalog require a paid plan (Starter and above) — Free accounts receive a 403. The tracking itself is not plan-gated: the script, the collect endpoints, and the install snippet work for any existing brand, including a Free brand. The net effect is that a Free brand can embed the tracker but cannot view results until it upgrades.
Live Traffic Feed
Real-time stream of the last N minutes of visitors (polls /recent), with classification, device, and country.
Agent Breakdown
AI-agent visits grouped by company (OpenAI, Anthropic, Google, Perplexity, Meta, Microsoft, xAI, …) with category and share.
Traffic Trends
Zero-filled daily time series of total / human / agent / bot / synthetic visits over the selected range.
Threat Analysis
BAD_BOT and SYNTHETIC threats grouped by source, with blocked/flagged counts and per-bot severity (low–critical).
Alongside these, the dashboard surfaces an Overview metric strip (totals, percentages, and period-over-period change), a Classification donut, and a paginated Visitor Log you can filter by classification and action. The Rules Manager edits your brand whitelist/blacklist, and the Pattern Catalog browser lets you explore the 93 known agents and 55 known bots.
Analytics endpoints
Overview
GET /traffic/{brand_id}/overviewTotals, percentages, and period-over-period change. days 1–90 (default 7), cached 60s.
Visitor logs
GET /traffic/{brand_id}/logsPaginated visitor log; filter by classification and action. Limit 1–100 (default 50) + offset.
Agent breakdown
GET /traffic/{brand_id}/agentsAI agents grouped by company, with category and share. days default 30, cached 5 min.
Threats
GET /traffic/{brand_id}/threatsBAD_BOT + SYNTHETIC by source, with blocked/flagged counts. days default 30, cached 5 min.
Trends
GET /traffic/{brand_id}/trendsZero-filled daily time series. days default 30, cached 10 min.
Recent feed
GET /traffic/{brand_id}/recentReal-time feed of the last N minutes. minutes 1–60 (default 10).
Rules (read/replace)
GET / PUT /traffic/{brand_id}/rulesList or replace-all whitelist/blacklist rules; PUT invalidates the cache.
Pattern Catalog
GET /traffic/patternsAll 93 known agents + 55 known bots and their category lists (paid plans).
Data retention
Individual visitor rows are kept for 30 days, then purged by a weekly cleanup (Sundays at 04:00 IST). Daily aggregates are kept indefinitely, so long-term trends, totals, and agent breakdowns survive even though the raw per-visit log only goes back 30 days. Retention is a flat 30 days of raw logs for every plan — there is no per-plan retention tier.
Why same-day numbers can shift
Real-time stats are incremented as beacons arrive, but a daily aggregation job re-computes the previous day from scratch at 03:00 IST to self-heal any missed increments. As a result, yesterday's totals can adjust slightly after midnight IST — this is expected.
Session & visit model
- One beacon per session — the tracker fires once, at 30 seconds or on page exit (visibilitychange/pagehide/beforeunload), with same-tab and cross-tab dedup.
- Session window is ~30 minutes (_ti_sid cookie); the visitor identifier persists for 365 days (_ti_vid cookie).
- Each visit records device type (desktop / mobile / tablet) and a best-effort 2-letter country from the IP (non-blocking geo lookup).
- Redis dedup returns the cached classification for a repeated {brand_id, session_id} for 30 minutes without re-queuing a write.
Ingestion endpoints
Two public, no-auth endpoints feed the single classifier. The client tracker uses the descriptive path; the server middleware uses a short alias specifically so privacy/ad-blocker extensions do not strip it (the words "traffic" and "collect" are common blocklist triggers).
Client collect
POST /api/v1/traffic/collectMain ingestion from tracker.js. Classifies in-process, queues the DB write, returns the result immediately.
Middleware alias
POST /api/v1/i/eAd-blocker-safe alias of /collect (same handler). Used by the server-side middleware snippets.
Install snippet
GET /api/v1/traffic/snippet/{brand_id}Returns a ready-to-paste HTML install snippet for a brand (404 if the brand does not exist).
Tracker script
GET /api/v1/traffic/tracker.jsServes the JavaScript tracker (Cache-Control public, max-age 3600).
Lead Verification
Installation
TruIntel's lead verification works by automatically intercepting form submissions on your website. A single script detects all <form> elements, captures submission data along with behavioral signals, and scores each lead in real time. No backend changes are required — the same tracker script used for traffic classification also handles lead verification.
One script, two features
If you've already installed the TruIntel tracker script for Traffic Classification, lead verification is already active. The same script handles both traffic tracking and form interception. No additional installation is needed — just verify that forms are being detected on the Leads page.
How It Works
Auto-Detects Forms
Finds every <form> that contains an email field, including forms added dynamically after page load (via MutationObserver). Forms without an email field are never tracked.
Captures Behavior
Records scroll events, mouse movements, keystrokes, touch events, and form-fill time (first focus to submit) before submission. The submit listener is passive — your form submits normally.
Scores in Real-Time
Deterministic Python scoring (no LLM, no external API, $0 per verification) across Behavior, Email, Identity, Source, and Device. Returns a 0-100 human score within milliseconds.
Zero Interruption
The form still submits normally to your backend. The listener is registered passive (cannot call preventDefault), so capture never blocks or alters your submission.
What Data Is Captured
The tracker reads form values through an explicit allowlist of CSS selectors — it only ever reads the specific contact fields below. Everything else (passwords, payment fields, hidden inputs, files) is simply never accessed. Behavioral counters and a few request metadata fields are sent alongside.
| Data Point | Source | Scored? |
|---|---|---|
| email (required — nothing is sent if empty) | Email input (allowlist selector) | Email dimension (regex + disposable / suspicious-TLD / free-provider lists) |
| name (or first_name + last_name) | Name input (allowlist selector) | Identity dimension |
| company, phone, subject, message | Allowlist selectors only | Identity (company, phone); message/subject stored, not scored |
| mouse_movements, scroll_events, key_events, touch_events | Document-level event counters | Behavior dimension |
| form_submit_time (first focus to submit, seconds) | Tracker timer per form | Behavior dimension |
| time_on_page, click_events | Tracker counters | Captured and sent but NOT used in the score |
| referrer / source, page (pathname+search) | Browser location & referrer | Source dimension (referrer/source only) |
| user_agent | Browser | Device dimension (User-Agent string only) |
| session_id | Tracker session | Links to traffic classification for a behavior +20 / -30 adjustment |
| honeypot_value | Hidden/offscreen filled field | If non-empty -> instant REJECTED (score 0) |
Allowlist capture — passwords are never even read
The tracker does not have an exclusion list; it has an allowlist. It only reads email, name, company, phone, subject, and message via specific selectors. Password fields, credit-card fields, SSN fields, hidden inputs, and file inputs are never accessed, so their values can never be transmitted. There is no credit-card pattern-matching or redaction code — those fields are simply not in the allowlist.
Do Not Track is respected
If the browser sends navigator.doNotTrack === "1", the script returns immediately and does nothing — no traffic beacon and no form capture. Honoring DNT is built in and cannot be overridden.
Installation Steps
Find your Brand ID
Go to Settings → Integrations in your TruIntel dashboard. Copy the Brand ID shown under your brand.
Install the tracking script
Choose your framework below and paste the snippet. The script must load on every page that has forms you want to track. If you've already installed it for Traffic Classification, skip this step.
Submit a test form
Fill out a form on your site with realistic data (real email, full name). Submit it and check the Leads page in TruIntel — the submission should appear within a few seconds with a score and status.
Review the score breakdown
Click on the test lead in TruIntel to see the full score breakdown: email analysis, behavior signals, identity match, traffic source quality, and device reputation.
Script Installation
This is the same script used for Traffic Classification. If you've already installed it, lead verification is already active.
HTML
Paste inside the <head> tag on every page that has forms you want to track.
<!-- TruIntel Lead Verification -->
<script>
window.TruIntel = {
brandId: 'YOUR_BRAND_ID',
apiUrl: 'https://api.truintel.ai/api/v1'
};
</script>
<script async src="https://api.truintel.ai/api/v1/traffic/tracker.js"></script>React
Add once in your App.tsx or root layout component. The tracker persists across route changes and detects dynamically rendered forms.
import { useEffect } from 'react';
export function TruIntelTracker() {
useEffect(() => {
window.TruIntel = {
brandId: 'YOUR_BRAND_ID',
apiUrl: 'https://api.truintel.ai/api/v1'
};
const s = document.createElement('script');
s.async = true;
s.src = 'https://api.truintel.ai/api/v1/traffic/tracker.js';
document.head.appendChild(s);
return () => { document.head.removeChild(s); };
}, []);
return null;
}
// Usage: <TruIntelTracker />Next.js
Add to your root layout (app/layout.tsx for App Router, _app.tsx for Pages Router).
import Script from 'next/script';
export default function RootLayout({ children }) {
return (
<html>
<head>
<Script id="truintel-config" strategy="beforeInteractive"
dangerouslySetInnerHTML={{
__html: `window.TruIntel={brandId:'YOUR_BRAND_ID',apiUrl:'https://api.truintel.ai/api/v1'};`,
}} />
<Script src="https://api.truintel.ai/api/v1/traffic/tracker.js"
strategy="afterInteractive" />
</head>
<body>{children}</body>
</html>
);
}Vue
Add to your root App.vue or main entry component.
<script setup>
import { onMounted, onUnmounted } from 'vue';
let script = null;
onMounted(() => {
window.TruIntel = {
brandId: 'YOUR_BRAND_ID',
apiUrl: 'https://api.truintel.ai/api/v1'
};
script = document.createElement('script');
script.async = true;
script.src = 'https://api.truintel.ai/api/v1/traffic/tracker.js';
document.head.appendChild(script);
});
onUnmounted(() => {
if (script) document.head.removeChild(script);
});
</script>Nuxt
Add to your nuxt.config.ts to inject the script globally on every page.
export default defineNuxtConfig({
app: {
head: {
script: [
{
innerHTML: "window.TruIntel={brandId:'YOUR_BRAND_ID',apiUrl:'https://api.truintel.ai/api/v1'};",
},
{
src: 'https://api.truintel.ai/api/v1/traffic/tracker.js',
async: true,
},
],
},
},
});Angular
Add to src/index.html inside the <head> tag.
<!-- Inside <head> -->
<script>
window.TruIntel = {
brandId: 'YOUR_BRAND_ID',
apiUrl: 'https://api.truintel.ai/api/v1'
};
</script>
<script async src="https://api.truintel.ai/api/v1/traffic/tracker.js"></script>Svelte / SvelteKit
Add to src/app.html inside the <head> tag, or use <svelte:head> in your root layout.
<svelte:head>
{@html `<script>window.TruIntel={brandId:'YOUR_BRAND_ID',apiUrl:'https://api.truintel.ai/api/v1'};</script>`}
{@html `<script async src="https://api.truintel.ai/api/v1/traffic/tracker.js"></script>`}
</svelte:head>WordPress
Add to your theme's functions.php, or use a plugin like "Insert Headers and Footers" or "WPCode" to inject without editing theme files.
// Add to your theme's functions.php
add_action('wp_head', function() {
?>
<script>
window.TruIntel = {
brandId: 'YOUR_BRAND_ID',
apiUrl: 'https://api.truintel.ai/api/v1'
};
</script>
<script async src="https://api.truintel.ai/api/v1/traffic/tracker.js"></script>
<?php
});Script Load Order
Replace YOUR_BRAND_ID with your actual Brand ID from Settings → Integrations. The config object (window.TruIntel) must load before the tracker script — otherwise forms will not be tracked.
Excluding Forms
A form is hooked only if it (a) contains an email field, (b) is not an auth/login form, and (c) does not carry the data-ti-ignore attribute. To exclude any other form from being tracked, add data-ti-ignore to the <form> element.
Email field is required
TruIntel only hooks forms that contain an email field (an input[type=email], or a name/id/placeholder containing "email"). Search forms, filter forms, and any form with no email input are never tracked — regardless of method or markup.
<!-- This form will NOT be tracked -->
<form action="/login" method="POST" data-ti-ignore>
<input type="email" name="email" />
<input type="password" name="password" />
<button type="submit">Log in</button>
</form>
<!-- This form WILL be tracked -->
<form action="/contact" method="POST">
<input type="text" name="name" placeholder="Full name" />
<input type="email" name="email" placeholder="Work email" />
<textarea name="message"></textarea>
<button type="submit">Send</button>
</form>| Form Type | Auto-Behavior | Action Needed |
|---|---|---|
| Contact / inquiry forms (with email) | Tracked automatically | None — these are your leads |
| Login / sign-in / password-reset forms | Auto-skipped (keyword + password-field heuristic) | None |
| Registration / sign-up forms (with email) | Tracked automatically | Add data-ti-ignore if you do not want sign-up tracking |
| Search / filter forms (no email field) | Never tracked (no email field) | None |
| Newsletter forms with an email field | Tracked automatically | Add data-ti-ignore to exclude |
| Internal admin forms (with email) | Tracked automatically | Add data-ti-ignore to exclude |
| Checkout / payment forms | Only the email field is read; payment fields are never accessed | Add data-ti-ignore for belt-and-braces exclusion |
| Forms with no email field | Never tracked | None |
Auto-skipped patterns (exact)
A form is treated as an auth form (and skipped) if its action + id + className combined contains any of: login, signin, sign-in, log-in, auth, password-reset, forgot-password. It is ALSO skipped if it has a password-type field and 2 or fewer non-checkbox/radio fields. You only need data-ti-ignore for other forms you want to exclude.
Edge Cases & Advanced Scenarios
| Scenario | How TruIntel Handles It |
|---|---|
| Dynamically rendered forms (React, Vue, Angular) | The tracker uses MutationObserver to detect forms added to the DOM after initial page load. Forms rendered by JavaScript frameworks are detected automatically — no extra config needed. |
| AJAX / fetch-based form submissions | If your form uses preventDefault() and submits via fetch/axios, TruIntel's submit event listener fires first and captures the data. The form data is captured regardless of how the submission is handled. |
| Multi-step / wizard forms | TruIntel captures the final submission (the actual form submit event). Intermediate "Next" buttons that don't trigger a form submit are not tracked. If each step is a separate <form>, each submission is tracked individually. |
| Forms inside modals or drawers | Forms in modals, popups, slide-out drawers, and dialog elements are detected when they're added to the DOM. Works with all UI libraries (Material UI, Chakra, Radix, Headless UI, etc.). |
| Third-party form builders (HubSpot, Typeform, Jotform) | Embedded iframes from third-party builders cannot be tracked (cross-origin restriction). HubSpot forms embedded as JavaScript (not iframe) may be detected. For best results, use native HTML forms. |
| File upload forms (multipart/form-data) | File inputs are never read at all. No file contents, no filename, and no file size are captured — only the allowlisted contact fields are read. |
| Forms submitted by pressing Enter | Enter-key submissions trigger the same submit event as clicking a button. They are tracked identically. |
| Multiple forms on one page | Each form is tracked independently. If a page has a contact form, newsletter signup, and search form, each gets its own score and lead entry when submitted. |
| SPA route changes | The tracker re-scans for forms on every route change (detects pushState/popstate). Forms on dynamically loaded pages are found automatically. |
| Forms with custom validation (e.g., Zod, Yup) | The tracker listens for the native submit event. If validation prevents the form from submitting (e.preventDefault without re-dispatching), the submission is not tracked. If the form eventually submits, it's tracked normally. |
| Honeypot fields for spam prevention | Dedicated detection: the tracker looks for a visually hidden / offscreen input that got filled (display:none, visibility:hidden, opacity:0, or positioned < -5000px) and sends it as honeypot_value. If it is non-empty, the server instant-rejects the lead — human_score 0, all dimensions 0, status REJECTED — overriding every other signal. |
| reCAPTCHA / hCaptcha forms | CAPTCHA widgets don't interfere with form tracking. The tracker captures form data on submit, regardless of CAPTCHA status. |
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| Form submissions not appearing in Leads page | Script not loading, wrong Brand ID, or form has data-ti-ignore | Check DevTools → Network for tracker.js. Verify Brand ID. Remove data-ti-ignore if accidentally added. Ensure the form has a submit event (not just a button click handler). |
| Score shows 0 for all leads | Form submitted too quickly (bot-like behavior) or from localhost | Submit the form naturally: spend 10+ seconds on the page, move the mouse, scroll, then fill the form slowly. Localhost submissions may score low due to missing referrer data. |
| Duplicate lead entries | Form submits twice (double-click or script re-injection) | Ensure the tracker script is loaded only once. Check for double form submission in your own code. React StrictMode causes double mounts in dev only — not an issue in production. |
| Login forms being tracked despite auto-skip | Form action URL doesn't match auto-skip patterns | Add data-ti-ignore to the form element manually. The auto-skip only catches common patterns like /login, /signin, /auth. |
| Unexpected field appearing in lead details | A custom field matched an allowlist selector (e.g. an input whose name/id/placeholder contains "email", "name", "company", "phone", "subject") | Rename the field so it does not collide with the allowlisted keywords, or add data-ti-ignore to the whole form. Only the six allowlisted contact fields are ever read. |
| Third-party iframe form not tracked | Cross-origin iframe restriction | Forms inside iframes from different domains cannot be accessed by the tracker (browser security policy). Use the provider's native webhook integration or TruIntel's API endpoint to submit lead data directly. |
| Form tracked but fields are empty | Form uses a non-standard submission pattern (e.g., FormData API with no DOM elements) | The tracker reads values from form input elements at submit time. If your form uses virtual/controlled inputs without DOM form elements, consider using TruIntel's API endpoint to submit lead data directly. |
| Lead score seems inaccurate | Insufficient behavioral data or disposable email used | Ensure the tracker script loads early (in <head>) so it captures the full session behavior. Test with a real business email — disposable email providers (mailinator, guerrillamail) receive low email scores. |
Privacy & Sensitive Data
- Allowlist capture: only email, name, company, phone, subject, and message are ever read. Passwords, payment fields, SSN fields, hidden inputs, and files are never accessed — there is no exclusion list to maintain and no credit-card pattern-matching/redaction code (those fields are simply never read).
- File inputs are never read — no contents, no filename, no file size are captured.
- Do Not Track is honored: if navigator.doNotTrack === "1" the tracker no-ops entirely (no traffic beacon, no form capture).
- Cookies ARE set: a persistent visitor cookie _ti_vid (365 days) and a session cookie _ti_sid (about 30 minutes), plus localStorage/sessionStorage for de-duplication. Account for these in your cookie/privacy notice.
- All data is transmitted over HTTPS. (TLS-version and at-rest-encryption specifics are infrastructure guarantees, not enforced in product code — confirm with your DPA rather than treating them as a product setting.)
- Retention: lead rows are kept indefinitely (until the brand is deleted, which cascade-deletes them). There is no automatic 90-day purge and no Settings -> Data Retention control. LeadStats daily rollups are kept forever. (Only separate traffic logs are purged after 30 days.)
- No deduplication: every submission creates a new lead. The API response field is_duplicate is always false.
- GDPR: lead data contains PII (email, name, phone). The lawful basis (e.g. legitimate interest) is a legal stance for your team to set — TruIntel does not assert one in code.
Payment & checkout forms
The tracker never reads payment fields (it only reads the six allowlisted contact fields), so card data cannot be captured. As defence-in-depth, you can still add data-ti-ignore to checkout/payment forms — though if a payment form has no email input it is never hooked in the first place.
Lead Scoring Formula
Every form submission receives a human score (0-100) — a deterministic weighted average of five dimensions, computed in pure Python with no LLM and no external API call. Final score = round(email x 0.25 + behavior x 0.40 + identity x 0.15 + source x 0.10 + device x 0.10). A filled honeypot overrides everything and forces score 0.
Human Score
| Dimension | Weight | What is actually analyzed |
|---|---|---|
| Behavior | 40% | Mouse movements, scroll events, keystrokes, touch events, form-fill time; plus a +20 / -30 adjustment from linked traffic classification. (time_on_page and click_events are captured but not scored.) |
| 25% | Regex format check + static lists: disposable-domain, suspicious-TLD, and free-provider sets. No MX/DNS lookup, no live reputation service. | |
| Identity | 15% | Name present / full name, company present, phone digit count. No name-consistency, company-domain match, or social-presence lookups. |
| Source | 10% | Referrer/source string matched to AI / organic / social / direct / unknown buckets. No UTM parsing, landing-page relevance, or session history. |
| Device | 10% | User-Agent string only: headless / bot / standard-browser detection. No canvas fingerprint, screen resolution, or timezone in the lead device score. |
Deterministic and free
Lead scoring is synchronous text-parsing — it returns the score in the API response immediately and costs $0 per verification. The database write is queued asynchronously via Celery, so submissions survive page navigation.
Email dimension bands (25%)
| Condition | Score |
|---|---|
| Invalid format (regex fails) | 0 (high risk) |
| Disposable domain (static list, ~34 domains) | 0 (critical) |
| Suspicious TLD (static list, 20 TLDs) | 30 (medium) |
| Free provider (Gmail/Yahoo/etc., ~26 domains) | 65 |
| Business email (custom domain) | 100 |
| Unknown (none of the above) | 50 |
Behavior dimension bands (40%)
| Signal | Bands -> score |
|---|---|
| Mouse movements | >20 -> 100; 5-20 -> 60; <5 -> 20 |
| Scroll events | >5 -> 100; 1-5 -> 60; 0 -> 30 |
| Key events (typing) | >10 -> 100; 1-10 -> 50; 0 -> 20 |
| Form-fill time (sec) | >30 -> 100; 10-30 -> 70; 3-10 -> 40; <3 -> 10 |
| Touch events | >0 adds a 100 to the average (mobile bonus) |
| Traffic cross-reference | Linked session HUMAN -> +20 (cap 100); BAD_BOT/SYNTHETIC -> -30 (floor 0) |
Identity, Source & Device bands
| Dimension | Rule |
|---|---|
| Identity | Base 50; full name (space, length >=5) +25, else any name +10, no name -10; company (length >=2) +15; phone (>=10 digits) +10; clamped 0-100. |
| Source | AI platform 95; organic search 85; social 80; direct/empty 60; unknown 50 (first match wins). Note: google.com/search & bing.com/search fall in the AI-platform set, so an organic search referrer can score 95. |
| Device | Headless indicators (selenium/puppeteer/webdriver/etc.) 0; bot indicators (bot/crawler/curl/python-requests/etc.) 10; standard browser 80; empty/unknown UA 50. |
Status Thresholds
Leads are automatically categorized into three status levels based on their score:
| API status | Score | Dashboard label | Action recommended |
|---|---|---|---|
| VERIFIED | 80-100 | Verified | High confidence — route directly to sales |
| REVIEW | 50-79 | Suspicious | Moderate confidence — manual review recommended |
| REJECTED | 0-49 | Spam | Low confidence — likely fake or bot submission |
Honeypot overrides the bands
A filled honeypot field forces human_score 0 and status REJECTED regardless of the dimension scores. The thresholds above only apply when the honeypot is empty.
Note the wording difference: the API enum is VERIFIED / REVIEW / REJECTED, while the in-app dashboard relabels them Verified / Suspicious / Spam. They are the same three statuses. verified_at is stamped when a lead becomes VERIFIED.
Leads Dashboard
The in-app Lead Verification dashboard lives at /leads and is a paid feature (Starter and above). Free orgs get a 403 (PAID_PLAN_REQUIRED) — the public scoring endpoint and tracker script still work for any brand. The dashboard has 7 / 30 / 90-day period tabs.
Overview stats
Five metrics: Total Leads, Verified, Suspicious (REVIEW), Spam (REJECTED), and Avg Score, with period-over-period change.
Score distribution
Histogram bucketed 0-20, 21-40, 41-60, 61-80, 81-100, plus a Verified / Review / Rejected status donut.
Trends & sources
Daily verified/review/rejected area chart and a Source Performance table (source, leads, verified, avg score, share).
Lead list & detail
Search by name/email/company, status + min/max score + date filters, 20 per page. Row click opens /leads/:id with the full dimension breakdown, signals, risk factors, and recommendation.
Manual override & bulk
Override a single lead status, or bulk Mark as Verified / Mark as Spam (up to 100 IDs).
CSV export
Export filtered leads to CSV (up to 10,000 rows) for CRM import.
Daily stat recomputation
LeadStats are updated incrementally on every write and fully recomputed each day. The lead.run_daily_aggregation cron runs at 03:30 IST (Asia/Kolkata) and recomputes yesterday's stats for all brands from raw lead rows, so totals stay correct even if an incremental update was missed.
API Integration
You can score a submission directly without the tracker by POSTing to the public verify endpoint. It requires no JWT (it validates that brand_id exists), is CORS-open, and is rate-limited to 20 requests/minute per client IP (a 429 with Retry-After is returned when exceeded). The request field for the page is page, not page_url.
POST https://api.truintel.ai/api/v1/leads/verify
# Rate limit: 20 requests/minute per IP
# No authentication required (validates brand_id exists)
# Request body:
{
"brand_id": "your-brand-id",
"email": "[email protected]",
"name": "John Doe",
"company": "Acme Inc",
"phone": "+1234567890",
"message": "Interested in a demo",
"subject": "Demo request",
"page": "/contact",
"referrer": "https://google.com",
"source": "Organic",
"user_agent": "Mozilla/5.0 ...",
"session_id": "abc-123",
"behavior": {
"mouse_movements": 42,
"scroll_events": 8,
"key_events": 25,
"click_events": 3,
"touch_events": 0,
"time_on_page": 35,
"form_submit_time": 22
},
"honeypot_value": ""
}Passing a session_id that matches a prior traffic beacon links the lead to its traffic classification and applies the behavior +20 / -30 adjustment automatically — a free accuracy boost for raw API callers. honeypot_value left empty scores normally; any non-empty value instant-rejects.
{
"success": true,
"data": {
"human_score": 85,
"status": "VERIFIED",
"scores": { "email": 100, "behavior": 80, "identity": 75, "source": 85, "device": 80 },
"signals": ["..."],
"risk_factors": ["..."],
"recommendation": "High-quality lead. Contact immediately.",
"lead_id": "generated-uuid",
"is_duplicate": false
},
"message": "..."
}No dedup, async write
Every call creates a new lead — is_duplicate is always false (there is no server-side deduplication). The lead_id is generated and returned immediately; the database row is written asynchronously via Celery, so the response is never blocked on the DB write.
No enforced monthly cap
A per-plan lead_verifications_monthly value exists in config (Free 0, Starter 200, Lite 1000, Pro 2000, Enterprise 5000) but it is NOT enforced anywhere in code. The only live throttle on the verify endpoint is 20 requests/minute per IP. Do not rely on a hard monthly verification ceiling.
Authenticated dashboard endpoints
These power the in-app dashboard. All require a JWT and a paid plan, and all verify the brand belongs to the caller's org. Prefix /api/v1.
Verify (public)
POST /leads/verifyScore a submission. Public, no auth, 20/min/IP. OPTIONS preflight supported.
Tracker script
GET /traffic/tracker.jsServes the shared tracking.js (cached public, max-age 3600). Same script handles traffic + leads.
Overview
GET /leads/{brand_id}/overviewTotals, percentages, avg score, period change, top sources (cached ~2 min).
List
GET /leads/{brand_id}Paginated, filterable by status, min/max score, date range, and search.
Detail
GET /leads/{brand_id}/detail/{lead_id}Full dimension breakdown, signals, risk factors, recommendation, metadata.
Status override
PATCH /leads/{brand_id}/status/{lead_id}Manually override a single lead status.
Bulk status
POST /leads/{brand_id}/bulk-statusBulk update up to 100 lead IDs.
Score distribution
GET /leads/{brand_id}/score-distributionGlobal bucket counts for the histogram.
Trends
GET /leads/{brand_id}/trendsDaily series (up to 90 days), zero-filled (cached ~5 min).
Sources
GET /leads/{brand_id}/sourcesSource-level breakdown.
CSV export
GET /leads/{brand_id}/exportExport filtered leads as CSV (up to 10,000 rows).
The API returns a score, status, and detailed signal breakdown. Use the public endpoint to pre-filter submissions before they reach your CRM, and the authenticated endpoints to read, override, and export leads.
SERP Simulator
Overview
The SERP Simulator is a 100% client-side Google search-snippet preview and SEO snippet scorer. You type (or auto-fetch) a page title, meta description, URL, and keywords, and watch a pixel-accurate Google search result update live — alongside a 0-100 snippet score, a position-based click-through estimate, rich-snippet mockups, and one-click exports. There is no AI model, no backend call, no credit cost, and no plan limit.
Free for everyone, no credits, no backend
The simulator runs entirely in your browser. It does not call the TruIntel API, does not spend AI credits, and is available on every plan including Free. The public, no-login version lives at truintel.ai/serp-preview-tool.
Live Google Preview
A pixel-accurate Google search result (desktop and mobile) updates as you type your title, description, and URL.
10-Factor SEO Score
A 0-100 snippet score across 10 weighted content and keyword factors, with a per-factor breakdown.
CTR & Clicks Estimate
A separate position-based estimate of organic click-through rate and projected monthly clicks.
Rich Snippets
Preview FAQ, Product, Recipe, Event, How-To, Video, and Sitelink rich-snippet treatments live.
Competitor Comparison
Add up to 10 sites and drag them across SERP positions 1-10 to model the full results page.
Meta Fetcher & Export
Auto-populate fields from any URL, then export Copy HTML (meta + JSON-LD), Copy JSON, or a PNG screenshot.
Google only
The on-screen preview renders Google search results exclusively. Earlier descriptions of Bing and on-screen social previews are no longer accurate — those code paths are not rendered. Open Graph and Twitter Card tags are still produced, but only inside the Copy HTML export.
Where to Find It & Access
The exact same tool ships in two places: an in-app tool for logged-in users and a fully public marketing tool. Both share the same component, so the editing experience is identical.
| Deployment | Where | Access | Notes |
|---|---|---|---|
| In-app tool | Launched from the Tools landing page; route /tools/serp-simulator | Any logged-in user, every plan including Free | Opens fullscreen with no app sidebar or header |
| Public tool | truintel.ai/serp-preview-tool | Fully public, no login required | Adds marketing copy and a lead-capture promo modal that appears after 10 seconds |
SERP Simulator (in-app)
/tools/serp-simulatorFullscreen tool for logged-in users; launched from the /tools landing page.
SERP Preview Tool (public)
/serp-preview-toolPublic, no-login marketing version of the identical simulator.
No plan gating
The in-app tool is protected only by login — there is no feature gate, no credit cost, and no per-org budget. Free, Starter, Lite, Pro, and Enterprise users all get the full tool. If you are not logged in, use the public /serp-preview-tool instead.
The 7 Editor Tabs
The editor panel is organized into seven tabs. (There is no separate "Keywords" or "Advanced settings" tab — keywords live inside Basic Info, and multi-site positioning lives in the Ranking tab.)
| # | Tab | What it does |
|---|---|---|
| 1 | Basic Info | Page URL + meta Fetch, Site Name, Favicon URL, SEO Title, Meta Description, and Bold Keywords |
| 2 | SERP Features | Seven toggles for SERP feature blocks and badges (AI Overview, Ads, PAA, Map Pack, PASF, Verified Badge, Heatmap) |
| 3 | Rich Snippet | Six schema rich-snippet types plus four "extras" decorations, each with its own enable toggle |
| 4 | Media | A video snippet OR thumbnail images (the two are mutually exclusive) |
| 5 | Ranking | Drag-reorder your and competitor sites across SERP positions 1-10 |
| 6 | SEO Score | The 0-100 snippet score, a 10-factor breakdown, and the CTR / clicks estimate |
| 7 | Library | Save, load, and delete snippets from your browser (localStorage) |
Basic Info
- Page URL + Fetch button — runs the 3-layer meta fetcher to auto-populate fields.
- Site Name and Favicon URL — with a live favicon preview; leave Favicon blank to auto-detect via the Google favicon service.
- SEO Title — a live character count and a pixel-width meter that warns when the title may truncate. Guidance: under 50 chars "Aim for 50-60", 50-60 "Ideal length", over 60 "May truncate".
- Meta Description — the same live count and pixel meter, targeting 150-160 characters.
- Bold Keywords — add keyword chips (Enter or +); they are bolded inside the preview snippet and feed the SEO score.
SERP Features (7 toggles)
Toggle which SERP feature blocks appear in the simulated results page: AI Overview, Ads Block, People Also Ask, Map Pack, People Also Search For, Verified Badge, and Attention Heatmap (an F-pattern overlay on the top three results). The first five render as static reference screenshots injected into the result list; the heatmap renders as a graduated overlay on positions 1-3.
Rich Snippet
Pill sub-tabs offer six schema snippet types — FAQ, Product, Recipe, Event, How-To, and Sitelinks ("Standard" means none; Video lives in the Media tab) — plus four "extras" decorations you can layer on:
- Rating — a 1-5 star slider with review count and a store/seller label.
- Price — a price range plus comma-separated green benefit labels.
- Extensions — sitelink-extension text links beneath the result.
- Date — a date-prefix string such as "3 days ago".
Sitelinks are capped at 6 entries; FAQ and How-To items are add/remove lists.
Media
Choose a Video snippet (duration, upload date, thumbnail URL, channel name, view count, follower count, and a Standard YouTube or Grid layout) or Thumbnail images (Single or 3-Strip layout, with 1 or 3 image-URL inputs). Video and Thumbnail are mutually exclusive — enabling Video sets the rich-snippet type to video.
Ranking
A drag-to-reorder list of 10 SERP slots. Your tracked sites occupy their chosen ranks while the remaining slots fill with greyed competitor placeholders, so you can model where your snippet sits within the full results page.
Library
Save the active site's snippet to your browser with a name, then list, load (it adds as a new site), or delete saved snippets. Everything is stored in localStorage — there is no server-side persistence.
SEO Snippet Score (10 Factors)
The SEO Score tab computes a deterministic 0-100 snippet score from your title, description, URL, keywords, and rich-snippet type. It is a content and keyword score — not a CTR score — calculated entirely in the browser with no AI. Ten factors sum to 100.
| Factor | Max points | Key rule |
|---|---|---|
| Keyword in Title | 18 | Keyword present as a substring of the title -> 18, else 0 |
| Title Length | 13 | 50-60 chars = 13; 40-70 = 9; under 40 or over 70 = 4; empty = 0 |
| Keyword in Description | 13 | Keyword present in the description -> 13, else 0 |
| Description Length | 13 | 150-160 chars = 13; 120-170 = 9; under 120 or over 170 = 4; empty = 0 |
| Power Words | 10 | 5 points per power word found across title + description, capped at 10 (27-word list) |
| Call-to-Action | 10 | Any CTA word in the description -> 10, else 0 (19-word list) |
| Readability | 8 | Avg word length <=5.5 and >=15 words = 8; <=6.5 = 5; otherwise 2 |
| URL Quality | 5 | Clean URL (no query string, <=4 segments) + keyword = 5; clean only = 3; else 1 |
| Schema Markup | 5 | A rich-snippet type other than Standard -> 5, else 0 |
| Title Front-Load | 5 | Keyword in the first 30 chars = 5; present but later = 2; absent = 0 |
SEO Snippet Score (top-weighted factors)
The total renders as a score ring with a tone and label, and the breakdown shows each factor as a bar: good when it earns 80%+ of its points, partial, or fail. Each factor carries a single-line detail string explaining the result — there is no separate "optimization suggestions" engine beyond those per-factor notes.
Quickest wins
Keyword-in-Title (18 pts) is the single heaviest factor, followed by title length, keyword-in-description, and description length (13 pts each). Getting your primary keyword into the title and keeping length in the ideal bands captures the bulk of the score.
CTR & Monthly Clicks Estimate
Shown alongside the SEO Score, this estimate is entirely separate from the 100-point content score. It projects organic click-through rate and monthly clicks from the position you set and a search-volume input — changing them does NOT change your content score.
| Position | CTR | Position | CTR |
|---|---|---|---|
| 1 | 27.6% | 6 | 4.9% |
| 2 | 15.8% | 7 | 3.9% |
| 3 | 11.0% | 8 | 3.3% |
| 4 | 8.4% | 9 | 2.7% |
| 5 | 6.3% | 10 | 2.4% |
Any position beyond 10 uses a flat 1.0% CTR. Monthly clicks = round(CTR / 100 x search volume). The defaults are position 1 and a search volume of 1,000. The CTR curve is based on the Backlinko organic click-through-rate study.
Two independent numbers
Keep them mentally separate: the 0-100 score grades how well-optimized your snippet copy is; the CTR / clicks figure estimates traffic for a given ranking position. A perfect snippet at position 9 will still show modest projected clicks.
Preview, Devices & Locales
The preview pane renders a full 10-slot Google results page: your sites sit at their chosen positions, skeleton competitor placeholders fill the rest, and enabled feature blocks and the heatmap are injected in place.
- Device toggle — switch between a desktop browser frame and a mobile phone frame (max ~420px wide).
- Pixel-width truncation meters — desktop title max 600px and description max 920px; mobile title 600px and description 680px. These drive the truncation warnings in Basic Info.
- Rich-snippet rendering — FAQ, Product, Recipe, Event, Video, How-To, and Sitelink treatments render inline, plus a Verified Badge.
- Bolded keywords — your Bold Keyword chips are emphasized inside the snippet text exactly as Google bolds query matches.
15 Google country locales
A shared locale field switches the Google country variant (TLD + flag) across all sites in the comparison. Available locales: US, GB, CA, AU, IN, DE, FR, ES, IT, BR, JP, NL, SE, MX, and SG.
Multi-Site Competitor Comparison
Beyond previewing a single page, you can model the whole results page by adding competitor sites — up to 10 in total — each with its own snippet, keywords, position, features, and rich snippet.
Add a site
Use the left-panel site selector to add a new site; it is auto-named "Competitor N" and seeded with sample data and a staggered position.
Edit each site independently
Switch the active site in the selector; the seven editor tabs apply to whichever site is selected.
Position them
Open the Ranking tab and drag sites across SERP positions 1-10 to set who ranks where.
Compare in the preview
The Google preview renders all your sites at their chosen ranks, with greyed placeholders filling any empty slots.
Device, search volume, and locale are shared across all sites; everything else is per-site.
Meta Fetcher (Auto-Populate)
The Fetch button in Basic Info auto-populates the title, description, Open Graph image, favicon, suggested keywords, and the detected schema type from any URL. It uses a 3-layer fallback strategy and caches results in memory for 5 minutes.
Server route (public tool only)
A Next.js /api/fetch-meta route fetches server-side with no CORS issues — 8s timeout, 2MB body cap, HTML-only, and an SSRF guard that blocks localhost and private IPs. This route exists only in the public tool; in the in-app tool this layer is skipped.
CORS proxies
Client-side fetch through public CORS proxies (corsproxy.io, then allorigins.win, then codetabs.com), with the returned HTML parsed in the browser.
Minimal fallback
If both fail, it derives the site name from the hostname and pulls the favicon from the Google favicon service.
After a successful fetch, Basic Info shows a status line such as "Fetched + Product schema - via proxy" so you can see which layer responded and what schema was detected.
Public sites only
The fetcher can only read publicly reachable HTML pages. Pages behind authentication, heavy JavaScript rendering, or strict CORS may not auto-populate — type those fields manually.
Export, Save & Reset
Once your snippet looks right, export it in whichever form you need. All actions are available from the desktop footer and the mobile actions menu.
Copy HTML
A full <head> block: title, description, canonical, favicon, keywords, robots, complete Open Graph + Twitter Card tags, optional product/rating/date comments, and a JSON-LD script.
Copy JSON
A full structured export of the snippet, SEO, rich-snippet, extras, active features, and parsed JSON-LD, tagged as a TruIntel SERP Simulator export.
Export PNG
A high-resolution (scale 2) screenshot of the preview frame, rendered client-side via html2canvas.
Reset All
Clears every field back to the default sample snippet.
Auto JSON-LD generation
The Copy HTML and Copy JSON exports include valid structured data generated from your rich-snippet choice: FAQPage, Product (with Offer + AggregateRating), Recipe, Event, VideoObject (ISO-8601 duration), HowTo, WebSite with SearchAction for sitelinks, or WebPage otherwise.
Persistence
- Saved snippets and your resizable panel widths are stored in browser localStorage — nothing is sent to the TruIntel backend.
- A base64 ?s= deep link will still load a shared snippet state if you have one, but there is no in-UI button to generate share links in the current tool.
Layout & Interface
The simulator adapts between a resizable desktop workspace and a streamlined mobile layout.
| Mode | Layout |
|---|---|
| Desktop | A resizable 3-panel workspace: a left tab rail (collapses to icon-only), a draggable content editor panel (drag its width to 0 to hide it), and the preview filling the remaining space |
| Mobile (small screens) | A single column with a header (logo, device toggle, actions menu), an Editor/Preview segmented toggle, and a horizontally scrolling tab strip |
Public-tool extras
The public /serp-preview-tool wraps the same simulator with SEO marketing sections and a lead-capture promo modal that auto-opens after about 10 seconds. The in-app tool shows neither — just the fullscreen workspace.
Because the entire tool is client-side, it needs no backend endpoints. The only network calls are the optional meta Fetch (the public /api/fetch-meta route or CORS proxies) and loading the static SERP feature screenshots — no TruIntel API endpoint is involved.
Notifications & Alerts
How Alerts Work
TruIntel runs a lightweight, brand-scoped alert system. Every time a full AI-visibility check finishes, a generator inspects the results and writes alert rows for a fixed set of conditions — score drops, milestones, competitor overtakes, negative mentions, and transient platform failures. A separate path writes an alert when your Google Search Console connection is revoked. Alerts surface in two places: the bell dropdown in the top bar and the Notifications tab under Settings.
Top-bar bell
A live unread-count badge plus a dropdown of your latest 10 alerts, refreshed automatically while you work.
Settings -> Notifications
The full, paginated alert history with All / Unread / Read / Critical tabs and bulk mark-all-read.
Email (HIGH only)
HIGH-severity visibility alerts are emailed immediately to every member of your organization via Resend.
Per-brand scope
Alerts belong to a brand, not a user. Everyone in the org sees the same alerts, and switching the active brand changes what you see.
Alerts are per-brand, shared across your team
There is no per-user notification feed. The bell and the Notifications page always show alerts for the brand you currently have selected, and every member of your organization sees the same set.
No preference toggles yet
You cannot currently turn individual alert types on or off, or change email cadence. The Settings -> Notifications tab shows your alert list, not a configuration panel. See the Limitations subsection for the full list of what is and is not available today.
Alert Types & Triggers
Five alert kinds are produced from the visibility-check path, plus a Google Search Console disconnect alert and an email-only deindexation alert. Each rule has an exact threshold — most compare the current check against the previous one, so a brand needs at least two checks before comparison-based alerts can fire.
| Alert | Exact trigger | Severity | Surfaces |
|---|---|---|---|
| Visibility Drop | Previous overall score minus current >= 5.0 points (needs a previous check) | HIGH if drop >= 10, otherwise MEDIUM | In-app + email when HIGH |
| Score Milestone | Score crosses 50, 75, or 90 upward (current >= milestone > previous) | LOW | In-app only |
| Competitor Overtake | An active competitor logs more AI mentions than your brand this check, and your brand has at least 1 mention | MEDIUM | In-app only |
| Negative Mention | More new NEGATIVE-sentiment mentions than the previous window (only an increase fires) | HIGH if 3+ new, otherwise MEDIUM | In-app + email when HIGH |
| Retry Available (platform failure) | One or more AEO responses failed with a retryable error (429, credit exhaustion, timeout, 503), grouped by platform | MEDIUM | In-app only |
| GSC Disconnected | A Google Search Console token refresh returns invalid_grant (revoked) | HIGH | In-app only (no email) |
| Deindexation | A tracked page transitions from indexed to not-indexed (Indexation Monitoring) | n/a (email) | Email only, Pro+ |
Two flavours of the same type
Both the GSC-disconnect alert (HIGH) and the transient platform-failure alert (MEDIUM, "N responses failed temporarily — retry available") use the same underlying integration_error type. They are distinct in practice: the retry alert sends you to Prompt Tracking -> Query Details -> Retry, while the GSC alert sends you to reconnect in Settings.
Reading the thresholds
- Visibility Drop fires at a 5-point fall; a fall of 10 or more is escalated to HIGH (and therefore emailed).
- Score Milestone fires only on an upward crossing of 50, 75, or 90 — never when the score falls back through a milestone.
- Competitor Overtake only considers competitors that are both active and have status active, and requires your brand to have at least one mention so you are never told you have been "overtaken" from zero.
- Negative Mention diffs the count of negative mentions against the prior check window — only an increase triggers it, and 3 or more new negatives escalate to HIGH.
- Retry Available groups failures by platform, so you get one MEDIUM alert per affected AI engine with a direct path to retry.
When alerts are generated
The visibility-check generator runs after every full brand visibility check, manual or scheduled. That means the weekly Monday 02:00 IST check (all paid brands), the Tue-Sun 02:00 IST daily priority check (Pro 5/brand, Enterprise 10/brand), and any manual check you trigger all produce alerts on completion.
Severity & Read State
Each alert carries a severity and two independent state flags. Severity drives colour, the Critical filter, and whether an email is sent; read and resolve are separate actions you take on each alert.
| Severity | Used by | Emailed? |
|---|---|---|
| LOW | Score Milestone | No |
| MEDIUM | Competitor Overtake, Negative Mention (1-2 new), Retry Available | No |
| HIGH | Visibility Drop (>= 10pt), Negative Mention (3+ new), GSC Disconnected | Visibility-path HIGH alerts only |
| CRITICAL | Defined in the model but never produced by any code | Would email, but never occurs |
CRITICAL is always empty
No code path ever produces a CRITICAL alert. The Critical filter tab and critical styling exist, but in practice the Critical tab shows HIGH-severity alerts via the severity filter and the literal CRITICAL bucket stays empty.
Read vs Resolve
- Read marks an alert as seen and clears it from the unread count and the bell badge. It does not remove the alert.
- Resolve marks the alert as handled or dismissed and also marks it read. Resolved alerts stay in the list with a "Resolved" label so you keep the history.
- Mark all as read clears the unread badge for the current brand in one action (shown only when there are unread alerts).
In-App Notification Surfaces
Alerts appear in two synchronized places: the top-bar bell for a quick glance and the Settings -> Notifications page for the full history. Both read from the same per-brand alert store.
Top-bar bell dropdown
- Shows a red unread-count badge, capped at "99+".
- The unread count is refetched every 60 seconds in the background.
- Opening the dropdown lists your latest 10 alerts, refreshed every 30 seconds while open.
- Each row shows a severity pill, type label, title, a 2-line message clamp, relative time, and per-row mark-read and resolve buttons.
- A "View all notifications" footer link takes you to Settings -> Notifications.
Settings -> Notifications page
- Filter tabs: All, Unread (with a count), Read, and Critical.
- Paginated at 20 alerts per page with prev/next controls and a "Showing X-Y of N" indicator.
- "Mark all as read" appears whenever there are unread alerts.
- Per-row mark-read and resolve actions; resolved rows display a "Resolved" tag.
- Tab-specific empty states ("All caught up!", "No critical alerts", "No notifications yet").
The /notifications route redirects
The live page lives at /settings/notifications. The bare /notifications route simply redirects there, so both links land on the same Settings tab.
Email Notifications
A subset of alerts is also emailed. Right after a visibility check generates its alerts, TruIntel loads the brand organization and every member, then emails each HIGH-severity alert from that check to all org members. Emails go out immediately via Resend — there is no batching or digest.
A visibility check completes
The alert generator writes any alert rows that match its rules for the brand.
HIGH-severity alerts are selected
Only alerts at HIGH (or CRITICAL, which never occurs) from the visibility-check path qualify for email.
Every org member is emailed
For each qualifying alert, an email is enqueued to each organization member with subject "{icon} Alert: {title}" (a per-type emoji prefix) and a "View Details" button linking to your visibility dashboard.
Which alerts actually email
In practice only two kinds email: Visibility Drop of 10+ points and Negative Mention with 3+ new negatives. The HIGH GSC-disconnect alert is created outside the visibility-check path, so despite being HIGH it is in-app only and never emails. MEDIUM and LOW alerts never email.
Deindexation email (Pro+)
Indexation Monitoring sends a separate "N page(s) deindexed" email when a tracked page drops out of the index. This is email-only — it never creates an in-app alert and never shows in the bell or Notifications page. It requires Indexation Monitoring, which is a Pro and Enterprise feature with a connected GSC.
Plan Gating
The alert system itself has no explicit plan gate — the alert center and all five alert endpoints are available on every plan, including Free. Gating is indirect, determined by what produces the alerts.
| Capability | Availability | Why |
|---|---|---|
| Alert center (bell + Settings tab) and all alert endpoints | All plans incl. Free | No plan check on the alert routes or service |
| Alert generation from visibility checks | Any brand that gets a check | Free can run manual checks; weekly scheduled checks are paid plans only |
| HIGH-severity alert emails | All plans | Tied to the check that produced the alert; no plan gate on emailing |
| GSC-disconnect alert | Requires a connected GSC | SEO/GSC features are Starter and up |
| Deindexation email | Pro / Enterprise only | Indexation Monitoring is a Pro+ feature (Pro 1000/day, Enterprise 2000/day pages) |
No retention or count caps
There is no per-plan limit on how many alerts a brand can accumulate or how long they are retained. All alerts persist until you resolve or stop tracking the brand.
API & Routes Reference
The alerts router is mounted under /api/v1. Every endpoint verifies that the target brand belongs to the caller current organization before returning data.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/v1/alerts/{brand_id} | List alerts (paginated; filter by is_read, severity, alert_type; limit 1-100, default 20) |
| GET | /api/v1/alerts/{brand_id}/count | Unread count for the brand |
| PATCH | /api/v1/alerts/{alert_id}/read | Mark a single alert as read |
| POST | /api/v1/alerts/{brand_id}/read-all | Mark all unread alerts read (returns updated_count) |
| POST | /api/v1/alerts/{alert_id}/resolve | Mark an alert resolved (also marks it read) |
The alert_type filter rejects integration_error
The alert_type query filter only accepts visibility_drop, competitor_overtake, negative_mention, new_competitor, and score_milestone. Filtering by integration_error returns a 400 validation error even though such alerts exist. In the UI, filtering is by severity and read-state, not by type.
Notifications (redirect)
/notificationsRedirects to the live Settings -> Notifications tab.
Settings -> Notifications
/settings/notificationsThe full paginated alert center with All / Unread / Read / Critical tabs.
Limitations & Roadmap
Notification preferences are not yet available. The Settings -> Notifications tab renders your alert list, not a configuration panel — there is no working toggle UI and no backend preferences endpoint today.
- No per-type opt-in/out: you cannot mute or enable individual alert kinds.
- No email-frequency or digest control: HIGH visibility alerts always email immediately, and there is no Instant / Daily / Weekly digest setting.
- No SMS, Slack, or webhook delivery for end users — alerts are in-app plus email only.
- CRITICAL severity is never produced, so the Critical bucket relies on HIGH alerts.
- The new_competitor alert type is defined but never generated — treat it as not implemented.
- Deindexation alerts are email-only (Pro+) and never appear in the in-app center.
What "Configuration" would have been
A preferences panel (alert toggles plus an Instant / Daily Digest / Weekly Digest selector) exists only as unreleased, non-rendered code with no backend support. Do not rely on it. Email cadence is fixed at immediate, for HIGH-severity visibility alerts only.
Settings & Account
Settings Overview
Settings is a single page in the TruIntel app, split into seven tabs driven by the URL path. Some tabs act on your user account, some on the current organization, and some on the currently-selected brand. Every Settings route is protected by login only — there is no plan-gating at the tab level, though individual capabilities inside a tab (such as Google Search Console) may require a paid plan.
| Tab | Route | Scope | What it controls |
|---|---|---|---|
| Profile | /settings | Per-user + org | Name, password, organization rename, sign out |
| Brand Voice | /settings/brand-voice | Per-brand + per-user | AI writing voice profile + outreach sender identity |
| Team | /settings/team | Per-organization | Members, roles, invitations |
| Billing | /settings/billing | Per-organization | Plan, usage meters, credits, payment history |
| Notifications | /settings/notifications | Per-brand | In-app alerts inbox |
| Integrations | /settings/integration | Per-brand | Google Search Console, Traffic, Lead Verification |
| Support | /settings/support | Per-user | Create and track support tickets |
No customer API keys
TruIntel does not offer a public or programmatic customer API. There is no API Keys tab in the live product — any /settings/api-keys URL simply falls through to the Profile tab. Do not expect to generate API tokens here.
Profile & Account
The Profile tab manages your personal account details and the name of your current organization. It has three cards: Profile info, Change Password, and a combined Organization rename + Sign Out card.
Full name
Editable and saved to your user account. The Save button stays disabled until you change something.
Email (read-only)
Your email is shown with a Verified badge but is a disabled field. Email cannot be changed from Settings.
Organization name
Rename the current organization inline. Owners and admins only — members cannot rename the org.
Avatar upload is coming soon
The avatar shows your initials (or a stored avatar URL). The camera overlay button is labeled "Upload avatar (coming soon)" and is not yet functional.
Change Password
Changing your password requires your current password plus the new password entered twice. The UI enforces a minimum of 8 characters and that both new-password fields match.
Google sign-in accounts cannot set a password here
Password change only works for email/password accounts. If you signed up with Google, the backend returns "Password change not available for Google OAuth users." Manage that login through Google.
Sign Out
The Logout button signs you out on this device only. It revokes just the one refresh token presented and clears the single sign-on cookie. This is a single-device logout — it is not a "sign out from all devices" action, and the current access token remains valid until its 30-minute expiry.
Switching organizations is separate
The Profile tab only renames the current organization. Switching between organizations you belong to is done from the organization/brand switcher elsewhere in the app, not on this page.
Brand Voice & Sender Identity
The Brand Voice tab defines how AI writes for the currently-selected brand. The voice profile is saved per-brand and is woven into every AI content generation prompt — CMS article drafts, content briefs, and AI outreach emails. If no brand is selected, the tab shows an empty state.
| Setting | Options / type | Purpose |
|---|---|---|
| Tone | Educational / Conversational / Authoritative | Overall writing voice |
| Reading level | Simple / General / Professional / Expert | Target audience sophistication |
| Allow em dashes | Toggle | Whether em-dash punctuation is permitted |
| Allow bullet lists | Toggle | Whether bulleted lists may be used |
| Allow calls to action | Toggle | Whether CTAs may be added |
| Audience | Free text (up to 500 chars) | Describes who the content is for |
| Do's | List editor | Phrases, themes, or rules to always follow |
| Don'ts | List editor | Phrases, themes, or rules to always avoid |
How the voice is applied
The saved profile is rendered into a "Writing style & voice" block that is spliced into the research, outline, draft, and quality stages of content generation. When no profile is set, it falls back to a sensible default keyed off the brand's scanned tone.
Editable on any plan, realized in CMS
Editing the voice profile is available on every plan because it is just a brand field. The content generation it influences (CMS drafts and briefs) requires Starter or above.
Outreach Sender Identity
The same tab includes an Outreach Sender Identity card. This is a per-user, account-wide identity used to fully sign AI-drafted outreach emails, removing placeholder fields like [Name] and [Title]. You enter it once and it auto-fills every draft across all of your brands. Your display name comes from the Full Name field in Profile.
- Job title — up to 120 characters
- Phone — optional, up to 40 characters
- Email signature — optional, up to 2000 characters
- Leaving a field empty explicitly clears it; saving the identity never wipes your display name
Team Management
The Team tab manages members of the current organization. You can view members with their roles and join dates, invite new people by email, change roles, and manage pending invitations. Permissions are strictly enforced by the backend based on your role.
Roles
| Role | Invite members | Change roles | Remove members | Rename org |
|---|---|---|---|---|
| Owner | Yes | Yes (only owners) | Anyone | Yes |
| Admin | Yes | No | Members only | Yes |
| Member | No | No | No | No |
The last owner is protected
Only owners can change roles, and the UI only assigns admin or member. The last remaining owner cannot be demoted or removed, so an organization always keeps at least one owner.
Inviting members
Enter the email
An owner or admin enters the invitee's email address.
Existing accounts join immediately
If the email already has a TruIntel account, the person is added directly as a member and emailed a "you've been added" notice.
New people get an invite link
Otherwise a pending invitation with a unique secure token is created and an invite email is sent. They join the org when they register or accept.
Manage pending invites
Owners and admins can view pending invitations, resend (re-inviting reuses the same token), or cancel them.
Invitations expire after 7 days
Each invitation token is valid for 7 days. After that the invitee must be re-invited.
Team size by plan
| Plan | Team members |
|---|---|
| Free | 1 |
| Starter | 1 |
| Lite | 2 |
| Pro | 3 |
| Enterprise | 10 |
Billing & Subscription
The Billing tab shows your current plan, live usage meters, your credit balance, and payment history. Billing is per-organization and processed through Razorpay. All plans are billed monthly — there is no annual billing in the product.
Current Plan
Plan name, a Monthly badge, price, and next-charge date (or "Access until" / "Cancels at period end" when cancelled). Includes Upgrade and Cancel buttons.
Usage & Credits
Four live meters computed across all brands: Brands, Queries, Competitors, and Plan Credits (shown under the legacy "Plus Credits" label).
Buy Extra Credits
Fixed top-up packs with a Buy Now Razorpay flow. Top-up credits never expire. India orders show a GST line.
Payment History
Plan and top-up charges with date, amount, GST line where applicable, status, and a downloadable invoice.
Corrections to older docs
There is no "checks performed" usage meter — the meters are Brands, Queries, Competitors, and Plan Credits. Top-ups are fixed packs, not custom amounts. Billing is monthly only; any "annual / save 20%" toggle in marketing is display-only.
Plans and prices
Free
$0
- Permanent, one-time-per-org tier
- 1 brand, 5 queries
- All 5 AEO platforms
- 0 monthly credits
Starter
$59/mo
- 1 brand, 10 queries
- 1 competitor, 1 member
- 50 monthly credits
- CMS, Insights, Reports
Lite
$139/mo
- 2 brands, 25 queries each
- 3 competitors, 2 members
- 150 monthly credits
- Everything in Starter
Pro
$479/mo
- 3 brands, 40 queries each
- 5 competitors, 3 members
- 500 monthly credits
- Rank Tracking, Outreach, daily checks
Enterprise pricing is custom (contact sales): up to 4 brands, 50 queries per brand, 5 competitors, 10 team members, and 1000 monthly credits. India organizations pay 18% GST on top of the USD price.
Credits and top-up packs
Credits live in two pools. Plan credits are your monthly allowance and reset on the 1st of each month. Extra credits come from top-up packs and never expire. When you spend, plan credits are used first, then extra credits.
| Top-up pack | Credits | Price | Expiry |
|---|---|---|---|
| Starter Pack | 50 | $10 | Never expire |
| Growth Pack | 150 | $25 | Never expire |
| Pro Pack | 400 | $50 | Never expire |
The old "TruIntel Plus" subscription is discontinued
A separate sellable Plus credit subscription no longer exists — that pool is always 0 and unspendable. The Billing UI still labels your monthly plan-credit allowance as "Plus Credits" for legacy reasons, but it is your regular plan credits.
Cancelling
Cancelling an active subscription stops it at the end of the current billing cycle — you keep access until the period end, after which the card shows "Cancels at period end." India payments also generate a self-served GST invoice in addition to the Razorpay invoice.
Notifications (Alerts Inbox)
The Notifications tab is your in-app alerts inbox, not a preferences screen. It is per-brand and surfaces important changes TruIntel detects for the currently-selected brand. The legacy /notifications route redirects here.
It is an inbox, not toggles
There are no notification preference switches here. This tab lists, filters, and lets you act on brand alerts. There is no notification-preferences backend route in the product.
Alert types
Visibility drop
Your AEO visibility score has fallen.
Competitor overtake
A tracked competitor has moved ahead of you.
Negative mention
A negative-sentiment brand mention was detected.
Score milestone
You hit a notable visibility-score milestone.
New competitor
A new competitor surfaced in AI answers.
Filters and actions
- Filter tabs: All, Unread (with count), Read, Critical
- Severities: critical, high, medium, low
- Mark a single alert as read, or mark all as read
- Resolve an alert to clear it from active view
- Paginated list for browsing alert history
Integrations
The Integrations tab connects external data sources to the currently-selected brand. There are three per-brand integrations: Google Search Console, Traffic Intelligence, and Lead Verification.
Google Search Console
Connect Google Search Console (GSC) to feed real search performance and indexation data into TruIntel's SEO features. Connection uses a Google OAuth popup with read-only Search Console scope. After authorizing, you pick which verified property to link to the brand and TruIntel queues an immediate data sync.
GSC requires a paid plan
The entire Integrations GSC flow requires a paid plan — the Free tier is excluded. Starter and above can connect GSC.
- OAuth tokens are Fernet-encrypted at rest (AES-128-CBC + HMAC-SHA256)
- Access tokens are proactively refreshed when under 5 minutes of validity remain
- If Google access is revoked, the integration is marked inactive with a "Please reconnect" prompt
- You can list verified properties, connect one, view connection status and last sync, and disconnect
Traffic Intelligence & Lead Verification
These two integrations are snippet-based: you install a small script on your site. Each card shows an Active badge and a 30-day count (visits for Traffic, leads for Lead Verification) once data flows in, plus a Setup / View Snippet button that opens an install modal.
Traffic Intelligence
Snippet-based visit tracking with bot-vs-human classification. Shows an Active badge and 30-day visit count. To disconnect, remove the snippet from your site.
Lead Verification
Snippet-based lead capture and scoring. Shows an Active badge and 30-day lead count. To disconnect, remove the snippet from your site.
Support
The Support tab lets you create support tickets and track their status. Tickets are reviewed by the TruIntel team and updated in-app — there is no separate email thread for status changes.
Creating a ticket
Pick a category
Choose one of four categories: Bug Report, Question, Feature Request, or Refund Request.
Add a subject and description
Subject up to 255 characters and a description up to 5000 characters.
Attach a payment for refunds
Refund tickets can optionally reference a specific payment, which is validated to belong to your organization.
Submit and track
Your ticket appears in the ticket history with its current status. Expand a row to see status and subject.
| Ticket status | Meaning |
|---|---|
| Open | Submitted and awaiting review |
| In progress | Being worked on by the team |
| Resolved | A resolution has been applied |
| Closed | Ticket has been closed out |
Status is in-app only
TruIntel does not send an email when a ticket's status changes. Check the Support tab in-app to see updates. A Help & resources panel with quick links is also shown alongside the Settings page.
Routes & Plan Gating
All Settings tabs are reachable by any logged-in user — the routes themselves are protected by login only. Plan gating happens inside specific capabilities (notably GSC) and through team-size and credit limits.
Profile
/settingsName, password, org rename, sign out
Brand Voice
/settings/brand-voiceAI voice profile + outreach sender identity
Team
/settings/teamMembers, roles, invitations
Billing
/settings/billingPlan, usage, credits, payment history
Notifications
/settings/notificationsPer-brand alerts inbox
Integrations
/settings/integrationGSC, Traffic, Lead Verification
Support
/settings/supportCreate and track tickets
| Capability | Free | Starter | Lite | Pro | Enterprise |
|---|---|---|---|---|---|
| Profile / password / org rename | Yes | Yes | Yes | Yes | Yes |
| Brand Voice + sender identity | Yes | Yes | Yes | Yes | Yes |
| Team members (cap) | 1 | 1 | 2 | 3 | 10 |
| Billing & credits | Yes | Yes | Yes | Yes | Yes |
| Notifications inbox | Yes | Yes | Yes | Yes | Yes |
| Google Search Console | No | Yes | Yes | Yes | Yes |
| Traffic / Lead snippet | Yes | Yes | Yes | Yes | Yes |
| Support tickets | Yes | Yes | Yes | Yes | Yes |
Security notes
OAuth integration tokens are encrypted at rest with Fernet. Access tokens (JWT) last 30 minutes; refresh tokens are database-stored with rotation. Logout is single-device and does not revoke other sessions.
Plans & Pricing
Plans Overview
TruIntel is sold as monthly subscriptions in five tiers: a permanent Free tier plus four paid plans — Starter, Lite, Pro, and Enterprise. Every plan includes all five AI Visibility platforms (ChatGPT, Claude, Gemini, Perplexity, and Google AI Overview); the tiers differ in brand count, query volume, monitoring cadence, AI credits, and access to advanced SEO and growth tooling.
Free
$0 / forever
- Permanent free tier (not a trial)
- 1 Brand, 5 queries
- All 5 AI platforms
- 1 Competitor tracked
- 500 Site Audit pages
- Weekly AI Visibility check
- No AI credits / no SEO keywords
- No credit card required
Starter
$59/mo
- 1 Brand, 10 queries
- All 5 AI platforms
- 1 Competitor tracked
- 50 AI Credits / month
- 750 GSC Keywords
- 10K Site Audit pages
- 200 Lead Verifications / month
- Weekly insights + email reports
Lite
$139/mo
- Everything in Starter, plus:
- 2 Brands, 25 queries / brand
- 3 Competitors tracked
- 150 AI Credits / month
- 2,000 GSC Keywords
- 50K Site Audit pages
- 1,000 Lead Verifications / month
- 2 Team members
Pro
$479/mo
- Everything in Lite, plus:
- 3 Brands, 40 queries / brand
- 5 Competitors tracked
- 500 AI Credits / month
- Daily priority AEO (5 / brand)
- Rank Tracking + Indexation Monitoring
- Weekly SEO + backlink checks
- 3 Team members, priority support
Enterprise — contact sales
Enterprise is custom-priced (shown as "Custom"). It extends Pro to 4 brands, 50 queries / brand (200 total), 10 daily-priority AEO checks per brand, 1,000 AI credits / month, 10 team members, and a dedicated account manager. Reach out to sales to activate it.
All AI platforms on every plan
There is no platform paywall. ChatGPT, Claude, Gemini, Perplexity, and Google AI Overview are tracked on all five tiers including Free — plans only change how many brands and queries you can monitor and how often.
The Free Tier
TruIntel does not run a time-based free trial. The Free offering is a permanent, hard-limited tier that you activate once per organization without a credit card. It is meant for evaluating the platform and tracking a single brand at a small scale.
Not a 7-day trial
Some marketing copy references a "free trial," but there is no trial timer in the product. Free is a permanent $0 tier with fixed caps — it never expires and never auto-converts to a paid plan. (The only time-boxed window in billing is a 3-day grace period after a failed paid renewal, which is unrelated to Free.)
What Free includes
- 1 brand and 5 tracked queries
- All 5 AI Visibility platforms
- 1 competitor tracked
- Weekly AI Visibility check (no daily-priority monitoring)
- AI Crawlability / site audit up to 500 pages
- 1 PageSpeed check per month
- Core AEO surfaces: Overview, AI Visibility, Prompt Tracking, Prompt Research, Sources, Competitors, Internal Linking, Indexation view, SEO Dashboard, OnPage Checker, Visualization, Settings
What Free does NOT include
- No AI credits (CMS / Plus content generation is unavailable)
- No GSC keyword tracking, keyword suggestions, or backlink tracking
- No Prompt Volumes lookups
- No Insights, Tasks, Reports, Traffic, or Leads pages
- No CMS / Content Studio publishing
- No team members beyond the owner (team size 1)
Feature Limits by Plan
The table below is the authoritative per-plan limit matrix. A dash (—) means the feature is locked on that tier. All prices are monthly in USD.
| Feature | Free | Starter ($59) | Lite ($139) | Pro ($479) | Enterprise |
|---|---|---|---|---|---|
| Brands | 1 | 1 | 2 | 3 | 4 |
| Queries / Brand | 5 | 10 | 25 | 40 | 50 |
| Total Tracked Queries | 5 | 10 | 50 | 120 | 200 |
| AI Platforms | 5 | 5 | 5 | 5 | 5 |
| Competitors Tracked | 1 | 1 | 3 | 5 | 5 |
| Team Members | 1 | 1 | 2 | 3 | 10 |
| AEO Check Cadence | Weekly | Weekly | Weekly | Weekly | Weekly |
| Daily Priority AEO / Brand | — | — | — | 5 | 10 |
| AI Credits / month | 0 | 50 | 150 | 500 | 1,000 |
| GSC Keywords | — | 750 | 2,000 | 5,000 | 10,000 |
| Keyword Suggestions / mo | — | 20 | 50 | 200 | 500 |
| Keyword Rank History | — | 1 mo | 3 mo | 12 mo | 12 mo |
| Site Audit Pages | 500 | 10K | 50K | 100K | 200K |
| Top Organic Keywords | 50 | 50 | 100 | 200 | 500 |
| SEO Competitors | 1 | 1 | 3 | 5 | 5 |
| Backlink Tracking | — | Monthly | Monthly | Weekly | Weekly |
| PageSpeed Checks / mo | 1 | 5 | 5 | 15 | 30 |
| CTR Suggestions / mo | — | 5 | 15 | 50 | 200 |
| Internal Link Scans / mo | — | 5 | 15 | 50 | 200 |
| Indexation Monitoring | — | — | — | 1,000 / day | 2,000 / day |
| Rank Tracking Keywords | — | — | — | 25 | 100 |
| Traffic Log Retention | — | 1K | 5K | 25K | 50K |
| Lead Verifications / mo | — | 200 | 1,000 | 2,000 | 5,000 |
| Prompt Volumes Page | — | 2 / mo | 5 / mo | 25 / day | 200 / day |
| Onboarding AI Volume | — | 5 / day | 6 / day | 30 / day | 60 / day |
| Topics Perf. AI Volume | — | — | — | 20 / day | 100 / day |
| Credit Redemption (5 = 1 PV) | — | ✓ | ✓ | ✓ | ✓ |
How limits resolve
For an unknown plan the system falls back to Starter limits, and an unrecognized limit key resolves to 0. The weekly AEO check covers every paid plan; daily-priority AEO is the additive Pro/Enterprise feature on top of it.
Pro & Enterprise-Only Features
A handful of higher-cost, metered features are gated to Pro and Enterprise because they carry per-call SERP or LLM spend or run on a daily cadence. These are locked (limit 0) on Free, Starter, and Lite.
Rank Tracking
Track SERP positions for tracked keywords via DataForSEO. Pro: 25 keywords with a $10/brand/month spend cap; Enterprise: 100 keywords at $40/brand/month. Manual-run, metered, and budget-guarded per brand.
Indexation Monitoring
Daily GSC URL Inspection index-status tracking. Pro inspects up to 1,000 URLs/day, Enterprise up to 2,000/day. Requires a connected Google Search Console property.
Daily-Cadence Prompt Volumes
The Prompt Volumes page runs on a daily cadence on Pro (25/day) and Enterprise (200/day). Starter and Lite only get a small monthly allotment (2/mo and 5/mo).
Topics Performance "AI Volume"
The AI search-volume column on Topics Performance is Pro+ only: Pro 20 batches/day, Enterprise 100 batches/day.
Daily-Priority AEO Checks
Beyond the weekly full check, Pro and Enterprise re-run your highest-priority prompts daily (Tue–Sun): 5 per brand on Pro, 10 per brand on Enterprise.
Gap & Outreach Tooling
Keyword Gap, Backlink Gap, Outreach, and Directory pages are Pro+ growth features for competitive and link-building workflows.
AI Credits
AI credits power generative actions — primarily CMS / Plus content drafting and repurposing. Each paid plan includes a monthly credit allowance, and you can top up with one-time packs that never expire.
Two credit pools
- Plan credits — your monthly allowance (Starter 50, Lite 150, Pro 500, Enterprise 1,000; Free 0). Granted on plan activation and reset to the plan allowance on the 1st of each month. Upgrading or downgrading immediately re-syncs this pool to the new plan.
- Extra credits — bought via top-up packs. These never expire and are never reset on the monthly rollover.
Deduction order
Spending always draws from your monthly plan credits first, then from your never-expiring extra credits. If your combined balance is below the action cost, the action is blocked and nothing is deducted.
What credits cost
| Action | Credit cost |
|---|---|
| CMS / Plus content draft (standard tier) | 1 credit |
| CMS / Plus content draft (premium tier) | 3 credits |
| Content brief generation | Free (0 credits) |
| Repurpose existing content | 1 credit |
| Prompt Volumes page lookup (after cap) | 5 credits = 1 lookup |
Prompt Volumes redemption
On paid plans, once your daily/monthly Prompt Volumes cap is exhausted you can keep going by redeeming 5 credits per page lookup. Redemption applies to Prompt Volumes page lookups only (not onboarding lookups) and requires at least 5 available credits.
"TruIntel Plus $99/mo" is discontinued
An older "TruIntel Plus" subscription credit pool has been retired and is unspendable. There is no sellable Plus subscription — credits come only from your monthly plan allowance and one-time top-up packs.
Credit Top-Up Packs
When you need more credits than your monthly allowance, buy a one-time top-up pack. Packs are charged as a single payment (not a subscription), land in your extra-credits pool, and never expire. Pricing is flat regardless of your plan.
| Pack | Credits | Price | Per credit |
|---|---|---|---|
| Starter Pack | 50 | $10 | $0.20 |
| Growth Pack | 150 | $25 | ~$0.17 |
| Pro Pack | 400 | $50 | $0.125 |
Buying more is cheaper
Larger packs lower the per-credit price. Extra credits never expire and are never wiped on the monthly reset, so it is safe to stock up.
Billing & Payments
Subscriptions are billed monthly through Razorpay. Recurring plans use the Razorpay Subscriptions API; credit top-ups use the one-time Orders API.
- Billing is monthly only — there is no annual billing in the product.
- Plans renew automatically each month until cancelled.
- Upgrade or downgrade at any time; the change takes effect immediately and re-syncs your credit allowance to the new plan.
- A failed renewal charge enters a 3-day grace period before the plan is deactivated, with an email notification.
- Credit top-ups are one-time, non-refundable purchases that never expire.
- Payment history and downloadable receipts are available in Settings → Billing.
Annual "Save 20%" is display-only
The marketing site shows an Annual toggle with "Save 20%" pricing and struck-through regular prices ($79 / $189 / $599). Both are visual only — there is no backend annual billing and no separate annual plan. Every checkout subscribes you on the monthly cycle at the launch price ($59 / $139 / $479).
India GST
For organizations billed in India (billing country IN), 18% GST is added on top of the USD price. The charge currency stays USD; GST is computed on top under SAC code 998314 (seller: Marqait AI LLP, Bengaluru).
Plan Gating by Feature Area
Beyond numeric limits, some product areas only unlock at higher tiers. This is the practical map of which sections each tier can reach.
| Feature area | Minimum plan |
|---|---|
| Overview, AI Visibility, Prompt Tracking / Research | Free |
| Sources, Competitors, AI Crawlability / Site Audit | Free |
| Internal Linking, Indexation view, OnPage Checker | Free |
| SEO Dashboard, PageSpeed, Visualization, Settings | Free |
| Prompt Volumes, Insights, Tasks, Reports | Starter |
| CTR Optimization, Plus, Content Studio, CMS | Starter |
| Traffic, Leads | Starter |
| SEO Backlinks, Keywords, Keyword Suggestions, Images | Starter |
| Keyword Gap, Backlink Gap | Pro |
| Outreach, Directory | Pro |
| Rank Tracking | Pro |
Manage your subscription
Change plans, buy credit packs, view invoices, and update payment details from Settings → Billing inside the app.
AI Models & Platform Schedule
How TruIntel Uses AI
TruIntel calls several different AI models, each for a specific job. Five of them power the AI Visibility scoreboard (the "platforms" you see your brand ranked across); the rest quietly run content generation, insights, scoring, and source classification behind the scenes. This section is the single source of truth for which model runs where, and on what schedule the automated jobs fire.
Model ids change — capabilities do not
The specific model versions below are the production defaults verified on 2026-06-27. The three foundation AEO models (ChatGPT, Claude, Gemini) are environment-overridable, so TruIntel can swap to a newer snapshot without a code change. Treat the ids as "current", not permanent.
AI Visibility (5 platforms)
ChatGPT, Claude, Gemini, Perplexity, and Google AI Overview answer your tracking queries so TruIntel can measure how each AI engine sees your brand.
Response enrichment
A non-blocking Kimi K2.5 pass refines rank position, mention type, and brand attributes — it never changes the numeric visibility score.
Content & insights
CMS content generation, AI SEO scoring, the Insights pipeline, and brand-report summaries run on Gemini 3.1 Flash-Lite.
Utility fallback chain
Onboarding suggestions, competitor discovery, and CMS drafts use a provider-agnostic chain (Anthropic-first) that self-heals if one provider is down.
The same models on every plan
Model selection is global, never plan-tiered. A Free brand is checked with the exact same gpt-5.4-mini / claude-haiku-4-5 / gemini-3.1-flash-lite / perplexity-sonar / SearchAPI AIO stack as Enterprise. Plans gate how many queries, brands, and features you get — not which AI runs.
The 5 AI Visibility Platforms
Every AI Visibility check runs each active tracking query against all five platforms concurrently. Each one is a different real AI engine (or, for Google AI Overview, Google's own AIO surfaced through a SERP vendor). All five are available on every plan, including Free.
ChatGPT
OpenAI gpt-5.4-mini via the Responses API with the web_search tool (default search context depth: low). Env-overridable via OPENAI_MODEL.
Claude
Anthropic claude-haiku-4-5 (Haiku 4.5) via the Messages API with the web_search_20250305 server tool (max 5 searches per query). Env-overridable via ANTHROPIC_MODEL.
Gemini
Google gemini-3.1-flash-lite via the google-genai SDK with Google Search grounding; falls back to non-grounded mode if grounding is blocked. Env-overridable via GEMINI_MODEL.
Perplexity
perplexity/sonar routed through OpenRouter (not Perplexity's own API). Sonar performs real-time web search and returns citations.
Google AI Overview
Google's own AI Overview, scraped live from the SERP via SearchAPI.io in a two-step flow. Not a model TruIntel runs — many queries simply have no AI Overview.
| Platform | Model id | Route / API | Web search | Client timeout |
|---|---|---|---|---|
| ChatGPT | gpt-5.4-mini | OpenAI Responses API (/v1/responses) | web_search tool, context = low | 150s |
| Claude | claude-haiku-4-5 | Anthropic Messages API (SDK) | web_search_20250305, max_uses 5 | 60s |
| Gemini | gemini-3.1-flash-lite | google-genai SDK | Google Search grounding (+ fallback) | 60s |
| Perplexity | perplexity/sonar | OpenRouter (/api/v1/chat/completions) | Sonar built-in, returns citations | 30s |
| Google AI Overview | n/a (Google's AIO) | SearchAPI.io two-step search | live SERP scrape | 30s |
Perplexity runs through OpenRouter
TruIntel does not call Perplexity's own API. The Perplexity platform is the perplexity/sonar model accessed via OpenRouter using OPENROUTER_API_KEY. The standalone Perplexity API key in config is dead/unused.
"No AI Overview" is normal, not a failure
Google AI Overview does not exist for every query. When SearchAPI.io returns no AIO for a query, TruIntel flags it aio_available = false, treats it as a legitimate non-result, and excludes that query/platform pair from the visibility-score denominator so your brand is never penalised for it.
Per-Platform Query Mechanics
Each platform client is tuned to its provider's quirks. The ChatGPT and Claude clients make a single attempt, while the Gemini and Perplexity clients do a few short in-call retries on transient errors; all persistent, cross-time retrying is owned by the retry pipeline (see AI Visibility docs). Here is what is special about each one.
ChatGPT — search depth controls cost
Uses the Responses API with web_search at context depth low by default (env OPENAI_SEARCH_CONTEXT_SIZE). Low is the cheapest tier; medium and high cost more per call. Country and date are injected via the top-level instructions field.
Claude — capped web searches
The Messages API web-search server tool is capped at max_uses 5 per query. A hard 400 "credit balance is too low" is treated as fatal and non-retryable so doomed retries never burn the backoff window.
Gemini — grounding with a safety net
Uses Google Search grounding; if grounding hits a billing/permission/quota error it automatically falls back to a non-grounded answer so the brand still gets a Gemini response. After a quota/429 error it opens a 1-hour module-level cooldown and skips Gemini calls.
Perplexity — fresh citations
Sends return_citations = true with no recency filter, so Sonar returns real-time web results with source links. Routed through OpenRouter; retries up to 3 times on 429/503/timeout.
Google AI Overview — two-step scrape
Step 1 runs a normal google search to obtain a page_token; step 2 calls the google_ai_overview engine with that token (which expires in under a minute) to fetch the AIO content. Concurrency is capped at 2 with a 1.5-2.5s stagger to avoid 429 bursts.
Deterministic scoring (no LLM)
Once each AI response is returned, brand-mention parsing is pure text/regex (sub-50ms, no AI call). The visibility score is computed deterministically from that parse — no model sees the scoring step.
Brand-name alias gap (known limitation)
Mention matching only covers name and domain morphologies (lowercased, CamelCase split, hyphenated, no-spaces, domain, domain-without-TLD). It does not match custom aliases or the bare first word of a multi-word name. A brand surfaced under a short product name (e.g. "River Mobility" appearing as "River Indie") can be under-counted as not mentioned even when it was recommended.
Prompt Localization & Response Cache
Two behaviours explain "why do I see local brands instead of global ones?" and "why does a manual re-run differ from the weekly snapshot?" — prompt localization and the 24-hour response cache.
Country + date anchoring
A user_location hint alone only biases the search backend — the model still tends to answer globally. So TruIntel injects the brand's country, the current local date, and a "prefer local brands/retailers/sources, only fall back to international when no strong local option exists" directive into both the system prompt and the user query, mirroring what a real in-country user would see.
- The original query text is always preserved for storage, display, and the cache key — only the model-facing copy is localized.
- Date is the local "now" for the brand's country (e.g. Asia/Kolkata for India), so answers reflect current information as of the right month.
- Queries that already name the country are sent untouched (no double-anchoring).
- 20 supported countries: IN, US, GB, CA, AU, DE, FR, AE, SG, JP, BR, SA, ID, MY, PH, NG, KE, ZA, MX, TR (default IN). Unmapped countries fall back to a US/English search profile.
- Kill switch AEO_LOCALIZE_PROMPTS (default true) reverts to the bare persona prompt with no code change.
24-hour response cache
| Check type | Reads cache? | Writes cache? | Why |
|---|---|---|---|
| Scheduled / weekly | Yes (24h TTL) | Yes | Reuses recent AI answers to control provider cost. |
| Manual / on-demand | No (force fresh) | Yes | Always hits the provider live, but stores the result for later scheduled checks. |
| Retries | No (force fresh) | Yes | A stale success must never masquerade as a recovery. |
Why a manual re-run can differ
The cache key is the original query text plus country (md5), so localization changes never bust it. Only successful, non-empty responses are cached; errors never are. Because manual checks skip the read cache, a fresh "run now" can return different results than the cached weekly snapshot.
Content, Insights & Utility Models
Beyond the five visibility platforms, TruIntel uses a small set of models for content generation, scoring, insights, and classification. Many of these route through a shared, provider-agnostic fallback chain so a single provider outage never breaks the feature.
Utility LLM fallback chain
Onboarding topic/query/competitor suggestions, CMS content steps, and the insights fallback all flow through a shared chain that tries providers in order until one returns valid output. Default order is anthropic, gemini, openai, openrouter (Anthropic-first, because it is the most reliably funded provider). Any exception, timeout, or unparseable/non-JSON output advances to the next key, then the next provider.
| Chain slot | Model used | Route |
|---|---|---|
| anthropic | claude-haiku-4-5 | Anthropic Messages API |
| gemini | gemini-3.1-flash-lite | google-genai SDK |
| openai | gpt-5.4-mini | OpenAI (JSON mode) |
| openrouter | moonshotai/kimi-k2.5 (Kimi K2.5) | OpenRouter |
Self-healing — no redeploy needed
When a blocked provider's billing is restored, the chain picks it up automatically on the next call. Keeping the healthy provider first means the happy path never wastes an attempt on a dead provider.
Where each model runs
| Surface | Model | Notes |
|---|---|---|
| ChatGPT visibility | gpt-5.4-mini | OpenAI Responses API + web_search |
| Claude visibility | claude-haiku-4-5 | Anthropic Messages API + web search |
| Gemini visibility | gemini-3.1-flash-lite | Google Search grounding |
| Perplexity visibility | perplexity/sonar | via OpenRouter |
| Google AI Overview | Google's own AIO | via SearchAPI.io (two-step) |
| Response analyzer (enrichment) | moonshotai/kimi-k2.5 | Non-blocking; refines rank/type/attributes only |
| Source-domain classifier | moonshotai/kimi-k2.5 | Classifies cited-source domain types |
| CMS content generation | gemini-3.1-flash-lite | Via utility chain; premium tier costs 3 credits |
| CMS AI SEO scoring | gemini-3.1-flash-lite | 5-dimension LLM rubric, JSON mode |
| AI Insights pipeline | gemini-3.1-flash-lite | Falls back to utility chain on failure |
| Brand-report summaries | gemini-3.1-flash-lite | Weekly/monthly executive summaries |
| Prompt Volumes generator | gemini-3.1-flash-lite | Direct Gemini only (no fallback) |
| Onboarding / competitor suggestions | Utility chain (Anthropic-first) | topic/query/competitor discovery |
| SEO data (DA, backlinks, keywords) | DataForSEO APIs | Not an LLM — third-party SEO data |
Kimi K2.5 never changes your score
The response analyzer (Kimi K2.5) only enriches mentioned rows with a refined rank position, mention type, and brand attributes. It is non-blocking and, if it fails, the deterministic defaults stand. The numeric visibility score always comes from the pure-text parser.
Automated Schedule (IST)
TruIntel's background jobs run on a fixed Celery beat schedule of 27 tasks. All times are genuinely Indian Standard Time (Asia/Kolkata, UTC+5:30) — the scheduler evaluates crontab hours in IST, so do not mentally convert these to UTC.
Reading these times
To express any time below in UTC, subtract 5:30 (e.g. 02:00 IST = 20:30 UTC the previous day). The timezone is hard-coded and not environment-overridable.
| Time (IST) | Cadence | Task | Plans |
|---|---|---|---|
| Every minute | Always | CMS scheduled-publish poller | All |
| Every minute | Always | AEO failure auto-retry sweeper | All paid |
| Every 10 min | Always | Clean up stuck CMS generations (>15 min) | All |
| 12:00 AM | 1st of month | Monthly AI credit reset | All |
| 12:30 AM | Daily | Expired-trials / grace-period lock | All |
| 01:00 AM | Daily | Expired-subscriptions check | All |
| 02:00 AM | Monday | Weekly full AEO visibility check | All paid |
| 02:00 AM | Tue–Sun | Daily priority AEO check | Pro, Enterprise |
| 02:30 AM | Daily | Stale-subscription (abandoned checkout) cleanup | All |
| 03:00 AM | Daily | Daily traffic aggregation | All |
| 03:00 AM | 1st of month | Monthly CMS content refresh | All |
| 03:30 AM | Daily | Daily lead aggregation | All |
| 04:00 AM | 1st of month | Monthly SEO checks (free + paid) | All paid |
| 04:00 AM | Monday | Weekly SEO checks (DA, backlinks, on-page, competitors) | Pro, Enterprise |
| 04:00 AM | Wednesday | Backlink verification (still live + linking) | All paid |
| 04:00 AM | Sunday | Weekly traffic-log cleanup (>30 days) | All |
| 05:00 AM | Daily | GSC keyword sync (clicks/impressions/CTR/position) | All paid (if connected) |
| 05:00 AM | Sunday | Expired-invitation cleanup (>30 days) | All |
| 05:30 AM | Daily | GSC indexation check | Pro+ with GSC |
| 06:00 AM | Monday | Weekly AI insight generation | All paid |
| 06:00 AM | 2nd of month | Monthly brand report (+ outcome attribution) | All paid |
| 08:00 AM | Monday | Weekly visibility email report (Resend) | All paid |
| 09:00 AM | Sunday | Weekly content-opportunity refresh | All paid |
| 10:00 AM | Monday | Weekly brand report | All paid |
| Hourly :30 | Always | Clean up stuck scan jobs (>30 min) | All |
| Quarterly | Jan/Apr/Jul/Oct | Insight-report cleanup (>90 days) | All |
| Quarterly | Jan/Apr/Jul/Oct | SEO-report cleanup (>180 days) | All |
Schedule corrections vs older docs
Weekly SEO checks run at 04:00 IST Monday (not 03:00) and are Pro + Enterprise only (not all paid). Weekly brand reports run at 10:00 IST Monday (not 09:00). The monthly brand report is on the 2nd (intentionally, so it lands after the 1st-of-month SEO data).
Schedule Plan-Gating & Cadence
Most scheduled jobs check a brand's plan before doing work. Housekeeping tasks (credit reset, subscription/trial cleanup, traffic/lead aggregation, CMS cleanup) run across all rows regardless of plan.
| Job | Cadence | Plan gate |
|---|---|---|
| Weekly full AEO visibility check | Mon 02:00 | All paid (plan != Free) |
| Daily priority AEO check | Tue–Sun 02:00 | Pro (5/brand) + Enterprise (10/brand) with a priority query |
| Monthly SEO checks | 1st 04:00 | All paid |
| Weekly SEO checks | Mon 04:00 | Pro + Enterprise only |
| Daily GSC sync | Daily 05:00 | All paid with GSC connected |
| Daily indexation check | Daily 05:30 | Pro+ with GSC (2k URLs/day/property budget) |
| Weekly AI insights | Mon 06:00 | All paid with recent data |
| Weekly + monthly brand reports | Mon 10:00 / 2nd 06:00 | All paid |
| Weekly content-opportunity refresh | Sun 09:00 | All paid |
Daily priority checks need a priority query
The Tue–Sun daily priority sweep only runs for paid brands that have at least one query marked priority. Daily-priority AEO capacity is 0 on Free/Starter/Lite, 5 per brand on Pro, and 10 per brand on Enterprise. Brands with no priority queries are covered by the Monday full sweep.
On-demand checks are independent of the schedule
You can always run a manual "check now" on the dashboard. Manual checks skip the 24h cache and are capped at 10 immediate runs per organization per IST day; weekly and priority schedules are separate from that cap.
Workers, Queues & Reliability
Scheduled and on-demand jobs are processed by four background workers, each consuming a dedicated set of queues so a long site crawl never blocks a fast AEO retry. This is reference-level detail — you do not need it to use the product, but it explains throughput and isolation.
| Worker | Queues | Concurrency | Handles |
|---|---|---|---|
| worker-aeo | visibility, visibility_priority, insights | 3 | AEO checks, retries, insights, brand reports |
| worker-seo | seo, crawl | 3 | SEO checks, GSC sync, indexation, site crawls |
| worker-general | celery, email, traffic | 2 | Email reports, traffic + lead aggregation |
| plus-worker | plus | 2 | CMS generation/publish/scan, credit + subscription tasks |
Every-minute retry sweeper
A beat task sweeps failed AI-platform calls whose backoff has elapsed and re-runs them (exponential backoff 30s to 1h, max 6 attempts). It is a no-op when nothing is due.
Every-minute publish poller
CMS content scheduled for a future time is published by an every-minute poller — a reliable replacement for fire-and-forget delayed tasks that could be silently dropped.
Resilient broker
Connection pooling is disabled and each dispatch opens a fresh broker connection with an explicit queue, fixing a Railway stale-connection issue that silently dropped messages.
Task time limits
Tasks have a 16-minute hard / 15-minute soft default limit; report and opportunity jobs raise this to ~61 minutes. Workers auto-restart after 200 tasks or 512 MB to stay healthy.
Platform Reference
App Map & Route Counts
The TruIntel product app lives at app.truintel.ai. This reference is a complete map of every page, its URL path, what it does, and which plan unlocks it. Use it to find a feature fast or to confirm exactly what a given plan can reach.
The app router declares 99 route entries. Of those, roughly 90 render an actual page component, 7 are redirect aliases (e.g. /ai-mentions to /prompt-volumes), 1 is the 404 catch-all, and 1 is a dev-only preview. Every product page is lazy-loaded and wrapped in an auth guard; paid features add a plan gate on top.
| Layer | Count | Notes |
|---|---|---|
| Route entries declared | 99 | 80 product routes + 16 CMS routes + 3 literal paths |
| Page-rendering routes | ~90 | The rest are redirects, the 404, and a dev preview |
| CMS pages | 16 | All under /cms/*, gated behind the cms feature |
| Visible nav sections | 8 | A 9th (Tools) is hidden but its routes still resolve |
| Backend routers registered | 37 | Across 28 v1 router files plus the v2 package |
| Backend HTTP endpoints | 379 | 205 GET, 128 POST, 21 PATCH, 21 DELETE, 4 PUT |
How access works
ProtectedRoute requires a logged-in user. PlanGatedRoute additionally checks your plan against the feature minimum (order: Free < Starter < Lite < Pro < Enterprise). CMS pages add CMSProtectedRoute, which also loads your credit balance.
AI Visibility Pages
The AI Visibility section is the AEO core: it monitors how the five AI engines answer buyer questions about your brand. Most pages here are available on every plan, including Free.
Overview
/overviewSingle home dashboard: visibility score, recognition rate, mentions, average position, citation rate, sentiment, plus platform breakdown, competitor ranking, top sources, latest insight, tasks, traffic and lead summaries. Free.
AI Visibility
/visibilityCore AEO dashboard with the 0-100 visibility score gauge, per-platform performance, query mentions, brand attributes, and source tracking. Free.
Prompt Tracking
/queriesManage your tracked queries: search, filter, assign topics, toggle priority, and run bulk operations. Nav label is "Prompt Tracking". Free.
Query Detail
/queries/:queryIdDeep dive on one query: each platform's AI response, whether your brand was mentioned, rank position, competitor mentions, and citation URLs. Free.
Prompt Research
/prompt-researchDiscover what the AI engines say about your industry and competitors, and surface new query ideas worth tracking. Free.
Prompt Volumes
/prompt-volumesPer-keyword AI prompt-volume lookup across all five platforms. Starter and up; page-level lookups are daily on Pro and up. Extra lookups cost 5 credits each after the cap.
Sources & Citations
/sourcesDomains the AI engines cite when answering, with type breakdown, a donut chart, citation gap analysis, and URL-level detail. Free.
AI Crawlability
/visibility/site-auditAudit whether AI crawlers can reach your site (robots rules, bot access). Free.
AI Competitors
/competitorsTrack competitors that surface in AI answers, with ranking, sentiment distribution, and score comparison. Free.
Where the data comes from
AI Visibility runs against ChatGPT (gpt-5.4-mini), Claude (claude-haiku-4-5), Gemini (gemini-3.1-flash-lite), Perplexity (sonar via OpenRouter), and Google AI Overview (via SearchAPI.io). All five platforms are included on every plan, including Free.
SEO Pages
The SEO section spans about 17 nav items grouped into Site Crawl, On-Page & Indexing, Keywords & Rankings, Backlinks & Authority, and Competitors. The dashboard, crawl, and on-page checks are Free; the data-heavy keyword, backlink, and ranking pages step up to Starter or Pro.
SEO Dashboard
/seoSite Health overview: pagespeed (mobile and desktop), Domain Authority, SSL status, technical checks, keyword and backlink summaries. Free.
PageSpeed
/seo/pagespeedLighthouse scores with Core Web Vitals and optimization tips. Free.
Site Audit
/seo/auditOn-page audit results grouped by severity, with readability and technical recommendations. Free.
Page Audit
/seo/audit/pageSingle-page breakdown: meta tags, headings, content, links, and technical checks. Free.
OnPage Checker
/seo/onpage-checkerWeighted 10-category on-page score with an A-F grade and actionable fixes per page. Free.
Visualization
/seo/visualizationInteractive crawl graph showing page relationships, link structure, and orphan pages. Free.
Internal Linking
/internal-linksInternal-link opportunities and orphan detection from the site crawl. Page is reachable on Free; richer data depends on crawl coverage.
Indexation
/indexationGoogle index-status tracking via the GSC URL Inspection API. Page is reachable, but daily indexation monitoring is a Pro feature and needs GSC connected.
CTR Optimization
/ctr-optimizationGSC impressions-vs-CTR analysis with AI title and meta rewrites. Starter and up.
Search Performance
/seo/keywordsKeyword rankings and Search Console performance with position changes, filters, and history. Nav label is "Search Performance". Starter and up.
Keyword Suggestions
/seo/keyword-suggestionsAI keyword discovery with volume, difficulty, and relevance scoring. Starter and up.
Keyword Gap
/seo/keyword-gapKeywords competitors rank for that you do not. Pro and up.
Rank Tracking
/rank-trackingSERP position tracking via DataForSEO, run on demand. Pro and up, with a per-brand budget guard.
Backlinks & DA
/seo/backlinksBacklink profile with referring domains, toxic detection, and opportunities. Starter and up.
Backlink Gap
/seo/backlink-gapDomains that link to competitors but not to you. Pro and up.
Competitor Analysis
/seo/competitorsSEO comparison vs competitors: DA, keyword overlap, top keywords. Starter and up.
Image Optimization
/seo/imagesImage audit for file size, format, alt text, and compression. Starter and up.
No single SEO score
TruIntel does not roll SEO up into one number. Site Health (an unweighted average of mobile/desktop pagespeed, Domain Authority, and SSL status) and the weighted OnPage Checker grade are separate scores, alongside per-page crawl penalties and content-quality buckets.
Insights, Content Studio & Off-Page Pages
The Insights section turns monitoring into action: weekly AI findings, a content opportunity pipeline with a publishing calendar, on-page fixes, and off-page outreach. Insights, Tasks, and Content Studio are Starter and up; Outreach and Directory are Pro and up.
Insights
/insightsWeekly AI insight reports with a findings tab and a tasks tab (split view plus kanban), each with impact scoring. Starter and up.
Finding Detail
/insights/:findingIdOne finding with its evidence, impact score, and the tasks it generated. Starter and up.
Tasks
/tasksStandalone task board (kanban plus split view) with filtering by status and category. Starter and up.
Opportunities
/content-studio/opportunitiesContent opportunity pipeline that feeds the staged generation flow. Starter and up.
Create (Staged Generation)
/content-studio/createBrief-to-draft staged content generation, also reachable at /content-studio/create/:briefId. Starter and up.
Calendar
/content-studio/calendarPublishing calendar over CMS content: schedule, reschedule by drag, and auto-publish. Starter and up.
Accountability
/content-studio/accountabilityTracks who owns and follows through on content work. Starter and up.
Library
/content-studio/libraryAll generated content with filter and search. Starter and up.
Page Fixes
/cms/optimizationAI on-page issue detection with fix preview and batch apply. Nav-homed under Insights even though the route is CMS-backed. Starter and up.
Off-Page Outreach
/outreachBacklink prospect CRM; opening a prospect at /outreach/:prospectId drafts one finished, signed email. Pro and up.
Directory & Citations
/directoryBusiness-directory and citation submission tracking. Pro and up.
TruIntel Plus generators
The Plus hub creates AEO/SEO-ready content artifacts. There are 8 live routes; five older generators (Website Copy, OG Tags, Directory Listing, Outreach Templates, Citation Plan) have been retired and their links now 404.
Plus Hub
/plusTemplate categories, credit balance, and launch into generation. Starter and up.
Generate
/plus/generateStep-based generation wizard with template selection, config, and preview. Starter and up.
History
/plus/historyGenerated content history with filter, search, and publish/archive. Starter and up.
Blog Post
/plus/blog-postAI blog posts optimized for AI visibility and SEO. Starter and up.
FAQ Page
/plus/faq-pageFAQ content with FAQPage schema markup. Starter and up.
Structured Answer
/plus/structured-answerAnswer blocks tuned for AI citation and featured snippets. Starter and up.
Schema Markup
/plus/schema-markupJSON-LD structured data (Article, FAQPage, HowTo, Product, etc.). Starter and up.
Meta Tags
/plus/meta-tagsTitle tags and meta descriptions with character counting and preview. Starter and up.
Credit costs
A standard content draft costs 1 credit; the premium quality tier costs 3 credits. Brief generation is free, and a repurpose costs 1 credit. Credits are spent from your monthly plan allowance first, then from never-expiring top-up credits.
CMS Module (16 Pages)
The CMS module is integrated under the /cms/* prefix. Every CMS page is wrapped in CMSProtectedRoute (auth plus the credit context) and plan-gated behind the cms feature, so it requires Starter or higher.
Dashboard
/cms/dashboardOverview of generated content, recent scans, and optimization activity.
Content Library
/cms/contentAll generated content, filterable by type and status.
Create Content
/cms/content/newStart a new generation from a template or custom prompt (deep-link).
Content Editor
/cms/content/:id/editTipTap rich-text editor with an AI SEO sidebar, AI assist, and auto-save (deep-link).
Page Fixes
/cms/optimizationAI page-issue detection with fix preview and batch apply. Surfaced in the Insights nav as "Page Fixes".
Edit Pages
/cms/edit-pagesList of published pages with quick edit actions.
Visual Page Editor
/cms/edit-pageFull-screen editor for changing specific elements on a published page (deep-link).
Domain Onboarding
/cms/onboardingConnect a custom domain, verify DNS via CNAME, and extract the brand template (deep-link).
Domain (Settings)
/cms/settingsBrand template management, re-extraction, SEO files, and domain config. Nav label is "Domain".
CMS Competitors
/cms/competitorsMonitor competitor content and publishing activity (routed; not in the current nav).
CMS Tasks
/cms/tasksCMS-specific tasks. Distinct from the standalone /tasks page the nav points to.
Backlink Fixes
/cms/backlink-fixesBacklink optimization tasks with fixes and progress tracking.
Robots.txt
/cms/robots-txtView and manage robots.txt crawler directives.
Sitemap
/cms/sitemapView and manage the XML sitemap of published pages.
Upgrade Plus
/cms/upgradeUpgrade prompt for premium CMS capacity (deep-link).
Credit History
/cms/creditsCredit usage log across the plan and top-up credit pools (deep-link).
AI SEO scoring in the editor
The content editor scores each draft with an LLM (gemini-3.1-flash-lite) across five dimensions: Content Quality (25), Readability & Structure (20), SEO Optimization (20), AI Engine Readiness/AEO (20), and Engagement & Hook (15). Bands: excellent at 85+, good at 65+, needs work at 40+, otherwise poor.
Grow, Reports, Settings & Tools Pages
The remaining sections cover audience analytics, brand reporting, account configuration, and the hidden Tools utilities.
Grow (Traffic & Leads)
Traffic Analysis
/trafficBot-vs-human traffic classification: live feed, agent breakdown, trends, threat analysis, and a rules manager. Starter and up.
Lead Verification
/leadsLead scoring and verification with a funnel, status management, and export. Starter and up.
Lead Detail
/leads/:leadIdOne lead: score breakdown, behavior signals, email analysis, and verification status. Starter and up.
Reports
Reports
/reportsWeekly, monthly, and one-time onboarding brand reports with multi-section executive summaries across AEO, SEO, traffic, and leads. Starter and up.
Report Detail
/reports/:reportIdFull report with visualizations, competitor benchmarking, and PDF export. Starter and up.
Settings (8 tabs, one page)
All Settings routes render the same Settings page, switching tab by URL. Every tab is available to logged-in users on any plan.
Profile
/settingsAccount profile and personal details.
Brand Voice
/settings/brand-voiceBrand voice profile used by content generation.
Team
/settings/teamTeam members and invitations (seats vary by plan).
Billing
/settings/billingPlan, invoices, credits, and top-up packs.
Notifications
/settings/notificationsAlert center and notification preferences. /notifications redirects here.
API Keys
/settings/api-keysProgrammatic access keys.
Integrations
/settings/integrationConnect Google Search Console and other integrations.
Support
/settings/supportSupport and bug reporting (the IconRail Bug button deep-links here).
Tools (hidden from nav, reachable by URL)
Tools Landing
/toolsTools hub. Routed and Free, but currently hidden from the sidebar.
SERP Simulator
/tools/serp-simulatorPreview how pages appear in Google, Bing, and social search results with live scoring.
Plan-Gating Reference
Every route falls into one of three access tiers. Free pages need only a logged-in user; Starter+ and Pro+ pages add a plan check. The tables below list exactly which features unlock at each tier.
| Tier | What it unlocks |
|---|---|
| Free (auth only) | Overview, AI Visibility, AI Crawlability, Prompt Tracking + Query Detail, Prompt Research, Sources, Competitors, Internal Linking, Indexation page, SEO Dashboard, PageSpeed, Site Audit + Page Audit, OnPage Checker, Visualization, all Settings tabs, Tools |
| Starter and up | Prompt Volumes, Insights + Tasks, Content Studio + Plus, Reports, CTR Optimization, Traffic, Leads, CMS (all 16), SEO Backlinks, Search Performance, Keyword Suggestions, SEO Competitors, Image Optimization |
| Pro and up | Keyword Gap, Backlink Gap, Rank Tracking, Off-Page Outreach, Directory & Citations, daily Indexation monitoring, daily-priority AEO checks |
Per-plan limits
| Limit | Free | Starter | Lite | Pro | Enterprise |
|---|---|---|---|---|---|
| Price (USD/mo) | $0 | $59 | $139 | $479 | Custom |
| Brands | 1 | 1 | 2 | 3 | 4 |
| Queries per brand | 5 | 10 | 25 | 40 | 50 |
| Total tracked queries | 5 | 10 | 50 | 120 | 200 |
| Competitors | 1 | 1 | 3 | 5 | 5 |
| Team members | 1 | 1 | 2 | 3 | 10 |
| Daily-priority AEO queries | 0 | 0 | 0 | 5 | 10 |
| AI credits per month | 0 | 50 | 150 | 500 | 1000 |
Free is permanent, not a trial
The Free plan is a permanent, hard-limited tier (not a 7-day trial). All five AEO platforms are available on every plan, including Free. The marketing site shows struck-through prices and an annual "Save 20%" toggle for display only; backend billing is monthly. In India, 18% GST is added on top of the USD price.
Credits and top-ups
Monthly plan credits reset on the 1st; one-time top-up credits never expire. Top-up packs: Starter Pack 50 credits for $10, Growth Pack 150 credits for $25, Pro Pack 400 credits for $50. The old TruIntel Plus $99/mo credit pool is discontinued and unspendable.
Backend API Surface
The TruIntel backend exposes 379 HTTP endpoints across 37 registered routers. Version 1 routers mount under /api/v1; the v2 package serves insights under /api/v2. Interactive API docs are available at /docs (Swagger) and /redoc, with the raw schema at /openapi.json.
| Method | Count |
|---|---|
| GET | 205 |
| POST | 128 |
| PATCH | 21 |
| DELETE | 21 |
| PUT | 4 |
Largest routers
| Router | Prefix | Endpoints |
|---|---|---|
| seo.py | /api/v1/seo | 78 |
| cms.py | /api/v1/cms | 60 |
| backlinks.py | mixed under /api/v1 | 26 |
| admin.py | /api/v1/admin | 19 |
| queries.py | /api/v1/queries | 16 |
| payments.py | /api/v1/payments | 16 |
| v2/insights.py | /api/v2/insights | 14 |
| competitors.py | /api/v1/competitors | 14 |
Health checks
Outside the versioned routers, the service exposes GET /health, GET /health/deep, and a GET / root probe for uptime monitoring.