First-Party Snippet Domain
Load apex.js from one of your own subdomains (e.g. m.skillshotgolf.com) so iOS Safari treats it as first-party. The privacy warning bar goes away, visitor cookies persist for the full 365 days instead of being capped at 7, and your data quality from Safari traffic improves.
Why It Matters
When the snippet is loaded from app.apex.inc on a customer's site, iOS Safari sees a cross-origin script doing tracker-shaped things: setting cookies, posting events to a different domain, reading screen dimensions and language. Safari's Advanced Tracking and Fingerprinting Protection (ATFP) classifies it as third-party tracking and applies these protections:
- Cookie cap at 7 days — visitor identity rolls forward less reliably; the same person looks like a new visitor every week.
- Fingerprinting API scrambling — some reads return spoofed values.
- Privacy warning banner — on pages where ATFP detects post-paint content shifts, Safari surfaces a "you can reduce advanced privacy protections" bar to the user.
CNAME loading converts the script and its API calls from cross-origin to same eTLD+1 (the same registrable domain as your site). Safari treats the whole flow as first-party — no ATFP, no cookie capping, no warning.
This is the same pattern Google Analytics 4 (Server-Side Tagging on a custom domain), HubSpot (Custom Tracking Domain), Optimizely Web (Custom Snippet), and VWO (Smartcode on First-Party Domain) all use.
How It Works
Browser merchant.com Apex
─────────────────────────────────────────────────────────────────
visit page ──> skillshotgolf.com
HTML loads html includes:
<script src="https://m.skillshotgolf.com/api/apex-js?...">
script load ──> DNS lookup of m.skillshotgolf.com
──CNAME──> Apex CloudFront distribution
──> serves apex.js
(apiBaseUrl baked in: m.skillshotgolf.com)
events POST ──> POST m.skillshotgolf.com/api/events
──> Apex CloudFront ──> Apex API
Three pieces of infrastructure get provisioned on your behalf when you submit a domain:
- An ACM certificate for your subdomain (us-east-1, issued via DNS-01 validation). You add the validation CNAMEs we provide.
- A dedicated CloudFront distribution with your subdomain as an alternate domain name. Origin is
app.apex.inc; the distribution forwardsHost: m.yourdomain.comto our API so the snippet'sapiBaseUrlmatches. - A reverse-index entry mapping your subdomain → workspace, so requests arriving at the CDN route to your project.
You only see the DNS records — everything else happens automatically.
Setup Walkthrough
Pick a subdomain
In your Apex dashboard, open Integrations and find the First-party snippet domain card. Type the subdomain you want to use — m.yourdomain.com, metrics.yourdomain.com, or whatever you prefer. It must be a subdomain (not a bare apex domain), and it must be on a public TLD that your DNS provider can serve records for.
Wait ~10 seconds
We validate the format, claim the subdomain (one Apex workspace per domain), and request the TLS certificate from ACM. The dashboard returns two DNS records you need to add at your registrar.
Add the DNS records
At your DNS provider:
- One CNAME pointing
m.yourdomain.comatdxxxxx.cloudfront.net(the CDN target shown in the dashboard). - One CNAME for ACM validation:
_xyz._acme.m.yourdomain.com→_abc.acm-validations.aws..
Most DNS providers propagate within 5-15 minutes. If you don't see status change after an hour, the dashboard surfaces a soft warning; after 24 hours it escalates so you know something's off.
Wait for `active`
The provisioning cron checks every 5 minutes and walks your domain through the lifecycle states. You can also click Check now in the card to nudge it manually. When the status flips to Active, you'll get a confirmation email and the install snippet on the same page automatically updates to use the new domain.
Update your install
Replace your existing install snippet on your site with the new copy from the dashboard. The new snippet src points at your custom domain; the inline anti-flicker stub stays the same. From this point on, iOS Safari treats Apex as first-party.
Lifecycle States
| State | What it means |
|---|---|
| Unconfigured | No domain submitted yet. |
| DNS pending | Cert requested. We're waiting for you to add the DNS records and for ACM to validate them. |
| Issuing certificate | DNS records validated, ACM is finalising the cert and we're spinning up the CloudFront distribution. Typically takes 15-30 minutes. |
| Active | Cert issued, distribution deployed, DNS resolves correctly. The snippet now loads first-party. |
| Failed | Something went wrong (cert validation timed out, DNS misconfigured, etc.). The dashboard shows a specific error message so you can fix it. Remove and re-add to retry. |
| Renew pending | Cert is within 30 days of expiry. ACM should auto-renew as long as the validation CNAMEs remain in DNS. The dashboard surfaces a banner reminding you to leave them in place. |
| Expired | Cert has expired. Restore the validation CNAMEs and contact support if your dashboard doesn't recover within an hour. |
Operational Details
Certificate auto-renewal
ACM auto-renews issued certificates around 60 days before expiry — but only if the DNS-01 validation CNAMEs are still in your DNS. Don't remove the _acme-challenge records after issue. They look optional but they're required for renewal. The dashboard surfaces a clear "renew_pending" banner once we're inside 30 days; if anything looks off, that's your cue to verify the records are still there.
Workspace transfer
If you set up a domain on a sandbox workspace and want to move it to your production workspace (or reorganise internally), use Transfer instead of disable + re-add. Transfer atomically re-points the cert + distribution to the destination workspace — no downtime, no DNS changes, no need to retype anything. You must be a workspace admin on both the source and destination.
Removal
The Disable button on the card releases the domain from Apex's side, clears the install snippet, and queues the cert + CloudFront distribution for teardown in the background. CloudFront takes ~15-30 minutes to fully delete a distribution; that's fine — your snippet has already reverted to the standard app.apex.inc URL by the time you see the confirmation.
Security
Two boundaries protect your traffic:
- Tenant isolation — the snippet endpoint cross-checks the incoming domain against an authoritative claim table. A poisoned
Hostheader from a misconfigured proxy can't make us serve a snippet that beacons events to an attacker-controlled origin. - Atomic claim — one domain belongs to exactly one Apex workspace. If you submit a domain that's already in use, you get a clear error rather than overwriting another workspace's setup.
Compatibility Notes
Cookie domain mode
When you activate a first-party snippet domain, the dashboard's install snippet automatically appends &domain=registrable. This tells the snippet to set cookies with Domain=.yourdomain.com so visitor identity follows users across all subdomains of your site (app.example.com, www.example.com, m.example.com all see the same apex_vid).
If you'd rather keep cookies host-scoped, remove the &domain=registrable parameter from your install. Most merchants want cross-subdomain cookies, which is why we suggest the registrable mode by default once you've gone through the trouble of setting up a CNAME.
Existing installs
Switching from app.apex.inc loading to a first-party domain is a soft migration: existing visitor IDs in apex_vid cookies persist (they're set on your site's domain, not the script's source domain). Visitors don't need to re-identify, attribution doesn't reset, and experiments keep running.
Mobile and SDK
This feature is web-snippet only. The mobile Capacitor plugin and SDK don't share Safari's classification — they post events directly to app.apex.inc regardless. iOS Safari ATFP doesn't apply to native networking calls.
Rate Limits and Quotas
- 5 domain submissions per hour per workspace. Generous enough for typo fixes, tight enough that an automation loop can't burn the shared ACM certificate quota.
- 20 submissions per hour per IP. Caps a misbehaving network independently of any one workspace.
- CloudFront account limit: 200 distributions. We log warnings at 75% utilisation and pause new distribution creation at 90% (existing domains keep working). Once we request and receive a quota raise, paused projects automatically resume on the next 5-minute sweep — no merchant action required.
API Reference
POST /api/snippet-domain— submit a domain. Body:{ "domain": "m.yourdomain.com" }. OptionalIdempotency-Keyheader. Returns DNS records and lifecycle status.GET /api/snippet-domain— current status, DNS records, certificate expiry.POST /api/snippet-domain/verify— manual recheck (the cron also runs every 5 minutes).POST /api/snippet-domain/transfer— atomic handoff between workspaces. Body:{ "toProjectKey": "..." }.DELETE /api/snippet-domain— disable.
All five require workspace-admin authorisation. Transfer requires admin on both source and destination.
Troubleshooting
Status stuck on "DNS pending" for over 24 hours. Almost always a DNS records mismatch. Open the card, copy the validation CNAMEs again, and compare exactly what's in your DNS — even a trailing dot can break ACM validation. ACM gives up at 72 hours, after which the status flips to Failed with a specific reason.
Status went to "Active" but my install still loads from app.apex.inc.
The install copy in the dashboard updates the moment status flips, but you need to deploy that new snippet to your site. Visitors loading the old snippet will keep working — just without the first-party benefits.
iOS Safari still shows the privacy warning. Two reasons it might persist:
- The DNS hasn't fully propagated globally yet — give it an hour.
- Your install hasn't been updated. Check the script src on your live site is now your custom subdomain, not
app.apex.inc.
If neither of those fixes it, contact support — there are some edge cases in older Safari versions where ATFP heuristics flag scripts even on first-party domains.
Cert renewal alert keeps firing. ACM needs the validation CNAMEs to renew. If you removed them after the original issue, re-add them. The next 5-minute sweep will pick them up and clear the alert.