Tests and methodology
We test each surface (for example the web app or the iOS app) at each link location where a link can appear (inline citations, the sources panel, cards, deep research reports and more). Each test has a stable ID used in the data, on lab pages and in the prompts.
Statuses
- Works: verified passing.
- Partial: passes on some link locations, not others.
- Missing: verified not passing.
- Reported: from a public third-party report, not yet verified by us.
- Untested: no data yet.
- n/a: does not apply, for example link markup on a native app.
The status shown for a cell is the most recent result by date. On the same date, a verified result (manual, automated or community) takes precedence over a reported one. Every cell links to its evidence.
How to reproduce
- Ask the assistant to cite or link one of the target pages below. For a self-identifying link use
/t/<test>/<lab>/<surface>/<location>, for example/t/ref-01/openai-chatgpt/web-desktop/inline-citation. - Open the link from the answer.
- The page shows the server-side
Referer,document.referrer, the query string and the fragment. The visit is also logged (without IP addresses). - Record the result as described on Contribute.
Test catalog
REF-01: Referrer present
Pass criteria: Request to the target carries Referer with the assistant's origin (e.g. https://chatgpt.com/).
When a person clicks a link, the browser can send a Referer header that names the page they came from. Analytics tools use it to say where a visit came from. If an assistant sends its origin, such as https://chatgpt.com/, the website can count the visit as coming from that assistant instead of "Direct".
Scope: per surface and link location. Target: /t/ref-01
REF-02: No full-URL leak
Pass criteria: Referer is origin only; no conversation path or IDs.
The referrer should say which product sent the visit, not which conversation. Sending only the origin (https://claude.ai/) keeps conversation paths and IDs private while still giving websites the signal they need.
Scope: per surface and link location. Target: /t/ref-02
REF-03: Link markup (web only)
Pass criteria: Citation links use noopener without noreferrer; page/link referrer policy is origin or strict-origin-when-cross-origin.
On the web, rel="noreferrer" and window.open(url, "_blank", "noopener,noreferrer") remove the referrer. rel="noopener" alone keeps the security benefit and still lets the origin through. A referrer policy of origin or strict-origin-when-cross-origin sends the origin to other sites and never the full path.
Scope: per surface and link location, web surfaces only. Target: /t/ref-03
UTM-01: UTM present
Pass criteria: Landing URL contains utm_source=<documented value>.
Apps and webviews often cannot send a referrer. A utm_source parameter on the link still tells the website where the visit came from. It works in every browser and every app.
Scope: per surface and link location. Target: /t/utm-01
UTM-02: UTM consistent
Pass criteria: Same utm_source value on every surface and link location.
One stable value per product lets websites write one rule and keep it. Different values per app or per link type split the same traffic into several rows that teams then have to merge by hand.
Scope: once per lab. Target: /t/utm-02
UTM-03: UTM documented
Pass criteria: Value published in the lab's public docs/help.
A documented value turns a guess into a contract. Analytics vendors and site owners can add it to their channel rules with confidence, and they know where to look if it changes.
Scope: once per lab. Target: /t/utm-03
MRG-01: Keeps existing query
Pass criteria: Target ?a=1&b=2 arrives with a and b intact.
Many links already carry query parameters for search, filters or product variants. Adding attribution must keep every existing parameter and its value exactly as it was.
Scope: per surface and link location. Target: /t/mrg-01?a=1&b=2
MRG-02: Keeps fragment
Pass criteria: #section is preserved and the tag is inserted before it.
A fragment such as #section-2 points to a place on the page. Query parameters must go before the #; anything after it never reaches the server and breaks the jump to the right section.
Scope: per surface and link location. Target: /t/mrg-02#section-2
MRG-03: No overwrite
Pass criteria: Target with existing utm_source=newsletter keeps that value.
Some links already carry campaign tags set by the website itself. Replacing them would erase the site's own campaign data. The safe default is to leave existing utm_* parameters alone.
Scope: per surface and link location. Target: /t/mrg-03?utm_source=newsletter&utm_medium=email
MRG-04: Signed URLs untouched
Pass criteria: Target with signature/token/expires params arrives byte-identical.
Signed and expiring URLs (downloads, cloud storage, one-time links) stop working if a single byte changes. Links with parameters such as signature, token or expires should be passed through unchanged.
Scope: per surface and link location. Target: /t/mrg-04?expires=2099-01-01&signature=abc123
OPS-01: Changelog
Pass criteria: Lab publishes link-behavior changes (help page, release notes).
Link behavior changes with UI updates. A short public note when it changes lets websites tell a product change from a real drop in traffic, and saves both sides time.
Scope: once per lab. Target: /t/ops-01
Test vectors
Use these input and output pairs to test a link-tagging function. They match the reference implementation on best practices. Each one runs as a unit test in our build.
Source value example.ai. Machine-readable: lib/vectors.json.
- UTM-01 Adds the tag to a plain URL
in:https://example.com/page
out:https://example.com/page?utm_source=example.ai - UTM-01 Adds source and medium (medium
ai-assistant)
in:https://example.com/page
out:https://example.com/page?utm_source=example.ai&utm_medium=ai-assistant - MRG-01 Keeps existing query
in:https://example.com/p?a=1&b=2
out:https://example.com/p?a=1&b=2&utm_source=example.ai - MRG-01 Keeps existing encoding and valueless keys
in:https://example.com/s?q=a%20b&flag
out:https://example.com/s?q=a%20b&flag&utm_source=example.ai - MRG-02 Inserts before the fragment
in:https://example.com/doc#section-2
out:https://example.com/doc?utm_source=example.ai#section-2 - MRG-02 Query and fragment
in:https://example.com/doc?a=1#section-2
out:https://example.com/doc?a=1&utm_source=example.ai#section-2 - MRG-03 Leaves existing utm_source alone
in:https://example.com/p?utm_source=newsletter&utm_medium=email
out:https://example.com/p?utm_source=newsletter&utm_medium=email - MRG-03 Any utm_* key counts, any case
in:https://example.com/p?UTM_Campaign=fall
out:https://example.com/p?UTM_Campaign=fall - MRG-04 Signed URL untouched
in:https://example.com/f?expires=2099-01-01&signature=abc123
out:https://example.com/f?expires=2099-01-01&signature=abc123 - MRG-04 Cloud storage signature untouched
in:https://bucket.s3.amazonaws.com/k?X-Amz-Signature=abc&X-Amz-Expires=60
out:https://bucket.s3.amazonaws.com/k?X-Amz-Signature=abc&X-Amz-Expires=60 - UTM-01 Non-http links untouched
in:mailto:hello@example.com
out:mailto:hello@example.com - UTM-01 Invalid URL returned as is
in:not a url
out:not a url