pex

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:

  1. An ACM certificate for your subdomain (us-east-1, issued via DNS-01 validation). You add the validation CNAMEs we provide.
  2. A dedicated CloudFront distribution with your subdomain as an alternate domain name. Origin is app.apex.inc; the distribution forwards Host: m.yourdomain.com to our API so the snippet's apiBaseUrl matches.
  3. 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.com at dxxxxx.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

StateWhat it means
UnconfiguredNo domain submitted yet.
DNS pendingCert requested. We're waiting for you to add the DNS records and for ACM to validate them.
Issuing certificateDNS records validated, ACM is finalising the cert and we're spinning up the CloudFront distribution. Typically takes 15-30 minutes.
ActiveCert issued, distribution deployed, DNS resolves correctly. The snippet now loads first-party.
FailedSomething 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 pendingCert 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.
ExpiredCert 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:

  1. Tenant isolation — the snippet endpoint cross-checks the incoming domain against an authoritative claim table. A poisoned Host header from a misconfigured proxy can't make us serve a snippet that beacons events to an attacker-controlled origin.
  2. 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

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" }. Optional Idempotency-Key header. 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:

  1. The DNS hasn't fully propagated globally yet — give it an hour.
  2. 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.