Link anatomy
- Domain — a host registered as an
AttributionDomain. A link can only use a registered domain. - Slug — lowercase
a-z0-9_-, up to 64 characters. Required on shared domains, optional on a custom domain.
getdupe.app with an empty slug is a valid link — that is what a creator says out loud on camera. Non-root slugs are for bio links and per-creator variants.
An unknown slug on a known domain falls back to that domain’s root link, so a mistyped or stale path still lands on the product page and still counts a click.
Link types
App links always use the EVO engine. Web links may instead use the
airbridge engine, which forwards through an Airbridge tracking link and lets Airbridge own the touchpoint.
What happens on each device
1
The hit reaches the redirect app
Any non-EVO host pointed at the Dialed frontend is treated as an attribution host and rewritten to
/l/<host>/<path>. There is no per-domain allowlist to update.2
The click is recorded
The route posts the domain, slug, query string, referrer, user agent, country, and visitor IP to
POST /api/public/attribution/clicks with a 4-second timeout. The backend returns the redirect URL, click token, detected platform, link type, and brand name.3
The visitor is forwarded
Desktop and unknown platforms get a
302. Phones on an app link get the smart install page instead.Desktop or unknown platform
A302 to the destination URL with the click token and UTMs appended. For an app link with no configured store URL for that platform, the same web fallback is used.
Store pages are the exception: an App Store or Google Play destination is forwarded untouched, because Apple and Google ignore the query string and the attribution happens in the app via the SDK instead.
Phone on an app link
EVO serves a small “Get Brand” page with a single Get the app button. There is no auto-redirect, on purpose: the clipboard handoff only works inside a real tap, and it is what turns a probabilistic IP match into a deterministic one. Tapping the button copies theevc_… click token to the clipboard, then opens the store:
- iOS — the App Store URL, with
?mt=8appended when it has no query string. - Android — an
intent://details?id=<package>URL withS.browser_fallback_urlset, when the package can be parsed from the Play URL’sidparameter. Otherwise the plain Play URL.
TikTok, Instagram, and other in-app browsers
When the user agent looks like an in-app webview (Instagram, Facebook, Messenger, TikTok, Snapchat, Twitter, LinkedIn, Pinterest, Line), the page also renders a hint card telling the visitor how to escape it:
On iOS the card also offers a direct Open in Safari link built from the current page URL.
When the backend is slow or the link is unknown
If the click cannot be recorded, the visitor still lands somewhere real. The route falls back to the per-domain URL inATTRIBUTION_FALLBACK_URLS, and if that is not configured, renders a link-not-found page with a 404. Attribution is lost; the visitor is not.
UTM passthrough
Incoming UTMs on the vanity URL are stored on the click and forwarded to web destinations. EVO fills in defaults only for keys the incoming URL did not carry:utm_source, utm_medium, utm_campaign, utm_content, and utm_term are all recorded on the click row. The rest of the incoming query string is forwarded too — except a replayed evo_cid, which is always stripped and replaced with the freshly minted token.
Shared vs custom domains
Shared domains require a slug precisely so several brands can safely share one host.
tryapp.link/overcomer and tryapp.link/dupe can coexist; a root link on a shared host cannot.
Connecting a custom domain
From the client portal, Results → Attribution → Domains, enter a domain you own. EVO registers it, attaches it to the Vercel project, and returns the exact DNS records to create.DNS records
Vercel’s rank-1 recommendation replaces those defaults when it returns one. An unverified domain also gets a
TXT verification challenge in its dns_records list — create that too.
Statuses
Press Check to re-run verification after your DNS changes propagate. Removing a domain that still has links returns
domain_has_links (409) with the offending links — delete or move them first.
For the EVO team
Registering a shared domain
Shared domains are registered from Sales Attribution → Domains in the admin console (POST /api/internal/attribution-domains with kind: "shared" and no brand). A shared domain must not have a brand; a custom domain must.
The app’s own hosts are rejected: anything on evomarketing.co or vercel.app cannot be registered as an attribution domain.
Cloudflare automation
For EVO-owned shared domains, the required A, CNAME, and TXT records can be created in Cloudflare automatically once Vercel reports them. After the records are upserted, EVO re-checks Vercel in the same request so a newly verified domain can goactive immediately.
Custom client domains are never changed in Cloudflare. A missing zone or an unset token simply leaves the Vercel status and the manual dns_records instructions intact.