@pickrate/attribution
v0.1.0
Published
Server-side agent-attribution events for Pickrate: touch, identify, convert.
Maintainers
Readme
@pickrate/attribution
Server-side agent attribution for Pickrate. Tell Pickrate when an AI agent drove a visitor who later converted — which agent, tied to the hard signals, connected to your Pick Rate.
Three events, one join:
touch— an agent-adjacent interaction on your surface (a?via=landing, an AI-referrer pageview, an MCP/WebMCP call to your endpoint, an AI-bot crawl).identify— bind an anonymousvisitorTokento 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.
Install
npm i @pickrate/attributionNode 18+ (uses global fetch). Get a secret key (sk_…) from your Pickrate tenant settings.
Use
import { createClient } from "@pickrate/attribution";
const pr = createClient({ secretKey: process.env.PICKRATE_SECRET_KEY });
// 1. 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, ua: req.headers["user-agent"] },
});
// 2. When the human signs in, bind the anonymous visitor to a stable identity.
await pr.identify({ visitorToken: cookies.pr_vt, email: user.email });
// 3. 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>).
Browser helper (optional)
For client-side funnels, drop the hosted helper on your pages with a publishable key (pk_):
<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 the 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("[email protected]"); // 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 (a secret key, above). 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": "[email protected]", "kind": "signup", "value": 0 }Send a single event, an array, or { "events": [ … ] } (max 100 per request). Response:
{ "accepted": 1, "rejected": 0 }Keys
sk_(secret) — server-side. Full ingest includingconvert. Keep it secret.pk_(publishable) — browser.touch/identifyonly (no money event), origin-allow-listed.
Get your data out
Attribution is most useful next to your existing data. Two ways out, both keyed to the identity you
sent — send your own userRef and every row joins 1:1 to your user table.
Webhook (push)
Register a destination 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.
Honesty
Every conversion is labeled by confidence: deterministic (a token chain or ?via= link),
ai-referrer (a known AI domain — real but commoditizing), or correlation (time + signal, 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.
