Sessions outside the browser we track
Event: a session happens somewhere the web SDK is not — an authenticated
portal, an AI agent’s conversation — and, inside it, goals are reached.
Endpoints: POST /v1/session; goals through POST /v1/collect
Status: /v1/session live in dev
Who sends this. Stitch does not know and does not need to. It sees a key,
which names the system the owner issued it to, and a payload. Today the
likeliest emitters are the borrower portal’s backend for portal sessions and
the MagicBlocks agent platform for ai_agent sessions; a middleware in front
of either sends exactly the same thing.
Authentication
Section titled “Authentication”Authorization: Bearer <your key>One key per emitting system, named by the owner after whatever actually sends;
401 {"error":"unauthorized"} without it, and nothing written. The system a
key belongs to is recorded on every row it creates as source_system.
/v1/collect takes no key; it is gated by the page’s origin.
1. A session
Section titled “1. A session”curl -X POST https://stitch-collector-dev.magic-blocks.workers.dev/v1/session \ -H "content-type: application/json" \ -H "authorization: Bearer <your key>" \ -d '{ "session_type": "ai_agent", "external_session_ref": "sess-77120", "octopus_id": "88213", "anon_id": "3f1a8c2e-9b4d-4a71-8e2f-0c5d6a7b8e91", "started_at": 1772539200000, "ended_at": 1772539980000, "ai_channel": "web_chat", "ai_direction": "inbound", "ai_agent_id": "agent-dscr-qualifier", "ai_agent_version": "v3.2" }'{ "session_id": "8c1d…" }session_type is portal or ai_agent; the ai_* fields belong to the
second. A portal session is the same shape without them:
{ "session_type": "portal", "external_session_ref": "portal-sess-99213", "octopus_id": "88213", "anon_id": "3f1a8c2e-9b4d-4a71-8e2f-0c5d6a7b8e91", "started_at": 1772539200000, "ended_at": 1772539860000 }external_session_ref is the idempotency key: re-sending returns the
id of the session that already exists rather than creating a second one, so
you can emit on both session start and session end with the same ref. Unlike
/v1/collect, a malformed payload here is a loud 400 — a silent success
would hide a broken integration for weeks.
Carry the anon_id when a session starts on a web page
Section titled “Carry the anon_id when a session starts on a web page”When a session begins on one of our pages — a chat widget, a sign-in on the
app host — pass the page’s anon_id into the session. A chat that ends in a
lead then carries the visitor’s full referral history with it. Without it,
the session is an island. For an authenticated session, also send
identity.md’s /v1/identify: that is what binds the browsing
history to the person for good.
2. Goal events
Section titled “2. Goal events”Goals are ordinary events carrying a session_id and goal_id. There is no
special machinery — a goal is just a named thing that happened inside a
session.
curl -X POST https://stitch-collector-dev.magic-blocks.workers.dev/v1/collect \ -H "content-type: application/json" \ -d '[{ "event_id": "goal-99123", "ts": 1772539900000, "name": "goal_completed", "url": "https://makeabeeline.com/", "referrer": null, "props": { "session_id": "sess-77120", "goal_id": "qualified_dscr", "agent_version": "v3.2" } }]'3. What Stitch does with these sessions
Section titled “3. What Stitch does with these sessions”They convert; they do not acquire. Portal and AI sessions are hard-classed
touch_class: "nurture" and can never take acquisition credit, whatever the
payload says ("touch_class": "discovery" is ignored rather than rejected).
They are by definition post-acquisition: the agent harvests and converts
demand a channel already produced. If these sessions could take credit, the
agent would progressively cannibalise credit from every real channel, and
the better it performed the more it would distort the picture — the same
logic that excludes brand search.
Portal sessions earn their place twice. They are authenticated, so every one is a free tier-1 identity confirmation (recorded at certainty A). And portal activity is a well-known pull-through predictor — people with three or more portal sessions tend to fund at a different rate, which makes it an early ops signal rather than only a marketing one.
4. How an agent’s contribution is measured instead: lift, not credit
Section titled “4. How an agent’s contribution is measured instead: lift, not credit”| Measure | What it needs from the emitter |
|---|---|
| Holdout lift | A configurable % of leads routed to human-only or delayed-agent handling. Even 5% gives a usable read within a quarter |
| AI-touched vs not, by acquisition channel | Nothing extra — falls out of the session records |
| Response latency curve | Accurate started_at, to the second |
| Identification rate | % of anonymous sessions promoted to known persons via an agent session. An attribution-coverage lever disguised as a product metric — and the one where the agent most clearly earns its place |
| Goal completion by agent version | ai_agent_version and any experiment variant in props |
Open question for whoever owns the agent: what holdout percentage is acceptable without hurting the business? It is the only honest measure of incremental contribution, and 5% is enough.
5. Smoke test
Section titled “5. Smoke test”STAMP=$(date +%s)curl -sS -X POST https://stitch-collector-dev.magic-blocks.workers.dev/v1/session \ -H "content-type: application/json" \ -H "authorization: Bearer <your key>" \ -d "{\"session_type\":\"portal\",\"external_session_ref\":\"smoke-portal-$STAMP\",\"octopus_id\":\"smoke-$STAMP\",\"started_at\":$(date +%s)000}"Expect 202 and a session_id. Re-send the same external_session_ref and
expect the same session_id back. And an agent session:
curl -sS -X POST https://stitch-collector-dev.magic-blocks.workers.dev/v1/session \ -H "content-type: application/json" \ -H "authorization: Bearer <your key>" \ -d "{\"session_type\":\"ai_agent\",\"external_session_ref\":\"smoke-ai-$STAMP\",\"started_at\":$(date +%s)000,\"ai_channel\":\"web_chat\",\"ai_direction\":\"inbound\"}"