Agent Attribution
Tell Pickrate when an AI agent drove a visitor who later converted — which agent, tied to the hard signals, connected to your Pick Rate. This is the bottom of your Agent Funnel.
The model: three events, one join
Attribution is three events that chain through a visitor and an identity:
- touch — an agent-adjacent interaction on your surface: a
?via=landing, an AI-referrer pageview, an MCP/WebMCP call, an AI-bot crawl. - identify — bind an anonymous
visitorTokento a stable identity when the human authenticates. This is the bridge. - convert — the money event (lead / signup / paid), carrying identity so we can walk back to the first touch.
touch(visitorToken) → identify(visitorToken → user) → convert(user)First touch wins: the earliest touch we can tie to the converting identity gets the credit.
1. Get a key
Mint keys in Settings → Agent Attribution keys (you'll need a verified company claim). Two kinds:
sk_(secret) — server-side. Full ingest includingconvert. Keep it secret.pk_(publishable) — browser.touch/identifyonly, origin-allow-listed.
2. Install the SDK
npm i @pickrate/attributionNode 18+ (uses global fetch). Zero dependencies.
import { createClient } from "@pickrate/attribution";
const pr = createClient({ secretKey: process.env.PICKRATE_SECRET_KEY });
// An agent-adjacent touch (server-side). Pass whatever signals you have.
await pr.touch({
visitorToken: cookies.pr_vt, // your first-party browser id
signals: { via: "mcp", agent: "Claude", referrer: req.headers.referer },
});
// When the human signs in, bind the anonymous visitor to a stable identity.
await pr.identify({ visitorToken: cookies.pr_vt, email: user.email });
// The conversion.
await pr.convert({ email: user.email, kind: "signup", value: 0 });Identity
Send email and we hash it on ingest — the raw email is never stored. Or send your own opaque userRef (a stable user id). Either works; email-hash is the default. If you'd rather hash yourself, send a userRef you've already prefixed (eh:<sha256>). Sending your own userRef is what makes every exported row join 1:1 to your user table.
3. Browser helper (optional)
For client-side funnels, drop the hosted helper on your pages with a publishable key:
<script src="https://pickrate.io/pr.js" data-key="pk_live_…" async></script>It mints a first-party pr_vt visitor cookie, fires a touch automatically when a visitor lands with a ?via= tag or an AI referrer, and exposes:
// Bridge the anonymous visitor to a known user the moment they sign in.
window.pickrate.identify("user@acme.com"); // or { userRef: "your-user-id" }
window.pickrate.touch({ via: "mcp" }); // record a touch manuallyA publishable key can only send touch / identify — the convert money event is server-side only. That keeps a key that ships in your HTML from forging revenue.
Raw HTTP
The SDK is sugar over one endpoint. Any language can speak it:
POST https://pickrate.io/api/collect
Authorization: Bearer sk_live_…
Content-Type: application/json
{ "type": "convert", "email": "user@acme.com", "kind": "signup", "value": 0 }Send a single event, an array, or { "events": [ … ] } (max 100 per request). Response: { "accepted": 1, "rejected": 0 }. A publishable key that tries to send convert is rejected.
Get your data out
Attribution is most useful next to your existing data. Two ways out, both keyed to the identity you sent.
Webhook (push)
Register a destination in Settings and we POST each attributed conversion the moment it resolves:
{
"type": "attribution.convert",
"tenant": "your-slug",
"userRef": "id:cust-777",
"kind": "paid",
"convertedAt": "2026-06-28T09:02:00.000Z",
"value": 150,
"channel": "agent",
"confidence": "confirmed",
"agent": "ChatGPT",
"firstTouchAt": "2026-06-28T09:00:00.000Z"
}Every request carries X-Pickrate-Signature: sha256=<hmac>. Verify it with your signing secret:
import { createHmac, timingSafeEqual } from "crypto";
function verify(rawBody, header, secret) {
const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
return timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}Point it anywhere — your backend, a queue, or a Zapier/Make webhook to fan it into other tools.
Export API (pull)
GET https://pickrate.io/api/attribution/export?type=attributions&format=csv
Authorization: Bearer sk_live_…type=attributions(default, one row per conversion) orevents(the raw log)format=json(default) orcsvsince=<ISO>andlimit=<n>to window it
Secret key only. Built for warehouse loads, reverse-ETL, and BI.
Confidence & honesty
Every conversion is labeled by how sure we are an agent drove it. We lead with what we can prove:
- Confirmed — a token chain or a
?via=link an agent handed over. Highest certainty. - Likely — arrived from a known AI assistant's domain. Real, but commoditizing.
- Inferred — self-report or timing lines up. Never claimed as causation.
A cold server-side agent read with no link and no token can't be tied to a later human — that edge is dropped, not guessed.
Questions
Do you store my users' email addresses?
No. Send an email and we hash it (SHA-256) at ingest — the raw email is never stored. Or send your own opaque userRef instead. Either works; email-hash is the default.
How is this different from GA4 or a brand-monitoring tool?
GA4's new AI channel and brand tools tell you an AI assistant referred a session. We tie a specific agent to a specific conversion through a deterministic link or identity bridge, label it by confidence, and connect it to your Pick Rate — selection and revenue in one place.
What's the difference between a publishable and a secret key?
A secret key (sk_) is server-side and can send every event, including the convert money event. A publishable key (pk_) ships in your browser HTML and can only send touch and identify — never convert — so a key that's visible in your page source can't forge revenue.
What happens to a touch that never connects to a person?
It's dropped, not guessed. A cold server-side agent read with no link and no identity bridge can't honestly be tied to a later human, so we don't. We only credit conversions we can actually walk back to a first touch.
Can I get the data into my warehouse?
Yes — two ways, both keyed to the identity you sent. A signed webhook pushes each attributed conversion as it resolves, and the export API pulls JSON or CSV for warehouse loads, reverse-ETL, and BI.