Tell GeoSwap when something good happens
| Where the conversion happens | Use | Set up |
|---|---|---|
| A page on your site (thank-you page, sign-up confirmation) | geoswap.track() | One line of JavaScript |
| Shopify checkout | The GeoSwap custom pixel | Paste it in Customer events |
| Your CRM, backend or a no-code tool | The conversions API | An API key and a POST |
You can mix them. A common setup: geoswap.track("lead") on the form's thank-you page, then your CRM sends qualified and signed later through the API, linked to that lead. Whichever you use, keep the GeoSwap script on your site: credit goes to visits, and the script (along with clicks on GeoSwap short links) is what records them.
Once the GeoSwap script is on the page, call geoswap.track() when the conversion happens. It sends the visitor id and the GeoSwap short-link click id along automatically.
<script>
window.geoswap = window.geoswap || { q: [], track: function () { this.q.push(arguments); } };
// On your thank-you page, after the GeoSwap script tag
geoswap.track("purchase", {
id: "order-1001", // your order or lead id: the same id is only counted once
value: 49.99, // optional, in currency units
currency: "USD", // optional, ISO 4217
email: "jane@example.com" // optional, hashed on arrival, never stored raw
});
</script>| Option | What it does |
|---|---|
event (first argument) | The stage name: lead, signup, purchase… Letters, digits and _, up to 40 characters. Stored lower-case, so "Lead" and "lead" are the same event. |
id | Your id for this conversion (order id, lead id), up to 128 characters. The same event and id are only stored once, so a refreshed thank-you page doesn't double count. Without an id, the same visitor's same event within 30 minutes counts once. |
value | A number in currency units, like 49.99. |
currency | A three-letter ISO 4217 code, like USD or EUR. Values in other currencies are converted for the report (see Settings). |
email, phone | Optional. Sent to GeoSwap and hashed on arrival; never stored or logged raw. They help join a person's conversions across devices. |
The default install snippet loads with defer, so a geoswap.track() call placed right after the tag can run before the script has — add this stub first, every time. It's harmless once GeoSwap has loaded: calls queue up and the script drains the queue in order as soon as it's ready.
<script>
// Safe to call before geoswap.js has loaded: calls are queued and sent in order
window.geoswap = window.geoswap || { q: [], track: function () { this.q.push(arguments); } };
geoswap.track("lead", { id: "form-8812" });
</script>Shopify's checkout and order confirmation page don't run theme scripts, so geoswap.track() can't see a purchase there. The GeoSwap custom pixel can: it sends each completed order as a purchase with its order id, total and currency, joined to the visitor's earlier visits. Copy it from Foundations → Sites & script in your dashboard (your Project ID is already in it) and paste it under Shopify Admin → Settings → Customer events. Step by step: Track purchases from checkout.
Add cart and checkout events to see them on Analytics → Funnel: product_viewed, add_to_cart, checkout_started and payment_info. Send them the same way as any other geoswap.track() call:
geoswap.track("add_to_cart");
// ...later, on your checkout page:
geoswap.track("checkout_started");On Shopify, add product_added_to_cart and checkout_started to the same custom pixel as your purchase events (Shopify checkout above) — GeoSwap recognizes Shopify's and GA4's names for these events automatically (view_item, product_added_to_cart, begin_checkout, add_payment_info).
POST https://api.geoswap.co/v1/conversions with an API key that has the conversions:write scope (Settings → API keys), or up to 500 at once with /conversions/batch. The API is the right choice for offline stages (qualified, signed, renewed), backfills of older conversions, and anything that happens away from the browser.
curl -X POST https://api.geoswap.co/v1/conversions \
-H "Authorization: Bearer $GEOSWAP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"event": "qualified",
"external_id": "006Hs00001AbCdE",
"parent_external_id": "00QHs00002XyZqR",
"occurred_at": "2026-10-12T14:03:00Z",
"visitor_id": "Xb3k9_Qm2LpA7vR1.20261003",
"email": "jane@example.com",
"value": 1200,
"currency": "USD",
"tags": {
"market": "co"
}
}'parent_external_id (the lead behind a deal). They can arrive in any order, and a later stage shares the first stage's visits.event and external_id. Change its time, value or identifiers and it's re-credited.occurred_at as when it really happened. Any age is accepted, but visits are only kept for 400 days, so very old conversions land on Unattributed.Full field list, errors and limits: API reference. Ready-made setups for Salesforce, HubSpot and Zapier: CRM recipes.
geoswap.track() calls still link visits to the CRM's later stages.Each stage is its own event and its own column in the report: lead, qualified, signed. Every stage is credited in full, so never add stages together (one journey with a lead and a signed deal would count twice). The report never shows a cross-event total for that reason.
Conversions show up in Attribution as Processing first (Overview's Tracking health), and are credited within about three hours. To check a specific one, open Journeys and use Look up a conversion with its id: you'll see its stages, the visits it was matched to and the credit each one got under every model.
Add what you spent to get cost per conversion and ROAS.