The web SDK
Owner: Web / marketing
Endpoint: POST /v1/collect
Status: live in dev. SDK is about 1.5 KB gzipped, against a 3 KB budget.
1. Install
Section titled “1. Install”<script src="https://t.makeabeeline.com/sdk.js" defer></script><script> window.addEventListener("load", function () { window.stitch = window.stitchInit({ collectorHost: "t.makeabeeline.com" }); });</script>Each property uses the collector on its own registrable domain. This is not a style preference:
| Site | Collector |
|---|---|
makeabeeline.com | t.makeabeeline.com |
app.makeabeeline.com | t.makeabeeline.com |
beelineequitynow.com | t.beelineequitynow.com |
Why a shared collector would break one brand entirely
Section titled “Why a shared collector would break one brand entirely”If beelineequitynow.com posted to t.makeabeeline.com, that collector would
be third-party to that origin. Safari would cap or block its cookie, and
attribution on that entire brand would silently degrade — no error, no alert,
just a slowly growing unattributed bucket.
The cookie is also set by the Worker over an HTTP header, never
document.cookie: Safari’s ITP caps JS-set cookies at 7 days regardless of
domain, against an acquisition lookback of 365 days.
2. What you get for free
Section titled “2. What you get for free”- Page views on load
- SPA route changes —
pushState,replaceStateandpopstateare hooked and emit a virtual pageview.app.makeabeeline.comis a single-page app, so without this the entire application funnel would be invisible - UTM parameters and click IDs —
gclid,gbraid,wbraid,fbclid,ttclid,msclkid,li_fat_id, and since plan 33 Taboola’stblci, Outbrain’sdicbo(andobclid), X’stwclidand Pinterest’sepik - Vendor pixel cookies —
_fbp,_fbc,_ttpand_gcl_aware read when the Meta, TikTok or Google pixels have set them and sent with each event; they are what those platforms match a conversion on when the click id is gone. Stitch sets none of them - Batching and retry — events flush on a 5-second interval and on
pagehideviasendBeacon, with alocalStorageretry buffer if a beacon is refused
3. Custom events
Section titled “3. Custom events”window.stitch.track("calculator_used", { term: 30, product: "dscr" });Most page events do not need this. Stitch maps URLs to event names through a
config table, so adding an event is a database row rather than a deploy —
marketing can add one without engineering. Use track() for things a URL
cannot express: button clicks, form field completion, scroll depth.
3a. The submission id, for the pixel
Section titled “3a. The submission id, for the pixel”A lead’s conversion goes to the ad platforms twice on purpose: once from the page by the platform’s own pixel, which carries the browser signals Stitch never stores, and once from Stitch’s server seconds later, which carries the value and lands whether or not the browser was blocked. The platform keeps one, if both carry the same id. The SDK mints it:
const id = window.stitch?.submission?.(); // one UUID per page, stable; undefined when the SDK is blockedif (id) { form.querySelector("[name=stitch_submission_id]").value = id; // a hidden field in the lead form fbq("track", "Lead", {}, { eventID: id }); // Meta pixel, no value ttq.track("SubmitForm", {}, { event_id: id }); // TikTok pixel, no value}Guard it as shown: with the SDK blocked, an unguarded window.stitch.submission()
throws inside the submit handler, and input.value = undefined writes the
string "undefined" into the field. Stitch ignores that string, null, the
field’s own name and any id another lead already carries — a second lead
posting the same page’s id gets no submission id rather than vanishing into
the first lead’s event — so leave the field empty when there is no id.
The form handler passes the hidden field on with the lead, and whichever
system emits the lead’s created event carries it as
lead.attrs.submission_id (see lead-events.md); Stitch
uses it as the lead’s event id. The application event stays server-only: its value
is the value model’s, which the page does not know.
3b. Consent
Section titled “3b. Consent”window.stitch.consent({ analytics: true, advertising: false });Call it from your CMP’s change callback. Global Privacy Control is picked up
on its own — from the request header at the edge, and from
navigator.globalPrivacyControl as a backstop — so a browser sending GPC is
honoured whether or not this is ever called.
Full detail, including the “Do Not Sell or Share” endpoint and what actually changes when someone refuses, is in consent.md.
4. What the SDK deliberately does not do
Section titled “4. What the SDK deliberately does not do”- It cannot read the
anon_id. The_bidcookie isHttpOnly. Ananon_idreadable by page JS is a shareable identifier — paste that link into Slack and five people merge into one person. Server-side handlers read it from the request instead (see identity.md). - It does not identify people. Identification is server-to-server, via
/v1/identify. - It does not count conversions. A
/thank-youpage hit is evidence someone probably submitted a form; the lead is whatever the system of record says it is (lead-events.md). - It is not a consent banner and will not become one. It reads a signal and enforces it; asking the question is a CMP’s job. See consent.md.
5. Cross-domain links
Section titled “5. Cross-domain links”Links from makeabeeline.com to beelineequitynow.com cross a registrable
domain boundary, so the cookie does not follow. Ask the collector for a
short-lived signed token and append it:
const res = await fetch( "https://t.makeabeeline.com/v1/decorate?to=beelineequitynow.com", { credentials: "include" });if (res.status === 200) { const { token } = await res.json(); link.href += (link.href.includes("?") ? "&" : "?") + "_bx=" + token;}The token is HMAC-signed and expires in 300 seconds. Both properties matter:
an unsigned id in a URL is forgeable, and a long-lived one in a bookmarked link
could merge strangers months later. The receiving Worker validates it and
strips _bx from the URL via history.replaceState.
Only the opaque anon_id ever travels in a URL. Never email, phone or any
other PII.
6. Smoke test
Section titled “6. Smoke test”curl -sS -i -X POST https://stitch-collector-dev.magic-blocks.workers.dev/v1/collect \ -H "content-type: application/json" \ -H "origin: https://makeabeeline.com" \ -d '[{"event_id":"smoke-1","ts":1772539200000,"name":"page_view","url":"https://makeabeeline.com/rates?gclid=abc","referrer":null,"props":{}}]'Expect 204, and a Set-Cookie with Domain=.makeabeeline.com, HttpOnly,
Secure, SameSite=Lax and Max-Age=34560000.