MCP App Store
by KeenEthics
Data & Analytics
Tembi Intelligence icon

Tembi Intelligence

by Tembi ApS

Overview

This MCP server based plugin provides tools for getting insights about e-commerce market across Europe.

Tools

assess_checkout_health

ChatGPT
Assess checkout-health across the caller's webshop base (read-only) Read-only dashboard: how healthy is our checkout footprint? for the caller's own provider (from `UserContext.delivery_providers — not overridable). Country scope: country_codes must sit inside UserContext.access_markets; omit for all accessible markets. Time scope: use_latest_snapshot defaults **true** — every number is pinned to the latest available scrape date for each source table (the *right-now* view). Set false to roll up over the trailing activity_months instead (activity_months is ignored when true). Signals scored (each include_* defaults true; max score = 3): include_brand_visibility (1 pt if provider is named), include_method_clarity (1 pt if delivery_method is a real value), include_time_clarity (1 pt if delivery_time is not null). Disabled signals drop from the score, coverage, and their checkout_missing_* count. Noise-row toggles — TWO separate scopes: (1) Base dashboard: include_unknown_providers (default true = keep unknown-provider rows and 'collect_yourself' method rows in base counts; false = strip). include_unknown_delivery_method (default true = keep 'unknown' method rows; false = strip). 'multiple_providers' always kept. (2) Competitor comparison: include_unknown_in_competitors (default false = strip unknown-provider / 'collect_yourself' rows from competitor_leads + healthier_competitor_examples, since neither is a real competitor; true keeps them). Independent of the base flags. T&Cs base: total_webshops_in_scope = every T&Cs mention of the caller's provider in scope, whether or not we've scraped a checkout for the shop. The T&C-only bucket splits into tandc_only_with_checkout (we HAVE their checkout, caller isn't at it) and tandc_only_no_checkout (we've never scraped their checkout — status unverifiable). Score distribution scores every shop in the base; no-checkout shops land in the brand-only bucket. Don't ask the user to confirm settings — pick them from the question and invoke. Answer from the returned counts, category_coverage, competitor_leads, examples, and summary_for_llm; quote the numbers and state settings_applied so the user can reconfigure. For richer segment slicing chain to calculate_delivery_market_share / assess_competitor_copresence. Responses: **200**: Successful Response (Success Response) Content-Type: application/json **Example Response:** `json { "access_denied": true, "category_coverage": [ { "category": "Category", "label": "Label", "webshops_meeting": 1, "webshops_missing": 1 } ], "score_distribution": [ { "score": 1, "webshops": 1 } ], "competitor_leads": [ { "delivery_provider": "Delivery Provider", "webshops": 1, "avg_score_gap": 1.0 } ], "tandc_only_with_checkout_examples": [ { "domain_id": "Domain Id", "domain": "Domain" } ], "tandc_only_no_checkout_examples": [ { "domain_id": "Domain Id", "domain": "Domain" } ], "missing_method_examples": [ { "domain_id": "Domain Id", "domain": "Domain" } ], "missing_time_examples": [ { "domain_id": "Domain Id", "domain": "Domain" } ], "healthier_competitor_examples": [ { "domain_id": "Domain Id", "domain": "Domain" } ], "settings_applied": { "delivery_provider": "Delivery Provider", "country_codes": [ "string" ], "use_latest_snapshot": true, "activity_months": 1, "min_health_gap": 1.0, "top_n_examples": 1, "assessed_categories": [ "string" ], "include_unknown_providers": true, "include_unknown_delivery_method": true, "include_unknown_in_competitors": true }, "summary_for_llm": "Summary For Llm", "timestamp": "2023-01-01T00:00:00Z" } ` **Output Schema:** `json { "properties": { "access_denied": { "type": "boolean", "title": "Access Denied", "description": "True when the tool could not run because the caller lacks a delivery provider on their profile, or requested country codes outside their UserContext.access_markets. Fact blocks are omitted; summary_for_llm` explains the denial.", "default": false }, "counts": { "descript…

assess_competitor_copresence

ChatGPT
Which carriers share a delivery provider's webshop checkouts Analyses which competing carriers appear at the same webshop checkouts as a target provider — the clearest point-of-sale signal of head-to-head competition. Runs on the checkout-observed population (`webshop_checkout_history, the platform's Delivery provider filter with Source: Checkout, home market), a tighter subset than the market-share tools' provider history. A carrier sighting counts even when the row's delivery method is unclassified (set require_joint_classification for strict cross-tab scoping); the default window is the latest monthly snapshot (current state). **Co-presence:** copresence_pct is the share of the target's webshops that also offer a given competitor; reverse_copresence_pct is the reverse, so you can see dependency asymmetry. penetration_pct grounds how big each rival is overall. **Contested base:** the response reports how much of the target's base is *exclusive* (no other identified carrier) versus *contested* (a rival is present). Exclusive splits further into fully_exclusive_shops (no other carrier rows at all) and exclusive_with_unclassified_shops (unattributed provider rows share the checkout — a possible hidden rival); surface the split when reporting exclusivity. **Method-aware:** set breakdown_by_method to require the competitor to share the same delivery method (rows become per (competitor, method)). **Position:** with include_position (default on) each row reports the average checkout positions and how often the target is listed above the competitor. **Market scope:** domestic (default) analyses webshops' own-market checkouts. cross_border runs the same analysis on webshops' export-market checkouts (webshop_checkout_export_market_info, a single undated snapshot): country_code scopes the SOURCE webshops' market(s) and target_country narrows the destination lane(s) — e.g. carriers contesting DHL on the DK→DE lane = market_scope='cross_border', country_code='DK', target_country='DE'. period_months is ignored for cross_border (the response sets snapshot_only=true and data_as_of=null). Cross-border scope — requires the checkout:export-markets module. Segment filters (cross_border, size, growth, main-product-category, export_markets, has_company_data, uses_delivery_*) apply as elsewhere. Returns a pre-rendered summary_for_llm. Responses: **200**: Successful Response (Success Response) Content-Type: application/json **Example Response:** `json { "entitled_markets": [ "string" ], "unauthorized_markets": [ "string" ], "markets_resolved": true, "country_codes": [ "string" ], "provider_resolved": true, "candidate_providers": [ "string" ], "delivery_provider": "Delivery Provider", "market_scope": "MarketScope", "target_countries": [ "string" ], "snapshot_only": true, "period_months": 1, "breakdown_by_method": true, "require_joint_classification": true, "include_position": true, "filters_summary": "Filters Summary", "population_shops": 1, "target_shops": 1, "exclusive_shops": 1, "exclusive_pct": 1.0, "fully_exclusive_shops": 1, "fully_exclusive_pct": 1.0, "exclusive_with_unclassified_shops": 1, "exclusive_with_unclassified_pct": 1.0, "contested_shops": 1, "contested_pct": 1.0, "competitors": [ { "rank": 1, "delivery_provider": "Delivery Provider", "competitor_shops": 1, "copresence_shops": 1, "copresence_pct": 1.0, "reverse_copresence_pct": 1.0, "penetration_pct": 1.0 } ], "total_competitors_available": 1, "summary_for_llm": "Summary For Llm" } ` **Output Schema:** ``json { "properties": { "entitled_markets": { "items": { "type": "string" }, "type": "array", "title": "Entitled Markets", "description": "Markets the caller's account is entitled to query (empty = no restriction)." }, "unauthorized_markets": { "items": { "type": "string" }, "type": "array", "title": "Unauthorized Markets", "description": "Requested markets dropped because they are outside the caller's entitlement." }, "markets_resolved": { "type": "boolean", "title": "Markets R…

calculate_delivery_market_share

ChatGPT
Rank delivery providers/methods by market share at a point in time Calculates a point-in-time ranking of delivery providers (or delivery methods) by the share of webshops using each. Defaults to the latest monthly snapshot (current state, aligned with the Tembi platform's filters: the 'delivery' basis ↔ Delivery provider with Source: All; the 'checkout' basis ↔ Source: Checkout, home market); raise `period_months to union several snapshots. The snapshot sibling of calculate_delivery_market_share_trend — same segment filters and baseline, but a ranked table rather than a time series. **Dimension:** rank by 'delivery_provider' (default) or 'delivery_method'. Only the ranked dimension must be classified on a row (e.g. a provider ranking keeps rows with an unclassified method; self-collection rows are always excluded — not carrier delivery) — counts can run slightly under the platform, whose current view persists state across crawls, but never far below. Set require_joint_classification for strict provider×method cross-tab scoping (checkout basis). **Market scope:** domestic (default) ranks webshops' own-market delivery usage. cross_border ranks what webshops offer consumers in OTHER markets: country_code scopes the source webshops' market(s) and target_country the destination(s) — e.g. carriers dominant on the Italy→Spain lane = market_scope='cross_border', country_code='IT', target_country='ES'. Cross-border data is a single current snapshot (period_months is ignored; the response sets snapshot_only); provider/method classification coverage is lower than domestic. (Two segment filters are related but distinct, and apply in either scope: cross_border — whether a shop exports at all — and export_markets — declared export destinations, as country names. Neither selects the lane rows analysed; only target_country does.) **Subject:** pass subject (or, for providers, rely on the caller's own provider from their Tembi context) to also get that value's rank and share alongside the table. The true number of distinct providers/methods is always returned as total_active_values (independent of top_n). **Baseline (denominator):** denominator_basis works exactly as in the trend tool; the response echoes the resolved basis and a plain-English denominator_explanation — surface it to the user, since it changes what the percentage means. In cross-border scope 'auto'/'checkout' measure against export-checkout-observed webshops in the lane slice and 'market' against all webshops in the segment ('delivery' is domestic-only). market_population additionally reports the full tracked webshop population per market as reference context — quote it when presenting shares, but never recompute percentages against it. **Filters (segment):** cross_border, size, growth, main-product-category, export_markets, has_company_data (webshops with an identified linked company), and uses_delivery_provider / uses_delivery_method. Resolve exact values with get_queryable_values. Returns the ranked table plus a summary_for_llm. Responses: **200**: Successful Response (Success Response) Content-Type: application/json **Example Response:** ``json { "entitled_markets": [ "string" ], "unauthorized_markets": [ "string" ], "markets_resolved": true, "dimension": "DeliveryShareDimension", "market_scope": "MarketScope", "country_codes": [ "string" ], "target_countries": [ "string" ], "snapshot_only": true, "period_months": 1, "denominator_basis": "DenominatorBasis", "denominator_explanation": "Denominator Explanation", "filters_summary": "Filters Summary", "excluded_values": [ "string" ], "require_joint_classification": true, "subject_resolved": true, "candidate_values": [ "string" ], "webshop_count": 1, "market_share_pct": 1.0, "rank": 1, "total_active_values": 1, "baseline_webshops": 1, "market_population": [ { "country_code": "Country Code", "total_webshops": 1, "data_month": "Data Month" } ], "rankings": [ { "rank": 1, "series_value": "Series Value", "webshop_count": 1, "…

calculate_delivery_market_share_trend

ChatGPT
Monthly market-share trend by delivery provider or method, with filters Returns a multi-series monthly market-share trend for the top delivery providers (or delivery methods) in a market. The trend sibling of`calculate_delivery_market_share. Domestic (own-market) data only — for cross-border/export analysis use the snapshot tool with market_scope='cross_border' (that data has no monthly history yet). **Series:** the top top_n providers/methods by webshop count in the most recent month, plus any explicit series_values. Set dimension to 'delivery_provider' (default) or 'delivery_method'. total_series_available reports how many exist so you can tell when the chart is truncated. **Baseline (denominator):** controlled by denominator_basis. 'auto' (default) uses webshops with delivery data for the chosen dimension; 'market' expresses penetration of all webshops in the segment. The response echoes the resolved basis and a plain-English denominator_explanation — **surface this to the user**, because it changes what the percentage means. When unsure which baseline the user wants, ask. market_population additionally reports the full tracked webshop population per market (latest month) as reference context — quote it when presenting shares, but never recompute percentages against it. **Filters (segment):** cross_border, size_categories / size_indication_min / size_indication_max, growth_categories, product_categories, export_markets, has_company_data, and uses_delivery_provider / uses_delivery_method — all narrow both numerator and denominator. Resolve exact filter and series values with get_queryable_values. Returns one series per provider/method (each a monthly point list), plus a pre-rendered summary_for_llm. Responses: **200**: Successful Response (Success Response) Content-Type: application/json **Example Response:** `json { "entitled_markets": [ "string" ], "unauthorized_markets": [ "string" ], "markets_resolved": true, "dimension": "DeliveryShareDimension", "country_codes": [ "string" ], "months": 1, "data_as_of": "2023-01-01", "denominator_basis": "DenominatorBasis", "denominator_explanation": "Denominator Explanation", "filters_summary": "Filters Summary", "excluded_values": [ "string" ], "require_joint_classification": true, "series": [ { "series_value": "Series Value", "points": [ { "month": "Month", "webshop_count": 1, "baseline_webshops": 1, "market_share_pct": 1.0 } ] } ], "market_population": [ { "country_code": "Country Code", "total_webshops": 1, "data_month": "Data Month" } ], "total_series_available": 1, "unresolved_series": [ "string" ], "candidate_values": [ "string" ], "summary_for_llm": "Summary For Llm" } ` **Output Schema:** `json { "properties": { "entitled_markets": { "items": { "type": "string" }, "type": "array", "title": "Entitled Markets", "description": "Markets the caller's account is entitled to query (empty = no restriction)." }, "unauthorized_markets": { "items": { "type": "string" }, "type": "array", "title": "Unauthorized Markets", "description": "Requested markets dropped because they are outside the caller's entitlement." }, "markets_resolved": { "type": "boolean", "title": "Markets Resolved", "description": "False when every explicitly requested market is outside the caller's entitlement. No data was queried in that case; 'entitled_markets' lists the markets to retry with.", "default": true }, "dimension": { "type": "string", "enum": [ "delivery_provider", "delivery_method" ], "title": "DeliveryShareDimension", "description": "What a delivery-share calculation is split by." }, "country_codes": { "items": { "type": "string" }, "type": "array", "title": "Country Codes" }, "months": { "type": "integer", "title": "Months" }, "data_as_of": { "type": "string", "format": "date", "title": "Data As Of", "description": "Latest month with data anchoring the window." }, "denominator_basis": { "description": "The population a delivery-share percentage is measured *against* (baseline).\n\n* delivery` \u2013 webshop…

calculate_webshop_population_share

ChatGPT
Share of the webshop population by attribute (e.g. % exporting), trend or snapshot Returns what proportion of the webshop population carries a given attribute, as a monthly trend or a current-composition snapshot — the data behind population charts like "cross-border retailers' market share" (proportion of webshops that export). Unlike the delivery tools, this is a population metric: the denominator is always all webshops in the segment (`webshop_history), not the delivery-tracked subset. **Attribute:** cross_border (default) tracks the single 'exporting' share — webshops with at least one primary (dedicated) export market. export_market gives one series per primary export market, keyed by full country names; series overlap, since a webshop can export to several markets. Both are time-aware, counting a webshop only in months it actually exported, so they show genuine market entry/exit over the window. webshop_size, growth, and country split the population into one series per current-state value; product_category splits by each webshop's single main product category. delivery_provider and delivery_method split by the provider/method a webshop offers at its checkout — pair with checkout_scope to read the webshop's own-market checkout (domestic) or its export-market checkout (export_market). Series overlap for delivery_* attributes (a webshop can offer several providers/methods), so shares do not sum to 100%. For every attribute except cross_border, series are the auto top-top_n ∪ explicit attribute_values. **Domestic vs export-market comparison (aggregate):** to compare a segment's provider mix at home vs abroad, call the tool twice with the same segment filters (e.g. cross_border=true, export_markets=['Germany'], country_code='DK') and switch only checkout_scope — 'domestic' shows the DK exporters' domestic carrier mix, 'export_market' with target_country='DE' shows the same shops' DE-checkout carrier mix. export_market scope is a single undated snapshot (snapshot_only=true, single point per series) and requires the checkout:export-markets module. **Snapshot:** set latest_only to return just the most recent month (current composition) instead of the full trend. Forced true when checkout_scope='export_market'. **Break down a provider's base:** combine the uses_delivery_provider (or uses_delivery_method) segment filter with an attribute — e.g. uses_delivery_provider='gls' + attribute='product_category' gives the main-category mix of GLS's webshops. **Filters (segment):** cross_border, size, growth, main-product-category, export_markets, has_company_data, and uses_delivery_*. Resolve exact values with get_queryable_values. Returns a pre-rendered summary_for_llm. **Modules:** the non-checkout attributes are free. The delivery_* attributes are gated by module: checkout_scope='domestic' requires checkout; checkout_scope='export_market' requires checkout:export-markets. Ask an account manager if the caller needs either enabled. Responses: **200**: Successful Response (Success Response) Content-Type: application/json **Example Response:** `json { "entitled_markets": [ "string" ], "unauthorized_markets": [ "string" ], "markets_resolved": true, "attribute": "PopulationAttribute", "checkout_scope": "CheckoutScope", "country_codes": [ "string" ], "target_countries": [ "string" ], "snapshot_only": true, "months": 1, "latest_only": true, "filters_summary": "Filters Summary", "series": [ { "series_value": "Series Value", "points": [ { "month": "Month", "webshop_count": 1, "baseline_webshops": 1, "market_share_pct": 1.0 } ] } ], "total_series_available": 1, "summary_for_llm": "Summary For Llm" } ` **Output Schema:** ``json { "properties": { "entitled_markets": { "items": { "type": "string" }, "type": "array", "title": "Entitled Markets", "description": "Markets the caller's account is entitled to query (empty = no restriction)." }, "unauthorized_markets": { "items": { "typ…

compare_checkout_positions

ChatGPT
Checkout positions, breadth and delivery terms (price, time) — snapshot Analyses how delivery providers (or delivery methods) are placed across checkout positions in one monthly snapshot — who holds position 1, how presence spreads across slots, how broad checkouts are, and what delivery terms apply per position: price and promised delivery time. The snapshot sibling of `compare_checkout_positions_trend. **Market scope:** domestic (default) analyses webshops' own-market checkouts. cross_border analyses what webshops offer consumers in OTHER markets: country_code scopes the source webshops' market(s) and target_country the destination(s) — e.g. Italy→Spain delivery terms = market_scope='cross_border', country_code='IT', target_country='ES', view='delivery_terms_by_position'. Cross-border data is currently a single snapshot (no snapshot_month/compare_month); a target_country stratification is available for pathway cross-tabs. (Two segment filters are related but distinct, and apply in either scope: cross_border — whether a shop exports at all — and export_markets — declared export destinations, as country names. Neither selects the lane rows analysed; only target_country does.) **Views** (pick one per call): share_by_position – % split of providers/methods within each position (each position sums to 100%); position_distribution – per provider/method spread across positions plus best-position stats (median/avg, count at #1); provider_breadth – webshops bucketed by how many distinct real providers their checkout offers (1..5+); delivery_terms_by_position – delivery price and delivery-time stats per method × position, each with coverage percentages (delivery time is partially published — quote the coverage). **Time:** snapshot_month ('YYYY-MM', default latest snapshot) and optional compare_month add per-row deltas (e.g. '2026-01' for 'since the start of the year'). The response echoes the exact snapshot dates used. **Slicing:** positions (default 1–6), delivery_methods, provider-count band, stratify_by (country, webshop_size, growth, product_category, municipality) for cross-tabs — strata below min_webshops are dropped and strata_available reports the pre-floor count (see the field for per-option caveats) — plus the shared segment filters. Webshops count once per provider/method at their best position. Resolve exact filter values with get_queryable_values. **Exclusions:** by default only the analysed dimension must be classified — sentinel values ('unknown', 'collect_yourself', 'multiple_providers') are dropped from it, the right scope for competitive comparisons. For a general analysis of what checkouts look like ('the full picture'), pass exclude_values=[] so the unclassified and self-collection buckets appear as ranked values (note: 'distinct providers' in provider_breadth is then a lower-confidence count, and the provider-count band always counts identified carriers only). require_joint_classification adds strict provider×method cross-tab scoping. **Subject grounding:** subject defaults to the caller's own provider; subject_context reports its rank among providers above a materiality floor, the leader, and the immediate neighbours with webshop gaps — and those neighbours are always included in the rows regardless of top_n. Returns rows for the chosen view plus a summary_for_llm. Responses: **200**: Successful Response (Success Response) Content-Type: application/json **Example Response:** ``json { "snapshot_resolved": true, "candidate_months": [ "string" ], "entitled_markets": [ "string" ], "unauthorized_markets": [ "string" ], "markets_resolved": true, "view": "CheckoutPositionsView", "dimension": "CheckoutDimension", "market_scope": "MarketScope", "country_codes": [ "string" ], "target_countries": [ "string" ], "snapshot_only": true, "positions": [ 1 ], "filters_summary": "Filters Summary", "excluded_providers": [ "string" ], "excluded_methods": [ "string" ], "subject_resolved": true, "candidate_va…

compare_checkout_positions_trend

ChatGPT
Monthly checkout position trend by provider or method Tracks checkout positions month by month: per provider (or delivery method), the number of webshops where it appears at checkout, how many list it first, and its average/median best position. Domestic checkout data only. The trend sibling of `compare_checkout_positions — same filters and dedupe convention, monthly series instead of one snapshot. Series are the top top_n providers/methods by latest-month presence, plus the caller's own provider (always included as a series and summarised — subject), plus any series_values (resolve exact values with get_queryable_values). For a head-to-head against your immediate rivals, take ahead/behind from compare_checkout_positions' subject_context and pass them as series_values. Window is the trailing months (default 12) anchored on the latest snapshot. positions, delivery_methods, provider-count band, and the shared segment filters apply. Returns per-series monthly points with start-to-end changes and a summary_for_llm. For month-vs-month position share deltas, prefer the snapshot tool with compare_month. Responses: **200**: Successful Response (Success Response) Content-Type: application/json **Example Response:** `json { "entitled_markets": [ "string" ], "unauthorized_markets": [ "string" ], "markets_resolved": true, "dimension": "CheckoutDimension", "country_codes": [ "string" ], "months": 1, "positions": [ 1 ], "filters_summary": "Filters Summary", "excluded_providers": [ "string" ], "excluded_methods": [ "string" ], "subject_resolved": true, "series": [ { "series_value": "Series Value", "points": [ { "month": "Month", "webshop_count": 1, "first_position_count": 1, "avg_best_position": 1.0, "median_best_position": 1.0 } ], "change_webshop_count": 1, "change_first_position_count": 1, "change_avg_best_position": 1.0 } ], "total_series_available": 1, "unresolved_series": [ "string" ], "candidate_values": [ "string" ], "summary_for_llm": "Summary For Llm" } ` **Output Schema:** ``json { "properties": { "entitled_markets": { "items": { "type": "string" }, "type": "array", "title": "Entitled Markets", "description": "Markets the caller's account is entitled to query (empty = no restriction)." }, "unauthorized_markets": { "items": { "type": "string" }, "type": "array", "title": "Unauthorized Markets", "description": "Requested markets dropped because they are outside the caller's entitlement." }, "markets_resolved": { "type": "boolean", "title": "Markets Resolved", "description": "False when every explicitly requested market is outside the caller's entitlement. No data was queried in that case; 'entitled_markets' lists the markets to retry with.", "default": true }, "dimension": { "type": "string", "enum": [ "delivery_provider", "delivery_method" ], "title": "CheckoutDimension", "description": "What the position analysis is split by." }, "country_codes": { "items": { "type": "string" }, "type": "array", "title": "Country Codes" }, "months": { "type": "integer", "title": "Months", "default": 12 }, "data_as_of": { "title": "Data As Of", "description": "Latest month present ('yyyy-MM')." }, "positions": { "items": { "type": "integer" }, "type": "array", "title": "Positions" }, "filters_summary": { "type": "string", "title": "Filters Summary", "default": "" }, "excluded_providers": { "items": { "type": "string" }, "type": "array", "title": "Excluded Providers", "description": "Provider values excluded from the analysed rows; the min/max-providers band always counts identified carriers, independent of this list." }, "excluded_methods": { "items": { "type": "string" }, "type": "array", "title": "Excluded Methods", "description": "Method values excluded from the analysed rows." }, "subject": { "title": "Subject", "description": "Resolved subject (lower-cased): the request's, or the caller's own provider." }, "subject_resolved": { "type": "boolean", "title": "Subject Resolved", "description": "False when the subject has no checkout data in this segment; candidate_values lis…

compare_delivery_pricing

ChatGPT
Compare delivery prices by provider and method at a point in time Compares the delivery prices webshops display at checkout, by delivery provider and method, over a trailing window ending at the latest month with data. The point-in-time sibling of `compare_delivery_pricing_trend. **Per-webshop method:** each shop contributes its latest in-band price (weighted once), matching the dashboards — not a raw row average. Returns a robust distribution per group: mean, **median**, p25/p75, min/max, and **free-delivery %**. **Market scope:** domestic (default) prices webshops' own-market checkouts. cross_border prices what webshops charge consumers in OTHER markets: country_code scopes the source webshops' market(s) and target_country the destination(s) — e.g. Italy→Spain checkout prices = market_scope='cross_border', country_code='IT', target_country='ES'. A target_country stratification gives per-lane cross-tabs. Cross-border data is a single current snapshot (period_months is ignored; the response sets snapshot_only); classification coverage is lower than domestic, and lanes are thinner — consider lowering min_webshops for narrow lanes. (Two segment filters are related but distinct, and apply in either scope: cross_border — whether a shop exports at all — and export_markets — declared export destinations, as country names. Neither selects the lane rows analysed; only target_country does.) **Grain:** price is method-specific, so groups are (provider, delivery_method) by default. Set breakdown_by_method=false for a method-blind provider average; narrow with delivery_methods; add a stratify_by segment (webshop_size, country, growth, cross_border, product_category, municipality; target_country in cross-border scope) for deeper comparison — when stratified, the response reports how many strata exist versus how many cleared the min_webshops floor (see the field for per-option caveats). **Subject:** pass subject (or rely on the caller's own provider) to get its prices vs the market median. **Outliers:** prices above max_price_eur (default 20) are dropped; groups below min_webshops are excluded. Segment filters (cross_border, size, growth, main-product-category, export_markets, has_company_data, uses_delivery_*) apply as elsewhere. Returns a summary_for_llm. Responses: **200**: Successful Response (Success Response) Content-Type: application/json **Example Response:** `json { "entitled_markets": [ "string" ], "unauthorized_markets": [ "string" ], "markets_resolved": true, "market_scope": "MarketScope", "country_codes": [ "string" ], "target_countries": [ "string" ], "snapshot_only": true, "period_months": 1, "breakdown_by_method": true, "price_band": "Price Band", "min_webshops": 1, "top_n": 1, "filters_summary": "Filters Summary", "excluded_values": [ "string" ], "excluded_methods": [ "string" ], "require_joint_classification": true, "subject_resolved": true, "candidate_values": [ "string" ], "rows": [ { "delivery_provider": "Delivery Provider", "avg_price_eur": 1.0, "median_price_eur": 1.0, "p25_price_eur": 1.0, "p75_price_eur": 1.0, "min_price_eur": 1.0, "max_price_eur": 1.0, "free_delivery_pct": 1.0, "webshop_count": 1 } ], "total_groups_available": 1, "summary_for_llm": "Summary For Llm" } ` **Output Schema:** ``json { "properties": { "entitled_markets": { "items": { "type": "string" }, "type": "array", "title": "Entitled Markets", "description": "Markets the caller's account is entitled to query (empty = no restriction)." }, "unauthorized_markets": { "items": { "type": "string" }, "type": "array", "title": "Unauthorized Markets", "description": "Requested markets dropped because they are outside the caller's entitlement." }, "markets_resolved": { "type": "boolean", "title": "Markets Resolved", "description": "False when every explicitly requested market is outside the caller's entitlement. No data was queried in that case; 'entitled_markets' lists the markets to retry with.", "default": true }, "market_scope": { "descriptio…

compare_delivery_pricing_trend

ChatGPT
Monthly delivery-price trend by provider and method Returns a monthly per-webshop average delivery-price series per provider (or provider × delivery method) — the data behind the Market Intelligence 'Average delivery price' line charts. The trend sibling of `compare_delivery_pricing. Domestic (own-market) prices only — for cross-border/export prices use the snapshot tool with market_scope='cross_border' (that data has no monthly history yet). Each shop contributes its latest in-band price per month (weighted once); months with fewer than min_webshops shops are suppressed. Series are the top top_n providers *per delivery method* by latest-month webshop count — the same within-method selection the per-method dashboard charts use — plus any series_values; breakdown_by_method and delivery_methods control the method split (narrow the methods to keep the number of lines readable). Outlier band (max_price_eur default 20) and the shared segment filters (cross_border, size, growth, main-product-category, export_markets, has_company_data, uses_delivery_*) apply. Returns a summary_for_llm. For distributions / subject-vs-market comparison, use compare_delivery_pricing. Responses: **200**: Successful Response (Success Response) Content-Type: application/json **Example Response:** `json { "entitled_markets": [ "string" ], "unauthorized_markets": [ "string" ], "markets_resolved": true, "country_codes": [ "string" ], "months": 1, "data_as_of": "2023-01-01", "breakdown_by_method": true, "price_band": "Price Band", "min_webshops": 1, "top_n": 1, "filters_summary": "Filters Summary", "excluded_values": [ "string" ], "excluded_methods": [ "string" ], "require_joint_classification": true, "series": [ { "delivery_provider": "Delivery Provider", "points": [ { "month": "Month", "avg_price_eur": 1.0, "webshop_count": 1 } ], "start_month": "Start Month", "months_covered": 1, "start_price_eur": 1.0, "end_price_eur": 1.0, "change_eur": 1.0, "change_pct": 1.0 } ], "total_series_available": 1, "unresolved_series": [ "string" ], "candidate_values": [ "string" ], "summary_for_llm": "Summary For Llm" } ` **Output Schema:** ``json { "properties": { "entitled_markets": { "items": { "type": "string" }, "type": "array", "title": "Entitled Markets", "description": "Markets the caller's account is entitled to query (empty = no restriction)." }, "unauthorized_markets": { "items": { "type": "string" }, "type": "array", "title": "Unauthorized Markets", "description": "Requested markets dropped because they are outside the caller's entitlement." }, "markets_resolved": { "type": "boolean", "title": "Markets Resolved", "description": "False when every explicitly requested market is outside the caller's entitlement. No data was queried in that case; 'entitled_markets' lists the markets to retry with.", "default": true }, "country_codes": { "items": { "type": "string" }, "type": "array", "title": "Country Codes" }, "months": { "type": "integer", "title": "Months" }, "data_as_of": { "type": "string", "format": "date", "title": "Data As Of", "description": "Latest month with data anchoring the window." }, "breakdown_by_method": { "type": "boolean", "title": "Breakdown By Method" }, "price_band": { "type": "string", "title": "Price Band", "description": "The applied outlier price band, e.g. '\u20ac0\u2013\u20ac20'." }, "min_webshops": { "type": "integer", "title": "Min Webshops", "description": "Per-month eligibility floor applied to every series point (echoed)." }, "top_n": { "type": "integer", "title": "Top N", "description": "Per-method provider cap applied to the auto-selected series (echoed \u2014 why some series may be omitted)." }, "filters_summary": { "type": "string", "title": "Filters Summary", "description": "Human-readable summary of segment filters." }, "excluded_values": { "items": { "type": "string" }, "type": "array", "title": "Excluded Values", "description": "Provider values excluded from this trend (echoed for clarity)." }, "excluded_methods": { "items": { "type": "string" }, "type": "array",…

create_list

ChatGPT
Creates a webshop list, optionally seeded from a selection. Omit webshops for an empty list. Returns the new listId, which update_leads takes as addToList. Idempotent by name: an existing list with the same name comes back with created:false, seeded:0 and its existing id, and is not seeded again; if the name is carried by more than one visible list the call is refused with AMBIGUOUS_NAME. Nothing on this surface deletes a list or a tag, so reuse a name instead of inventing a variant of it. Scoped to the current active market.

create_tags

ChatGPT
Creates tags by name, idempotently: a name that already exists comes back with created:false and its existing id. Nothing on this surface deletes a tag or a list, so reuse an existing name instead of inventing a variant of it.

find_webshops

ChatGPT
Finds the webshops matching a selection and returns them with lead status, owner and tags as names, not ids. Also the resolver: pass webshops.domains and every input that matched no webshop you can see comes back in unmatched. A webshop nobody has set a status on reads as the customer’s default status (usually Open). Paging is offset-based over live data: when you write while paging, stay on page: 1 and exclude what you already changed — for example tag each batch and pass that tag in webshops.excludeTags — instead of advancing page. Omit webshops to match everything you can see. Scoped to the current active market.

get_queryable_values

ChatGPT
List or search the queryable values for an analytics dimension Returns the real, queryable values that exist in the analytics data for a dimension, so you can resolve a user's free-text reference to the exact stored value the other tools expect — always prefer this over guessing a value. Values are returned exactly as stored and matched case-insensitively everywhere; stored names vary in style (e.g. 'gls', 'Inpost'). Two modes, selected by dimension: - Enumerate — delivery_provider, delivery_method, country, platform, product_category, webshop_size, growth, export_market (country names of declared export destinations — the values the export_markets segment filter takes), target_country (ISO codes of cross-border checkout destinations — the values the market-intelligence tools' target_country parameter takes), product_dimension, municipality (local-language names of registered-company municipalities — the values the municipalities filter and stratify_by='municipality' take): returns the full (or top-N) set of values, ranked by webshop count. - Search — brand, company, domain (high cardinality): a query term is required; returns ranked name matches. Each value carries a canonical_value to pass verbatim to other tools, a label, and—where meaningful—a webshop_count for materiality. Id-keyed dimensions (product_category, product_dimension, brand, company, domain) also return a value_id. Ordinal dimensions (webshop_size, growth, product_dimension) include an order (low→high), and webshop_size a score_range, so comparative phrasing such as 'above-average size' resolves to a contiguous set. Non-selectable placeholders (e.g. 'unknown') are flagged with is_sentinel: true. Scope: market-specific dimensions (delivery_provider, delivery_method, platform, product_category, webshop_size, growth, export_market, target_country, municipality) default to the caller's home market and can be overridden with a single country_code; the country dimension lists all available markets. Enumerated values for dated dimensions reflect recent activity (the current market), not all history. Filtering / example: for enumerate dimensions, pass query to narrow by substring (e.g. 'postn' → 'postnord', 'postnl'). To answer a question about 'Royal Mail', call with dimension='delivery_provider' to find 'royal_mail', then pass that canonical_value to the market-intelligence tools. Responses: 200: Successful Response (Success Response) Content-Type: application/json Example Response: ``json { "entitled_markets": [ "string" ], "unauthorized_markets": [ "string" ], "markets_resolved": true, "dimension": "DimensionName", "mode": "Mode", "country_codes": [ "string" ], "total_values": 1, "returned": 1, "values": [ { "canonical_value": "Canonical Value", "label": "Label", "is_sentinel": true } ], "summary_for_llm": "Summary For Llm" } ` **Output Schema:** ``json { "properties": { "entitled_markets": { "items": { "type": "string" }, "type": "array", "title": "Entitled Markets", "description": "Markets the caller's account is entitled to query (empty = no restriction)." }, "unauthorized_markets": { "items": { "type": "string" }, "type": "array", "title": "Unauthorized Markets", "description": "Requested markets dropped because they are outside the caller's entitlement." }, "markets_resolved": { "type": "boolean", "title": "Markets Resolved", "description": "False when every explicitly requested market is outside the caller's entitlement. No data was queried in that case; 'entitled_markets' lists the markets to retry with.", "default": true }, "dimension": { "type": "string", "enum": [ "delivery_provider", "delivery_method", "country", "platform", "product_category", "webshop_size", "growth", "export_market", "target_country", "product_dimension", "municipality", "brand", "company", "domain" ], "title": "DimensionName", "description": "Dimensions whose queryable values can be discovered." }, "mode": { "type": "string", "enum": [ "enumerate"…

get_report_content

ChatGPT
Fetch a report's rendered HTML + metadata (to read or to clone) Returns a single report's rendered HTML plus its metadata — to preview/read it (e.g. verify what you just pushed) or to clone it locally for editing. Get `report_id from list_reports or from a prior push_report's id. The HTML is the figure-injected render the customer sees — numbers are baked in, not fig:token placeholders. figures is included for provenance only and is NOT in the shape push_report expects — rebuild bindings from your sources rather than feeding it back. To re-publish an edited clone, re-introduce fig:token placeholders (token wrapped in double curly braces) and bind them in figures via push_report (pushing baked-in numbers trips the inline-number guard), and re-stamp the edited HTML with the tembi-report skill. Pass release_id to fetch a specific version: a prior published version, or a **draft** you just pushed (use the release_id returned by push_report). Omit release_id for the current published version — a bare fetch never returns an unpublished draft, and returns **empty** for a report that has never been published (e.g. one you just created), so pass the release_id to see that draft. Responses: **200**: Successful Response (Success Response) Content-Type: application/json **Example Response:** `json { "id": "Id", "title": "Title", "markets": [ "string" ], "required_modules": [ "string" ], "html": "Html", "figures": {}, "summary_for_llm": "Summary For Llm" } ` **Output Schema:** `json { "properties": { "id": { "type": "string", "title": "Id" }, "title": { "type": "string", "title": "Title" }, "status": { "title": "Status" }, "markets": { "items": { "type": "string" }, "type": "array", "title": "Markets", "default": [] }, "required_modules": { "items": { "type": "string" }, "type": "array", "title": "Required Modules", "default": [] }, "ai_summary": { "title": "Ai Summary" }, "data_as_of": { "title": "Data As Of" }, "generated_on": { "title": "Generated On" }, "html": { "type": "string", "title": "Html", "description": "Rendered report HTML (figures injected, numbers baked in).", "default": "" }, "figures": { "additionalProperties": true, "type": "object", "title": "Figures", "description": "token -> figure binding used at render (provenance only)." }, "summary_for_llm": { "type": "string", "title": "Summary For Llm" } }, "type": "object", "required": [ "id", "title", "summary_for_llm" ], "title": "ReportContentResponse" } ` **422**: Validation Error Content-Type: application/json **Example Response:** `json { "detail": [ { "loc": [], "msg": "Message", "type": "Error Type" } ] } ` **Output Schema:** `json { "properties": { "detail": { "items": { "properties": { "loc": { "items": {}, "type": "array", "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" } }, "type": "object", "required": [ "loc", "msg", "type" ], "title": "ValidationError" }, "type": "array", "title": "Detail" } }, "type": "object", "title": "HTTPValidationError" } ``

get_user_context

ChatGPT
Return the authenticated Tembi user's identity, permissions, and market scope Resolves the caller's Auth0 JWT to their Tembi user record and returns the identity, permissions, and market/provider scope needed to parameterise the other tools. Market scope: 'country_code' is the user's default/locale market, applied only when a tool call omits one — it is not a restriction. 'market_intelligence_markets' lists the markets the user may actually query. Responses: 200: Successful Response (Success Response) Content-Type: application/json Example Response: ``json { "user_id": "User Id", "is_admin": true, "custom_only": true, "modules": [ "string" ], "products_access": [ "string" ], "market_intelligence_markets": [ "string" ], "timestamp": "2023-01-01T00:00:00Z" } ` **Output Schema:** `json { "properties": { "user_id": { "type": "string", "title": "User Id", "description": "Tembi user ID (Auth0 subject)." }, "email": { "title": "Email", "description": "User's email address." }, "user_name": { "title": "User Name", "description": "Display name as stored in Tembi." }, "language": { "title": "Language", "description": "Preferred language code." }, "country_code": { "title": "Country Code", "description": "The user's default/locale market (ISO 3166-1 alpha-2, e.g. 'DK'). A convenience default applied when a tool call omits a market \u2014 not a restriction. The markets the user may query are in 'market_intelligence_markets'." }, "currency": { "title": "Currency", "description": "User's preferred currency code (e.g. 'DKK')." }, "providers": { "title": "Providers", "description": "Comma-separated delivery provider name(s) (e.g. 'gls' or 'gls,Inpost')." }, "delivery_providers": { "title": "Delivery Providers", "description": "Delivery provider names as a list (e.g. ['gls'])." }, "is_admin": { "type": "boolean", "title": "Is Admin", "description": "Whether the user has admin privileges.", "default": false }, "custom_only": { "type": "boolean", "title": "Custom Only", "description": "When true, the user only has access to their own customer's data.", "default": false }, "modules": { "items": { "type": "string" }, "type": "array", "title": "Modules", "description": "Permission modules the user has access to." }, "products_access": { "items": { "type": "string" }, "type": "array", "title": "Products Access", "description": "Products the user has been granted access to." }, "market_intelligence_markets": { "items": { "type": "string" }, "type": "array", "title": "Market Intelligence Markets", "description": "Markets (ISO country codes) this user may run the market-intelligence tools for. Empty = no explicit restriction (e.g. internal users may query any market)." }, "access_markets": { "title": "Access Markets", "description": "Markets (ISO country codes) this user has access to across Tembi products. None / empty = no explicit restriction (e.g. internal users)." }, "countries": { "title": "Countries", "description": "Countries/markets the customer organisation has access to." }, "domain_business_model": { "title": "Domain Business Model", "description": "Business model filter(s) active for this user (e.g. ['b2c'])." }, "customer": { "description": "Customer/organisation metadata (when requested)." }, "timestamp": { "type": "string", "format": "date-time", "title": "Timestamp", "description": "Server-side UTC timestamp of the invocation." } }, "type": "object", "required": [ "user_id", "timestamp" ], "title": "UserContextResponse", "description": "Response from the *get_user_context* tool.\n\nScoped to what an agent needs to parameterise the other tools \u2014 identity,\npermissions, and market/provider scope." } ` **422**: Validation Error Content-Type: application/json **Example Response:** `json { "detail": [ { "loc": [], "msg": "Message", "type": "Error Type" } ] } ` **Output Schema:** ``json { "properties": { "detail": { "items": { "properties": { "loc": { "items": {}, "type": "array", "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" } …

list_lead_options

ChatGPT
The vocabulary behind a selection: owners, tags, lists or statuses. Names and ids together.

list_reports

ChatGPT
List the reports visible to the calling user Lists every report visible to the authenticated user (own customer scope). Start here to find a report `id before fetching it with get_report_content or revising it via push_report's report_id. Each entry: id; title; status (draft = unpublished, or published = signed off and customer-visible); markets (ISO country codes, e.g. ["DK","SE"]); data_as_of (when the underlying figures were sourced); generated_on (when this version was rendered/pushed). One row per report, showing its latest version. Responses: **200**: Successful Response (Success Response) Content-Type: application/json **Example Response:** `json { "reports": [ { "id": "Id", "title": "Title", "markets": [ "string" ] } ], "summary_for_llm": "Summary For Llm" } ` **Output Schema:** `json { "properties": { "reports": { "items": { "properties": { "id": { "type": "string", "title": "Id" }, "title": { "type": "string", "title": "Title" }, "status": { "title": "Status" }, "markets": { "items": { "type": "string" }, "type": "array", "title": "Markets", "default": [] }, "data_as_of": { "title": "Data As Of" }, "generated_on": { "title": "Generated On" } }, "type": "object", "required": [ "id", "title" ], "title": "ReportSummary" }, "type": "array", "title": "Reports", "default": [] }, "summary_for_llm": { "type": "string", "title": "Summary For Llm" } }, "type": "object", "required": [ "summary_for_llm" ], "title": "ListReportsResponse" } ``

list_webshops

ChatGPT
List webshops matching flexible segment + checkout filters Returns webshops matching any combination of the supported filters, drawn from the tracked webshop population — webshops active in each market's own latest data month. Webshops no longer tracked are excluded; for wins and losses over time use `list_won_and_lost_webshops. Every filter is optional and AND-combined; each returned row carries the attributes the filter probed so the caller can quote concrete match evidence without a second call. **Segment filters:** country_code, cross_border, size_categories / size_indication_min / size_indication_max, growth_categories, product_categories (main product category), export_markets, platforms (e-commerce framework), has_company_data (webshops with an identified linked company), uses_delivery_provider / uses_delivery_method (point-in-time membership in the latest month with data). **Negative filter:** exclude is a dict mapping any of those same dimension names to values to EXCLUDE — e.g. exclude={'uses_delivery_method': ['parcel_box']} combined with uses_delivery_provider=['postnord'] returns PostNord webshops that do NOT offer box delivery. Values are auto-resolved the same way as positive filters. **Checkout filters** (apply to each shop's latest checkout scrape day by default; a shop qualifies when ≥ 1 offer satisfies ALL provided predicates): max_checkout_position, min_delivery_price_eur / max_delivery_price_eur, delivery_time_contains (case-insensitive substring, e.g. ['same day','1 day']). When any checkout filter is used, matched_checkouts on each row lists the qualifying offer(s) — narrowed to the requested provider/method when those filters are also set. **Historical checkout mode:** pass date_from and/or date_to (YYYY-MM, e.g. date_from='2025-06', date_to='2026-01') to scope checkout filters over a date window instead of the latest snapshot. Each matched_checkouts entry then aggregates per (provider, method) across the window and carries earliest observations (previous_*), latest observations, months_present and a changed_fields list — so historical questions like 'shops where GLS's checkout position changed in Q4 2025' work. Add require_checkout_change=['position'] to restrict to shops with an actual change in that dimension. The population anchor is unchanged in historical mode: matches are drawn from the currently tracked population, so shops that have since left the market are not included. **Delivery provider table routing:** uses_delivery_provider checks webshop_delivery_provider_history by default (broad, fast). Set use_checkout_history=true to verify membership through webshop_checkout_history instead — use this when the question is specifically about checkout behaviour (e.g. 'shops where DHL is offered at checkout'). When uses_delivery_method is set, checkout history is always used automatically (it's the only table that carries method data). **Checkout scope (domestic vs export-market):** checkout_scope controls which checkout fact the checkout-level filters + matched_checkouts output read from. 'domestic' (default) uses each webshop's own-market checkout (monthly history — supports date_from/date_to and require_checkout_change). 'export_market' uses each webshop's export-market checkout (webshop_checkout_export_market_info, a single undated snapshot — date_from/date_to and require_checkout_change are rejected). Combine with target_country to pin the destination lane, e.g. country_code='DK' + checkout_scope='export_market' + target_country='DE' for DK exporters' DE checkouts. Under export scope the uses_delivery_provider / uses_delivery_method filters and the returned offers all reference the export fact. This scope is premium — requires the checkout:export-markets module; if the caller lacks it, the request downgrades to domestic and access_denied` names the dropped export inputs. **Filter auto-res…

list_won_and_lost_webshops

ChatGPT
List webshops the caller's delivery provider won or lost (read-only) Read-only book-of-business movement: which webshops did a delivery provider win or lose across a range of months on checkout? Provider — `delivery_provider (optional; default: the caller's own from UserContext.delivery_providers). Set it to any provider name (e.g. 'gls' or 'Inpost') for competitor analysis. Country scope is still enforced via UserContext.access_markets and defaults to the user's country code. Source scope — checkout only (webshop_checkout_history is the ONLY table read anywhere in the SQL). webshop_delivery_provider_history is intentionally never touched. detected_in is always 'checkout' today. Per-month scan (contract) — the tool WALKS every month M in [date_from, date_to] (inclusive) and returns monthly_results: list[MonthlyResult] — one entry per month with month, wins_count, lost_count (unpaged), and wins / lost event lists (this page's slice for that month). Every month in the range appears, even quiet ones. Won criterion — provider present at M AND absent for each of M-1..M-6 (fixed 6-month lookback). Competitor attribution: competing_providers = carriers present at the shop in the trailing activity_months window before M but NOT at the shop in M itself. (These are the providers WE WON AGAINST for that month.) Lost criterion — volatility-gated absence with transition-month semantics (a persistent absence fires exactly one event): * low volatility → present at M-1, absent at M * medium volatility → present at M-2, absent at M-1 and M * high volatility → present at M-3, absent at M-2, M-1, M Volatility is derived per webshop from the flip count over activity_months ending immediately before date_from. Competitor attribution: competing_providers = carriers present at the shop somewhere in the range that were absent from that activity_months window before date_from. (These are the providers WE LOST TO.) NAMED-COMPETITOR LOSS GATE — a lost event only fires when the shop has at least one NAMED delivery provider (not 'unknown', not 'collect_yourself') at change_month. Loss months carrying only 'unknown' / 'collect_yourself' rows are dropped — the shop did not actually move to a competitor. Applied regardless of include_unknown_providers. TIMING — change_month is when the loss was CONFIRMED, not when the provider actually left. Medium/high-volatility losses fire only after 2-3 consecutive absent months, so change_month sits 1-2 months later than the actual departure — and a departure in the last 1-2 months of the range is not reported at all yet, because its confirming absent months fall outside the range. FULL-HISTORY GUARD — both wins and lost require that the webshop was scraped (any provider) in EVERY one of the previous max(activity_months, 6) months before M. Shops with any gap in that coverage window are dropped from every count and event list — a missing scrape is data non-availability, not provider absence. Dates — both default: date_to = checkout's latest data month; date_from = date_to − 3 months. Equal endpoints are allowed (single-month scan). date_from > date_to short-circuits to access_denied. Country scope — country_codes must sit inside UserContext.access_markets; omit for all accessible markets. Direction flags — include_wins and include_lost gate the per-month wins / lost event lists ONLY. Range-wide counts and per-month wins_count / lost_count always populate. Pagination — limit (default 100, max 1000) and offset (default 0) apply per direction across all months, then the paged events are distributed into monthly_results. Per-month wins_count / lost_count remain unpaged. pagination.has_more_* says whether another page exists. Totals in counts — DISTINCT webshops across the range (a shop won in two different months counts once in wins_total`). Per-month numbers are naturally unique — one webshop can only produce one win event…

lookup_webshops

ChatGPT
Look up a batch of specific webshops with month-by-month time-series Take a caller-supplied list of webshops — typically pasted into the chat client or extracted from an uploaded Excel file — and return a per-shop dossier with month-by-month time-series so the LLM can answer trend questions ('which of these did we lose', 'how has each shop's checkout position moved', 'did their delivery prices change over Q4', 'what platforms did each run last month'). Domains: each `domains entry is a full host with TLD ('acme.dk'), a ULID domain_id ('01G39F4E65QDY5ZT9MXGV9KNA9'), or a bare brand / company name without a TLD ('matas'). Hosts and ids are exact-matched. Bare brand names are never auto-resolved — they surface in unresolved_inputs with candidate_domains populated so the user can pick the exact host. Duplicate entries (host + id for the same shop) collapse into one dossier with every original value echoed under inputs. **Company numbers:** company_numbers is a parallel bulk list of registered company numbers (e.g. Danish CVR '12345678') matched case-insensitively against company_info.company_number. A single number can resolve to several webshops when one company owns multiple domains — each resulting dossier echoes the number under inputs. Numbers with no company in the caller's entitled markets surface in unresolved_inputs with reason='not_found'. Either domains or company_numbers must be non-empty; both may be provided together. **Market gating:** country_code restricts the batch to the given market(s), bounded by the caller's entitlement. Webshops resolved but sitting outside the entitled markets land in unresolved_inputs with reason='excluded_by_market' and their country_code so the caller can request access. **Time window:** date_from and date_to (YYYY-MM). When both are omitted, every activity-derived block anchors on its own source table's latest data month — the fastest 'current state' view. When either bound is set, the block returns one row per (month, dimension) inside the requested range so the caller can chart the trend. To ask for a single month explicitly (e.g. 'what did each shop show at checkout in October'), set date_from and date_to to the same value. Month granularity — day part is ignored. **Time-series blocks** (one row per (month, dimension key) inside the window): providers_by_month (combined checkout + T&Cs provider view — no per-method breakdown), pricing_by_month (per (month, method): median / avg / min / max price + n_rows), delivery_times_by_month (per (month, method): modal label + distinct-count), platforms_by_month (per month), products_by_month (per month product_count). checkout_history is the raw-observation stream — one row per scrape day with provider, method, checkout_position, delivery_price_eur and delivery_time — bounded by the same window. **Point-in-time blocks** (unaffected by the window): identity, company, size_indication/growth_category, main_product_category, export, activity (first_seen / last_seen / total months active), financials (annual). The inventory_snapshot block is the top brands / categories / dimensions taken from the latest month IN the window — a snapshot rather than a monthly series so the response stays bounded. **Block toggles:** every non-identity block can be turned off (include_company, include_size_growth, include_financials, include_activity, include_platform, include_main_category, include_export, include_providers, include_checkout_history, include_pricing, include_delivery_times, include_products). include_financials and include_products are off by default because they involve heavier joins — flip on when the caller's question needs them. **Provider / method exclusions:** exclude_providers and exclude_methods strip specific values from every checkout-related block. Case-insensitive. providers_by_month always drops 'unknown'` regar…

push_report

ChatGPT
Create or revise an HTML report (draft) in Market Intelligence Creates a new HTML report in the Market Intelligence → Reports section. Always lands as a draft — a human publisher signs it off before any customer sees it. There is no publish-from-MCP path. Visibility is locked to your own customer account — no other customer, market, or module can see it until a human editor widens visibility in the UI. `data_as_of is optional — omit to default to the current UTC time. By default each push creates a new report keyed by a server-assigned id. To revise an existing report instead, pass its report_id — the push lands as a new draft version (release) on that report rather than a new one. Always a draft; a publisher still signs off the new version. **Provenance is mandatory:** every number in the HTML must be a fig:token placeholder (token wrapped in double curly braces) bound in figures to a value sourced from the Market Intelligence tools. HTML with bare numbers in prose is rejected unless allow_inline_numbers is set. **Restyle stamp required:** the HTML must carry a valid tembi-restyle stamp (an HTML meta tag the tembi-report skill injects) or the push is rejected with 422. The check is presence-only — the server does not recompute the hash. Run the skill on the HTML **before the first push, not only after edits** — freshly authored HTML is unstamped and will be rejected. Stamp after your last content edit so your own local validate stays meaningful. A fetched clone keeps a stamp that still passes, so the bare-number guard is the only check on baked-in literals. **Confidentiality:** reports are company-scoped — never public/global. **Too large for one call?** If the HTML would not fit in a single response, send it in several calls instead of shortening the report. First call: the opening of the document (doctype, head, style, and the first section) with is_final=false — the response returns an chunk_upload_id. Each later call: the next section, with that chunk_upload_id, seq set to the next_seq you were given, and is_final=false. Last call: the closing section with is_final=true — the server assembles the document and creates the draft. Chunks are joined with **no separator**: concatenating the html strings you send, in order, must reproduce the stamped file byte-for-byte — end each non-final chunk with its trailing newline (\n). Cut at section boundaries, never inside a tag or a <script>; bind the fig:token placeholder values in the figures of whichever call uses them. title, report_id, ai_summary, data_as_of and allow_inline_numbers are read from the first call and ignored afterwards. Finish and **stamp the whole report first**, then split the stamped file — chunking is only a transfer mechanism, not a way to author in pieces. The restyle stamp travels in whichever section carries the <head>, and the assembled document is checked for it exactly as a single-call push would be. Sections are not validated for style individually. Responses: **200**: Successful Response (Success Response) Content-Type: application/json **Example Response:** `json { "id": "Id", "release_id": "Release Id", "content_url": "Content Url", "inline_number_warnings": [ "string" ], "chunk_upload_id": "Chunk Upload Id", "summary_for_llm": "Summary For Llm" } ` **Output Schema:** ``json { "properties": { "id": { "type": "string", "title": "Id", "default": "" }, "release_id": { "type": "string", "title": "Release Id", "default": "" }, "content_url": { "type": "string", "title": "Content Url", "default": "" }, "inline_number_warnings": { "items": { "type": "string" }, "type": "array", "title": "Inline Number Warnings", "default": [] }, "chunk_upload_id": { "type": "string", "title": "Chunk Upload Id", "description": "Set on a non-final chunk; pass it back on the next call.", "default": "" }, "next_seq": { "title": "Next Seq", "description": "The seq the next call must use." }, "chars_total": { "title": "Chars Total", "description": "Characters of HTML accumulated…

rename_tag

ChatGPT
Renames one tag. Assignments are untouched. Renaming a tag to the name it already has succeeds and changes nothing. Refused with TAG_NAME_TAKEN if another tag already has the new name.

summarize_domain

ChatGPT
Summarise everything Tembi knows about a webshop / online retailer (read-only) Read-only per-domain summary of a webshop. Input: `domain_id (ULID) or domain (full host or bare brand). Unresolved input returns resolved=false with candidate_domains. Scope the include_* flags to the user's question — don't ask them to confirm first. Available block toggles: include_financials, include_pricing, include_products, include_size_growth, include_export, include_delivery, include_export_checkouts (each defaults true). Flip off the blocks that don't relate to the question (e.g. checkout question → drop financials, products, size_growth, export; financials question → drop delivery, pricing, products, export). Also tune activity_months and exclude_providers / exclude_methods from the question. Module gates: the domestic checkout blocks (delivery, pricing, delivery_times) require the checkout module; the export_checkouts block (per-destination-market checkout snapshot compared to the home market) requires the checkout:export-markets module. When the caller lacks a module, the relevant block returns with access_denied set — the widget renders a locked card and the narrative names the missing module. Answer from the returned structured blocks and summary_for_llm — quote the numbers, don't paraphrase. Widget UI is attached on every branch. On resolved=true + access_denied=false: give the answer, note settings_applied and that they're reconfigurable. On resolved=false: surface candidate_domains and wait. On access_denied=true: relay the denial. Country-scoped to UserContext.access_markets. Responses: **200**: Successful Response (Success Response) Content-Type: application/json **Example Response:** `json { "resolved": true, "access_denied": true, "candidate_domains": [ "string" ], "activity_window_months": 1, "settings_applied": { "activity_months": 1, "include_financials": true, "include_pricing": true, "include_products": true, "include_size_growth": true, "include_export": true, "include_delivery": true, "include_delivery_times": true, "include_export_checkouts": true, "top_n_brands": 1, "top_n_categories": 1, "top_n_checkout_history": 1, "excluded_providers": [ "string" ], "excluded_methods": [ "string" ], "all_defaults": true }, "summary_for_llm": "Summary For Llm", "timestamp": "2023-01-01T00:00:00Z" } ` **Output Schema:** `json { "properties": { "resolved": { "type": "boolean", "title": "Resolved", "description": "False when the requested domain has no data. Structured blocks are then omitted and 'candidate_domains' lists real hosts the caller can retry with.", "default": true }, "access_denied": { "type": "boolean", "title": "Access Denied", "description": "True when the webshop was found but its country_code is outside the caller's UserContext.countries access list. Fact blocks are omitted; identity is populated so the caller sees which market they need access to and summary_for_llm explains the denial.", "default": false }, "candidate_domains": { "items": { "type": "string" }, "type": "array", "title": "Candidate Domains", "description": "Closest domain host matches when the requested domain could not be resolved." }, "identity": {}, "company": {}, "financials": {}, "size": {}, "activity": {}, "platform": {}, "category": {}, "products": {}, "brands": {}, "product_categories": {}, "product_dimensions": {}, "delivery": {}, "pricing": {}, "delivery_times": {}, "export": {}, "export_checkouts": {}, "activity_window_months": { "type": "integer", "title": "Activity Window Months", "description": "Trailing window (months) used for the activity-derived facts." }, "settings_applied": { "description": "Effective settings the tool ran with, echoed back so the client can\nsurface them to the user and offer re-runs with different knobs.\n\nThe values here are what actually shaped the answer \u2014 after defaults were\napplied for anything the caller omitted, and after sentinel lists resolved\nfor `exclude_provide…

update_leads

ChatGPT
The single lead write: sets status and/or owner, adds/removes tags, adds/removes list members — all against one selection resolved once, so every field lands on the same webshops. The fields are applied in that order, and a retried addTags is safe. applied counts the webshops whose value actually changes, so a re-run of the same call reports zeros; matched stays the size of the selection. dryRun: true returns that same projection with nothing written. Needs at least one selection key and at least one field to change. Scoped to the current active market.

Capabilities

WritesInteractive

Example Prompts

Click any prompt to copy it.

App Stats

24

Tools

3

Prompts

ChatGPT

Platforms

Works with

ChatGPT

Data refreshed daily