Docs

Tracking: one script tag and one API

Two pieces tie every customer to the channel, campaign and asset that brought them in: one script tag on your site, and one API call from your server for the signups, payments and meetings your backend sees. The autopilot reads the same numbers every day and moves the budget to the cheapest customer.

1. Install the snippet

Paste one line into the <head> of every page. The cabinet prints it with your product’s key on the product’s Tracking screen.

HTML
<script async src="https://gtmsdr.com/t.js" data-key="gs_pub_YOUR_KEY"></script>

The gs_pub_ key is publishable: it ships in the page. Hits count only from your product’s domain, its subdomains and the extra hosts you add in Tracking, so a copied tag counts nothing anywhere else.

AttributeDefaultWhat it does
data-keynoneYour product’s snippet key, gs_pub_…. Required.
data-apihttps://api.gtmsdr.comWhere hits go. Point it at a first-party proxy if you run one.
data-formsonoff stops form capture. data-gtmsdr="off" on a single <form> skips that form.

2. What the snippet records

  • A pageview on load and on every single-page navigation.
  • Three first-party cookies on your registrable domain: gs_vid, the visitor, kept 2 years and readable by your server; gs_sid, the session, closed after 30 idle minutes or by a new campaign or click id; gs_ft, the first landing page and referrer, kept 90 days.
  • Click ids, kept with the session: gclid, gbraid, wbraid, msclkid, li_fat_id, rdt_cid, oppref, ttclid, twclid, fbclid.
  • Every <form> with an email field, as a lead with the page, campaign and channel that brought the visitor. Password, card-number, hidden and file fields never leave the page.

Hits go to POST /api/v1/t/hit as a text/plain JSON body (no preflight), by sendBeacon with a fetch(keepalive) fallback. The endpoint answers 204 when the hit is counted or dropped as a bot, 400 for a malformed hit, 401 for an unknown key and 403 for a host outside your list.

3. Name people in the browser

Call identify when someone signs in and signup when an account is created. Calls made before the script loads wait in a queue, so the stub below can sit above the tag.

JavaScript
window.gtmsdr = window.gtmsdr || function () {
  (gtmsdr.q = gtmsdr.q || []).push(arguments)
}

gtmsdr('identify', { email: '[email protected]', user_id: 'u-42', company: 'Acme' })
gtmsdr('signup', { email: '[email protected]', user_id: 'u-42' })

// Once t.js has loaded: the visitor id, to forward to your server as visitor_id
const visitorId = gtmsdr.visitorId()

identify takes email, user_id, name, company, phone and title; signup takes the same fields.

4. Send events from your server

Your backend knows what the browser cannot: the payment that cleared, the refund, the meeting that took place. Send them to the events API with a secret key made on the Tracking screen (New key; the secret is shown once). Use Authorization: Bearer gs_evt_… or the X-Api-Key header.

Shell
curl https://api.gtmsdr.com/api/v1/events \
  -H "Authorization: Bearer gs_evt_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "payment",
    "id": "ch_3Q8Xr2",
    "user_id": "u-42",
    "email": "[email protected]",
    "amount_cents": 4900,
    "currency": "USD",
    "visitor_id": "<gs_vid cookie>",
    "occurred_at": "2026-10-15T10:00:00Z",
    "properties": {"plan": "pro"}
  }'
typeNeedsRecords
identifyemail or user_idJoins the person, and the visitor_id when you send it.
signupemail or user_idA signup, once per person.
paymentid, amount_centsA payment, once per id. The person’s first payment also makes them a customer.
refundid, amount_centsA refund, once per id.
meeting.heldid, email or user_idA meeting that took place, once per id.

Send visitor_id (the gs_vid cookie) whenever you have it: it joins the server event to the visits that led to it. The same id sent twice records once, so retries are safe.

5. Batches and answers

Send up to 100 events in one call as {"events": [...]}.

RequestAnswer
One event201 accepted, 200 duplicate, 400 rejected with the reason.
A batch200 with results: one {index, status, event_id, error} per event, in order.

CORS is open to every origin on /api/v1/events and /api/v1/t/hit only.

7. How credit is split

Every lead, signup, paying customer and meeting is split over the person’s touches in the 90 days before it: their web sessions, and the channel events about them (an email clicked or answered, an ad clicked, a document viewed). Three models read the same touches:

  • position, the default: 40% to the first touch, 40% to the last, 20% across the middle. One touch takes all of it, two split it in half.
  • first_touch: all of it to the first touch.
  • last_touch: all of it to the last touch.

A direct visit counts only when nothing else touched the person. With no touch at all, the outcome’s own channel takes the credit, then the channel of the lead it came from. The funnel shows the share attributed, and the target is nine in ten.