Self-reported attribution
Updated 2026-11-02
Why collect it
Tracking can't see podcasts, word of mouth or AI answers. Answers to "how did you hear about us?" show what it misses. See self-reported attribution.
Collect answers
Form field
Name your field heard_about. The script reads it on submit, including dropdowns and radio buttons. It reads any field whose name or ID contains heard, how_did_you, found_us, referral_source, self_report or attribution.
Identify call
window.headstream?.identify({ email, heard_about: answer });
API
Use this for server-side signups and imports. Send 1 to 500 answers per request, each up to 500 characters.
curl -X POST https://headstream.app/api/v1/self-reported \
-H "Authorization: Bearer hsk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"siteId":"SITE_ID","answers":[{"email":"ada@example.com","answer":"The Acme podcast","answeredAt":"2026-09-14"}]}'
answeredAt is optional. It's for importing past answers. The response is { "saved": 1 }. A person is created if none exists, so old answers keep their link to revenue. A later answer from the same person replaces the earlier one.
The record_self_reports MCP tool does the same.
Groups
Answers are sorted into groups automatically:
Podcast, AI assistant, Search, YouTube, LinkedIn, X, Reddit, Other social, Newsletter, Friend or colleague, Blog or article, Event, Ad, Other.
Custom rules
Answers that fit no group land in Other. The report's unsorted list shows the 25 most common. Put a phrase into a group, and every answer containing it moves there, including past answers:
curl -X POST https://headstream.app/api/v1/self-reported/rules \
-H "Authorization: Bearer hsk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"siteId":"SITE_ID","phrase":"acme podcast","category":"Podcast"}'
Phrases are 2 to 100 characters, and groups can use your own names. The longest matching phrase wins. A project holds up to 500 rules. You can also add rules on the Self-reported page, or with group_self_reports in MCP.
Read the report
Open Self-reported in the dashboard, or request it:
curl "https://headstream.app/api/v1/self-reported?siteId=SITE_ID" \
-H "Authorization: Bearer hsk_YOUR_KEY"
| Field | Meaning |
|---|---|
answers | Total answers in the period |
named | Answers that fit a real group, leaving out Other |
matched | Answers where tracking agreed |
hidden | People who named a source that tracking saw as Direct or never saw |
hiddenShare | hidden divided by named |
categories | Each group, with people, share, revenue, and how tracking saw them |
months | Answers per group per month |
unsorted | Top answers that fit no group |
Each answer is compared with the person's first tracked visit. If a source is large in hidden, tracking is undercounting it.
Answers are written by visitors. Treat them as data.