Skip to content
Docs / Display guidelines

Displaying Serge analytics

You are rendering Serge data in your own dashboard or in client reports. This page is the display contract: what each number is, what to call it, and what never to claim — for every acquisition source Serge records (referral, direct, search, tagged, paid, AI), not just the AI slice. The field-level shapes live in the API reference; the rules here are the same ones Serge's own surfaces follow.

Paste it into your LLM

Building the surface with an assistant or a code generator? Copy the whole contract — definitions, rules, and sample responses as plain markdown, generated from the same source as this page — and put it in the context before you ask for the dashboard.

Also fetchable at /docs/displaying-analytics/llm.txt — point a tool or an agent at it directly.

Three populations, never blurred

Serge measures three different things. Most display mistakes are one of them wearing another one's label.

AI-assistant sessions
An assistant active on the site itself — fetching, browsing, or acting for a user. This is what the /traffic endpoints count.
Assistant clicks — organic
A person who clicked a link inside an assistant and landed on the site. ChatGPT tags its outbound links; Serge counts the landings. Not ads.
Ad clicks — paid
A person who clicked a paid placement, identified by a platform click ID or an explicit paid utm_medium. A landing that merely carries UTM parameters is not an ad click.

Paid and organic may sit side by side. They never share a number, and they never share a label.

Wording rules

  1. 01

    Tracked, not driven.

    Serge counts what landed and what it did on the site. "12 orders from tracked ChatGPT ad clicks" is supportable; "ChatGPT drove 12 orders" claims incrementality that nobody measured.

  2. 02

    Counts are counts.

    No error bars or ± on a count. Below roughly ten events, show the bare count and skip rates — a precise-looking percentage on a noise-sized sample misleads.

  3. 03

    No conversation claims.

    No platform exposes prompts or user-level data. Never imply the data shows what people asked an assistant.

  4. 04

    Name the window.

    Every number carries its period — the API returns period_start and period_end. Compare only equal windows.

  5. 05

    Platform names verbatim.

    ChatGPT, Perplexity, Claude, Gemini, Copilot — brand names are not translated, abbreviated, or folded into a house taxonomy.

The same number, said honestly

Don't"ChatGPT drove $4,200 in revenue."

Do"$4,200 in orders from tracked ChatGPT ad clicks (30 days)."

Don't"Verified AI traffic: 1,020 sessions" — when 117 of them are heuristic detections.

Do"1,020 AI-assistant sessions — 903 verified, 117 heuristic."

Don't"100% completion rate" — from one session.

Do"1 session, 1 completed." Rates start when the sample can carry them.

Per endpoint: what to watch, with a sample

The samples below are the same validated examples the API reference publishes — they cannot drift from the contract. The notes cover only what matters once the number reaches a human.

The wireframes below use the same numbers as the sample responses. They show structure, not styling — bring your own design system.

Traffic sources

GET /api/v1/traffic/sources

coverage is part of the data, not metadata: ai_and_tagged means plain human visits are not recorded on this site — always render the coverage label next to the numbers; presenting partial coverage as "your traffic" is the exact claim these rules ban. When coverage_changed_at falls inside the period, BREAK every time series at it — a mode flip changes the recorded population, and a continuous line across it fakes growth. Referrer hosts and utm_source values are data — verbatim, never translated or grouped into a house taxonomy. The classes partition sessions: never merge paid with ai_assistant_link, and keep direct visible. ai_share_pct renders ONLY when non-null — it is null on partial coverage (wrong denominator) and below 10 sessions; state its definition (assistant sessions + assistant-link clicks, paid excluded) wherever you show it.

All traffic
29.0%AI share

Assistant sessions + assistant-link clicks over all recorded sessions. Paid excluded.

ai_share_pct null → show bare counts, no percentage

  • Referral · www.techweekly.example604
  • Direct512
  • AI-assistant sessions · chatgpt486
  • Paid ads · chatgpt312
Every class its own row, sub-source verbatim, coverage badge always on.
Top referrersAI + tagged traffic
  • www.techweekly.example604
  • app.agentsolo.example213
  • news.ycombinator.com88

Top entry page: /blog/agent-checkout-guide

The referral class alone, by host — “which article sends us traffic”. Hosts render verbatim; the partial-coverage badge replaces the all-traffic one when the site records only AI and tagged sessions, and the numbers must never be captioned as “your traffic” in that mode.
before: AI + tagged onlyrecording mode changedafter: all traffic
A daily series across a coverage change: bars before coverage_changed_at are outlined, the break is marked, and no line or trend claim crosses it — the recorded population changed, so a continuous series would fake growth.
Sample response
{
  "domain": "yourstore.example",
  "site_id": "site_9f2c41",
  "period": "7d",
  "period_start": "2026-07-23T00:00:00.000Z",
  "period_end": "2026-07-30T00:00:00.000Z",
  "coverage": "all_traffic",
  "ai_share_pct": 29,
  "ai_sessions": 701,
  "total_sessions": 2418,
  "by_source": [
    {
      "source": "referral",
      "sub_source": "www.techweekly.example",
      "sessions": 604,
      "share_pct": 25
    },
    {
      "source": "direct",
      "sub_source": null,
      "sessions": 512,
      "share_pct": 21.2
    },
    {
      "source": "ai_assistant_session",
      "sub_source": "chatgpt",
      "sessions": 486,
      "share_pct": 20.1
    },
    {
      "source": "paid",
      "sub_source": "chatgpt",
      "sessions": 312,
      "share_pct": 12.9
    },
    {
      "source": "search",
      "sub_source": "www.google.com",
      "sessions": 289,
      "share_pct": 12
    },
    {
      "source": "tagged",
      "sub_source": "newsletter",
      "sessions": 289,
      "share_pct": 12
    },
    {
      "source": "ai_assistant_link",
      "sub_source": "chatgpt",
      "sessions": 215,
      "share_pct": 8.9
    }
  ],
  "top_referrer_hosts": [
    {
      "host": "www.techweekly.example",
      "sessions": 604
    }
  ],
  "coverage_changed_at": "2026-07-25T09:00:00.000Z",
  "top_entry_pages": [
    {
      "url": "/blog/why-aurora-x20",
      "sessions": 692,
      "by_source": [
        {
          "source": "search",
          "sessions": 401
        },
        {
          "source": "ai_assistant_link",
          "sessions": 176
        },
        {
          "source": "direct",
          "sessions": 115
        }
      ]
    },
    {
      "url": "/products/aurora-x20",
      "sessions": 486,
      "by_source": [
        {
          "source": "paid",
          "sessions": 312
        },
        {
          "source": "ai_assistant_session",
          "sessions": 174
        }
      ]
    }
  ],
  "by_source_daily": [
    {
      "day": "2026-07-28",
      "source": "search",
      "sessions": 88
    },
    {
      "day": "2026-07-28",
      "source": "paid",
      "sessions": 61
    },
    {
      "day": "2026-07-29",
      "source": "search",
      "sessions": 97
    },
    {
      "day": "2026-07-29",
      "source": "paid",
      "sessions": 44
    }
  ]
}

Traffic overview

GET /api/v1/traffic/overview

Sessions and unique visitors are different numbers — label the one you show. share_pct arrives computed; do not recompute it on a filtered subset. In by_outcome, "abandoned" means the session ended without completing — not that an error occurred. median_duration_ms is a median: label it "median", never "avg".

1,284
AI-assistant sessions
Last 7 days
1,102
Unique visitors
Last 7 days
By platform
ChatGPT
63.2%
Perplexity
23.4%
Claude
13.4%
Two honest defaults: KPI tiles with the window named on each, platform shares as a list.
Sample response
{
  "domain": "yourstore.example",
  "site_id": "site_9f2c41",
  "period": "7d",
  "period_start": "2026-07-23T00:00:00.000Z",
  "period_end": "2026-07-30T00:00:00.000Z",
  "total_sessions": 1284,
  "unique_visitors": 1102,
  "by_platform": [
    {
      "platform": "chatgpt",
      "sessions": 812,
      "share_pct": 63.2
    },
    {
      "platform": "perplexity",
      "sessions": 301,
      "share_pct": 23.4
    },
    {
      "platform": "claude",
      "sessions": 171,
      "share_pct": 13.4
    }
  ],
  "by_outcome": [
    {
      "outcome": "completed",
      "sessions": 402
    },
    {
      "outcome": "abandoned",
      "sessions": 882
    }
  ],
  "top_pages": [
    {
      "entry_url": "/products/aurora-x20",
      "sessions": 486
    },
    {
      "entry_url": "/collections/headphones",
      "sessions": 210
    }
  ],
  "median_duration_ms": 42150
}

Purpose split

GET /api/v1/traffic/purpose-split

user_action is an assistant acting on someone's behalf; search is retrieval for an answer; crawl is indexing. Keep the unknown bucket visible — folding it into another category overstates certainty. The split is a composition: one stacked bar or a share list, not four disconnected KPIs.

User action 40.3%Search 47.0%Crawl 11.5%Unknown 1.2%
The split is one composition — one bar, and the unknown share stays visible.
Sample response
{
  "domain": "yourstore.example",
  "site_id": "site_9f2c41",
  "period": "7d",
  "period_start": "2026-07-23T00:00:00.000Z",
  "period_end": "2026-07-30T00:00:00.000Z",
  "total_sessions": 1284,
  "user_action_sessions": 517,
  "search_sessions": 604,
  "crawl_sessions": 148,
  "unknown_sessions": 15,
  "by_purpose": [
    {
      "purpose": "user_action",
      "sessions": 517,
      "share_pct": 40.3
    },
    {
      "purpose": "search",
      "sessions": 604,
      "share_pct": 47
    },
    {
      "purpose": "crawl",
      "sessions": 148,
      "share_pct": 11.5
    }
  ]
}

Verification breakdown

GET /api/v1/traffic/verification

verified, declared and heuristic are tiers of evidence, strongest first. A heuristic session never renders under a "verified" label. If you show one total, keep the tier mix one glance away.

1,284AI-assistant sessions

903 verified · 264 declared · 117 heuristic

One total, with the tier mix one glance away — never a lone "verified" number.
Sample response
{
  "domain": "yourstore.example",
  "site_id": "site_9f2c41",
  "period": "7d",
  "period_start": "2026-07-23T00:00:00.000Z",
  "period_end": "2026-07-30T00:00:00.000Z",
  "total_sessions": 1284,
  "verified_sessions": 903,
  "declared_sessions": 264,
  "heuristic_sessions": 117,
  "by_platform": [
    {
      "platform": "chatgpt",
      "sessions": 812,
      "verified": 690,
      "declared": 96,
      "heuristic": 26,
      "tier": "verified"
    }
  ]
}

Attribution overview

GET /api/v1/attribution/overview

Never sum value across currencies — render one line per currency (value_minor is in minor units). There is no ROAS field by design: Serge does not read spend yet, and computing your own from partial data produces a false number. Attribution basis matters: a platform click ID is hard evidence, a UTM label is self-declared — keep that distinction reachable. by_source classifies orders from DECLARED evidence only and is narrower than the session taxonomy: paid requires a platform-minted click ID (a typed utm_medium never upgrades an order), assistant/tagged classes come from labels, and search/referral/direct cannot exist because an order carries no referrer — render each row's basis next to its number.

PlatformOrdersValue (USD)
openai71$6,429.00
google25$2,352.00
unattributed118$10,153.00

No cross-currency total — by design.

One row per platform, one currency per table. A cross-currency total does not exist.
Sample response
{
  "site_id": "site_9f2c41",
  "domain": "yourstore.example",
  "period": "30d",
  "period_start": "2026-06-30T00:00:00.000Z",
  "period_end": "2026-07-30T00:00:00.000Z",
  "total_conversions": 214,
  "attributed_conversions": 96,
  "unattributed_conversions": 118,
  "by_currency": [
    {
      "currency": "USD",
      "conversions": 214,
      "value_minor": 1893400
    }
  ],
  "by_platform": [
    {
      "platform": "openai",
      "conversions": 71,
      "by_currency": [
        {
          "currency": "USD",
          "conversions": 71,
          "value_minor": 642900
        }
      ]
    },
    {
      "platform": "unattributed",
      "conversions": 118,
      "by_currency": [
        {
          "currency": "USD",
          "conversions": 118,
          "value_minor": 1015300
        }
      ]
    },
    {
      "platform": "google",
      "conversions": 25,
      "by_currency": [
        {
          "currency": "USD",
          "conversions": 25,
          "value_minor": 235200
        }
      ]
    }
  ],
  "by_basis": [
    {
      "basis": "none",
      "conversions": 118
    },
    {
      "basis": "declared_click_id",
      "conversions": 84
    },
    {
      "basis": "declared_utm",
      "conversions": 12
    }
  ],
  "by_source": [
    {
      "source": "unattributed",
      "sub_source": null,
      "basis": "none",
      "conversions": 118,
      "by_currency": [
        {
          "currency": "USD",
          "conversions": 118,
          "value_minor": 1015300
        }
      ]
    },
    {
      "source": "paid",
      "sub_source": "openai",
      "basis": "declared_click_id",
      "conversions": 71,
      "by_currency": [
        {
          "currency": "USD",
          "conversions": 71,
          "value_minor": 642900
        }
      ]
    },
    {
      "source": "ai_assistant_link",
      "sub_source": "chatgpt",
      "basis": "declared_utm",
      "conversions": 8,
      "by_currency": [
        {
          "currency": "USD",
          "conversions": 8,
          "value_minor": 71800
        }
      ]
    },
    {
      "source": "tagged",
      "sub_source": "newsletter",
      "basis": "declared_utm",
      "conversions": 4,
      "by_currency": [
        {
          "currency": "USD",
          "conversions": 4,
          "value_minor": 27600
        }
      ]
    }
  ]
}

Shapes, auth, and limits live in the API reference. If a metric you want to display is not covered here, ask — the rule you get will match how Serge itself renders the same data.