Tracked links

One short address for each post, influencer or flyer: where it leads, a QR code to print, and the clicks, orders and sales it brings.

Written 2026-09-29. A tracked link is one short address on your store for one post, influencer or flyer. When someone opens it, your store sends them to the page you picked and records where they came from, so you can see which post brings visits and sales.

https://shop.example.com/l/anna-ig leads to /products/linen-apron?utm_source=instagram&utm_medium=social&fh_link=anna-ig.

Everything here is live data: a link works as soon as it is saved.

1. Fields and rules

FieldRule
nameYour own name for it, e.g. "Anna, Instagram bio". 1 to 80 characters. Shoppers never see it.
codeThe short address after /l/. 4 to 24 characters: a to z, 0 to 9 and single hyphens, no hyphen at either end. Unique in your store. Left out, one is made from the name. Typed in capitals, it is saved in lower case.
targetPathWhere it goes, a page of your store: / (home), /products/<handle>, /collections/<handle> or /pages/<handle>. The page must be one shoppers can see. No query string, no other link, never an outside address.
channelWhere you share it. Fills source and medium (table below).
source, medium, campaignLower case letters, numbers, dots, hyphens and underscores, up to 60 each. source is required (the channel fills it).
discountCodeOptional. An existing, active code shoppers can use now, not a template or a one-shopper code. Applied in the cart on arrival.
activeOn by default. Off pauses the link.
Limit500 links a store.

The short address cannot change once the link is made: it may already be printed on a flyer or pinned in a bio. Everything else can change. To use another address, make a new link.

Reserved words. These codes are refused: edit, admin, link, links, null, undefined, none, true, false, test, home, index, cart, checkout, account, login, logout, signup, register, password, settings, orders, track, products, collections, pages, blog, search, media, assets, static, favicon, well-known, robots, sitemap, qr-code, qrcode, formahand. (new and api are reserved too, and too short anyway.)

Discount codes that end. If the code has expired, is paused or is used up when a shopper clicks, the link still works and simply arrives without it.

Channels

Channelsourcemedium
Instagraminstagramsocial
Facebookfacebooksocial
TikToktiktoksocial
YouTubeyoutubesocial
Pinterestpinterestsocial
Xxsocial
WhatsAppwhatsappsocial
Email newsletternewsletteremail
Influencerinfluencerreferral
Flyer or printprintoffline
QR code in storeqroffline
Otherotherlink

The channel ids for tools and routes are the sources above (instagram … other). Give source or medium yourself to override the channel's.

2. What happens on a click

Someone opensYour store answers
An active link302 to its page on the same address they used, with utm_source, utm_medium, utm_campaign (each only when set) and fh_link=<code>. Anything they added to the short address is dropped.
An active link with a usable discount codethe same, and the code goes into their cart
A paused link, an unknown code, a malformed address302 to the home page, with no parameters. Never an error page.
HEADthe same answer as a visit, not counted
Any other method405

Every answer says noindex, so search engines do not list short addresses, and no-store, so nothing caches them.

3. What is counted, and what is not

Clicks. One counter per link per day (UTC). The list shows every click and the last 30 days. Bots and browser prefetches are not counted.

Nothing about the visitor is stored. No address, no browser details, no referrer. A tracked link sets no new cookie. When the link carries a discount code, the code goes into the cart's own discount cookie, the one a typed code uses.

Orders and revenue per link come from your store's sales sources: the order remembers the fh_link it arrived with, and this respects the shopper's tracking choice. Only paid orders count. If sales cannot be read for a moment, the list shows a dash for orders and revenue, and the tools answer salesKnown: false. Clicks work from the first day.

4. QR code

Every link has a QR code, made in the dashboard itself: nothing is sent anywhere to draw it.

DownloadsPNG, 1024 px square, and SVG
Error correctionM
Sizeversions 1 to 10, addresses up to 213 bytes
Border4 modules of white on every side

For print: keep the white border around the code. Test it with a phone before you print a batch.

5. Dashboard

Analytics → Tracked links. The list shows each link's name, short address, clicks, orders and revenue, with Copy and QR buttons; the row's menu has Edit, Pause (or Resume) and Delete, which asks first. A new store has none: the screen says what a link is and offers New link.

New link takes three steps:

  1. Where it goes. Pick the home page, a product, a collection or a page.
  2. Who it is for. A name, the channel, an optional campaign. The short address is suggested from the name; change it now or never.
  3. Discount (optional). Pick an active code.

The last screen shows the short address and its QR code, ready to copy or download.

6. Routes

GET /api/tracked-links lists every link, newest first; ?id= or ?code= answers one. PUT /api/tracked-links creates a link, or changes one when the body has its id (only the fields given change). DELETE /api/tracked-links?id= or ?code= removes one. storefront:read / storefront:write.

{ "name": "Anna, Instagram bio", "code": "anna-ig", "targetPath": "/products/linen-apron",
  "channel": "instagram", "campaign": "spring", "discountCode": "ANNA10" }

Answer (201 when made, 200 when changed):

{ "link": { "id": "8c1f…", "code": "anna-ig", "name": "Anna, Instagram bio",
  "targetPath": "/products/linen-apron", "source": "instagram", "medium": "social",
  "campaign": "spring", "discountCode": "ANNA10", "active": true,
  "createdByKind": "person", "createdBy": "…",
  "createdAt": "2026-09-29T10:00:00.000Z", "updatedAt": "2026-09-29T10:00:00.000Z" } }

The list answers { links, limit, channels, hosts, currency, salesKnown }; each link adds clicks, clicksLast30Days, orders and revenueMinor (minor units of currency). A refusal is a 400 with errors: [{ field, error }] naming every field at once; a code already in use is a 409.

7. Tools

  • list_tracked_links { limit?, offset?, id?, code? }: links with a ready-to-share url, clicks, orders and revenueMinor, 25 a page (up to 100). Read-only.
  • upsert_tracked_link { id?, code?, name, targetPath, channel?, source?, medium?, campaign?, discountCode?, active? }: without id it makes a link; with id only the given fields change, so { id, active: false } pauses one.
  • delete_tracked_link { id | code }: the short address then leads home and its click history is removed. To stop a link and keep its history, pause it instead.

8. Events

Every change is in the store's event log (events.md): tracked_link.created, tracked_link.updated and tracked_link.deleted. The resource id is the link id.

See also