# Custom Domains

A custom domain puts your own name on a WarpLink link, so `aplnk.to/x7k2pq` becomes `go.yourapp.com/spring-sale`. Redirects, deep links, and analytics work the same as on the default domain. Every organization can run one custom domain free for 90 days, and paid plans keep custom domains running with a certificate that renews on its own.

Same sub-10ms redirects, same deep links, same real-time analytics. See [how we measure this](https://warplink.app/docs/trust/performance). The only thing that changes is the name on the link, and that name does a lot of work.

## Why use a custom domain

- **More taps.** People click links they recognize. A branded domain reads as "this is from a product I know," not "this is a random shortener."
- **Trust.** Your domain signals that the link is safe to open. Generic shorteners get filtered, flagged, and skipped.
- **Brand recall.** Every share reinforces your product. The link itself becomes a tiny billboard.

Custom domains are most valuable for links that travel: campaign URLs, social bios, QR codes, referral links, anything a real person reads before tapping.

## Try it free

You do not need a paid plan to try a custom domain. Every organization can run **one custom domain free for 90 days (about 3 months)**, no card required.

During the trial the domain is fully functional: real branded links, deep links, and live analytics, exactly like a paid domain. The one difference is that **analytics for the trial domain reset to zero every 30 days**. During the trial your links never break and your settings are never touched, only the click history is temporary. Upgrade at any time to keep your data and stop the monthly resets. A paid domain serves while your plan includes it, and after a paid plan ends there is a grace period of up to 30 days (see [plan changes](https://warplink.app/docs/trust/plan-changes)).

When the 90 days end, the domain stops serving unless you have upgraded. We keep your verification and certificate, so upgrading switches it back on instantly.

## Choose a subdomain

We recommend a **subdomain** such as `links.yourapp.com` or `go.yourapp.com`. The reason is technical but simple: setup uses a `CNAME` record, and DNS rules do not allow a `CNAME` at the root of a domain (the root already holds your `NS`, `SOA`, and `MX` records, which a `CNAME` cannot sit alongside). A subdomain is an ordinary hostname, so a standard `CNAME` works on every DNS provider. A subdomain also keeps your root domain free for your main site and email.

A root domain (`yourapp.com` with no prefix) cannot point to WarpLink, so WarpLink does not accept one. Add a subdomain such as `links.yourapp.com` instead. A root domain saved earlier cannot be verified, so remove it and add a subdomain.

## Set it up

Setup is two DNS records, added once. The dashboard generates the exact values for your domain when you add it.

1. **Add the domain** in the dashboard under Custom Domains. Pick your subdomain.
2. **Add the routing record.** A `CNAME` that points your subdomain at WarpLink. This is what sends traffic to us.
3. **Add the certificate record.** A second one-time `CNAME` that lets WarpLink issue and automatically renew the HTTPS certificate for your domain. You add it once and never touch it again, even at renewal.
4. **Watch it go live.** The dashboard checks your records automatically and walks through each stage: records detected, ownership verified, certificate issued, live. You can leave the page; provisioning keeps running and the live status stays on your domains page.

That second record is the difference between a domain that works today and one that keeps working. It hands certificate renewals to us, so your branded links never show a security warning, even years later.

HTTPS is fully managed. You never upload, configure, or renew a certificate yourself.

## How long it takes

Most DNS changes apply in 5 to 15 minutes. They can take up to 48 hours to propagate fully, but that is rare. Certificate issuance usually finishes within a minute or two of your records being detected.

## Use it in your app

A branded domain only opens your app if the app knows about it. Two things are needed, and WarpLink does most of it.

**On our side, automatically.** App association files (`apple-app-site-association` and `assetlinks.json`) are generated and served for every verified custom domain, so iOS and Android will hand the link to your app instead of a browser.

**On your side, two small declarations.**

1. Add the domain where you already list `yourapp.aplnk.to`: the Associated Domains entry on iOS (`applinks:links.yourapp.com`) and the App Links intent filter host on Android. This is what makes the OS open your app.
2. Tell the SDK your hosts: your app's subdomain, `yourapp.aplnk.to`, and the custom domain. The SDK decides "is this link mine, or is it yours to route?" the instant the URL arrives, so it can only answer from what it already knows. Declare both and a link on either resolves on the very first launch, with no network round trip. Leave one out and a fresh install can hand your own link back to your router, because the SDK learns the other hosts from the server only after its first check.

```swift
WarpLink.configure(
    apiKey: "wl_live_yoursdkkeyhere000000000000000000",
    options: WarpLinkOptions(linkDomains: ["yourapp.aplnk.to", "links.yourapp.com"])
)
```

Or with no code, as a `WarpLinkDomains` string array in `Info.plist`. See the [iOS guide](https://warplink.app/docs/sdks/ios#declare-your-link-domains).

```kotlin
WarpLink.configure(
    context = this,
    apiKey = "wl_live_yoursdkkeyhere000000000000000000",
    options = WarpLinkOptions(linkDomains = listOf("yourapp.aplnk.to", "links.yourapp.com"))
)
```

Or with no code, as a comma separated `app.warplink.DOMAINS` meta-data entry in `AndroidManifest.xml`. See the [Android guide](https://warplink.app/docs/sdks/android#declare-your-link-domains).

```tsx
WarpLink.configure({
  apiKey: 'wl_live_yoursdkkeyhere000000000000000000',
  linkDomains: ['yourapp.aplnk.to', 'links.yourapp.com'],
});
```

The list is forwarded to the native SDKs. See the [React Native guide](https://warplink.app/docs/sdks/react-native#declare-your-link-domains).

```dart
await WarpLink.configure(
  apiKey: 'wl_live_yoursdkkeyhere000000000000000000',
  linkDomains: ['yourapp.aplnk.to', 'links.yourapp.com'],
);
```

The list is forwarded to the native SDKs. See the [Flutter guide](https://warplink.app/docs/sdks/flutter#declare-your-link-domains).

Declaring domains is additive: `aplnk.to` keeps working, and so does every domain the SDK learns from the server.

## Plans and limits

| Plan | Custom domains |
|------|----------------|
| Free | 1 trial domain for 90 days (about 3 months), analytics reset every 30 days |
| Starter | 3 domains |
| Growth | 5 domains |
| Scale | 10 domains |
| Unlimited | Unlimited domains |

Paid domains have no resets and no time limit. App association files for deep linking are generated automatically for every verified custom domain.

## Troubleshooting

**The dashboard still says "Pending DNS."** DNS changes take a few minutes to propagate. Confirm both records were saved at your DNS provider with the exact host and target shown in the dashboard, then use Refresh. If you copied a trailing dot or extra characters, remove them.

**The status shows "Needs attention."** This usually means the certificate record was removed or never added. Re-add the second record exactly as shown and the certificate will reissue automatically.

**WarpLink will not accept my root domain.** A root domain cannot point to WarpLink, because DNS does not allow a `CNAME` there. Add a subdomain such as `links.yourapp.com` instead.

**The app opens for my `yourapp.aplnk.to` links but not for my custom domain.** The app was never told about the domain, or the platform has not verified it. Paste the domain into our [deep link checker](https://warplink.app/tools/deep-link-checker) to read both association files. Add it to the Associated Domains entry (iOS) or the App Links intent filter (Android), and declare it to the SDK with `linkDomains` or the plist / manifest key. See [Use it in your app](#use-it-in-your-app).

**The custom domain works after a while, but not on the first launch.** Same cause. Without a local declaration the SDK only learns the domain from the server, which is why it starts working on a later launch. Declaring it removes the wait.

**A link 404s after the trial ended.** Trial domains stop serving at the 90-day mark. Upgrade to a paid plan to switch the domain back on instantly with its certificate intact.
