- Brand
- ChatFeatured
- Category
- Marketing
- Primary Subcategory
- AI Search & LLM Visibility (AEO/GEO)
Integration details
Description
ChatFeatured helps brands understand and improve how they appear in AI-generated answers. Users can analyze visibility, competitors, citations, crawler activity, and site health; manage tracked prompts, brand data, opportunities, and tasks; and create, review, schedule, and publish optimized articles to connected content systems.
- Integration type
- Plugin
- Verification status
- Not applicable
- Platform
- ChatGPT
- Primary Subcategory
- AI Search & LLM Visibility (AEO/GEO)
- Secondary Subcategories
- None listed
- Brand
- ChatFeatured
- Access
- Account required
- First tracked
- 2026-09-24
- Tool count
- 50
- Market
- US
The Primary Subcategory used for this profile’s headline score.
Other Subcategories where the Integration is listed.
Your score is coming
ChatGPT now suggests Plugins on its own when they match a user's request.Your Plugin Discovery Score measures how often yours appears, and it will show here as soon as it’s ready.
What discovery looks like

Get alerts for ChatFeatured
Get updates when ChatFeatured’s Discoverability Score or category rank changes.
Competing in ChatGPT AI Search & LLM Visibility (AEO/GEO)
View Category50 tools agents can invoke
Activates or deactivates tracking prompts. In the dashboard this is the Status filter on the Prompts page, which offers Tracked and Paused: "inactive" is what the user calls paused. When activating, checks billing limits to ensure there are enough prompt slots available. When deactivating, frees up prompt slots for other prompts. A paused prompt stops running, so it also drops out of getPromptGapsTool: pausing a prompt hides the gap, it does not close it. Use this when users want to pause/resume tracking for specific prompts. The brand ID is automatically provided from context.
update_prompt_status
Adds existing keyword IDs or new phrases to the project's keyword bank. New phrases are queued for Google measurement.
add_keywords
Adds one or more opportunities to the user's task list (writes to the database). Call this ONLY when the user has explicitly asked to add, track, or save an opportunity as a task. This creates a real task that appears on the Tasks page — it is NOT the same as surfacing an opportunity for review. Each opportunity becomes an "opportunity" task with status "open". Duplicates (same type + title already added for this brand) are skipped and reported. If task email notifications and an assignee are configured, creating a task sends an assignment email that cannot be recalled. Pass the same opportunity fields you would surface (type, title, description, priority, sourceUrls, context) — typically the ones you just surfaced.
add_opportunity_task
Approves pending articles and queues them for content generation. First checks billing limits (trial users: 3 articles/month, paid users: 40 articles/month) and pipeline limits (max unscheduled articles). If limits are reached, returns an error with limitReached=true and a message explaining the limit. Otherwise, manages the writing queue (max 3 articles in draft/review at once) and triggers background content generation. Responses include articleLimits (used / limit / remaining for the monthly article allowance) so you can report how many articles the brand has left. Returns immediately - does not wait for content generation to complete. Approving already STARTS the writing workflow (2-5 minutes in the background) — never also call generate-articles on an article you just approved; that would run generation twice. Use this when users want to approve articles, start writing content, or begin the content creation process. The writing workflow can send notification emails and configured article-review webhooks that cannot be recalled.
approve_articles
Archives articles by ID and dismisses associated tasks. This does not refund article-generation usage. Use list_articles with status='archived' to see archived articles, or unarchiveArticle to restore. Prefer this over deleteArticles when articles might be needed later. Archiving a ChatFeatured CMS article removes its public page; external CMS copies remain unchanged.
archive_articles
Start here for "how is my brand performing", "how do I compare to my competitors", "what changed this week" or any weekly or monthly report. One call returns, for the period and the one before it: - headline: visibility, sentiment, mentions, citations and prompts, with changes. These match the dashboard; quote them as they are. - platforms: each AI platform's visibility, sentiment and mentions, with mentionRuns (the sample behind its visibility) and thinSample when that sample is small next to the others. - competitors: the project's rank and the top competitors by visibility, with changes. - promptMovers: the prompts that gained and lost the most visibility, with ids and dashboard links. - domains: the most cited domains, and the ones that gained and lost the most citations. - tracking: prompts added or paused in the period and platforms locked behind an add-on. Check this before explaining a drop: pausing or adding prompts moves the numbers on its own. - links: dashboard pages for each section, ready to paste. Any prompt's page is links.prompts followed by ?prompt=<prompt id>, so build prompt links from it rather than calling buildDashboardLinkTool for each. - A previous value or change of null means the previous period had no data to compare (a new project, platform or prompt), not a drop or a gain. Drill into anything here with queryAnalyticsTool using the ids given (filters.prompts, groupBy ["date"] for when a change happened, groupBy ["prompt", "competitor"] for where a competitor wins).
get_brand_brief
Creates article ideas and linked tasks. When a task assignee and email notifications are configured, this can send an assignment email that cannot be recalled. Saves article titles with their descriptions and "why" explanations to the database, along with associations to keywords and prompts. Use this after generating your content strategy to persist all article recommendations. Prefer linking by id: pass promptIds and keywordIds when you know them (fetch-prompts returns `id`, fetch-keywords returns `keywordId`). Only pass ids copied from a tool result in this conversation. If any id is not one of this brand's, nothing is saved and the call returns success: false with the unknown ids; re-run fetch-prompts / fetch-keywords and retry with real ids. The free-text "prompts" and "keywords" arrays are fuzzy matched and can miss; texts that matched nothing come back in "unresolvedPrompts" / "unresolvedKeywords". Articles whose title already exists for the brand (same slug) are skipped, not saved — they are returned in the "skipped" array so you can tell the user they already exist and avoid re-suggesting them. When the article was planned with the user in this conversation (an outline, an angle, things to include or leave out), pass that plan as writingInstructions, and a length as targetWordCount when one was agreed. Approving the idea then writes it from that brief. Without them the writer only sees the title, description and why.
create_article_ideas
Creates a new tracking prompt for the brand. Each prompt is a search query that will be tracked across AI models to monitor brand visibility. Respects billing limits — checks available prompt slots before creating. Geo defaults to ["US"] if not specified. Each geo location uses one prompt slot. The prompt is created as "active" by default and will be picked up by the next scheduled run. Research or fetch keywords first. Pass keywordIds to associate multiple relevant researched keywords explicitly; exact wording overlap is not required for a useful topic association. Never invent keyword IDs or demand numbers. Chosen keyword links are returned with computed demand. When keywordIds and phrases are omitted, automatic keyword research is queued. The brand ID and user ID are automatically provided from context.
create_prompt
Permanently deletes articles. This cannot be undone. Consider using archiveArticles instead if the articles might be needed later. Cleans up associated tasks before deleting. A ChatFeatured CMS article's public page is also removed; external CMS copies remain unchanged.
delete_articles
Triggers the article research, writing, and metadata generation workflow for one or more articles. Articles must be in "draft" status (approved). The workflow conducts web research, generates a GEO-optimized article, and creates metadata. Articles typically take 2-5 minutes to complete. NOTE: approve-articles already starts this workflow — do NOT call this for an article you just approved (the duplicate run is rejected). Use this only to restart generation for an article whose previous generation stalled or was interrupted; failed articles (status "error") are retried via the retry flow instead. Regeneration overwrites generated draft fields and can send notification emails and configured article-review webhooks that cannot be recalled.
generate_articles
Reads actual AI response text from prompt responses. Returns the full text of what AI models said, along with which model generated it, the country, prompt text, and any brand mentions with sentiment context. Use this ONLY when the user explicitly asks about what an AI model said or wants to see actual response content. Do NOT call this proactively — it returns large amounts of text. Use mentionedBrandId to filter to only responses where a specific brand (own or competitor) was mentioned. Use brandMentioned=true as a shorthand to filter to only responses where the user's own brand was mentioned.
get_response_text
Fetches an article by articleId, including: - Article title, description, why, status, and timestamps - The full article body as Markdown, plus excerpt, meta description, and word count. The body is empty until the draft has been written (pending ideas and articles still generating). - All associated keywords (keyword text, search volume, competition data) - All associated prompts (prompt title and content) Use this to read or review a draft and to check status and targeting. The body is read-only here; edits to it happen in the ChatFeatured dashboard.
get_article
Gets the current schedule information for an article. Use this when users want to check when an article is scheduled, verify the scheduled date/time, or see the schedule status before making changes.
get_article_schedule
Fetches comprehensive brand book information including brand name, industry, categories, and brand description. Use this to understand the brand's context before generating keyword recommendations.
get_brand_book
Returns an organization's plan (tier, status, billing source, agency flag, billing cycle) and its usage against every limit: projects, tracked prompt slots, articles, image tokens and page audits this billing period, and prompt runs today. Limits include custom overrides and trial caps. Requires billing access in the organization (owners and admins). Call list_organizations first to get the organizationId.
get_organization_usage
Checks the publish/sync status of an article on its connected CMS platform. Returns sync status, external URL, external ID, and platform-specific metadata. Use this after publishing to verify the article reached the external CMS.
get_publish_status
Lists all CMS integrations connected to the brand (WordPress, Ghost, HubSpot, Wix, Sanity, Webflow, Framer, Shopify, Git-based CMS) and the ChatFeatured CMS with available authors. Shows integration type, active status, connection health, and platform-specific details. For Ghost: returns available authors (with emails) and tags — you MUST pass an authorEmail when publishing to Ghost. For HubSpot: returns available authors and tags with their ids — HubSpot addresses both by id. Pass hubspotAuthorId when the connection has no default author (requiresAuthorId). For Sanity: returns author document ids. Pass sanityAuthorId to override the default author. For Wix: returns site members with their ids. Pass wixMemberId when the connection has no default member (requiresMemberId). When the brand's ChatFeatured CMS site is enabled it is a publish destination alongside integrations — address it in publishArticle with integrationId "chatfeatured-cms". Returns available authors — pass authorIds when publishing or scheduling. Use this before publishing to see what options are available for each platform.
list_integrations
Lists articles for a brand with optional status filtering. Returns a lightweight list with ID, title, status, and creation date. Use this to discover what articles exist, especially to find pending articles that need approval or to check article statuses. Much more efficient than fetching full article details. The brand ID is automatically provided from context - do not pass organizationBrandId.
list_articles
Fetches all competitors configured for the current brand. Returns each competitor group with its display name, domain, associated brands, and total mention count across all AI model responses. The brand ID is automatically provided from context.
list_competitors
Lists keywords already in the project's keyword bank. Optionally filters by phrase. Bid values are returned in dollars. Set includeTrend for each keyword's month-by-month search volume.
fetch_keywords
Fetches existing opportunities that have been previously identified for this brand. Use this at the start of analysis to understand what opportunities already exist, so you can avoid suggesting duplicates and focus on new insights. Returns opportunities from the last 30 days with their type, title, and creation date.
get_existing_opportunities
Lists all organizations the authenticated user is a member of, including their role in each. Next step: list_brands to see available projects.
list_organizations
Lists all projects the user has access to, optionally filtered by organization. Use the returned project ID as the `brandId` argument for other tools.
list_brands
Fetches all tags for the current brand. Returns tag IDs, names, colors, and how many prompts each tag is associated with. The brand ID is automatically provided from context.
list_tags
Lists tasks for the brand (read-only). Use to answer "what tasks do I have?", to check progress, or to find a task's ID before updating its status. Filter by status and/or task type. Returns the taskId for follow-up calls — refer to tasks by title in your replies, never by ID.
list_tasks
Fetches the prompts for an organization brand. Returns prompt IDs, text content, status, geo targeting, tags, and metadata. Inactive (deactivated) prompts are excluded by default — pass includeInactive: true only when the user explicitly asks about deactivated/old prompts. Use this to see what prompts are available when associating articles with prompts. Tags can be used to filter prompts by category/topic when querying analytics. The returned prompt ids are exactly what saveArticlesTool.promptIds (and updateArticleTool) expect, so link articles by id rather than by prompt text. Pass includeMetrics: true to also get each prompt's 28-day visibility, gapScore, demand and article count. For "where are my biggest gaps", prefer getPromptGapsTool if you have it: it returns the same prompts already ranked by gapScore. If you do not, keep only the rows that have at least one gapReasons entry and a gapScore of 20 or more, then sort those by gapScore descending: a row without a reason is not a gap, whatever its score. gapScore is an internal ranking signal that appears nowhere in the dashboard: use it to order prompts, but never quote it, show it or call it a gap score to the user. Explain a gap with what they can see instead: search demand, the brand's visibility, the leading competitor, and whether an article targets the prompt. The brand ID is automatically provided from context.
list_prompts
Manages brand personas for the current brand. Use action "list" to get all personas, "create" to add a new one, "update" to modify an existing one, or "delete" to remove one. The brand ID is automatically provided from context.
manage_brand_persona
Add, update, or remove competitors for the current brand. - "add": Creates a new competitor, with the named brand as its first name. Requires name and domain. Will reject if the name is already one of the project's competitors or its own names, or the domain already belongs to a competitor. - "update": Updates an existing competitor's display name and/or domain. Requires competitorGroupId. - "remove": Deletes a competitor group. Requires competitorGroupId. - "add_brand": Adds a brand to an existing competitor group (confirms a fuzzy suggestion). Requires competitorGroupId and memberBrandId. Rejected if the brand is already in another competitor or is one of the project's own names. - "ignore_brand": Marks a suggested brand as NOT belonging to this competitor (user said "No"). Requires competitorGroupId and memberBrandId. The brand won't be suggested again for this competitor. - "remove_brand": Removes a brand from a competitor group. Requires competitorGroupId and memberBrandId. The (organization) brand ID is automatically provided from context.
manage_competitor
Creates, updates, or deletes tags for the current brand. Use action "create" to create a new tag with a name and optional color. Use action "update" to rename or recolor an existing tag, or change its prompt associations. Use action "delete" to remove a tag entirely (cascades to prompt associations). The brand ID is automatically provided from context.
manage_tag
Publishes an article to one of the brand's publish destinations: a connected CMS integration (WordPress, Ghost, HubSpot, Wix, Sanity, Webflow, Framer, Shopify, Git-based CMS) or the ChatFeatured CMS when the brand's cms site is live. Brands can have multiple destinations (even several integrations of the same type, e.g. two WordPress sites): pass integrationId to pick one — use "chatfeatured-cms" for the ChatFeatured CMS. With a single destination it is chosen automatically; with several and no integrationId the call fails and lists the options. If no destination exists, marks the article as published locally. Article must be in review, scheduled, or draft status. An article whose content is still being written cannot be published — the call fails while generation is in progress. Wait for the generation-completed notification, then publish. Use listIntegrations first to see connected integrations and their ids, then call this to publish.
publish_article
Queries AI crawler/agent analytics data from Tinybird. Shows how AI crawlers (ChatGPT, Claude, Perplexity) are visiting the brand's website. Query types: - "stats": Request counts per crawler type (ChatGPT, Claude, Perplexity) with period-over-period change % - "timeseries": Daily breakdown by crawler type for charting trends - "top_pages": Most visited host+pathname pairs by crawlers (top 20) - "top_crawlers": Most active crawler agents (top 20) - "page_detail": Per-page, per-crawler breakdown with visit counts and first/last seen dates. Use with "pages" filter to check specific URLs. - "logs": Raw crawler log entries (most recent 100). Use with "pages" and/or "crawlers" filters to investigate specific crawl events. - "search_pages": Discover all crawled pages. Use "pathPattern" for LIKE filtering (e.g. "%/blog/%"). Use this when users ask about: - Which AI bots are crawling their site - How often ChatGPT/Claude/Perplexity visit - Which pages are being crawled most - Crawler activity trends over time - Whether a specific page/article has been crawled - Raw crawl event details for a page Semantics of this dataset: - It records only requests that REACHED the site. A block (firewall, WAF, robots.txt) produces no row at all, never an error row. This data can therefore never show that a crawler is blocked, and any row for a crawler proves it is not. - A brand can span several hosts (apex domain, blog subdomain). Every page-level row carries "host" — always read host + pathname together. - "status" is null when no HTTP status was recorded (always the case on the ChatFeatured CMS, where hits are logged before a response exists). Null means unknown, not an error. - "agent_type" is the self-reported User-Agent and is NOT verified. Vulnerability scanners spoof well-known bots while probing for exploits. Requests from IPs that probed for secrets (/.env, /.git/config) or cycled through many bot identities are already excluded from every query here. That filter is not exhaustive, so requests to /wp-login.php, /wp-admin/*, /xmlrpc.php and similar are still scanner traffic, not real crawls. Every public domain gets them, whatever it runs on. Never treat such rows as evidence of the site's CMS or hosting stack, and don't count them as crawler interest in a page.
query_crawler_analytics
Slices the brand's AI search metrics by any mix of prompt, model, competitor, cited page or domain, country and date, with optional comparison to the previous period. For "how is my brand doing", "how do I compare to competitors" or "what changed this week", call getBrandBriefTool first: it returns the headline, platforms, competitor ranking, prompt movers, domains and tracking changes in one small result, with the ids to drill into here. ## Presets (override any field): - source_analysis: top 5 cited pages per prompt - source_changes: cited pages that gained or lost the most vs the previous period - cited_urls: top 50 cited pages, with citations and the number of prompts citing each - cited_urls_by_platform: cited_urls plus citationsByModel, each page's citations split by platform - source_mentions: top 50 cited domains, with citations and prompt counts ## Query patterns: - Which models dropped: { metrics: ["visibility"], groupBy: ["model"], compareWithPrevious: true, sortBy: "delta.visibility", sortDirection: "asc" } - One prompt across models: { metrics: ["visibility", "sentiment"], groupBy: ["model"], filters: { prompts: ["id"] } } - A competitor by prompt: { metrics: ["visibility"], groupBy: ["prompt", "competitor"], sortBy: "visibility" } - Prompts where the brand is missing: { metrics: ["visibility"], groupBy: ["prompt"], filters: { brandPresence: "not_mentioned" } } - Visibility per day: { metrics: ["visibility", "mention_runs"], groupBy: ["date"] }; per day and model: groupBy: ["date", "model"] - A specific window: filters: { startDate: "2026-09-01", endDate: "2026-09-30" } (at most 92 days) - Are competitors citing new sources: { metrics: ["citations"], groupBy: ["source", "competitor"], compareWithPrevious: true } ## Reading results: - visibility = % of runs naming any brand that name this brand: the dashboard's formula. mention_runs is that base, so a visibility from a handful of runs reads as the small sample it is. - meta.summary is the whole filtered period: quote it for headline numbers. Never average row values; rows cover different run counts. - At most 200 rows come back. A larger result returns the top and bottom of the requested sort plus meta.rowStats (each metric's min, median and max over every row); a date grouping returns its first 200 rows in date order. meta.matchedRows is the full count, and meta.nextOffset pages through with offset. - A date grouping is a series in date order and ignores compareWithPrevious. ## Prompt status: - By default this counts the prompts the dashboard counts. filters.promptStatus: ["active"] keeps live prompts only; ["inactive"] reviews paused prompts' past results.
query_analytics
Reads the brand's connected Google Analytics 4 property: website traffic and users, where visitors come from, how much traffic AI assistants (ChatGPT, Perplexity, Gemini, Claude, Copilot, …) refer, which landing pages they land on, and which sources convert. Use it for any question about site traffic, sessions, users, referral sources, AI-referred visits, landing pages, or conversions. It is separate from the AI-visibility analytics tools (those measure mentions and citations in AI answers; this measures visits to the website). Data is synced from GA4 once a day and covers up to 90 days back from when the property was connected; the latest day available is yesterday. Rates are fractions (0..1), durations are seconds. If Google Analytics isn't connected, has no property selected, or needs reconnecting, the result says so (status ≠ "connected") and its message tells you what to say — explain in one sentence and don't retry.
query_google_analytics
Reads the brand's connected Google Search Console property: Google Search clicks, impressions, CTR and average position by query, page, date, country or device; submitted sitemaps; and whether a specific URL is indexed by Google. Use it for questions about Google search traffic, which queries or pages get impressions and clicks, ranking position, and indexing or crawl status. Search Console measures classic Google Search, not AI answers — never present it as AI visibility, and don't recommend sitemap submission as an AI-visibility fix. Search analytics is live from Google and lags about two days; the default window is the last 28 days. URL inspections are quota-limited and cached for 24 hours. If Search Console isn't connected, has no property selected, or needs reconnecting, the result says so (status ≠ "connected") and its message tells you what to say — explain in one sentence and don't retry.
query_search_console
Advanced analytics tool: run a single READ-ONLY SQL SELECT over THIS brand's AI-search data. Use it for custom analysis the queryAnalyticsTool presets can't express: per-day or per-week trends, visibility or share-of-voice over time, source/domain breakdowns, custom pivots, distinct counts. (queryAnalyticsTool is still the quickest path for its standard preset reports.) CALL explain:true FIRST. The SQL runs on this brand's analytics store, which is PostgreSQL for some brands and ClickHouse for others. explain:true returns the `dialect`, the authoritative schema and recipes for it, this brand's exact competitor group and tag names (competitorGroups, tags), and the models it can query (availableModels). Everything below runs as written in both dialects. For anything else (array, string-aggregation, cast or date functions), use the syntax the explain notes give rather than guessing. Your query is AUTOMATICALLY scoped to the current brand. Never add an organization/brand filter: it is injected server-side, and you cannot see other brands. RELATIONS (the only names allowed; explain:true lists every column): - mentions(created_at, prompt_run_id, prompt_response_id, prompt_id, model, country, brand_id, brand_name, sentiment, is_self, competitor_group): ONE ROW PER BRAND MENTIONED in an AI response, COMPETITORS INCLUDED. Visibility is computed from this relation alone. - citations(created_at, prompt_run_id, prompt_response_id, prompt_id, model, country, source_domain, source_type, source_url, source_title, brand_found_in_source): one row per page cited in an AI response. brand_found_in_source is TRUE when YOUR brand is on the page, FALSE when verified absent, NULL when not verified. - runs(prompt_run_id, prompt_id, model, country, created_at): EVERY completed run, including runs that mentioned nobody. Use it for run counts and coverage, never as the visibility denominator. - competitor_dim(competitor_group, competitor_domain): the configured competitor groups. - prompt_dim(prompt_id, prompt_text): tracked prompts. Inactive prompts and their runs are excluded from every relation. - tag_dim(tag_id, name) and prompt_tag_dim(prompt_id, tag_id): tags, and which prompts carry them. SELF / COMPETITORS / METRICS (use the server flags, never hard-coded brand names): - YOUR brand = WHERE is_self. - A specific competitor = competitor_group, NOT brand_name (one competitor spans several brand_name variants). competitor_group is a CASE-SENSITIVE display name ('Semrush', not 'semrush'), so never compare it with '=' and a guessed literal. Match with ILIKE: WHERE competitor_group ILIKE 'semrush'. If a named-competitor filter returns 0 rows, the label almost certainly didn't match: check competitorGroups (or SELECT competitor_group FROM competitor_dim) and retry before reporting absence. - VISIBILITY (the dashboard formula) = 100.0 * COUNT(DISTINCT prompt_run_id) FILTER (WHERE is_self) / NULLIF(COUNT(DISTINCT prompt_run_id), 0) over mentions. The denominator is runs with at least one brand mention. Dividing by runs understates it, and ratioing raw mention rows gives share-of-voice instead. Put a competitor_group condition in the FILTER for a competitor's visibility. - SHARE-OF-VOICE = 100.0 * COUNT(*) FILTER (WHERE is_self) / NULLIF(COUNT(*), 0) over mentions. - CITATIONS = COUNT(DISTINCT prompt_run_id) over citations: runs citing the page, domain or type, grouped at the level you measure. Never SUM(citation_count). prompt_count = COUNT(DISTINCT prompt_id). Your sentiment = AVG(sentiment) WHERE is_self. - selfBrandResolved false means the brand has never been mentioned in a tracked AI response, so is_self is false on every row. Report the 0% as the real answer and say the brand hasn't been detected yet. KEY FACTS: - model holds the display name, and these are the ONLY values: ChatGPT, Perplexity, Google AI, Gemini, Claude, Grok, Copilot, Muse. WHERE model = 'ChatGPT' works; values are case-sensitive. Models outside this brand's plan (Claude and Grok are paid add-ons) have no rows; availableModels lists the ones this brand has. - Time windows: created_at >= current_date - 29 is the last 30 days, today included. A date literal such as created_at >= '2026-01-01' also works. - Tags: join prompt_tag_dim and tag_dim, as in the last example. - runs, mentions and citations SHARE column names (prompt_run_id, prompt_id, model, country, created_at). When joining, alias each relation and qualify EVERY column reference, in SELECT, GROUP BY and ORDER BY too (r.model, not model), or the query fails as ambiguous. - Never reuse a column name as the alias of a different expression (e.g. AS source_type on citations). Pick a fresh name. - When joining, put the created_at window on EVERY joined relation, or the unfiltered side is read in full and can time out. Compare several brands in one pass with conditional aggregates (COUNT(DISTINCT ...) FILTER (WHERE ...)), never one join per brand. RULES: - Single SELECT (or WITH … SELECT). No DDL/DML, no parameter placeholders, no recursive CTEs. - Results are HARD-CAPPED at 1000 rows. Aggregate in SQL for overviews and averages: a small LIMIT corrupts the aggregate. Add LIMIT only for explicit top-N rankings. - Results are also capped at ~200,000 serialized characters. An oversized result is refused with NO rows, never shortened. Long text such as prompt_text across many rows triggers it: aggregate, select fewer columns, or narrow the range. - Returns { columns, rows, rowCount, truncated, sql, selfBrandResolved, dialect }. truncated: true means more rows exist; aggregate or narrow the range. EXAMPLES (each runs as written in both dialects): -- Your visibility by week since tracking started (the dashboard formula) SELECT date_trunc('week', created_at) AS week, COUNT(DISTINCT prompt_run_id) AS runs_with_any_mention, COUNT(DISTINCT prompt_run_id) FILTER (WHERE is_self) AS runs_with_you, ROUND(100.0 * COUNT(DISTINCT prompt_run_id) FILTER (WHERE is_self) / NULLIF(COUNT(DISTINCT prompt_run_id), 0), 2) AS visibility_pct FROM mentions GROUP BY week ORDER BY week -- Your visibility vs one named competitor, last 90 days, in one pass. -- competitor_runs = 0 means the label didn't match: check competitorGroups, don't report 0%. SELECT COUNT(DISTINCT prompt_run_id) AS runs_with_any_mention, ROUND(100.0 * COUNT(DISTINCT prompt_run_id) FILTER (WHERE is_self) / NULLIF(COUNT(DISTINCT prompt_run_id), 0), 2) AS your_visibility_pct, COUNT(DISTINCT prompt_run_id) FILTER (WHERE competitor_group ILIKE 'semrush') AS competitor_runs, ROUND(100.0 * COUNT(DISTINCT prompt_run_id) FILTER (WHERE competitor_group ILIKE 'semrush') / NULLIF(COUNT(DISTINCT prompt_run_id), 0), 2) AS competitor_visibility_pct FROM mentions WHERE created_at >= current_date - 89 -- Share of all brand mentions, you vs each competitor group, last 30 days SELECT CASE WHEN is_self THEN 'You' ELSE COALESCE(competitor_group, 'Other') END AS brand_group, COUNT(*) AS mention_rows, ROUND(100.0 * COUNT(*) / SUM(COUNT(*)) OVER (), 2) AS share_pct FROM mentions WHERE created_at >= current_date - 29 GROUP BY brand_group ORDER BY mention_rows DESC -- How often each brand is mentioned (counts, not visibility), last 30 days SELECT brand_name, COUNT(*) AS mention_rows, COUNT(DISTINCT prompt_run_id) AS runs_mentioning FROM mentions WHERE created_at >= current_date - 29 GROUP BY brand_name ORDER BY mention_rows DESC LIMIT 25 -- Top cited URLs: citing runs and distinct prompts, last 30 days SELECT source_url, COUNT(DISTINCT prompt_run_id) AS citing_runs, COUNT(DISTINCT prompt_id) AS prompt_count FROM citations WHERE created_at >= current_date - 29 GROUP BY source_url ORDER BY citing_runs DESC LIMIT 25 -- Cited pages that feature your brand, last 90 days SELECT source_domain, source_url, COUNT(DISTINCT prompt_run_id) AS citing_runs FROM citations WHERE brand_found_in_source AND created_at >= current_date - 89 GROUP BY source_domain, source_url ORDER BY citing_runs DESC LIMIT 25 -- Citations per source domain split by platform, last 30 days SELECT source_domain, COUNT(DISTINCT prompt_run_id) FILTER (WHERE model = 'ChatGPT') AS chatgpt, COUNT(DISTINCT prompt_run_id) FILTER (WHERE model = 'Perplexity') AS perplexity, COUNT(DISTINCT prompt_run_id) AS all_platforms FROM citations WHERE created_at >= current_date - 29 GROUP BY source_domain ORDER BY all_platforms DESC LIMIT 25 -- Daily visibility on the prompts with one tag (take the name from tags), last 30 days SELECT CAST(m.created_at AS DATE) AS day, ROUND(100.0 * COUNT(DISTINCT m.prompt_run_id) FILTER (WHERE m.is_self) / NULLIF(COUNT(DISTINCT m.prompt_run_id), 0), 2) AS visibility_pct FROM mentions m WHERE m.created_at >= current_date - 29 AND m.prompt_id IN (SELECT pt.prompt_id FROM prompt_tag_dim pt JOIN tag_dim t ON t.tag_id = pt.tag_id WHERE t.name ILIKE 'comparison') GROUP BY day ORDER BY day
query_analytics_sql
Query site audit data for the brand's website. Supports multiple query types: - "overview": Run counts by status + latest run, page counts by audit status, average scores, and open-issue counts - "pages_needing_improvement": Pages ranked by lowest scores with their open issues (audited pages only) - "open_issues": All open issues grouped by category and check type - "page_detail": Detailed scores and issues for a single page; pass the page's URL in "pageUrl". Over MCP there is no current-page context, so "pageUrl" is required here; in the in-app chat it defaults to the page in view. - "run_status": Status of the last 5 audit runs Semantics: a run maps the brand's configured domain and DISCOVERS pages; each page is then scored only when the user runs it from the Site Audit page. Read "pages.byStatus" to tell those cases apart — "not_audited" means nobody has run that page, "failed" means the audit ran and could not fetch or score it. Only "failed" pages (or a failed run, see "runs.byStatus" and "runs.latest.errorMessage") indicate something went wrong; a large "not_audited" count is just unused coverage. Scores and issue counts describe the audited pages only. The audit does not follow links onto other hosts — a subdomain needs its own audit.
query_site_audit
Read knowledge base documents in full. This is the default way to use the knowledge base: the project's own reference material and the source of truth for facts about the company (certifications, product details, pricing, terms, case studies). - With no documentIds, returns every document. With documentIds, returns those documents. - Returns complete text, so what you get back is everything those documents say. If a fact is not in the text, the documents do not support it. - If the requested documents are too large to return at once, the call fails with a list of documents and their sizes. Ask again for fewer documents, or use knowledge-base-query to search inside them. - list: true returns the document list (ids, titles, summaries, sizes) without any text.
knowledge_base_read
Asks Google for keyword ideas around a phrase or website, with each idea's last 12 months of search volume. Results are cached but are not added to the project's keyword bank.
research_keywords
Manages article publishing schedule - can schedule, reschedule, or cancel. Use action "schedule" to: - Set a publish date for an article (article must be in "review" status) - Change the date for an already scheduled article (automatically detects and reschedules) Use action "cancel" to: - Remove the scheduled publish date (article must be in "scheduled" status) - Article status will change back to "review" Pass integrationId (from listIntegrations) to schedule the publish on a specific CMS integration — the platform then publishes it at the scheduled time. Without it the article simply flips to published at the scheduled time (or commits to the repo when the brand has a Git-based CMS connected). The tool automatically handles whether this is a new schedule or a reschedule.
schedule_article
Scores candidate prompt text against the brand's keyword research, without creating anything. Use this BEFORE create-prompt to check whether a prompt you are about to propose has real search demand behind it. Returns, per candidate, a demand band and the keyword it was matched to. Pass keywordGroups (one array of keyword IDs per prompt) alongside promptTexts to score multiple explicit researched associations, including broader topic matches. Without IDs it infers associations from wording; "none" is missing data, not zero demand. Do not distort natural prompt wording just to force a lexical match. The brand ID is automatically provided from context.
score_prompt_demand
Search inside the project's knowledge base and get back the most relevant excerpts. Prefer knowledge-base-read, which returns whole documents. Use this search when a read failed because the documents were too large, or to find which document covers a topic. Results are excerpts, not whole documents. An excerpt can show that a document says something. A search that finds nothing does not show that the documents don't say it, so never report a fact as missing or a claim as verified from search results alone. Each result carries its documentId: read that document with knowledge-base-read when you need the full picture.
knowledge_base_query
Searches Pexels for stock photos to use as a featured image for an article. Returns a list of photos with preview URLs, photographer info, and dimensions. Use this to find a suitable featured image, then call setArticleFeaturedImage with the chosen photo details.
search_pexels_images
Downloads a Pexels photo and sets it as an article's featured image. Use searchPexelsImages first to find photos, then pass the chosen photo's details here. Each call downloads a new copy to public R2 storage and updates the article's featuredImageUrl and attribution metadata. For an article already published through ChatFeatured CMS, this changes its public image.
set_article_featured_image
Add keyword associations to an existing prompt using keywordIds or phrases, and optionally remove specific associations with removeKeywordIds. Existing links remain unless explicitly removed. All IDs must belong to the current brand. Demand is computed from the highest-volume linked keyword. Does not edit prompt text.
set_prompt_keyword
Restores an archived article to its appropriate status: - If it was published → "published" - If it was scheduled → "scheduled" - Otherwise → "review" Only works on articles with "archived" status. Restoring a previously published ChatFeatured CMS article makes its public page accessible again; restoring scheduled status can allow its scheduled publish to proceed.
unarchive_article
Updates an article and its associations to keywords and prompts. Can update article title, description, and why fields, the length the writer aims for, and the SEO fields a CMS publish carries (meta description, SEO title, focus keyphrase, URL slug). Use the SEO fields to apply SEO suggestions you made instead of leaving the user to paste them into their CMS. Can add writing instructions to an idea's brief before it is written (addWritingInstructions). Can add new keyword/prompt associations and remove existing ones. Scheduled, published, archived and approval-locked articles can't take length, SEO or slug changes. Use this to update article metadata or keyword/prompt relationships. It does not edit the draft body: read it with the get article tool and edit it in the ChatFeatured dashboard. Changing the title of an article already published through ChatFeatured CMS also changes its public page.
update_article_content
Sets an article's workflow status directly. Use it when the article is published outside ChatFeatured's CMS integrations: fetch the body with get_article, publish it through your own pipeline, then call this with status "published" and the live URL in externalUrls. Statuses you can set: "review", "awaiting_approval", "approved", "published". - This changes status only. It does not publish to, update, or remove anything from a connected CMS (use publish_article for that). - "published" records the publish date, completes the article's content tasks, and fires the article.published webhook. Requires publish permission. - externalUrls (only with "published") are added to the article's tracked live URLs, which AI-citation attribution matches against, and are submitted to search engines for indexing. Existing URLs are kept. Calling again on an already published article adds more URLs. - Moving an article out of "published" clears its publish date and takes its ChatFeatured CMS page offline if it has one; copies on external platforms are untouched. - Ideas ("pending"), failed generations ("error"), scheduled and archived articles are rejected. Use approve_articles, the dashboard retry, schedule_article with action "cancel", or unarchive_article for those. To archive, use archive_articles.
update_article_status
Updates brand book fields for the current brand. Supports partial updates — only provided fields will be changed. Fetches the current brand book first and merges the provided fields before saving. The brand ID is automatically provided from context.
update_brand_book
Updates a task's status. Use to mark a task completed, dismissed, in progress, or to reopen it (open). Find the taskId first with list-tasks. Only call when the user asks to change a task's status.
update_task_status
Updates an existing tracking prompt's geo targeting or tags. Prompt text can't be changed. To reword a prompt, pause it with update-prompt-status and create a new one. To update multiple prompts with the same geo or tags, pass promptIds once instead of calling this tool repeatedly. At least one of geo or tags must be provided. When updating geo, billing slot limits are checked (each country uses one slot). The brand ID is automatically provided from context.
update_prompt
ChatFeatured ChatGPT Plugin FAQ
How the directory, categories and Discoverability Score work.
Read the methodologyHow do I improve ChatFeatured's ChatGPT Plugin discoverability?
The levers are the listing surface agents actually read: names, descriptions, keywords, tool metadata, and registry health. Which lever matters depends on where discovery breaks, which is what continuous measurement shows.
What are ChatFeatured alternatives on ChatGPT?
As of 2026-11-02, ChatFeatured competes with 3RD, 8ight SEO & GEO, Actionbase, AEO/GEO by VibeSEO, Agent Ready, Agent Ready Checker, Agenticaso, AI Interpretation Layer Audit™ and 95 more in ChatGPT AI Search & LLM Visibility (AEO/GEO), ranked by public Discoverability Score.
Where is this profile measured?
This profile uses the geography attached to the latest public registry snapshot: US. Locale tags are intentionally omitted.