List Projects
List active projects the authenticated user has access to. By default, only projects with an active status (CUSTOMER, PITCH, TRIAL, ONBOARDING, API_PARTNER) are returned — this is what you want in almost every case. Only set include_inactive to true if the user asked for a specific project that wasn't in the active list; do not set it preemptively. Returns columnar JSON: {columns, rows, rowCount}. Columns: id, name, status. The id is used as project_id in other tools. Call this first to discover available projects.
List Topics
List topics in a project. Topics are folder-like groupings — each prompt belongs to exactly one topic. Use this tool to resolve topic names to IDs before filtering (topic_id filter/dimension, list_prompts), and to label topic IDs from report output with their human-readable names before presenting results. Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matching records ignoring limit/offset. Columns: id, name.
List Tags
List tags in a project. Tags are cross-cutting labels that can be assigned to any prompt. Use this tool to resolve tag names to IDs before filtering (tag_id filter/dimension, list_prompts), and to label tag IDs from report output with their human-readable names before presenting results. Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matching records ignoring limit/offset. Columns: id, name, is_system, group. System tags (is_system=true) are maintained by Peec and auto-assigned to every prompt by its branding/intent classification — they cannot be created, edited, or deleted, but they can be assigned to prompts. Each system tag has a `group` (null for user tags): "branding" (branded / non-branded) or "intentType" (informational / commercial / transactional). The groups are mutually exclusive: a prompt carries exactly one tag per group, so assigning a system tag (via update_prompts) overwrites whichever tag that prompt already had in that group rather than adding a second. Filter on the existing system tags — never create your own branded/intent tags.
List Brands
List brands tracked in a project — includes the user's own brand and competitors. Use this tool to resolve brand names to IDs before filtering reports (brand_id filter), and to label brand IDs from report output with their human-readable names before presenting results. Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matching records ignoring limit/offset. Columns: id, name, domains, aliases, is_own. aliases are alternate names the brand is matched under. is_own indicates which brand belongs to the user. These are the brands a project tracks for visibility reporting — separate from list_global_brands, which searches the global product catalog used when creating products.
List Global Brands
Search Peec's global brand catalog — the shared registry of real-world brands (e.g. Nike, Apple) that products attach to. Use it to find the global_brand_id for a brand before creating products with create_products. This is NOT the same as list_brands: list_brands returns the brands you track inside a project (your own brand plus the competitors you monitor for AI visibility), whereas list_global_brands searches every brand in Peec's catalog. The two have different ids and are not interchangeable. Primarily used with `search` to look up a brand by name (matches names and aliases). Pass ownership ('own', 'competitor', or 'all') instead to list the project's shopping brands by global_brand_id — the ids shopping product filters take — ranked by mentions, with their mention_count and is_own. Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matches ignoring limit/offset. Columns: id, name, domain, description, mention_count, is_own.
List Models
Deprecated — prefer list_model_channels, which returns stable channel IDs that survive model upgrades. List AI engines (models) tracked by Peec. Use this tool to resolve model names (e.g., "ChatGPT", "Perplexity", "Gemini") to IDs before filtering reports (model_id filter/dimension), and to label model IDs from report output with their human-readable names before presenting results. Match user-supplied names against the name column; the id column is the canonical string to pass back as model_id. is_active indicates whether the model is enabled for this project — inactive models will return empty data in reports. Returns columnar JSON: {columns, rows, rowCount}. Columns: id, name, is_active.
List Model Channels
List the AI engine channels tracked by Peec. A model channel is a stable identifier for an AI engine (e.g. "openai-0" = ChatGPT UI) that persists even as the underlying model is upgraded — use it to filter or break down reports by engine without worrying about model version changes. Use this tool to resolve channel descriptions (e.g. "ChatGPT UI", "Perplexity") to channel IDs before filtering reports (model_channel_id filter), and to label channel IDs from report output before presenting results. The current_model_id column gives the model ID currently active in the channel — pass this as model_id where reports require it. is_active indicates whether the channel is enabled for this project — inactive channels return empty data. unsupported_country_codes lists country codes that cannot be used with this channel (chats requested for those countries are not created). Returns columnar JSON: {columns, rows, rowCount}. Columns: id, description, current_model_id, is_active, unsupported_country_codes.
List Prompts
List prompts (conversational questions tracked daily across AI engines) in a project. Supports filtering by topic_id, tag_id, and is_archived. Returns only active prompts by default — pass is_archived=true to list archived prompts instead (e.g. when a prompt_id from older report output isn't found among the active ones). Use this tool to resolve prompt text to IDs before filtering reports (prompt_id filter/dimension), and to label prompt IDs from report output with their actual text before presenting results. Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matching records ignoring limit/offset. Columns: id, text, tag_ids (array of tag ID strings), topic_id (string or null), volume (relative search volume bucket: "very low" | "low" | "medium" | "high" | "very high", or null when unavailable — describe volume to users using the bucket label), is_archived (boolean — true when the prompt is no longer tracked daily), created_at (ISO 8601 date string, e.g. "2025-09-22", when the prompt was created — use to identify prompts older than a given age). Pass `fields` to return only the columns you need.
List Chats
List chats (individual AI responses) for a project over a date range. Each chat is produced by running one prompt against one AI engine on a given date.
Filters:
- brand_id: only chats that mentioned the given brand
- prompt_id: only chats produced by the given prompt
- model_id: only chats from the given AI engine (chatgpt-scraper, gpt-4o, gpt-4o-search, gpt-3.5-turbo, llama-sonar, perplexity-scraper, sonar, gemini-2.5-flash, gemini-scraper, google-ai-overview-scraper, google-ai-mode-scraper, llama-3.3-70b-instruct, deepseek-r1, deepseek-v4-pro, claude-3.5-haiku, claude-haiku-4.5, claude-sonnet-4, grok-scraper, microsoft-copilot-scraper, grok-4, grok-4.3, qwen-3-6-plus, qwen-3-7-plus, amazon-rufus-scraper) — deprecated, prefer model_channel_id
- model_channel_id: only chats from the given engine channel (openai-0, openai-1, qwen-0, openai-2, perplexity-0, perplexity-1, google-0, google-1, google-2, google-3, anthropic-0, anthropic-1, deepseek-0, meta-0, xai-0, xai-1, microsoft-0, amazon-0)
- features: only chats that contain all of the given features (SHOPPING, PRODUCT_COMPARISON, AD, MAP, WEB_SEARCH)
If both model_id and model_channel_id are provided, model_channel_id takes precedence and model_id is ignored.
Excludes chats whose prompt has been deleted or archived. Set include_archived_prompts=true to include chats for archived prompts (e.g. historical lookback for a prompt that is no longer tracked). Chats for deleted prompts are always excluded.
Use the returned chat IDs with get_chat to retrieve full message content, sources, and brand mentions.
Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matching records ignoring limit/offset. Columns: id, prompt_id, model_id, model_channel_id, date, features.
List Search Queries
List the search queries an AI engine fanned out to while answering prompts in a project over a date range. Each row represents one sub-query the engine issued for a given chat.
Filters:
- prompt_id: only queries from chats produced by this prompt
- chat_id: only queries from this chat
- model_id: only queries from this AI engine (chatgpt-scraper, gpt-4o, gpt-4o-search, gpt-3.5-turbo, llama-sonar, perplexity-scraper, sonar, gemini-2.5-flash, gemini-scraper, google-ai-overview-scraper, google-ai-mode-scraper, llama-3.3-70b-instruct, deepseek-r1, deepseek-v4-pro, claude-3.5-haiku, claude-haiku-4.5, claude-sonnet-4, grok-scraper, microsoft-copilot-scraper, grok-4, grok-4.3, qwen-3-6-plus, qwen-3-7-plus, amazon-rufus-scraper)
- model_channel_id: only queries from this channel (openai-0, openai-1, qwen-0, openai-2, perplexity-0, perplexity-1, google-0, google-1, google-2, google-3, anthropic-0, anthropic-1, deepseek-0, meta-0, xai-0, xai-1, microsoft-0, amazon-0)
- topic_id: only queries from chats whose prompt belongs to this topic
- tag_id: only queries from chats whose prompt carries this tag
Use get_chat with a returned chat_id to inspect the full AI response that produced these sub-queries.
Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matching records ignoring limit/offset. Columns: prompt_id, chat_id, model_id, model_channel_id, date, query_index, query_text.
List Shopping Queries
List the product/shopping queries an AI engine fanned out to while answering prompts in a project over a date range. Each row represents one shopping sub-query and the distinct products returned for it in a given chat.
Filters:
- prompt_id: only queries from chats produced by this prompt
- chat_id: only queries from this chat
- model_id: only queries from this AI engine (chatgpt-scraper, gpt-4o, gpt-4o-search, gpt-3.5-turbo, llama-sonar, perplexity-scraper, sonar, gemini-2.5-flash, gemini-scraper, google-ai-overview-scraper, google-ai-mode-scraper, llama-3.3-70b-instruct, deepseek-r1, deepseek-v4-pro, claude-3.5-haiku, claude-haiku-4.5, claude-sonnet-4, grok-scraper, microsoft-copilot-scraper, grok-4, grok-4.3, qwen-3-6-plus, qwen-3-7-plus, amazon-rufus-scraper)
- model_channel_id: only queries from this channel (openai-0, openai-1, qwen-0, openai-2, perplexity-0, perplexity-1, google-0, google-1, google-2, google-3, anthropic-0, anthropic-1, deepseek-0, meta-0, xai-0, xai-1, microsoft-0, amazon-0)
- topic_id: only queries from chats whose prompt belongs to this topic
- tag_id: only queries from chats whose prompt carries this tag
Use get_chat with a returned chat_id to inspect the full AI response that produced these sub-queries.
Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matching records ignoring limit/offset. Columns: prompt_id, chat_id, model_id, model_channel_id, date, query_text, products (array of product names).
List Products
List a project's products with headline metrics over a date range, filtered by the shopping filter pack.
Returns columnar JSON: {columns, rows, rowCount, totalCount}. rowCount is the rows in this page; totalCount is the total matching records ignoring limit/offset. Columns: id, name, brand, image_url, price_range (per-currency effective range), categories (category ids), mention_count, win_count, avg_position, avg_rating (mean 0–5 star rating across the product's AI mentions; null when none carried a rating), visibility, share_of_voice. Metrics cover [start_date, end_date].
Get Product
Get one product's detailed metrics over a date range — the drill-down companion to list_products, which returns a thin row per product.
Returns {data, primary_currency}, where data is null if the product is not in the project. data always includes:
- id, name, brand, source (CATALOG or LLM), first_seen_at
- visibility (0–1), win_rate (0–1), avg_position (rank, lower is better), avg_rating (mean 0–5 star rating across the product's AI mentions; null when none carried a rating), mention_count
- *_delta for each metric above (visibility_delta, win_rate_delta, avg_position_delta, avg_rating_delta, mention_count_delta) — change vs the immediately preceding equal-length period; null when the previous period had nothing to compare against
Everything else is opt-in via the 'fields' argument (default: none) to keep the response lean — list any of these to include them:
- description, image_url
- price_range / price_override — per-currency effective catalog range (min/max) and the raw overrides behind it
- ai_price_map / ai_price_delta_map — per-currency median price across the product's AI mentions, and its change
- four QFO (query fan-out) breakdowns of what surfaced this product, each up to 25 rows with distinct_chat_count (+ its previous-period value and delta): shopping_queries / shopping_query_terms (Google-Shopping fan-out queries and their n-grams) and fanout_queries / fanout_query_terms (web-search fan-out queries and their n-grams). Unrequested QFO breakdowns are skipped, not just omitted — no ClickHouse pass runs for them.
Visibility divides by the product's relevant-prompt chats (prompts it is mentioned in or SODA-linked to), not all shopping chats. Metrics cover [start_date, end_date]; deltas compare against the equal-length period ending the day before start_date.
Get Shopping Attributes
The LLM-extracted attribute comparison grid — characteristics the AI associates with a product (or the whole catalog), against competitors.
scope=product compares one product (needs product_id); scope=overview compares the whole catalog. compare_by picks the grid columns: brand (default) or product (scope=product only). tab picks the value type.
Returns a nested grid (not columnar): { tab, competitors[], groups[], total_groups }. Each competitor is a brand ({global_brand_id, name, domain, mentions}) or product ({product_id, name, brand_name, image_url, mentions}); every group's per-competitor arrays align to the competitors order. Group shape by tab:
- characteristics: { dimension_id, name, total_mentions(+_delta), value_count, values: [{value, mentions(+_delta), competitor_mentions[]}] }
- facts: { ..., value_type: BOOLEAN, unit, own: {true_count, false_count, unrecognized_count}, competitor_values[] }
- dimensions: { ..., own: {min, max, average, median} | null, competitor_values: (stats | null)[] }
Deltas are vs an explicit comparison window (previous_start_date/previous_end_date) or the equal-length window immediately before.
Get Shopping Summary
Return aggregate shopping metrics over a date range. Deltas compare against previous_start_date/previous_end_date when given, else the auto-derived previous period.
Get Shopping Trend
Return a product or brand shopping time series. Requires bucket and exactly one of product_ids or brand_ids.
List Shopping Performance
List ranked product or category shopping performance over a date range.
List Shopping Demand
List top shopping queries, search fan-out queries, or query terms over a date range.
Create Products
Create products in a project. Each item needs a global_brand_id (use list_global_brands to find it by name) and a name unique within that brand; description, image_url, per-currency price_override, and category_ids (use list_categories to find category ids) are optional.
Returns per-item results { created, rejected } — the batch never fails as a whole. Rejection reasons: name_conflict (name already used in the brand), brand_not_found, category_not_found.
Confirm with the user before calling — this mutates project data.
Create Global Brand
Create a brand and return its global_brand_id. Use this when list_global_brands finds no match for a brand you need, or when create_products rejects an item with brand_not_found.
Returns { id, name }. The returned name is the brand's canonical name and may differ slightly from the name you sent.
This is separate from create_brand, which creates a brand a project tracks for AI-visibility reporting; the two have different ids and are not interchangeable.
Confirm with the user before calling — this creates a brand.
Update Products
Update products by id (use list_products to resolve ids). Per item, set any of: name (unique within the brand), description, image_url, price_override (per-currency; replaces all overrides, {} clears them), category_ids (replaces the product's categories, [] uncategorizes it; use list_categories to find category ids). Omitted fields are left unchanged.
Returns per-item results { updated, skipped, rejected } — the batch never fails as a whole. An item with nothing to apply is skipped (not_found / no_changes / duplicate_id); an unsatisfiable change (name conflict, unknown category) is rejected.
Confirm with the user before calling — this mutates project data.
Delete Products
Delete products by id (use list_products to resolve ids). Returns per-item results { deleted, skipped } — ids not present in the project are skipped (not_found).
Confirm with the user before calling — this mutates project data.
List Categories
List a project's product categories. Categories are an org-wide tree (e.g. Footwear > Shoes > Running Shoes); each row carries its full `path` and its `parent_id`, so the flat list rebuilds the tree. Use a row's `id` in the `category_ids` of create_products or update_products to categorize products. Returns columnar JSON: {columns, rows, rowCount, totalCount}. Columns: id, name, path, parent_id.
Create Categories
Create product categories. Each item needs a name and an optional parent_id — omit it for a top-level category, or pass another category's id (use list_categories to find it) to nest it underneath. A name must be unique among its siblings. Items apply in order.
Returns per-item results { created, rejected } — the batch never fails as a whole. Rejection reasons: parent_not_found, name_conflict.
Confirm with the user before calling — this mutates project data.
Update Categories
Rename and/or move categories by id (use list_categories to resolve ids). Each item sets name (rename it), parent_id (move it under that category, or null to promote it to the top level), or both.
Returns per-item results { updated, rejected } — the batch never fails as a whole. Rejection reasons: not_found, name_conflict (a sibling already has that name), invalid_move (would nest a category inside itself).
Confirm with the user before calling — this mutates project data.
Delete Categories
Delete categories by id (use list_categories to resolve ids). A deleted category's child categories and products move up to its parent (a top-level delete sends children to the top level and its products to Uncategorized).
Returns per-item results { deleted, rejected } — the batch never fails as a whole. Rejection reasons: not_found, name_conflict (a child being moved up would clash with an existing sibling name).
Confirm with the user before calling — this mutates project data.
List Bots
List all known AI agent bots tracked in Agent analytics. Each bot has an ID, provider, and type.
Bot types:
- training: crawlers that collect data for model training (e.g. GPTBot, ClaudeBot)
- search: bots that retrieve content for AI-powered search (e.g. PerplexityBot)
- userQuery: bots triggered by real-time user queries
- other: miscellaneous bots
Use the returned bot IDs with get_agent_visits to filter visit counts by specific bots.
Returns: {data: [{id, provider, type}]}
Get Agent Visits
Aggregate AI agent visit counts from access logs over a date range.
Without group_by: returns total visit count as a single {visits: N} row.
With group_by: returns one row per distinct value of the chosen dimension(s), sorted by visits descending.
group_by dimensions:
- bot_id: break down by bot (use list_bots to resolve names)
- response_status: break down by HTTP response status (200, 404, etc.)
- request_host: break down by hostname
- request_path: break down by URL path
Multiple group_by values produce a cross-dimensional breakdown (e.g. bot_id + response_status gives per-bot per-status counts).
Use bot_ids to filter to specific bots before grouping.
time_bucket: bucket results by time period (hour, day, week, month). Each row will include a `time_bucket` timestamp marking the start of the bucket. Combine with group_by to get e.g. per-bot per-day counts.
Returns: {data: [{bot_id?, response_status?, request_host?, request_path?, time_bucket?, visits}], totalCount}
List Domain Classifications
List the custom domain classifications defined for a project. These complement the built-in classifications (Corporate, Competitor, Editorial, Institutional, Other, Reference, UGC, You, Related) and can be assigned to domains via assign_domain_classification. Returns columnar JSON: {columns, rows, rowCount, totalCount}. Columns: name, color.
List Url Classifications
List the custom URL classifications defined for a project. These complement the built-in classifications (Homepage, Category Page, Product Page, Listicle, Comparison, Profile, Alternative, Discussion, How-To Guide, Article, Other) and can be assigned to URLs via assign_url_classification. Returns columnar JSON: {columns, rows, rowCount, totalCount}. Columns: name, color.
Get Chat
Get the full content of a single chat (one AI engine's response to one prompt on one date). Returns:
- messages: the user prompt and assistant response(s)
- brands_mentioned: brands detected in the response with their position
- sources: URLs the model retrieved, with citation counts and position
- queries: search queries the model issued
- products: product gallery entries extracted from the response
- features: signal flags for special elements detected in the assistant response (SHOPPING, PRODUCT_COMPARISON, AD, MAP, WEB_SEARCH). Use list_chats with the `features` filter to query chats by these.
- maps: local-business map cards (one per business pinned in a map widget) with { name, url } where `url` is the Google Maps directions deeplink for that business.
- ads: paid ad placements rendered by the model. Each ad has { brandName, url, id, adUnitType, adsRequestId, cards } where cards carry { title, body, imageUrl, targetUrl } and targetUrl is the clickout URL (with attribution UTM params).
- prompt: { id }
- model: { id } — deprecated, prefer model_channel
- model_channel: { id } — stable engine channel id (e.g. "openai-0")
Use list_chats to discover chat IDs for a project.
Get Brand Report
Get a report on brand visibility, sentiment, and position across AI search engines.
Results are aggregated for the entire date range by default. Use the "date", "week", or "month" dimension for time-bucketed breakdowns (daily, ISO week, or calendar month). When the range spans many days, prefer "week" or "month" over "date": coarser buckets return far fewer rows (180 days ≈ 26 weeks or 6 months vs. 180 daily rows), which avoids the row limit truncating the series and keeps the trend readable. Reach for "date" only when day-level granularity is essential and the range is short. Edge buckets may be partial: a range starting or ending mid-week/mid-month produces a first/last bucket that only aggregates the in-range days, so its values can look low — don't read that as a real dip.
Dimensions multiply the row count: rows = entities × dimension value combinations (e.g. 2 brands broken down by "date" over 180 days = 360 rows). The default limit of 100 will silently truncate such queries — always set "limit" to at least the expected row count (max 10000). If rowCount equals limit, results were truncated; re-query with a higher limit or paginate with offset.
Returns columnar JSON: {columns, rows, rowCount, total}. Each row is an array of values matching column order. Columns:
- brand_id — the brand ID
- brand_name — the brand name
- visibility: 0–1 ratio — fraction of AI responses that mention this brand. 0.45 means 45% of conversations.
- mention_count: number of times the brand was mentioned
- share_of_voice: 0–1 ratio — brand's fraction of total mentions across all tracked brands
- sentiment: 0–100 scale — how positively AI platforms describe the brand (most brands score 65–85)
- position: average ranking when the brand appears (lower is better, 1 = mentioned first)
- Raw aggregation fields (for custom calculations): visibility_count, visibility_total, sentiment_sum, sentiment_count, position_sum, position_count
When dimensions are selected, rows also include the relevant dimension columns: prompt_id, model_id, model_channel_id, tag_id, topic_id, chat_id, date, week, month, country_code. model_channel_id, tag_id and topic_id are each followed by a model_channel_name / tag_name / topic_name column with the human-readable name.
Dimensions explained:
- prompt_id: individual search queries/prompts
- model_id: AI search engine (e.g. chatgpt-scraper, gpt-4o, gpt-4o-search, gpt-3.5-turbo, llama-sonar, perplexity-scraper, sonar, gemini-2.5-flash, gemini-scraper, google-ai-overview-scraper, google-ai-mode-scraper, llama-3.3-70b-instruct, deepseek-r1, deepseek-v4-pro, claude-3.5-haiku, claude-haiku-4.5, claude-sonnet-4, grok-scraper, microsoft-copilot-scraper, grok-4, grok-4.3, qwen-3-6-plus, qwen-3-7-plus, amazon-rufus-scraper) — deprecated, prefer model_channel_id
- model_channel_id: stable engine channel (e.g. openai-0, openai-1, qwen-0, openai-2, perplexity-0, perplexity-1, google-0, google-1, google-2, google-3, anthropic-0, anthropic-1, deepseek-0, meta-0, xai-0, xai-1, microsoft-0, amazon-0) — survives model upgrades
- tag_id: custom user-defined tags
- topic_id: topic groupings
- date: daily bucket (YYYY-MM-DD)
- week: ISO week bucket, value is the Monday start (YYYY-MM-DD)
- month: calendar month bucket, value is the first of the month (YYYY-MM-DD)
- country_code: country (ISO 3166-1 alpha-2, e.g. "US", "DE")
- chat_id: individual AI chat/conversation ID
Two filter inputs at different SQL stages — pick by intent:
- filters (WHERE, pre-aggregation): shrinks BOTH numerator and denominator of ratio metrics. Putting brand_id here narrows everything to that brand — including share_of_voice's denominator — so SoV collapses to 1.0.
- having (HAVING, post-aggregation): selects which aggregated rows are returned WITHOUT shrinking ratio-metric denominators. Use brand_id here for a single-brand report where SoV stays a real [0,1] fraction (X's share against competitors per dimension).
Metric semantics:
- visibility = visibility_count / visibility_total within each (brand × dimension) group. Per-row by construction; both filters and having behave intuitively.
- share_of_voice = brand's mention_count / sum of mentions across all brands in the same (dimension) cell. Pre-aggregation filters narrow num+denom together; post-aggregation having does not.
- When dimensions are requested, share_of_voice's denominator is per-dimension. Example: dimension prompt_id ⇒ share_of_voice for (brand X, prompt P) = X's mentions in P / all brands' mentions in P.
Sort results with order_by: array of {field, direction} entries. Direction defaults to desc. Sortable fields: visibility, visibility_count, mention_count, sentiment, position, share_of_voice, date, week, month. Multiple entries create a multi-key sort. Sorting by a time field (date, week or month) requires the matching dimension to be selected.
Get Domain Report
Get a report on source domain visibility and citations across AI search engines.
Results are aggregated for the entire date range by default. Use the "date", "week", or "month" dimension for time-bucketed breakdowns (daily, ISO week, or calendar month). When the range spans many days, prefer "week" or "month" over "date": coarser buckets return far fewer rows (180 days ≈ 26 weeks or 6 months vs. 180 daily rows), which avoids the row limit truncating the series and keeps the trend readable. Reach for "date" only when day-level granularity is essential and the range is short. Edge buckets may be partial: a range starting or ending mid-week/mid-month produces a first/last bucket that only aggregates the in-range days, so its values can look low — don't read that as a real dip.
Dimensions multiply the row count: rows = domains × dimension value combinations (e.g. 5 domains broken down by "date" over 180 days = 900 rows). The default limit of 100 will silently truncate such queries — always set "limit" to at least the expected row count (max 10000). If rowCount equals limit, results were truncated; re-query with a higher limit or paginate with offset.
Returns columnar JSON: {columns, rows, rowCount}. Each row is an array of values matching column order. Columns:
- domain: the source domain (e.g. "example.com")
- classification: domain type — Corporate (official company sites), Editorial (news, blogs, magazines), Institutional (government, education, nonprofit), UGC (social media, forums, communities), Reference (encyclopedias, documentation), Competitor (direct competitors), You (the user's own domains), Other, or null
- retrieved_percentage: 0–1 ratio — fraction of chats that included at least one URL from this domain. 0.30 means 30% of chats.
- retrieval_rate: average number of URLs from this domain pulled per chat. Can exceed 1.0 — values above 1.0 mean multiple pages from the same domain are retrieved per conversation.
- citation_rate: average number of inline citations when this domain is retrieved. Can exceed 1.0 — higher values indicate stronger content authority.
- retrieval_count: total number of distinct URL retrievals from this domain across all chats (raw count — numerator of retrieval_rate).
- citation_count: total number of citations from this domain (raw count).
- mentioned_brand_ids: array of brand IDs mentioned alongside URLs from this domain (may be empty)
When dimensions are selected, rows also include the relevant dimension columns: prompt_id, model_id, model_channel_id, tag_id, topic_id, chat_id, date, week, month, country_code. model_channel_id, tag_id and topic_id are each followed by a model_channel_name / tag_name / topic_name column with the human-readable name.
Dimensions explained:
- prompt_id: individual search queries/prompts
- model_id: AI search engine (e.g. chatgpt-scraper, gpt-4o, gpt-4o-search, gpt-3.5-turbo, llama-sonar, perplexity-scraper, sonar, gemini-2.5-flash, gemini-scraper, google-ai-overview-scraper, google-ai-mode-scraper, llama-3.3-70b-instruct, deepseek-r1, deepseek-v4-pro, claude-3.5-haiku, claude-haiku-4.5, claude-sonnet-4, grok-scraper, microsoft-copilot-scraper, grok-4, grok-4.3, qwen-3-6-plus, qwen-3-7-plus, amazon-rufus-scraper) — deprecated, prefer model_channel_id
- model_channel_id: stable engine channel (e.g. openai-0, openai-1, qwen-0, openai-2, perplexity-0, perplexity-1, google-0, google-1, google-2, google-3, anthropic-0, anthropic-1, deepseek-0, meta-0, xai-0, xai-1, microsoft-0, amazon-0) — survives model upgrades
- tag_id: custom user-defined tags
- topic_id: topic groupings
- date: daily bucket (YYYY-MM-DD)
- week: ISO week bucket, value is the Monday start (YYYY-MM-DD)
- month: calendar month bucket, value is the first of the month (YYYY-MM-DD)
- country_code: country (ISO 3166-1 alpha-2, e.g. "US", "DE")
- chat_id: individual AI chat/conversation ID
Two filter inputs at different SQL stages — pick by intent:
- filters (WHERE, pre-aggregation): population fields (model/country/prompt/tag/topic/chat) shrink both numerator and denominator (total_chat_count). Source-side fields (domain/url/classification) and per-row mentioned-brand predicates (mentioned_brand_id, mentioned_brand_count, gap) shrink only the source-row scope; total_chat_count is computed from a chat-level table that doesn't carry these columns.
- having (HAVING, post-aggregation): selects which aggregated rows are returned WITHOUT shrinking ratio-metric denominators.
Mentioned-brand predicates and source-side fields exist in both — the WHERE form tests each source row pre-aggregation (shrinks numerators); the HAVING form tests the aggregated mentioned_brands union per domain (purely selective).
Numeric predicates:
- mentioned_brand_count: {field: "mentioned_brand_count", operator: "gt"|"gte"|"lt"|"lte", value: <number>} — filter by number of unique brands mentioned alongside the domain.
- gap: {field: "gap", operator: "gt"|"gte"|"lt"|"lte", value: <number>} — gap analysis filter. Excludes domains where the project's own brand is mentioned, and filters by the number of competitor brands present. Example: {field: "gap", operator: "gte", value: 2} returns domains where the own brand is absent but at least 2 competitors are mentioned.
Metric semantics:
- retrieved_percentage and retrieval_rate use total_chat_count as the denominator. Filtering to a single domain via having still yields a meaningful retrieved_percentage < 1.0 — do not interpret it as 100%.
- When dimensions are requested, total_chat_count is per-dimension. Example: dimension prompt_id ⇒ each row's total_chat_count is the chat count for that prompt, so retrieved_percentage is the per-prompt retrieval rate.
- citation_rate = citation_count / retrieved_chat_count per row; it's independent of total_chat_count and naturally per-(domain × dimension).
Sort results with order_by: array of {field, direction} entries. Direction defaults to desc. Sortable fields: citation_rate, retrieval_count, citation_count, date, week, month. Sorting by a time field (date, week or month) requires the matching dimension to be selected. (retrieved_percentage and retrieval_rate are not sortable because they depend on totalChatCount fetched in a separate query.)
Get Url Report
Get a report on source URL visibility and citations across AI search engines.
Results are aggregated for the entire date range by default. Use the "date", "week", or "month" dimension for time-bucketed breakdowns (daily, ISO week, or calendar month). When the range spans many days, prefer "week" or "month" over "date": coarser buckets return far fewer rows (180 days ≈ 26 weeks or 6 months vs. 180 daily rows), which avoids the row limit truncating the series and keeps the trend readable. Reach for "date" only when day-level granularity is essential and the range is short. Edge buckets may be partial: a range starting or ending mid-week/mid-month produces a first/last bucket that only aggregates the in-range days, so its values can look low — don't read that as a real dip.
Dimensions multiply the row count: rows = URLs × dimension value combinations (e.g. 5 URLs broken down by "date" over 180 days = 900 rows). The default limit of 100 will silently truncate such queries — always set "limit" to at least the expected row count (max 10000). If rowCount equals limit, results were truncated; re-query with a higher limit or paginate with offset.
Returns columnar JSON: {columns, rows, rowCount}. Each row is an array of values matching column order. Columns:
- url: the full source URL (e.g. "https://example.com/page")
- classification: page type — Homepage, Category Page, Product Page, Listicle (list-structured articles), Comparison (product/service comparisons), Profile (directory entries like G2 or Yelp), Alternative (alternatives-to articles), Discussion (forums, comment threads), How-To Guide, Article (general editorial content), Other, or null
- title: page title or null
- channel_title: channel or author name (e.g. YouTube channel, subreddit) or null
- citation_count: total number of explicit citations across all chats
- retrieval_count: total number of distinct chats that retrieved this URL, regardless of whether it was cited
- citation_rate: average number of inline citations per chat when this URL is retrieved. Can exceed 1.0 — higher values indicate more authoritative content.
- mentioned_brand_ids: array of brand IDs mentioned alongside this URL (may be empty)
When dimensions are selected, rows also include the relevant dimension columns: prompt_id, model_id, model_channel_id, tag_id, topic_id, chat_id, date, week, month, country_code. model_channel_id, tag_id and topic_id are each followed by a model_channel_name / tag_name / topic_name column with the human-readable name.
Dimensions explained:
- prompt_id: individual search queries/prompts
- model_id: AI search engine (e.g. chatgpt-scraper, gpt-4o, gpt-4o-search, gpt-3.5-turbo, llama-sonar, perplexity-scraper, sonar, gemini-2.5-flash, gemini-scraper, google-ai-overview-scraper, google-ai-mode-scraper, llama-3.3-70b-instruct, deepseek-r1, deepseek-v4-pro, claude-3.5-haiku, claude-haiku-4.5, claude-sonnet-4, grok-scraper, microsoft-copilot-scraper, grok-4, grok-4.3, qwen-3-6-plus, qwen-3-7-plus, amazon-rufus-scraper) — deprecated, prefer model_channel_id
- model_channel_id: stable engine channel (e.g. openai-0, openai-1, qwen-0, openai-2, perplexity-0, perplexity-1, google-0, google-1, google-2, google-3, anthropic-0, anthropic-1, deepseek-0, meta-0, xai-0, xai-1, microsoft-0, amazon-0) — survives model upgrades
- tag_id: custom user-defined tags
- topic_id: topic groupings
- date: daily bucket (YYYY-MM-DD)
- week: ISO week bucket, value is the Monday start (YYYY-MM-DD)
- month: calendar month bucket, value is the first of the month (YYYY-MM-DD)
- country_code: country (ISO 3166-1 alpha-2, e.g. "US", "DE")
- chat_id: individual AI chat/conversation ID
Two filter inputs at different SQL stages — pick by intent:
- filters (WHERE, pre-aggregation): population fields shrink the chat scope; source-side and per-row mentioned-brand predicates (mentioned_brand_id, mentioned_brand_count, gap) shrink the source-row scope.
- having (HAVING, post-aggregation): selects which aggregated URL rows are returned.
Shared fields exist on both — the WHERE form tests each source row pre-aggregation (shrinks numerators); the HAVING form tests the aggregated mentioned_brands union per URL (purely selective). Use filters to prune source rows early; use having to combine with mentioned_brand_*/gap expressions on the grouped union.
Numeric predicates:
- mentioned_brand_count: {field: "mentioned_brand_count", operator: "gt"|"gte"|"lt"|"lte", value: <number>} — filter by number of unique brands mentioned alongside the URL.
- gap: {field: "gap", operator: "gt"|"gte"|"lt"|"lte", value: <number>} — gap analysis filter. Excludes URLs where the project's own brand is mentioned, and filters by the number of competitor brands present. Example: {field: "gap", operator: "gte", value: 2} returns URLs where the own brand is absent but at least 2 competitors are mentioned.
Metric semantics:
- citation_rate = citation_count / retrieval_count, both aggregated per (url × dimension) row from the same filtered group. There's no cross-row denominator, so neither filters nor having can collapse the metric — it stays meaningful even when narrowing to a single url or domain.
- retrieval_count is the count of distinct chats containing this URL within the filtered + dimension-grouped scope. Compare across rows to gauge a URL's relative retrieval strength. Do not divide retrieval_count by an external chat-count to compute a "retrieval percentage" — use the domain report for that aggregate.
Sort results with order_by: array of {field, direction} entries. Direction defaults to desc. Sortable fields: retrieval_count, retrievals, citation_count, citation_rate, date, week, month. Multiple entries create a multi-key sort. Sorting by a time field (date, week or month) requires the matching dimension to be selected.
Get Url Content
Get the scraped markdown content of a source URL Peec has indexed.
Use this after get_url_report to inspect the actual content an AI engine read — useful for content gap analysis and competitive content comparison.
Input notes:
- url is the full URL. Copy it verbatim from get_url_report output. Trailing slashes and scheme variations change the resolved source ID.
- Returns 404 if Peec has no record of the URL (it hasn't been scraped from any project).
- max_length caps the returned content (default 100000 characters). If the stored content is longer, truncated=true and you can re-request with a higher max_length.
Returned fields:
- url, title, domain, channel_title: page metadata
- classification: domain-level classification
- url_classification: page-level classification (HOMEPAGE, LISTICLE, COMPARISON, ...)
- content: markdown content, already extracted via Mozilla Readability and converted with Turndown GFM. null when the URL is tracked but scraping hasn't completed yet (can take up to 24h).
- content_length: original character length before truncation (0 when content is null)
- truncated: true if content was truncated to max_length
- content_updated_at: ISO timestamp of last scrape, or null if not yet scraped
Get Actions
Get Peec's opportunity-scored action recommendations for improving brand visibility in AI search engines. **Always call with `scope=overview` first** to see which slices have the biggest opportunity, then drill down into `owned`, `editorial`, `reference`, or `ugc` with the surfaced url_classification or domain.
## Required parameters (read before calling)
Every call must include:
- `project_id` — the project to analyze.
- `scope` — one of `overview` | `owned` | `editorial` | `reference` | `ugc`. **Start with `scope=overview`.**
Recommended:
- `start_date` and `end_date` (ISO YYYY-MM-DD). Optional — if omitted, defaults to the last 30 days (today − 30d to today). Prefer a 30-day window unless the user asks for a different one.
Per-scope extras (the call will fail without them):
- `scope=owned` → `url_classification` is **required** (e.g. "LISTICLE").
- `scope=editorial` → `url_classification` is **required** (e.g. "LISTICLE").
- `scope=reference` → `domain` is **required** (e.g. "wikipedia.org").
- `scope=ugc` → `domain` is **required** (e.g. "reddit.com", "youtube.com").
- `scope=overview` → no extras beyond the base params.
Use this tool whenever the user asks for recommendations, next steps, what to do, how to improve, "what actions should I take", or any "based on this data, what should I do?" question. Never invent SEO advice.
## Two-step workflow
**Step 1 — `scope=overview`:** returns opportunity rollups grouped by `action_group_type` × (`url_classification` | `domain`). These are *navigation metadata*, NOT the recommendations themselves. Use them to find which slices have the largest gap.
**Step 2 — drill down:** for each high-opportunity slice, call again with the matching scope (`owned` | `editorial` | `reference` | `ugc`) to get the actual textual recommendations (the `text` column, often with markdown links to examples or targets).
Mapping — how to turn an overview row into the follow-up call:
- `action_group_type=OWNED`, `url_classification=X` → call `scope=owned, url_classification=X`.
- `action_group_type=EDITORIAL`, `url_classification=X` → call `scope=editorial, url_classification=X`.
- `action_group_type=REFERENCE`, `domain=Y` → call `scope=reference, domain=Y`.
- `action_group_type=UGC`, `domain=Y` → call `scope=ugc, domain=Y`.
Worked example — overview returns a row `{action_group_type: "UGC", domain: "youtube.com", opportunity_score: 0.30, ...}`. Follow up with `scope=ugc, domain="youtube.com"` and you get rows like `{text: "Contact [AutoPedia](https://...). Ask them for a collaboration.", group_type: "UGC", domain: "youtube.com", opportunity_score: 3, ...}`.
## Response shape
Returns columnar JSON: `{columns, rows, rowCount}`. Each row is an array of values matching column order.
**`scope=overview` columns:**
- `action_group_type`: OWNED | EDITORIAL | REFERENCE | UGC
- `url_classification`: populated for OWNED / EDITORIAL rows (e.g. "LISTICLE", "ARTICLE", "COMPARISON"). `null` for REFERENCE / UGC.
- `domain`: populated for REFERENCE / UGC rows (e.g. "youtube.com", "wikipedia.org"). `null` for OWNED / EDITORIAL.
- `opportunity_score`: continuous. **Use this to sort and rank** — it's the reliable ordering signal.
- `relative_opportunity_score`: 1–3 tier (1=Low, 2=Medium, 3=High). **Use this to label** strength in prose. Too coarse to sort by.
- `gap_percentage`, `coverage_percentage`, `used_ratio`, `used_total`: supporting stats.
Exactly one of `url_classification` / `domain` is populated per overview row — that's the value to pass to the follow-up call.
**`scope=owned | editorial | reference | ugc` columns:**
- `text`: the recommendation string; may include markdown links.
- `group_type`: OWNED | EDITORIAL | REFERENCE | UGC.
- `url_classification`: e.g. "LISTICLE" (may be null).
- `domain`: e.g. "youtube.com" (may be null).
- `opportunity_score`: continuous — sort/rank by this.
- `relative_opportunity_score`: 1–3 tier — label strength with this (1=Low, 2=Medium, 3=High).
## Presenting results
After overview + drill-downs, pick the shape that fits:
- **Strong signal** (top slice's `opportunity_score` is clearly ahead AND its drill-down returned 2+ rows whose `text` contains a markdown link): one sentence of reasoning tied to the user's question (call out the biggest lever), then 2-3 named slices with 2-3 bullets pulled verbatim from the drill-down `text`.
- **Moderate signal**: compact list, one sentence per slice, bullets only where drill-down returned specific targets.
- **Low signal** (overview empty or top `opportunity_score` very low): single line, e.g., "Top opportunity: {slice} (Low). Low signal this period; prompts need a few more daily cycles to stabilize."
## Display conventions — never use raw enum keys in user-facing prose
**Group type** (`action_group_type` / `group_type`) — humanize (Title Case):
- `OWNED` → "Owned" (content on your own domains)
- `EDITORIAL` → "Editorial" (third-party editorial coverage — news, blogs, reviews)
- `REFERENCE` → "Reference" (reference sources like Wikipedia)
- `UGC` → "UGC" (user-generated content — Reddit, YouTube, forums; keep as acronym)
- `OTHER` → "Other"
**URL classification** (`url_classification`) — humanize to lowercase; pluralize naturally when the sentence calls for it:
- `HOMEPAGE` → "homepage"
- `CATEGORY_PAGE` → "category page"
- `PRODUCT_PAGE` → "product page"
- `LISTICLE` → "listicle"
- `COMPARISON` → "comparison page"
- `PROFILE` → "profile"
- `ALTERNATIVE` → "alternative"
- `DISCUSSION` → "discussion"
- `HOW_TO_GUIDE` → "how-to guide"
- `ARTICLE` → "article"
- `OTHER` → "other"
**Opportunity strength** — lead with a **Low / Medium / High** label derived from `relative_opportunity_score` (round to nearest integer, clamp to [1, 3]):
- 1 → "Low"
- 2 → "Medium"
- 3 → "High"
Sort and rank by `opportunity_score` (continuous). **Verbalize** strength with the Low/Medium/High tier above. The raw `opportunity_score` is optional supporting context in parens — never the headline number.
**Gap percentage** (`gap_percentage`, 0–1 ratio) — lead with a plain-language qualifier; the raw % can follow in parens when useful:
- ≥0.90 → "nearly all missing"
- 0.60–0.89 → "wide gap"
- 0.30–0.59 → "partial gap"
- <0.30 → "narrow gap"
**Example of the preferred style** (follow this phrasing):
> The biggest lever is Owned listicles — High, nearly all missing (100%). Build listicle-style pages on yourbrand.com that target "best X" queries.
>
> Secondary: YouTube UGC (Medium, wide gap), Reddit UGC (Medium, partial gap), Editorial listicles (Medium, nearly all missing). Full list: https://app.peec.ai/actions.
Close with one line: "Secondary opportunities: {slice} ({Low|Medium|High}), {slice} ({Low|Medium|High}). Full list: https://app.peec.ai/actions."
Use the drill-down `text` field as the source of truth. Never invent recommendations, targets, or names. Sort by `opportunity_score`; label strength via `relative_opportunity_score`.
Get Project Profile
Read a project's brand profile — the description, industry, brand-identity adjectives, target markets, audience distribution, and product/service list that Peec uses to generate prompt suggestions. Returns { profile } where profile may be null if the project hasn't been profiled yet. Call this before set_project_profile so you can show the user the current values.
Search Docs
Search the Peec product documentation (https://docs.peec.ai) and return the most relevant pages. Use this for any question about how Peec works — what a metric means, how a feature behaves, setup steps, plan/credit rules, or 'how do I…' — then call read_doc on the best result to read it in full before answering. Returns columnar JSON {columns, rows, rowCount}. Columns: title, path, url, snippet. Pass the returned path to read_doc. No project context or auth required.
Read Doc
Read the full markdown content of a single Peec documentation page. Pass the path returned by search_docs (e.g. 'metrics/brand-metrics/visibility'). Answer strictly from the returned content — never invent product behaviour, metric definitions, limits, or feature claims. Returns {title, path, url, content, videos}, where videos lists the page's YouTube explainer videos as {videoId, title}. When a video directly helps answer the user, embed it by putting [youtube:<videoId>] on its own line in your reply. No project context or auth required.
Create Brand
Create a new brand (competitor or own) tracked in a project. Returns the created brand id. A project can track at most 200 brands total (own + competitors); this fails once the limit is reached.
Update Brand
Update a brand's name, regex, aliases, domains, or color. Changes to name/regex/aliases trigger background metric recalculation; repeat attempts during recalculation will fail. Color updates do not trigger recalculation. Confirm with the user before calling.
Delete Brand
Soft-delete a brand within a project. This is destructive — always confirm with the user before calling.
Create Prompt
Create a new prompt in a project. Returns the created prompt id. Every prompt needs a topic: pick the best-fitting existing topic, or create one with create_topic first. Confirm with the user before calling — this mutates project data and may consume plan credits.
Update Prompt
Update a prompt's topic and/or tags. Pass tag_ids to fully replace the prompt's tag set, or topic_id = null to detach its topic. Confirm with the user before calling.
Archive Prompt
Archive a prompt (sets is_archived = true) so it stops running while keeping its chats and history intact. Use this instead of delete_prompt when the data should be retained. Confirm with the user before calling.
Unarchive Prompt
Unarchive a prompt (sets is_archived = false), reactivating it so it resumes running. Subject to the project's active-prompt plan limit. Confirm with the user before calling.
Delete Prompt
Delete a prompt and cascade the deletion to its chats. This is destructive — always confirm with the user before calling.
Create Tag
Create a new tag in a project. Tags are cross-cutting labels you can attach to prompts. Returns the created tag id. Confirm with the user before calling.
Update Tag
Update a tag's name or color. Confirm with the user before calling.
Delete Tag
Soft-delete a tag within a project and detach it from all prompts. This is destructive — always confirm with the user before calling.
Create Topic
Create a new topic in a project. Topics group related prompts. Returns the created topic id. Confirm with the user before calling.
Update Topic
Rename a topic within a project. Confirm with the user before calling.
Delete Topic
Soft-delete a topic within a project. Associated prompts are detached (not deleted); prompt suggestions on the topic are deleted. This is destructive — always confirm with the user before calling.
Create Brands
Create up to 50 brands (competitors or own) in a project in one call. Returns the created brands with their new ids, plus any skipped items. Duplicates are matched case-insensitively on name. A project can track at most 200 brands total (own + competitors); a call that would exceed this fails.
Update Brands
Update up to 50 brands in one call (name, regex, aliases, domains, color per item). Changes to name/regex/aliases trigger background metric recalculation per brand; updates during an ongoing recalculation are rejected. Returns a count of updated brands plus any skipped/rejected items (successful items are not echoed back).
Delete Brands
Soft-delete up to 50 brands in a project. Returns a count of deleted brands plus any skipped items (successful items are not echoed back). This is destructive — always confirm with the user before calling.
Create Prompts
Create up to 50 prompts in a project in one call. Returns the created prompts with their new ids, plus any skipped/rejected items. Every prompt needs a topic: pick the best-fitting existing topic, or create one with create_topics first. Accepts existing topic_id and tag_ids only — this tool does not auto-create topics or tags. Confirm with the user before calling — this mutates project data and may consume plan credits.
Update Prompts
Update the topic and/or tags of up to 50 prompts in one call. Per item, pass tag_ids to fully replace the prompt's tag set, or topic_id = null to detach its topic. Topic and tag ids must already exist. Returns a count of updated prompts plus any skipped/rejected items (successful items are not echoed back).
Delete Prompts
Soft-delete up to 50 prompts in a project. Deletions run asynchronously — returns a count of queued deletions plus any skipped (not found / already deleted) or rejected items (queued items are not echoed back). This is destructive — always confirm with the user before calling.
Create Tags
Create up to 50 tags in a project in one call. Returns the created tags with their new ids, plus any skipped items. Duplicates are matched case-insensitively on name.
Update Tags
Update up to 50 tags in one call (name and/or color per item). Returns a count of updated tags plus any skipped/rejected items (successful items are not echoed back); system tags are skipped.
Delete Tags
Soft-delete up to 50 tags in a project. Removes tag associations from prompts. Returns a count of deleted tags plus any skipped items (successful items are not echoed back). This is destructive — always confirm with the user before calling.
Create Topics
Create up to 50 topics in a project in one call. Topics group related prompts. Returns the created topics with their new ids, plus any skipped/rejected items. Duplicates are matched case-insensitively on name. Items beyond the project's topic limit land in `rejected`.
Update Topics
Rename up to 50 topics in one call. Returns a count of updated topics plus any skipped/rejected items (successful items are not echoed back).
Delete Topics
Soft-delete up to 50 topics in a project. Detaches associated prompts (prompts are kept) and soft-deletes any prompt suggestions linked to the topics. Returns a count of deleted topics plus any skipped items (successful items are not echoed back). This is destructive — always confirm with the user before calling.
Create Domain Classification
Define a new custom domain classification on a project. This creates the classification entity — it does not assign it to any domain (use assign_domain_classification for that). Confirm with the user before calling.
Create Url Classification
Define a new custom URL classification on a project. This creates the classification entity — it does not assign it to any URL (use assign_url_classification for that). Confirm with the user before calling.
Delete Domain Classification
Permanently delete a custom domain classification entity. Cascades through the override table — any domains currently assigned this classification fall back to their heuristic classification. To only clear a single domain's assignment, use unassign_domain_classification instead. This is destructive — always confirm with the user before calling.
Delete Url Classification
Permanently delete a custom URL classification entity. Cascades through the override table — any URLs currently assigned this classification fall back to their heuristic classification. To only clear a single URL's assignment, use unassign_url_classification instead. This is destructive — always confirm with the user before calling.
Assign Domain Classification
Assign a built-in classification (Corporate, Competitor, Editorial, Institutional, Other, Reference, UGC, You, Related) or the name of a custom domain classification to a domain. Overrides any heuristic classification. The override applies at apex granularity. If a display label collides with an existing custom classification name, the custom classification wins — pass the built-in enum value (e.g. "OTHER") to force the built-in. Confirm with the user before calling.
Assign Url Classification
Assign a built-in classification (Homepage, Category Page, Product Page, Listicle, Comparison, Profile, Alternative, Discussion, How-To Guide, Article, Other) or the name of a custom URL classification to a URL. Overrides any heuristic classification. The override applies to the normalized URL form. If a display label collides with an existing custom classification name, the custom classification wins — pass the built-in enum value (e.g. "OTHER") to force the built-in. Confirm with the user before calling.
Unassign Domain Classification
Clear the classification override on a domain, restoring the heuristic classification. Does NOT delete the custom classification entity itself (use delete_domain_classification for that). Confirm with the user before calling.
Unassign Url Classification
Clear the classification override on a URL, restoring the heuristic classification. Does NOT delete the custom classification entity itself (use delete_url_classification for that). Confirm with the user before calling.
Set Project Profile
Replace a project's brand profile with the supplied values. All fields are required — the whole profile is overwritten, so first call get_project_profile, merge your changes into the existing values, then send the complete profile here. Saving triggers a background refresh of prompt suggestions. Confirm changes with the user before calling. Audience distribution percentages must sum to 100. The project's display name is not part of the profile and cannot be changed via this tool.