Domain Connect is an open standard that lets a service, say a website builder or an email host, set up DNS records for a customer's domain at the customer's DNS provider. The customer doesn't type a single record. The service finds the DNS provider through a _domainconnect TXT record, checks that the provider knows the service's template, and sends the customer there with a link. The customer logs in, confirms, and the DNS provider applies the records.
I wrote the first version of this post in 2020. The example domain I used back then has lost its record since, so everything below is re-checked against live DNS in 2026.
Why it exists #
Before Domain Connect a service had to guess. It looked up the nameservers, matched them against a hard-coded table of providers and showed the customer screenshots of someone else's DNS panel. Every time the DNS provider redesigned that panel, the screenshots went stale, and both companies got the support tickets.
There are two sides. The service provider is the app that needs the records. The DNS provider is whoever hosts the zone. The records live in a template: a JSON file that the service provider onboards with DNS providers once. Public templates are in the Domain-Connect/Templates repository. For an email service the template is usually MX, SPF and DKIM records.
Step 1: find the DNS provider #
dig _domainconnect.domainconnect.org TXT +noall +answer
# _domainconnect.domainconnect.org. 3587 IN CNAME _domainconnect.gd.domaincontrol.com.
# _domainconnect.gd.domaincontrol.com. 3587 IN TXT "domainconnect.api.godaddy.com"The record doesn't have to sit in the zone itself. Here it's a CNAME to one record that GoDaddy keeps for all its zones, and the spec allows that. Cloudflare answers with a plain TXT, and its value has a path in it: api.cloudflare.com/client/v4/dns/domainconnect. So treat the value as a URL prefix, not a host name.
No record means the DNS provider doesn't support Domain Connect. Then you're back to manual instructions.
Step 2: read the settings #
The prefix from step 1 gives you the settings for this domain:
curl -s https://domainconnect.api.godaddy.com/v2/domainconnect.org/settings{
"nameServers": ["ns53.domaincontrol.com", "ns54.domaincontrol.com"],
"providerDisplayName": "GoDaddy",
"providerId": "godaddy.com",
"providerName": "GoDaddy",
"urlAPI": "https://domainconnect.api.godaddy.com",
"urlAsyncUX": "https://dcc.godaddy.com/manage",
"urlControlPanel": "https://dcc.godaddy.com/manage/dns",
"urlSyncUX": "https://dcc.godaddy.com/manage"
}urlSyncUX is where you send the customer in the synchronous flow, the one this post is about. urlAsyncUX is for the OAuth flow, when your service changes DNS later without the customer in front of the screen. urlAPI is for API calls like the check in step 3. The fields are optional, and it shows: Cloudflare returns no urlAsyncUX, so for domains on Cloudflare there's only the synchronous flow.
Step 3: check that the provider has your template #
curl -s -o /dev/null -w "%{http_code}\n" \
https://domainconnect.api.godaddy.com/v2/domainTemplates/providers/exampleservice.domainconnect.org/services/template1
# 200200 means the template is supported, 404 means it isn't. A template in the public repository isn't automatically live everywhere. The example template exampleservice.domainconnect.org/template1 gives 200 at GoDaddy and 404 at Cloudflare. Check every DNS provider you care about.
Step 4: build the apply URL #
{urlSyncUX}/v2/domainTemplates/providers/{providerId}/services/{serviceId}/apply?domain=...&{variables}The variables come from the template. template1 has an A record with %IP% and a TXT record with %RANDOMTEXT%, so the link carries IP=... and RANDOMTEXT=.... Two more parameters are worth adding. redirect_uri is where the DNS provider sends the customer back, and it has to be on the template's syncRedirectDomain unless you sign the request. state is a random string you check when the customer returns.
Signing means a sig parameter made with your private key and a key parameter that points to a TXT record with the public key in the template's syncPubKeyDomain. The spec has the details.
After the click the DNS provider does its part: signs the customer in, checks that they control the zone, shows the changes, applies them after confirmation and redirects back.
The whole thing in PHP #
No library needed. This is plain PHP 8:
<?php
$domain = $argv[1] ?? 'example.com';
$providerId = 'exampleservice.domainconnect.org';
$serviceId = 'template1';
// 1. Discovery: who runs DNS for this domain, and do they speak Domain Connect?
$records = dns_get_record('_domainconnect.' . $domain, DNS_TXT) ?: [];
$prefix = $records[0]['txt'] ?? null;
if ($prefix === null) {
exit("No Domain Connect at this DNS provider, show manual instructions\n");
}
// 2. Settings: the URLs to use for this domain.
$settings = json_decode(
file_get_contents("https://{$prefix}/v2/{$domain}/settings"),
true,
flags: JSON_THROW_ON_ERROR,
);
// 3. Does this DNS provider have our template?
$templateUrl = "{$settings['urlAPI']}/v2/domainTemplates/providers/{$providerId}/services/{$serviceId}";
$status = (int) substr(get_headers($templateUrl)[0], 9, 3);
if ($status !== 200 || !isset($settings['urlSyncUX'])) {
exit("{$settings['providerDisplayName']} can't apply {$serviceId}, show manual instructions\n");
}
// 4. The link for the "Connect your domain" button.
$applyUrl = "{$settings['urlSyncUX']}/v2/domainTemplates/providers/{$providerId}/services/{$serviceId}/apply?"
. http_build_query([
'domain' => $domain,
'IP' => '203.0.113.10',
'RANDOMTEXT' => 'shm:1234567890',
'redirect_uri' => 'https://exampleservice.domainconnect.org/done',
'state' => bin2hex(random_bytes(8)),
]);
echo $applyUrl, "\n";For domainconnect.org it prints the GoDaddy link:
https://dcc.godaddy.com/manage/v2/domainTemplates/providers/exampleservice.domainconnect.org/services/template1/apply?domain=domainconnect.org&IP=203.0.113.10&RANDOMTEXT=shm%3A1234567890&redirect_uri=https%3A%2F%2Fexampleservice.domainconnect.org%2Fdone&state=7251b4ef58951e20For a domain on Cloudflare it stops at step 3. For this site it stops at step 1, because my hosting provider doesn't support Domain Connect. All three cases need the manual fallback, so build that page anyway.
Two notes on the code. dns_get_record() follows the CNAME for you, and host in its result is the CNAME target, not your domain. And file_get_contents() is only there to keep the example short. The settings call happens while the customer waits for the page, so in real code use an HTTP client with a short timeout.