How Our /go Affiliate Redirect and Click Tracking Actually Works
A walkthrough of the database-backed redirect, UTM tagging, and click logging behind every affiliate link on Pickuma — and why we never hardcode a vendor URL.
Every affiliate link on this site points at one place: /go/<slug>. You never see a raw vendor URL with a tracking parameter glued on the end in our MDX. That single layer of indirection is the difference between a blog that can swap a partner program in thirty seconds and one that has to grep through 490 articles to fix a dead link. Here is exactly what happens between the click and the vendor.
The redirect path, slug by slug
When you click a tool link, the request hits a single dynamic route: /go/[slug]. The slug is the only thing the link carries — notion, cursor, beehiiv, and so on. The route does one job: look that slug up in a Supabase table called affiliate_links, read the destination URL and the link’s status, and respond.
The response depends on the status column:
active→ a redirect to the real affiliate URL, with UTM parameters attached.paused→ an HTTP410 Gone. Not a 404, not a silent redirect to the homepage. A 410 tells search engines and link checkers that the resource is deliberately retired, which keeps crawlers from hammering a link we’ve turned off.
The URL itself lives in the database, never in the article. That matters more than it sounds. Affiliate programs change their tracking URLs, migrate between networks like Reditus and PartnerStack, and occasionally revoke an approval. When that happens we update one row in Supabase. Every article that mentions that tool — footer links, inline cards, the /tools/<slug> hub page — points at the new destination on the next request. No rebuild, no content edit, no risk of a stale ?ref= parameter sitting in a post from eight months ago.
Why the click gets logged before the redirect
The redirect isn’t the whole job. Before /go/<slug> sends you onward, it writes a row to a clicks table: the slug you clicked, a timestamp, and the country the request came from. That’s it — no fingerprinting, no cookie, no cross-site identifier. Three columns.
That tiny log is what turns affiliate income from a black box into something you can actually reason about. You can pull the last fifty clicks straight from the database and see which tools people are clicking, when, and roughly from where:
curl -sX GET "$SUPABASE_URL/rest/v1/clicks?select=slug,ts,country&order=ts.desc&limit=50" \
-H "apikey: $SUPABASE_SERVICE_ROLE_KEY" \
-H "Authorization: Bearer $SUPABASE_SERVICE_ROLE_KEY"
The click log is our copy of the truth. Affiliate dashboards report conversions on their own schedule and with their own attribution windows; some don’t show you a raw click count at all. By logging the click on our side at redirect time, we always know how much interest a tool is getting independent of what the partner network chooses to surface. If our clicks count for a slug is healthy but the partner reports nothing, that’s a signal something is broken on their end — a tracking URL that changed, an approval that lapsed — and we go look.
UTM tags: the same click, attributed by surface
The redirect doesn’t just forward you. It appends UTM parameters so that whatever analytics the destination feeds into — and our own GA4 — can split traffic by where the click originated. The same tool can be linked from several surfaces, and each gets its own campaign tag:
| Surface | utm_source | utm_medium |
|---|---|---|
| Article footer (tools mentioned) | article-footer | internal |
| Tool hub CTA (/tools/[slug]) | tool-hub | internal |
| Bluesky crosspost card | bluesky | social |
| dev.to body footer link | devto | crosspost |
This is why the indirection earns its keep a second time. Because every link routes through /go, the UTM convention is applied in one place instead of being copy-pasted (and mistyped) across hundreds of posts. One rule about how a Bluesky click should be tagged, enforced centrally.
Two sources of truth, kept in sync
There’s a subtlety worth naming. The redirect reads from Supabase, but the UI — the footers, the /tools directory, the inline cards — reads from a TypeScript file, src/data/affiliates.ts. That’s deliberate: the site is statically built, so the components need their data at build time, while the redirect needs it at request time.
The rule that keeps this from rotting is simple: change both. Add an affiliate, and it goes into the Supabase affiliate_links table and into affiliates.ts. Pause one, and you flip the status in the database and remove it from the TypeScript file so it stops appearing in footers and the tools directory. Drift between the two is the one failure mode this design can’t catch on its own, so it’s the one thing we check by hand.
If you run something similar, keep a written record of which slug maps to which program and network — it’s the kind of operational map that lives well in a shared doc.
Notion
Where we keep the affiliate inventory map — slug, program, network, approval status, and the last time each tracking URL was verified. A single source of operational truth alongside the code.
Free for personal use; paid plans from $10/user/mo
Affiliate link · We earn a commission at no cost to you.
FAQ
Why not just put the affiliate URL directly in the article?+
What data does the click log actually store?+
What happens when an affiliate link is retired?+
None of this is exotic. It’s one dynamic route, one lookup table, one three-column log, and a discipline about never hardcoding a vendor URL. But that small amount of structure is what lets a catalog of hundreds of reviews stay swappable, measurable, and honest about where its money comes from.
Related reading
2026-06-09
What Shipping 490 Articles Taught Us About Content Velocity
Lessons from running an automated editorial pipeline to 490 published reviews: where velocity actually breaks, and the checks that keep throughput from becoming a liability.
2026-06-09
How We Avoid Keyword Cannibalization Across 490 Reviews
When a blog grows past a few hundred posts, your own pages start fighting each other in search. Here's the workflow we use to detect and fix keyword cannibalization at scale.
2026-06-09
How We Keep 490 Published Reviews From Going Stale
A look at the systems we use to stop nearly 500 tool reviews from quietly rotting: staleness scoring, automated link checks, and visible changelogs.
2026-06-08
Why We Kill Articles That Don't Earn Their Place
An honest look at our content pruning process: how we decide which articles to keep, revise, or delete — and why deleting them often helps the rest of the site rank.
2026-06-08
How We Fact-Check a Tool Review Before We Publish It
The exact checklist a tool review passes before it goes live on Pickuma: sourcing every claim, testing the product, and making the numbers reproduce.
Get the best tools, weekly
One email every Friday. No spam, unsubscribe anytime.