Polar Analytics
Connect your ecommerce data
- Category
- Data & Analytics
- Primary Subcategory
- Marketing & Commerce Data Integration
Integration details
Description
Turn ChatGPT into your data analyst. Ask questions about your Shopify store, Meta Ads, Google Ads, and other channels in plain language. Get answers on ROAS, CAC, top-performing SKUs, profit margins, and more, all pulled from Polar’s semantic layer for accuracy. Supports multi-store setups and works with major ecommerce integrations, including TikTok Ads and Klaviyo.
- Integration type
- Plugin
- Verification status
- Not applicable
- Platform
- ChatGPT
- Primary Subcategory
- Marketing & Commerce Data Integration
- Secondary Subcategories
- None listed
- Brand
- Polar Analytics
- Access
- Account required
- First tracked
- 2026-06-12
- Tool count
- 29
- Geography
- US
The Primary Subcategory used for this profile’s headline score.
Other Subcategories where the Integration is listed.
Get alerts for Polar Analytics
Get updates when Polar Analytics’s Discoverability Score or category rank changes.
ChatGPT Plugin Discovery Score
ChatGPT Plugin discovery is coming soon
ChatGPT can surface a Plugin when it matches a user's request.Your Plugin Discovery Score measures how often yours appears.
No spam. Unsubscribe any time.
What discovery looks like

Competing in ChatGPT Marketing & Commerce Data Integration
View Category29 tools agents can invoke
Create a new custom dimension for this workspace. A custom dimension labels rows via when/then/else rules AND can compute derived values with functions (split, concat, regex, date and math operations) in any value block — it is NOT limited to matching discrete values. **Prefer reusing an existing custom dimension.** Before calling this tool, inspect the `custom_dimensions` field returned by `get_context` and, if needed, fetch details via `get_custom_dimension_details`. Only create a new one if no existing dimension matches the user's intent semantically. ===== json payload shape (CustomDimension) ===== { "whenThen": [ { "when": [ WhenBlock, ... ], "then": ValueBlock }, ... ], "else": ValueBlock } WhenBlock = { "lineOperator": "" | "and" | "or", // Trailing connector — joins this block to the NEXT one. // Last block always "". For multi-block whens, the first // and middle blocks carry "and"/"or"; only the final one is "". "leftValue": ValueBlock, // usually [ "dimension.<table>.<field>" ] "operator": "contains" | "containsCaseSensitive" | "notContains" | "notContainsCaseSensitive" | "startsWith" | "endsWith" | "lower" | "lowerOrEqual" | "greater" | "greaterOrEqual" | "is" | "isAfter" | "isBefore" | "isEmpty" | "isInList" | "isNot" | "isNotEmpty" | "isNotInList" | "isNotNull" | "isNull", "value": "<string>" // for isInList / isNotInList use a // comma-joined string: "US,CA,MX" } ValueBlock = a prefix-notation expression, serialized as a flat array of strings. Each entry is one of: - "operation.<FN>" — a function call; its parameters are the entries that follow, consumed in declaration order. Nest by placing another "operation.<FN>" where a parameter is expected. - "dimension.<table>.<field>" — a dimension reference ("dimension.*.<field>" matches the field on any table). - any other string — a literal. Numbers are passed as strings ("1"). A plain label is a single-literal ValueBlock: [ "<label>" ]. Available functions (token(params) -> returns): operation.LEFT(value, length) -> string operation.RIGHT(value, length) -> string operation.LOWER(value) -> string operation.UPPER(value) -> string operation.CONCAT(value1, value2) -> string operation.TRIM(value) -> string operation.REPLACE(subject, pattern, replacement) -> string operation.DATEDIFF(date_part, date1, date2) -> integer operation.DATEFORMAT(date, format_string) -> string operation.DATEADD(date_part, amount, date) -> date operation.DATETRUNC(date_part, date) -> date operation.POW(base, exponent) -> number operation.DIV0(dividend, divisor) -> number operation.MOD(dividend, divisor) -> number operation.ABS(expression) -> number operation.FLOOR(expression) -> integer operation.CEIL(expression) -> integer operation.ROUND(expression) -> integer operation.SPLIT_PART(string, delimiter, part number) -> string operation.DATE(datetime) -> date operation.CONVERT_TIMEZONE(string, datetime) -> datetime operation.CURRENT_TIMESTAMP() -> datetime operation.DAY(date) -> integer operation.DAYOFWEEK(date) -> integer operation.DAYNAME(date) -> string operation.WEEK(date) -> integer operation.MONTH(date) -> integer operation.MONTHNAME(date) -> string operation.QUARTER(date) -> integer operation.YEAR(date) -> integer operation.REGEXP(subject, pattern) -> string Note: operation.REGEXP compiles to Snowflake REGEXP_SUBSTR(subject, pattern, 1, 1, 'i', 1) — case-insensitive, first match, extracting capture group 1. The pattern MUST contain a capturing group (parentheses); without one the function returns NULL for every row. Example — "CC#" = first three dash-delimited parts of a SKU (NK-108-01K4-0 -> NK-108-01K4): { "whenThen": [{ "when": [{ "lineOperator": "", "leftValue": ["dimension.shopify_sales_main.sku"], "operator": "isNotEmpty", "value": "" }], "then": ["operation.REGEXP", "dimension.shopify_sales_main.sku", "^([^-]+-[^-]+-[^-]+)"] }], "else": ["Other"] } Nested functions — CONCAT(SPLIT_PART(sku, "-", 1), "-suffix"): ["operation.CONCAT", "operation.SPLIT_PART", "dimension.shopify_sales_main.sku", "-", "1", "-suffix"] IMPORTANT — availability: at least one when-block leftValue must reference a "dimension.<table>.<field>" entry. Dimension dependencies are collected from when-blocks only; a custom dimension without any never surfaces in the breakdown list. Anchor formula-only dimensions with a guard such as WHEN <field> isNotEmpty THEN <formula>. Required inputs: - conversation_id - title (short, human-readable name) - json (per the shape above) On success returns { id, title, editUrl }. The chat UI surfaces the editUrl as an "Open dimension editor" button on the tool-call card — do NOT repeat it as a markdown link in your visible response. Just tell the user the dimension was created and briefly describe what it groups.
Create a new custom metric for this workspace. A custom metric is a formula built from a sequence of `elements` (existing metric refs, operators, values). **Prefer reusing an existing metric.** Call `get_metrics` first with `custom-metrics` included to see what already exists; only create if no existing metric matches the user's intent semantically. Aggregation note: the formula is evaluated per-row then SUMmed at the outer query level (ratio-of-SUM for simple arithmetic). For division on same-source metrics the aggregation becomes SUM-of-ratio, which can surprise the user on multi-day granularity. ===== metricData payload shape ===== Required: title, elements, formatting. description is optional. elements: array of typed-object nodes. Every element is an OBJECT, never a bare string. Valid shapes: - Metric reference: { "type": "metric", "value": "<table>.raw.<key>" // e.g. shopify_sales_main.raw.gross_sales | "<table>.computed.<key>" | "custom_<N>", "filters": [] // optional; omit or [] // when no filters apply } - Arithmetic operator: { "type": "operator", "value": "+" | "-" | "*" | "/" | "(" | ")" | "x" } - Literal value (numeric constant inside the formula): { "type": "value", "value": "<number-as-string>" } // e.g. "2" - Line break (visual only, rare): { "type": "linebreak", "value": "" } Filters on a metric element (optional). Each filter is one of: - OR element: { "dimension": "<dimKey>", "operator": "IS" | "ISNOT" | "CONTAINS" | "NOTCONTAINS", "values": ["a","b"] } - AND group: an array of the OR elements above Empty array or omitted field = "no filter on this metric ref". formatting: REQUIRED object, one of four discriminated shapes: - { "type": "number", "numberType": "integer" | "decimal", "prefix"?: string, "suffix"?: string, "metricRepresentation"?: "higher_is_better" | "lower_is_better" } - { "type": "percent", "metricRepresentation"?: "higher_is_better" | "lower_is_better" } - { "type": "currency", "currency"?: string, // 3-letter ISO "metricRepresentation"?: "higher_is_better" | "lower_is_better" } - { "type": "timedelta", "metricRepresentation"?: "higher_is_better" | "lower_is_better" } Required inputs: - conversation_id - metricData (per shape above) On success returns { id, title, editUrl }. The chat UI surfaces the editUrl as an "Open metric editor" button on the tool-call card — do NOT repeat it as a markdown link in your visible response. Just tell the user the metric was created and briefly describe what it computes.
Returns a short-lived, credential-free URL to upload ANY file (CSV, PDF, image, JSON, …) and the public link to share once it's uploaded. Use this to send a file the merchant can open. How it works (two steps — never put file contents into this call): 1. Call this tool with the file's name. It returns { upload_url, file_url }. 2. Upload the file's BYTES straight to upload_url with the baked helper: python3 /opt/hermes/polar-upload-file.py <local_path> "<upload_url>" then put file_url in your reply. (file_url is a CDN link the merchant can open; images unfurl inline in Slack.) Why not pass the file to a tool directly: tool arguments travel through the model's output, so a file encoded into a call is truncated and arrives corrupt. This path sends the bytes from disk straight to storage, so any size / type works.
Delete an existing custom dimension. Deletion is permanent (hard delete); only the MCP audit log retains the payload for recovery. CRITICAL (non-self-created items): If you did NOT create this dimension in the current conversation, BEFORE calling this tool you MUST: 1. Call `get_custom_dimension_usages` to see what depends on it. If non-empty, tell the user in plain English what would be orphaned (names + types, not just counts) and stop — deletion is blocked anyway, so don't try. 2. If the usages list is empty, tell the user the dimension's name (and id, for reference) and ask whether to proceed with deletion. 3. Once the user gives a clear affirmation ("yes", "go ahead", "delete it", etc.), call this tool. You do NOT need the user to repeat the id. For items you created in the CURRENT conversation: delete freely when asked. The server also runs an in-use check; if any references exist it returns a `blockedBy` list. Relay that list to the user and stop — do not cascade or work around the block. Required inputs: - conversation_id - id (the custom_<N> id)
Delete an existing custom metric. Deletion is permanent (hard delete); only the MCP audit log retains the payload for recovery. CRITICAL (non-self-created items): If you did NOT create this metric in the current conversation, BEFORE calling this tool you MUST: 1. Call `get_custom_metric_usages` to see what depends on it. If non-empty, tell the user in plain English what would be orphaned (names + types, not just counts) and stop — deletion is blocked anyway, so don't try. 2. If the usages list is empty, tell the user the metric's name (and id, for reference) and ask whether to proceed with deletion. 3. Once the user gives a clear affirmation ("yes", "go ahead", "delete it", etc.), call this tool. You do NOT need the user to repeat the id. For items you created in the CURRENT conversation: delete freely when asked. The server also runs an in-use check; if any references exist it returns a `blockedBy` list. Relay that list to the user and stop — do not cascade or work around the block. Required inputs: - conversation_id - id (the custom_<N> id)
Reads a connector the workspace has already connected directly from its provider API, bypassing Polar's warehouse. Always prefer Polar's warehoused data through generate_report whenever it can answer the question, including when the requested range contains today or the last few hours. Recency alone is not a reason to use this tool. Use this tool at your discretion only when at least one of these is true: - the user explicitly asks for live data, provider-side data, or a direct API read - the answer requires a metric, field, breakdown, or current entity state that the warehouse does not expose but one of the connector's live operations does - the answer materially depends on data newer than the relevant account's warehouse coverage When freshness could change the answer, call get_connector_statuses first. Compare the requested period with the account's data_available_through in its timezone; that field is the warehouse coverage boundary, while last_completed_sync_at is not. Prefer generate_report for the covered period and use this tool only for the necessary uncovered portion. This also applies to a freshly connected account that is still backfilling. A user saying "from Meta" or "from Google" names the source, not necessarily the live API — Polar's warehouse already contains that source's data. If the user explicitly asks for live or API-side figures ("read the API directly", "what does Meta itself report"), honour that for any range within the caps. State when figures came from the provider API rather than Polar's warehouse, and do not silently combine the two when their metric definitions or currencies differ. Warehouse reads are faster, cheaper, historically consistent, and model the metrics shown in Polar; live reads spend the workspace's provider API quota, which its syncs also depend on. Connectors that support live reads: Meta (Facebook Ads) — connector_key "facebook-ads" Google Ads — connector_key "google-ads" Google Analytics 4 — connector_key "google-analytics-four" Amazon Seller Central — connector_key "amazon-selling-partner" Amazon Vendor Central — connector_key "amazon-vendor-central" Amazon Ads — connector_key "amazon-ads" TikTok Ads — connector_key "tiktok-ads" Shopify — connector_key "shopify" Klaviyo — connector_key "klaviyo" Google Sheets — connector_key "google-sheets" Google Search Console — connector_key "google-search-console" Instagram Business — connector_key "instagram-business" Facebook Pages — connector_key "facebook-pages" Microsoft Advertising (Bing Ads) — connector_key "bing-ads" Custom integrations — connector_key "custom" (workspaces that have AI-built custom datasources; datasource_id is REQUIRED, and each custom datasource declares its own operations — describe lists them). Every source listed under custom_connectors in your context IS one of these: Superfiliate, ShopMy, AppLovin and the like are read with connector_key "custom" plus their datasource_id. Never conclude a named custom source lacks live reads without running describe against its datasource — calling without datasource_id lists the workspace's custom datasources by name. Discover a connector's operations before the first read: call this tool with operation "describe" — it returns one row per operation with its summary, exact params object (defaults included) and row fields. Pass params: { operation: "<name>" } to narrow to one operation. Validation errors also name the valid values, so a wrong guess is corrected in one step. Inputs: conversation_id (from get_context), connector_key, operation, params (the operation's own object, as documented by describe), and optionally datasource_id to scope to one connected account — get_connector_statuses lists the datasource_ids for native connectors. Omitted on a native connector, the read covers every connected account at once (up to 5; each row then carries account_label); omitted on connector_key "custom", the call lists the workspace's custom datasources by name instead of reading. Returns { rows, row_count, truncated, notes, fetched_at, cached, account_label }. rows follow the operation's row fields as documented by describe. notes carry provider-side caveats and must be reflected in your answer when they qualify the numbers. When a row carries a currency field, report figures in that currency, not the workspace reporting currency. Credentials are never exposed here: the request carries identifiers only and the read happens server-side.
Generates a deep-link URL into the app. These deep links open visualizations and configuration screens that are designed for end-user consumption. Required inputs: - conversation_id Supported visualizations: - Tables (default, returned by generate_report; best when breakdowns are present) - Line Charts (with date granularity) - Bar Charts (without date granularity) - Pie Charts (without date granularity) Calling generate_report with the same arguments returns the dataset that the link will display. Required inputs: - metrics (comma-separated keys exactly as received from the `metrics` field of the `get_context` tool), - dimensions (comma-separated keys exactly as received from the `breakdowns` field in list_dimensions), - dateRangeFrom/dateRangeTo (YYYY-MM-DD), - granularity (one of: none, day, week, month, year), - settings (for example attribution model; pass an empty object for non-attribution queries) - ordering (comma-separated key-value pairs of key and direction) - rules (JSON string like {"country":[{"value":["US"],"operator":"IS"}]}), - metricRules (JSON string like {"shopify_sales_main.raw.total_orders":[{"operator":"GREATER","value":["0"]}]}), - views (comma-separated ids exactly as received from the `view` field of `list_dimensions`), - reflexion (analysis of the user's goal and the rationale for generating this report). Output: JSON object containing the deep link. The deep link is an opaque URL that must be reproduced verbatim.
Generate a comprehensive analytics report. Required inputs: - conversation_id, - metrics (comma-separated keys exactly as received from the `metrics` field of the `get_context` tool), - dimensions (comma-separated keys exactly as received from the `breakdowns` field in list_dimensions), - dateRangeFrom/dateRangeTo (YYYY-MM-DD), - ordering (comma-separated key-value pairs of key and direction) - granularity (none|day|week|month|year), - settings (e.g. attribution model - pass empty object if non-attribution query) - rules (JSON string like {"country":[{"value":["US"],"operator":"IS"}]}), - metricRules (JSON string like {"shopify_sales_main.raw.total_orders":[{"operator":"GREATER","value":["0"]}]}), - views (comma-separated ids exactly as received from the `view` field of `list_dimensions`), - limit (optional, integer): Max rows to return (default 1000). ALWAYS set an explicit limit appropriate to the query — for browsing/listing choose a single integer between 10 and 25, for analysis use only as many rows as needed. Fetching too many rows wastes context. The full dataset is always available via the returned deep link. - comparisonPeriod (optional): Compare against a previous period. Values: previousPeriod (same duration before current range), previousYear, previousMonth, or range (requires comparisonDateRangeFrom/To). - comparisonDateRangeFrom/comparisonDateRangeTo (optional, YYYY-MM-DD): Custom comparison date range, only used when comparisonPeriod is "range". Output: JSON report table data, totals data and a deep link to display the same table (with totals) in-app. When comparison is requested, also returns compareTableData, compareTotalData, and comparisonDateRange. Only include attribution_model in settings when generating reports that involve marketing channel attribution or cross-channel analysis. For Shopify metrics (sales, orders, product data, customer segments), pass an empty settings object if you do not know the attribution model. Totals data: returns a row of totals for every metric in the metrics list. Always use these provided totals; do not recompute sums yourself unless you really need to. For grouped/segmented reports (e.g., “top 5 cities”), sum the returned rows and verify they never exceed the corresponding server totals; if there’s a mismatch, prefer the server totals and call it out as a reconciliation note. For ungrouped reports, display the returned totals directly. When presenting percentages or shares, compute them as row_value / total_value (not from independently summed rows) If a total is missing for a metric, state that totals are unavailable and avoid inferring them. When the data is useful, include the exact deep link so the customer can explore further on their own in-app. Use the 👉 emoji to make it very clear that it's a link. Use generate_app_report_link to generate other links or visualizations as needed. If you got the report from an element in the `get_dashboard_details` tool, use those deep links. This shouldn't prevent you from generating an artifact with other content if needed, the goal is for the user to link to the Polar report if they want to validate the data or explore further. After responding, ask user for a 1-10 rating, then call rate_report. Gating: this tool refuses with `{ error: true, reason: "needs_activation" | "needs_connectors", message, book_call_url, connectors_url }` when `account_status` from `get_context` indicates the tenant is not yet activated or has no loaded connector data. Don't retry on the same tenant in the same conversation — deliver the message verbatim or paraphrase, point the user at the URL, and stop.
Detailed per-account connector health from /api/datasources/statuses. Drills into the rollup returned by get_context.connectors when more detail is needed on why a connector is Warning, Broken or Syncing history. Each entry is { key, health, connections, accounts[] }. connections is the per-status breakdown of this connector's accounts (sparse: absent keys mean zero). accounts[] carries { datasource_id, account_name, health, issues, last_completed_sync_at, data_available_through, timezone, data_integrity }. data_integrity is a rollup of the account's data integrity checks: { ok, failed, total } counts, or null when no check signal is available. "ok" includes both exact matches and within-tolerance results: both pass from the user's perspective. The per-check breakdown (name, status, match rate, in/out-of-tolerance days) is available via get_data_integrity_report({ datasource_id }). health is one of: healthy, syncing_history, warning, broken, paused, pending. The connector-level health is the worst across its non-paused accounts, with the exception that an all-paused connector reports paused. A connector or account in warning, broken, or syncing_history state is a data-quality condition relevant to any numbers derived from it. A paused (deactivated) account always reports paused even when it has unresolved errors underneath — the user paused it on purpose, so it does not belong in a "needs attention"/broken rundown. Its issues[] are still listed so the user knows reconnecting won't help until they fix and unpause it. issues[] is a structured array describing the conditions that drive each account's health: - error: { kind: "error", message }: generic plumbing error surfaced verbatim from the connector. - needs_setup: account is connected but no sub-account is selected; setup is incomplete. - missing_scopes: { kind: "missing_scopes", scopes }: the connector is missing OAuth scopes and needs to be reconnected granting these scopes. - delayed: no sync today past the morning threshold in the connector's timezone. Hourly-refresh staleness is not flagged until the team calibrates a per-pipeline threshold. - drift_summary: { kind: "drift_summary", failed_count, tolerated_count, total_count }: at least one data integrity check has a match rate below the threshold. The data integrity report describes this as "X of Y checks failed". tolerated_count is informational; those days are within tolerance and the user sees them as "OK". - drift_verified: { kind: "drift_verified", tolerated_count, total_count }: every data integrity check passed (some may be within tolerance, which the user sees as "OK"). For deciding whether a requested period extends beyond Polar's warehoused data, data_available_through is the coverage boundary for that account; interpret it in the account's timezone. last_completed_sync_at is an operational sync timestamp, not a warehouse coverage cutoff, and must not be used as a substitute. For connector health and delay warnings, explicit issues[] entries remain the canonical source. Deep-link pattern (account names are not slugified): /connectors/<connector-key>: connector page /connectors/<connector-key>/<datasource_id>: specific account row, where datasource_id is the UUID from accounts[].datasource_id /connectors/<connector-key>/<datasource_id>/data-integrity: data integrity report (full pass/fail breakdown plus per-check drill-down) Example: /connectors/facebook-ads/8f3a1c0e-44d6-4d8a-9f8b-7c2a1e5d9a40 Required input: conversation_id (from get_context). Optional: connectorKeys to narrow the response to a subset of connectors from get_context.connectors.
Prepares the analytics environment for a conversation and returns the workspace context required by every other tool. It returns a combined JSON object with the following elements: conversation_id: Conversation identifier consumed verbatim by the other tools in this session. context: Workspace context { currency, timezone, brand_name } (brand_name defaults to the email of the first registered user when no company name is set: often the brand's domain, sometimes a personal email). account_status: { activation, data, book_call_url, connectors_url, message } describing whether the workspace is activated and whether its data is loaded. activation is one of 'pending_call', 'call_booked', 'activated'. data is one of 'none', 'loading', 'ready'. When activation is not 'activated', booking the setup call at book_call_url is the prerequisite next step and data tools will refuse the request. Otherwise, when data is not 'ready', connecting data sources at connectors_url is the prerequisite. The 'message' field is a ready-to-deliver explanation that pairs with the relevant URL. connectors: Canonical per-connector rollup. Each entry { key, health, connections } where health is one of healthy, syncing_history, warning, broken, paused, pending (worst across non-paused accounts; an all-paused connector reports paused). connections is the per-status breakdown of this connector's accounts and only includes health states with at least one account (absent keys mean zero). Total account count equals the sum of connections values. get_connector_statuses drills into specific connectors and exposes the structured issues[] used to compose remediation prose. A connector in warning, broken, or syncing_history state is a data-quality condition relevant to any numbers derived from it, and its connector page has a user-facing deep link for review. A paused account always reports paused even with unresolved errors underneath: treat it as paused, not as needing action — keep it out of any broken/"needs attention" rundown. polar-pixel appears in this list like any other connector: health syncing_history means it is still warming up, healthy means it has matured. The canonical health currently surfaces only daily delay signals (no sync today past the morning threshold in the connector's timezone). Hourly-refresh staleness is not flagged until the team calibrates a per-pipeline threshold. last_completed_sync_at on its own is not a reliable freshness signal; explicit issues[] entries are the canonical source. custom_connectors: Array of { key } for connectors that have queryable data tables but no live monitoring (custom imports, data warehouses, legacy entries). Health is unknown for these. connector_status: DEPRECATED, superseded by 'connectors' and 'custom_connectors'. Kept only for backward compatibility and will be removed in a future MCP version. Legacy shape: array of { name, status, until } where status is 'historical' (data not ready), 'incremental' (data ready up to 'until'), or 'paused' (data exists but syncing is paused; reports use data up to 'until'). Customers can manage connectors at https://app.polaranalytics.com/connectors. A polar-pixel legacy entry with is_warmed_up=false signals the pixel needs attention: when installed_at is a date, the pixel is still warming up since installed_at and conversion / attribution numbers are not yet statistically meaningful (the calibration window depends on traffic volume and industry but is typically at least a couple of weeks); when installed_at is null, the pipeline last produced data on polar-pixel 'until' and may not be currently flowing. Deep-link pattern for the Connectors area (use canonical 'connectors' key and accounts[].datasource_id from get_connector_statuses; account names are not slugified): https://app.polaranalytics.com/connectors/<connector-key> for a connector page, https://app.polaranalytics.com/connectors/<connector-key>/<datasource_id> for an account row. Example: https://app.polaranalytics.com/connectors/facebook-ads/8f3a1c0e-44d6-4d8a-9f8b-7c2a1e5d9a40. custom_dimensions: Initial page of the workspace's custom dimensions: the 50 most recently created or updated. Each entry carries { id, label } plus ONE of: - summary: human-readable one-line purpose (set when authored or AI-summarized) - refs: array of underlying dimension field names the formula touches (for example ["billing_country"]); a structural hint when no summary exists (a label like "geo v2 jamie" with refs ["billing_country"] is typically a country grouping). Label, summary and refs together describe whether an existing dimension covers a given concept. The full whenThen formula is not inlined; `get_custom_dimension_details(id)` returns it for a single candidate. The `list_custom_dimensions(q?, offset?, limit?)` tool paginates beyond this initial page and searches by case-insensitive substring on title and summary. views: Views list as an array of { id, title }. labels: Present only when the workspace has enabled metric labels: the full label dictionary as an array of { id, name, category, description, metric_count }. Custom metrics may carry `labels`: workspace-defined tags { name, category, description } that the workspace's team attached on purpose (for example a "Certified" label under a "Governance" category). A label's description is authoritative guidance on when to use the metric: when several metrics could answer a question, prefer the one whose labels and label descriptions match the question, and mention the label when you cite the metric. Use this dictionary to understand what each label on a custom metric (from get_metrics / list_custom_metrics / get_custom_metric_details) means before choosing between metrics. snowflake_database: Present only when the `query_snowflake` tool is available for this workspace: the workspace's own Snowflake database name, already quoted and ready to interpolate into fully-qualified identifiers (there is no session default database, so every table reference needs it). Required input: - initialQuestion (user's initial prompt, stated exactly as received) - version (the MCP client version; current is 4.3) The response may include a 'warning' field describing an outdated client version or other notice intended for the user.
Returns the full definition of a single custom dimension (rules and connector filters). The compact entry in `get_context.custom_dimensions` is usually enough to identify a dimension by intent; this tool returns the full body when the formula itself is needed. Required inputs: - conversation_id - id (the id of a custom dimension, exactly as obtained from the `custom_dimensions` field of `get_context`)
Lists every workspace-owned object that currently depends on a given custom dimension: reports, custom metrics, and KI sections. Updating or deleting a dimension that was not created in the current conversation can affect these objects; the usage list is the canonical source for the human-readable impact summary that should precede confirmation of such a change. Returns { usages: [{ id, title, type }] }. type is one of "report", "custom_metric", "ki_section". An empty list means nothing currently depends on the dimension. Required inputs: - conversation_id - id (the custom_<N> id of the dimension)
Returns the full formula of a single custom metric by id, including elements and formatting. The compact entry from `list_custom_metrics` or `get_metrics` identifies the metric; this tool returns the formula body needed to inspect or extend it. Required inputs: - conversation_id - id (the custom_<N> id) Returns { id, label, description?, elements, formatting }.
Lists every workspace-owned object that currently depends on a given custom metric: reports, other custom metrics, and KI sections. Updating or deleting a metric that was not created in the current conversation can affect these objects; the usage list is the canonical source for the human-readable impact summary that should precede confirmation of such a change. Returns { usages: [{ id, title, type }] }. type is one of "report", "custom_metric", "ki_section". An empty list means nothing currently depends on the metric. Required inputs: - conversation_id - id (the custom_<N> id of the metric)
Returns the full definition of a single dashboard, including all of its elements. Customers generally refer to dashboards and elements by their titles. Required inputs: - conversation_id Output: A single dashboard with the details of its elements, along with deep links. The dashboard and each report element carries a user-facing deep link to its in-app view. The table data has a limit of 50 applied; more rows can be retrieved with the `generate_report` tool. If the workspace is not yet activated or has no connector data loaded, the response contains the dashboard structure (titles, layout, deep links) but skips the per-element data and includes `data_skipped: true` plus a `reason` and `message` from `account_status` in `get_context`. An element whose data failed to load carries a `data_error` message, and `data: null` when nothing could be fetched at all. Never present such an element's numbers as fact — report the other elements normally and tell the customer which ones failed.
Per-check breakdown of the latest data integrity report run for a single account. This is the same report the user sees at /connectors/<connector-key>/<datasource_id>/data-integrity, and provides the drill-down for a non-null `data_integrity` rollup returned by get_connector_statuses. Returns { tests: [{ name, status, errorRatio, matchedValues, unmatchedValues }] }. Each test is a "check" on the report. A check's status is driven by its overall match rate, not by individual days. match rate equals (1 - errorRatio) and is surfaced as a percentage (for example errorRatio 0.109 corresponds to "89.1% match rate"). A check is marked Failed when its match rate is below the configured threshold; a single day out of tolerance does not fail the check on its own. The report surface uses the phrasing "match rate". status values: - SUCCESS: matches the source exactly. The report shows this as "OK". - WITHIN_TOLERANCE: differs from the source but within the configured tolerance. The report also shows this as "OK" (no distinction from SUCCESS on the public surface). - FAILED: the match rate is below the threshold. The report shows this as "Failed". - SKIPPED: the check did not run for this account. - ERROR: the check could not execute (for example an API call failed). A check that ran but had zero days to compare (both matchedValues and unmatchedValues empty) is shown as "No data" on the report. errorRatio is the magnitude of mismatch as a fraction of expected (0 means exact match); the report surfaces its inverse as the match rate. matchedValues and unmatchedValues are per-day breakdowns keyed by date with { expected, current } pairs (numbers, or "No value" when the source returned nothing for that day). On the report each matched day is labeled "In tolerance" and each unmatched day "Out of tolerance". Only the latest incremental run is returned; historical runs are not surfaced. Required input: conversation_id and datasource_id (the UUID from get_connector_statuses accounts[].datasource_id).
Get the possible values for a given dimension. Inputs: - dimension: required. The dimension to get the values for. - tables: required. The tables to get the values from, as an array of strings. - search: optional. The search text to filter the values. If set, only the values containing the search text will be returned. Returns { dimensions: [...] } with the possible values for the dimension, up to a maximum of 500 values.
Returns the metrics available for a list of sources. Connector metrics are returned as { key, label }. Custom metrics carry { key, label } plus ONE of: - summary: human-readable one-line purpose (set when authored or AI-summarized) - refs: array of underlying metric labels the formula touches; a structural hint when no summary exists (a label like "ROAS v2 jamie" with refs ["Total ad spend", "Net sales"] is typically a return-on-ad-spend metric). Label, summary and refs together describe whether an existing custom metric covers a given concept. Custom metrics may carry `labels`: workspace-defined tags { name, category, description } that the workspace's team attached on purpose (for example a "Certified" label under a "Governance" category). A label's description is authoritative guidance on when to use the metric: when several metrics could answer a question, prefer the one whose labels and label descriptions match the question, and mention the label when you cite the metric. The 'key' values are opaque identifiers consumed verbatim by other tools. The full elements + filters formula is not inlined; `get_custom_metric_details(id)` returns the full body for a single candidate. The custom-metrics list is capped at the 300 most recently created/updated custom metrics. The `list_custom_metrics(q?, offset?, limit?)` tool paginates the full set and searches by case-insensitive substring on title and summary. SOURCES: A source is either a connector name (from `connector_status`) or 'custom-metrics'. Including 'custom-metrics' in the sourceList returns custom metrics for the workspace. Custom metrics contain user-defined business logic that connector metrics do not capture. A sourceList that omits 'custom-metrics' returns connector metrics only, so a report built from that result does not reflect the workspace's user-defined metrics. SOURCE-TO-CONNECTOR MAPPING: Common product names map to canonical connector keys: - "GA4", "Google Analytics", "Google Analytics 4" → `google-analytics-four` - "Facebook", "Facebook Ads", "Meta", "Meta Ads" → `facebook-ads` - "Google Ads", "Adwords" → `google-ads` - "Shopify" → `shopify` - "TikTok", "TikTok Ads" → `tiktok-ads` - "Klaviyo" → `klaviyo` - "Polar Pixel", "Pixel" → `polar-pixel` The `name` field in `connector_status` is the authoritative source; matching is case-insensitive. The result is scoped to the sources in sourceList; a source that is not listed contributes no metrics, so a sourceList narrower than the question's scope yields an incomplete metric set. A question spanning multiple sources is covered by listing every referenced connector alongside 'custom-metrics'. EXAMPLES: - "google analytics four landing page data" → sourceList: "google-analytics-four,custom-metrics" - "Shopify revenue last 30 days" → sourceList: "shopify,custom-metrics" - "blended ROAS this month" → sourceList: every ad-platform connector + shopify + custom-metrics - "how is my business doing" → sourceList: every connector in `connector_status` + custom-metrics Custom metrics (keys starting with 'custom_') encode user-defined business logic specific to this account; when a matching custom metric exists, it takes precedence over the equivalent standard metric. METRIC KEY ARCHITECTURE: Metric keys follow different patterns based on their scope: - Table-specific metrics have table prefixes: `shopify_sales_main.raw.*`, `shopify_sales_main.computed.*`, `facebook_ads_main.raw.*` - Blended metrics have no table prefix (for example `polar_pixel_conversion_rate`, `blended_roas`, `pixel_cac`). These are workspace-level metrics that combine data from multiple integrations/tables. - Custom metrics start with `custom_` prefix. Metric keys are opaque strings returned by this tool and consumed verbatim by other tools; the patterns above describe naming conventions, not a generative grammar.
Returns the full definition of a view (rules and connector filters). A view is a segment or set of dimensions pre-configured by the user. The compact entry in `get_context.views` is usually enough to identify a view by title; this tool returns the rules and connector filters when those details matter. Required inputs: - conversation_id - view (the id of a view, exactly as obtained from the `view` field of `get_context`)
Browses or searches the workspace's custom dimensions beyond the first page surfaced by `get_context`. Sorted by recency-of-update (most recently edited or created first). The `q` parameter filters by case-insensitive substring on title or summary; omitting `q` pages through everything. Searching by an underlying field name (for example q="country") surfaces every dimension whose label or summary references that field. Returns { items: [{ id, label, summary?, refs? }], total, offset, limit, hasMore }. Each item carries the same compact shape as get_context's custom_dimensions slot. The `get_custom_dimension_details(id)` tool returns the full whenThen formula for a single candidate. Required inputs: - conversation_id Optional inputs: - q (case-insensitive substring; matches title or summary) - offset (default 0) - limit (default 50, max 200)
Browses or searches the workspace's custom metrics. Sorted by recency-of-update (most recently edited or created first). The `q` parameter filters by case-insensitive substring on title or description; omitting `q` pages through everything. Returns { items: [{ id, label, summary?, refs? }], total, offset, limit, hasMore }. `refs` lists the underlying metric keys that the custom metric's formula depends on. The full elements formula is not inlined; the get_metrics tool returns it for items in the standard custom-metrics page. Required inputs: - conversation_id Optional inputs: - q (case-insensitive substring; matches title or description) - offset (default 0) - limit (default 50, max 200)
Returns the list of dashboards configured in the workspace. A dashboard is a core building block in the Polar platform. Supported dashboard elements include reports (tables, charts etc.) and key indicator sections (i.e. metric grids). Customers generally refer to dashboards and elements by their titles. Further details on a specific dashboard can be retrieved with get_dashboard_details. Required inputs: - conversation_id Output: A list of dashboards and their elements, along with deep links. Each dashboard and report element carries a user-facing deep link to its in-app view.
Given comma-separated metric keys from get_context, returns the breakdowns, filters, and settings (such as attribution_model) compatible with those metrics. Compatible dimensions vary by metric set. All metrics support the 'date' dimension implicitly. Omitting the metrics argument instead returns the full catalog of the workspace's groupable dimensions as { key, label } pairs. Name-based dimensions are preferred over their id-based counterparts (for example sales_channel_name rather than sales_channel). get_dimension_values returns the possible values for a dimension. Custom dimensions (keys starting with 'custom_') hold account-specific logic and take precedence over the equivalent standard dimension, except custom_default.
Given a dimension key, returns the metrics that support it as a breakdown. When generate_report is called with this breakdown, including any metric not in this list will cause the breakdown to fail with "cannot apply breakdown by <dimension>". Computed metrics from the same table are not interchangeable with raw metrics for this purpose. Required inputs: - conversation_id - dimension (a single dimension key from list_dimensions) Returns an array of { key, label } metrics. Empty when the dimension does not exist or no metrics support it. Coverage: this tool does not include blended metrics (no table prefix), custom metrics, or custom dimensions (keys starting with custom_). Compatibility for those can be verified via list_dimensions.
Submit the user's rating for your previous analysis. ONLY call this after the user explicitly provides a rating. Do not rate yourself. Inputs: - rate (string number 1-10), - reflexion (your analysis of their goal and why they are rating it this way). Output: confirmation message.
Converts a date expression into exact dates in the workspace timezone, honouring the workspace week start, and returns the weekday name of each boundary. It also returns the workspace's current date and weekday, which are the anchor every relative expression is resolved against. One call resolves one period. Its dates are the ones generate_report will agree with, since both read the same workspace calendar. Anything it cannot read exactly, it declines and returns expressions to try instead, rather than guessing. SYNTAX: a base period, optionally followed by one shift. BASE PERIODS today, yesterday, N days ago a single day last N days last N weeks, months, quarters, years last N whole weeks, months, quarters, years completed periods only week to date, month to date, quarter to date, year to date this week, last week, week before last this month, last month, month before last this quarter, this year monday this week, friday last week a weekday inside a named week, which may still be in the future this week most recent tuesday the latest one before today 2026-07-28, or 2026-07-20 to 2026-07-26 explicit dates SHIFTS (appended, arithmetic, applied left to right) - N days, - N weeks, - N months, - N quarters, - N years last week - 1 year the same calendar week a year earlier last week - 52 weeks the weekday-aligned week a year earlier Ranges run up to and including today by default, matching Polar's date picker. Appending 'excluding today' stops at yesterday instead. The dayCount field is the period's true length: the picker fixes the start and moves the end, so 'last 7 days' covers 8 days when today is included. Every result carries an interpretation stating in words what was resolved, so the reading is visible alongside the dates.
Resolves a fuzzy workspace name (or partial name) to one or more canonical workspace IDs. The resulting workspaceId is the value to pass as the `workspace_id` parameter on subsequent tool calls (get_metrics, list_dashboards, etc.) to query that workspace's data. Matching is substring and case-insensitive. When multiple workspaces match the input, all candidates are returned and require disambiguation. Required inputs: - name: workspace name or partial name to search for Output: Array of { workspaceId, name } objects. An empty array means no match.
Update an existing custom dimension. CRITICAL (non-self-created items): If you did NOT create this dimension in the current conversation, BEFORE calling this tool you MUST: 1. Call `get_custom_dimension_usages` with the dimension's id to discover exactly what depends on it. 2. In plain English, tell the user: - the dimension's name (and id, for reference) - what the proposed change is - which downstream objects would be affected — names and types (reports, custom metrics, KI sections), not just counts - that the change will apply immediately to those objects 3. Ask whether to proceed. 4. Once the user gives a clear affirmation ("yes", "go ahead", "do it", "update it", etc.), set `userConfirmedInChat: true` and call this tool. You do NOT need the user to repeat the id — they already know which item you mean from your previous message. For items you created in the CURRENT conversation: low risk, proceed without the usages lookup. Still set `userConfirmedInChat: true` (the flag protects the write path regardless). See `create_custom_dimension` for the full CustomDimension payload shape. Required inputs: - conversation_id - id (the custom_<N> id exactly as surfaced by get_context) - title (new title) - json (per the shape above) - userConfirmedInChat (must be true) On success returns { id, title, editUrl }. The chat UI surfaces the editUrl as an "Open dimension editor" button — do NOT repeat it as a markdown link in your visible response.
Update an existing custom metric. CRITICAL (non-self-created items): If you did NOT create this metric in the current conversation, BEFORE calling this tool you MUST: 1. Call `get_custom_metric_usages` with the metric's id to discover exactly what depends on it. 2. In plain English, tell the user: - the metric's name (and id, for reference) - what the proposed change is - which downstream objects would be affected — names and types (reports, other custom metrics, KI sections), not just counts - that the change will apply immediately to those objects 3. Ask whether to proceed. 4. Once the user gives a clear affirmation ("yes", "go ahead", "do it", "update it", etc.), set `userConfirmedInChat: true` and call this tool. You do NOT need the user to repeat the id — they already know which item you mean from your previous message. For items you created in the CURRENT conversation: low risk, proceed without the usages lookup. Still set `userConfirmedInChat: true`. The server will additionally reject the update if existing reports would become incompatible — that is an extra safeguard, not a substitute for your caution. See `create_custom_metric` for the full metricData payload shape. Required inputs: - conversation_id - id (the custom_<N> id) - metricData (full replacement CustomMetric payload) - userConfirmedInChat (must be true) On success returns { id, title, editUrl }. The chat UI surfaces the editUrl as an "Open metric editor" button — do NOT repeat it as a markdown link in your visible response.
How do I improve a ChatGPT Plugin's discoverability?
The levers are the listing surface agents actually read: names, descriptions, keywords, tool metadata, and registry health. Which lever matters depends on where discovery breaks, which is what continuous measurement shows.
What are Polar Analytics alternatives on ChatGPT?
As of 2026-09-28, Polar Analytics competes with Adzviser, Catchr, Coupler.io, Dataslayer, Faraday, Feedoptimise, Funnel, Improvado AI Agent, Ingest Labs, InsightfulPipe, Lytical, Master Metrics, Quanti IA, Windsor.ai in ChatGPT Marketing & Commerce Data Integration, ranked by public Discoverability Score.
Where is this profile measured?
This profile uses the geography attached to the latest public registry snapshot: US. Locale tags are intentionally omitted.