Ask for the domain.
One POST with your customer's hostname. You get back the record they need and a connect link to send them. Subdomains get a CNAME; apex domains like globexcareers.com get an A record and a TXT token.
curl -X POST https://api.apexly.dev/v1/domains \
-H "Authorization: Bearer sk_live_…" \
-d '{"hostname":"careers.acme.com"}'{
"status": "pending_dns",
"cname": { "value": "d-tuih5b.cname.apexly.dev" }, "connect_url": "https://connect.apexly.dev/c/kuu…"}They add one record.
The connect page looks up where their DNS lives (we recognise 46 providers, from GoDaddy to Route 53) and shows exactly where to click, in plain words. No support ticket, no screenshot of a DNS panel.
We check it three times.
Cloudflare's resolver, Google's, and the domain's own nameservers must all agree before we move on. If the record is wrong, we say what we expected and what we found.
We get the certificate.
Only for hostnames we verified, never on a guess. Let's Encrypt issues it, ZeroSSL steps in if it can't, and renewal starts 30 days before expiry. You never see a certificate again.
Visitors reach your app.
Our edge terminates HTTPS and forwards each request to your origin with the original Host header and a signature. You get a domain.active webhook and mark the tenant live.
Your app changes one thing.
Read the Host header to find the tenant, and check the signature so nobody can reach your origin pretending to be us. Copy-paste helpers for Next.js, NestJS, Rails, Django, Laravel and Go are in the docs.
Host: careers.acme.com X-Forwarded-Proto: https X-Apexly-Domain-Id: dom_8f3a X-Apexly-Timestamp: 1760587971 X-Apexly-Signature: 9a3f…// signature = HMAC-SHA256(secret, timestamp + "." + host) const tenant = await tenants.byHost(req.headers.host);
And when something goes wrong, it says so.
Every domain is always in one of these states. Each change fires a signed webhook, shows in the dashboard, and comes with a reason and a fix you can show your customer.
We check every 15 seconds for the first hour, then every 5 minutes for 72 hours.
Three resolvers must agree, so a stale cache never fools us.
Let's Encrypt first, ZeroSSL if it has a bad day.
We re-check DNS every 6 hours and renew from day 60 of 90.
We keep serving the cert, keep checking, and tell you which record moved.
Usually a CAA record. The error names the exact record to add.
Built so traffic keeps flowing.
The edge that serves your customers is separate from the API and dashboard. It keeps its own copy of every route and certificate, so if our control plane is down for maintenance, your customers' domains keep working.