Skip to main content

Public pixels, redirects, and short links

Orbit’s engagement surface includes a band of root-mounted endpoints — mounted directly on the API host before the /api/v1 auth middleware, so anonymous browsers and email clients can reach them without an API key. They are the public face of several artifact families, but they share one contract: the endpoint is anonymous, it always answers with content a browser can render, and a malformed request never breaks the user-facing page (a pixel replies with a 1×1 transparent GIF; a bad link reply is a branded HTML error page). PKCE and Clerk don’t apply here. The auth is HMAC-SHA256 embedded in the URL itself, and abuse defences are per-IP rate limits plus, on the one POST endpoint, Idempotency-Key support.

The endpoint families

The two /r/ routes coexist on one prefix: a single-segment code is a referral, and a two-segment sig/target pair is an email tracking link. The router disambiguates them by path-segment count.

Goal pixel — GET /g/:goalId/:contactId.gif?sig=<hmac>&schema=<tenant schema>

A goal pixel lets a third-party page (a checkout confirmation, a signup “thanks” screen) fire a conversion for a specific contact without carrying an API key. The signature binds goalId, contactId, and the tenant schema, so a leaked pixel URL can’t be replayed against a different tenant’s schema by swapping a query param.
The GIF is returned even when the signature fails — a page that embeds the pixel never renders a broken-image glyph, and the rejected hit is logged for forensics.

Email open pixel — GET /p/:sig.gif?m=<messageId>&t=<tenantId>&ts=<unix-seconds>

Outbound email bodies embed this 1×1 pixel. The signature is HMAC-SHA256 over the canonical string <messageId>:<tenantId>:<ts> (hex-encoded), so the three signed fields ride the query string while the sig stays in the path.
Cache-Control: no-store is non-negotiable: a caching proxy that served the same 200 to every recipient would mask all subsequent opens. On a verified hit the platform stamps the message’s opened_at engagement marker first-event-wins (repeated hits can’t overwrite the earliest timestamp), classifies the hit as human or machine (Apple Mail Privacy Protection relay, Gmail Image Proxy, bot prefetch), and collapses rapid proxy fan-outs into a single engagement event with a 60-second dedup window.

Referral redirect — GET /r/:code

A referral program mints per-contact codes; the redirect resolves the code, appends ref_code to the destination URL as the attribution carrier, records the visit (IP, user-agent, geo headers), and 302s the browser.
An unknown or malformed code answers with a branded HTML error page when the request asks for HTML — never a raw JSON blob. Short links are the generic mintable artifact created with POST /api/v1/links. Same contract as the referral redirect: resolve, attribute, 302, branded HTML error page on failure.
Per-IP rate limit: 60 requests/minute (tighter than the tracker families’ 120, because a short link is a public redirect anyone can paste).

Landing page — GET /lp/:code and POST /lp/:code/convert

A landing page is a published renderable block tree (hero, product grid, opt-in form, coupon) served at its public code. The page’s short link redirects here, so clicks stay attributable through the same tracking suite while visits and conversions are recorded against the page.
The convert hook accepts native HTML form posts (application/x-www-form-urlencoded) and JSON:
Both routes are rate-limited at 120 requests/IP/minute, and the convert hook is the one endpoint in this family that honors Idempotency-Key (see below). Inbound email tracking links are HMAC-signed click-throughs. The :target segment is base64url JSON binding tenant_id, message_id, the raw target_url, a mint timestamp, and a wire version; :sig is base64url HMAC-SHA256 over the exact :target bytes (signing the wire form, not the decoded JSON, so re-serialization can’t change the signed payload).
Referrer-Policy: no-referrer keeps the signed link from leaking into the destination’s Referer header. A verified click stamps the message’s clicked_at engagement marker (first-event-wins) and 302s; a forged or expired link answers 400 with a plain-text body — it did not render inline like a pixel, so a plain “invalid or expired” page is the neutral failure.

Signing model

Every tracker endpoint authenticates with HMAC-SHA256 over an encoded payload, never an API key embedded in a public URL:
  • Goals sign goalId:contactId:tenantSchema — the tenant schema is signed so a replay with a swapped ?schema= param fails.
  • Open pixels sign messageId:tenantId:ts — the timestamp is signed, so a tampered ts changes the signed payload and fails verification.
  • Email links sign the full encoded target segment — tenant, message, destination URL, and mint time are all bound, so editing any field (including ts) fails verification before the redirect fires.
Verification is fail-closed but neutral: the signature gate decides whether engagement is recorded, while the response body a recipient sees is always present. A pixel replies with the transparent GIF and no-store headers on every path, including rejection, because an email client that shows a broken-image glyph teaches recipients that the sender’s mail is corrupted. A redirect with a bad signature replies 400 (or 410-class expiry) with a short text body. Timestamps carry a bounded window. Open-pixel tokens accept up to 365 days of age (archived mail opened months later still counts) with 60 seconds of future-skew tolerance across replicas. Email-link tokens default to a 30-day TTL with the same skew tolerance; operators can shorten the TTL without a redeploy. Pixels still render on stale tokens; link redirects refuse.

Idempotency integration

The landing-page convert hook is the only POST in this family, so it’s the only endpoint where Idempotency-Key applies (the platform-wide key hook covers POST/PUT only; safe methods are ignored). The per-IP rate limit applies to both /lp routes — it rides in front of the idempotency layer, so a retried submit costs budget either way. There are two shapes of idempotency here:
  • The key contract — a convert request carrying Idempotency-Key plus a tenant credential replays the cached response for 24 hours instead of double-recording the conversion. The rendered opt-in form omits the header, so anonymous browser submits record once per submit (there is no credential to scope a key to).
  • Idempotent engagement markers by design — email opens, clicks, and goal conversions stamp opened_at / clicked_at first-event-wins, so a replayed GET pixel or link click can’t corrupt analytics. The open pixel adds a 60-second dedup window to collapse proxy fan-outs (Gmail Image Proxy, Apple relay) into one engagement event.
Pixels don’t participate in the key contract, and that’s the point: a GET pixel always renders regardless.

Interaction with the delivery lifecycle

Open and click markers are engagement signals, not delivery proof. An open pixel that fires tells you the recipient’s client rendered the image — it says nothing about inbox placement, and Apple Mail Privacy Protection pre-fetches the pixel before any human reads the message. A click proves even less about delivery: the recipient already has the mail. This is why the email delivery lifecycle tracks delivery from provider webhooks (the delivered/bounced classes), while opened_at and clicked_at are additive engagement stamped on the same message row. Never gate a sent-mark on a pixel firing, and never treat “no opens” as “not delivered” — suppressed images, MPP relay classification, and plain non-opens all leave the delivery row exactly as the provider reported it.

Common pitfalls

  • Tampered ts on an open pixel. The timestamp is inside the signed payload. Editing ts invalidates the signature — the hit fails closed, returns the GIF anyway, and records nothing. If your dashboard shows no open for a mail the recipient demonstrably rendered, check the pixel URL wasn’t rewritten by an intermediate that normalized the query string’s order or encoding.
  • Pixel URL cached by a proxy. Without no-store a shared caching layer would serve the first recipient’s 200 to everyone and mask all later opens. The endpoints set it; passing the pixel URL through your own image-caching edge breaks it again — don’t.
  • Broken Accept negotiation on a short link. A client that sends no Accept (or Accept: application/json) gets a JSON error envelope, not the branded HTML page. Browsers always send Accept: text/html and get the friendly page — but when you test with curl or an SDK, set the header if you want to see what an end user sees.
  • Editing a tracking link’s destination. Email link signatures cover the encoded target segment byte-for-byte. Re-encoding the JSON (changing key order, re-serializing numbers) produces a segment that doesn’t match the signature → 400. Mint links at email-render time; don’t re-mint or normalize them downstream.
  • Sending the convert hook a replay without a key. Because the opt-in form POSTs without Idempotency-Key, a browser double-submit records two conversions. If you call convert programmatically, send a key (plus your credential) so retries and double-clicks replay.
  • Expecting a pixel hit to prove delivery. It’s an engagement marker only — delivery comes from the provider webhook path, and the pixel coming back as “machine-classified” (MPP/Gmail proxy) is a normal partial signal, not a failed pipeline.