add_keywords
ChatGPTAdds existing keyword IDs or new phrases to the project's keyword bank. New phrases are queued for Google measurement.
add_opportunity_task
ChatGPTAdds 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.
approve_articles
ChatGPTApproves 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.
archive_articles
ChatGPTArchives 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.
create_article_ideas
ChatGPTCreates 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). The free-text "prompts" and "keywords" arrays are fuzzy matched and can miss; anything that could not be resolved comes 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.
create_prompt
ChatGPTCreates 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.
delete_articles
ChatGPTPermanently 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.
fetch_keywords
ChatGPTLists keywords already in the project's keyword bank. Optionally filters by phrase. Bid values are returned in dollars.
generate_articles
ChatGPTTriggers 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.
get_article
ChatGPTFetches 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_schedule
ChatGPTGets 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_brand_book
ChatGPTFetches 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_existing_opportunities
ChatGPTFetches 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_publish_status
ChatGPTChecks 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_response_text
ChatGPTReads 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.
knowledge_base_query
ChatGPTSearch 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_read
ChatGPTRead 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.
list_articles
ChatGPTLists 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_brands
ChatGPTLists all projects the user has access to, optionally filtered by organization. Use the returned project ID as the brandId argument for other tools.
list_competitors
ChatGPTFetches 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_integrations
ChatGPTLists 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_organizations
ChatGPTLists all organizations the authenticated user is a member of, including their role in each. Next step: list_brands to see available projects.
list_prompts
ChatGPTFetches 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_tags
ChatGPTFetches 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_tasks
ChatGPTLists 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.
manage_brand_persona
ChatGPTManages 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_competitor
ChatGPTAdd, update, or remove competitors for the current brand. - "add": Creates a new competitor group. Requires name and domain. Will reject if a competitor with the same name or domain already exists. - "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. - "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_tag
ChatGPTCreates, 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.
publish_article
ChatGPTPublishes 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.
query_analytics
ChatGPTGeneric analytics query tool that replaces all individual analytics tools. Supports presets for common queries and custom dimensions/metrics/filters for ad-hoc analysis. Presets (use these first, override fields as needed): - brand_overview: Brand visibility & sentiment by prompt and model - competitor_comparison: Brand vs competitor visibility/sentiment/mentions by prompt - source_analysis: Top cited sources per prompt (top 5 per prompt) - time_comparison: Visibility & sentiment changes vs previous period, by prompt and model - source_changes: Sources that appeared/disappeared vs previous period (sorted by delta) - cited_urls: Top 50 cited source URLs, each with its citation count AND the number of distinct prompts citing it (prompt_count) - cited_urls_by_platform: Same as cited_urls but each row ALSO includes citationsByModel — citations split by AI platform (ChatGPT, Perplexity, Google AI, Gemini, etc.). Use this for the full "source URL + citations + prompts + per-platform split" report in ONE query. - source_mentions: Top 50 cited domains, each with its citation count AND the number of distinct prompts citing it (prompt_count) Common query patterns: - Brand overview: { preset: "brand_overview" } - What changed this week: { preset: "time_comparison" } - Source changes (appeared/disappeared): { preset: "source_changes" } - Competitor comparison: { preset: "competitor_comparison" } - Top sources per prompt: { preset: "source_analysis" } - Top source URLs by citations AND how many prompts cite each: { preset: "cited_urls" } - Top source domains by citations AND how many prompts cite each: { preset: "source_mentions" } - Source URLs ranked by how many distinct prompts cite them: { preset: "cited_urls", sortBy: "prompt_count" } - Top source URLs with citations, prompts, AND per-platform (ChatGPT/Perplexity/Google AI) citation split: { preset: "cited_urls_by_platform" } — read citationsByModel on each row; it sums to current.citations, and whether the brand was found in the source is on source.brandFoundInSource - Source changes for specific prompt: { preset: "source_changes", filters: { prompts: ["id"] } } - All prompts with a specific tag: { preset: "brand_overview", filters: { tags: ["tagId"] } } - Which models dropped visibility: { metrics: ["visibility"], groupBy: ["model"], compareWithPrevious: true, sortBy: "delta.visibility", sortDirection: "asc" } - Sources only where brand is mentioned: { preset: "source_analysis", filters: { brandPresence: "mentioned" } } - Where am I not showing up: { preset: "brand_overview", filters: { brandPresence: "not_mentioned" } } - Are competitors citing new sources: { metrics: ["citations"], groupBy: ["source", "competitor"], compareWithPrevious: true } Sort options: - Sort by current metric: sortBy: "visibility", "sentiment", "citations", "mention_count", "prompt_count" - Sort by delta: sortBy: "delta.visibility", "delta.citations", etc. - Sort by previous: sortBy: "previous.visibility", etc. Reading results — quoting numbers accurately: - visibility = % of AI runs (among runs where at least one brand was mentioned) where THIS brand appears. This is the exact formula the dashboard uses, so the values here match the dashboard. - meta.summary.current holds the OVERALL metrics for the entire filtered period (plus meta.summary.previous / meta.summary.delta when comparing periods). For any headline number — "what's my visibility?", "how's my sentiment?" — quote meta.summary, which matches the dashboard's top-level widgets. - NEVER average per-row visibility/sentiment values to produce an overall figure: rows cover different numbers of runs, so an unweighted average is wrong and will not match the dashboard. Rows are for per-prompt/per-model/per-competitor detail only. Prompt status: - History counts regardless of status. A paused (inactive) prompt stops producing new runs, but its past responses stay in every number here, exactly as the dashboard and the Prompts page count them. Do not treat a paused prompt's data as missing. - To look only at prompts that are live right no…
query_analytics_sql
ChatGPTAdvanced analytics tool: run a single READ-ONLY PostgreSQL SELECT over THIS brand's AI-search data. Use it for complex or custom analysis the queryAnalyticsTool presets can't express — per-day/per-week trends, visibility or share-of-voice over time, source/domain breakdowns, custom pivots, distinct counts, or any custom grouping. (queryAnalyticsTool is still the quickest path for its standard preset reports.) 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). METRIC MODEL: Always call explain:true first. Its schema is authoritative for this organization. With the citation model, mentions exposes only direct mentions (mention_type direct, NULL reads as direct), and citations exposes only cited sources (citation_role cited, NULL reads as cited). Each source row is one page in one answer: citation metrics are COUNT(DISTINCT prompt_response_id), grouped at the page/domain/brand/prompt/platform being measured; never SUM(citation_count), which the citation model doesn't expose. prompt_count counts distinct prompts over cited rows. The examples below show the legacy model; use the explain schema recipes when citation-model is enabled. TIP: call this tool with explain:true to get the full schema PLUS this brand's actual competitor group names and tag names (returned as competitorGroups / tags). Do that first whenever you need an exact competitor or tag label to filter on — don't guess it. RELATIONS YOU MAY QUERY (these are the only names allowed; call with explain:true for full column lists): - mentions(created_at, prompt_run_id, prompt_response_id, prompt_id, model, country, brand_id, brand_name, sentiment, tags, is_self, competitor_group) — ONE ROW PER BRAND MENTIONED in an AI response. INCLUDES COMPETITORS. Use is_self (server-computed boolean) for YOUR brand and competitor_group to compare specific competitors (see SELF / COMPETITORS). Visibility is computed from this relation alone — see VISIBILITY. - citations(created_at, prompt_run_id, prompt_response_id, prompt_id, model, country, source_domain, source_type, source_url, source_title, citation_count, tags, brand_found_in_source) — one row per source citation in an AI response. brand_found_in_source (nullable boolean, server-computed) = was YOUR brand actually found on the cited page (TRUE features you, FALSE verified-absent, NULL not verified) — use WHERE brand_found_in_source for "sources that feature my brand". - runs(prompt_run_id, prompt_id, model, country, created_at) — the FULL universe of completed AI runs, INCLUDING runs that mentioned nobody. Use it for run counts and coverage questions (how many runs executed, per model/day). It is NOT the visibility denominator — see VISIBILITY. - competitor_dim(competitor_group, competitor_domain) — the brand's configured competitor groups (use to discover/list competitors). - prompt_dim(prompt_id, prompt_text) — the brand's tracked prompts. INACTIVE prompts are excluded everywhere (prompt_dim, runs, mentions, citations) — deactivated prompts and their historical runs never appear in results. - tag_dim(tag_id, name) — the brand's tags. - prompt_tag_dim(prompt_id, tag_id) — which prompts have which tags. SELF / COMPETITORS / VISIBILITY (use the server flags — never hard-code brand names): - YOUR brand = WHERE is_self. - A specific COMPETITOR = filter on competitor_group, NOT brand_name (one competitor spans several brand_name variants; competitor_group groups them). competitor_group is the configured DISPLAY NAME and is CASE-SENSITIVE (e.g. 'Semrush', 'HubSpot' — not 'semrush'). NEVER compare it with '=' and a guessed literal — a case/spelling mismatch silently returns 0 rows and looks like the competitor has no presence. Instead match case-insensitively with ILIKE: WHERE competitor_group ILIKE 'semrush' (no wildcards = case-insensitive exact), or resolve the exact stored label inside the query: WHERE competitor_group IN (SELECT competitor_group FROM competitor_dim WHERE competitor_group ILIKE '%semrush%'). Discover the e…
query_crawler_analytics
ChatGPTQueries 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_google_analytics
ChatGPTReads 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_site_audit
ChatGPTQuery 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.
research_keywords
ChatGPTAsks Google for keyword ideas around a phrase or website. Results are cached but are not added to the project's keyword bank.
schedule_article
ChatGPTManages 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.
score_prompt_demand
ChatGPTScores 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.
search_pexels_images
ChatGPTSearches 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.
set_article_featured_image
ChatGPTDownloads 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_prompt_keyword
ChatGPTAdd 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.
unarchive_article
ChatGPTRestores 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.
update_article_content
ChatGPTUpdates an article and its associations to keywords and prompts. Can update article title, description, and why fields. Can add new keyword/prompt associations and remove existing ones. 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_status
ChatGPTSets 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_brand_book
ChatGPTUpdates 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_prompt
ChatGPTUpdates 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_status
ChatGPTActivates 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_task_status
ChatGPTUpdates 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.