Get connect instructions
GET /v1/domains/{id}/instructions — one-click Domain Connect where available, provider-specific manual steps everywhere else.
GET /v1/domains/{id}/instructions
Returns setup steps written for the DNS provider actually serving the customer's domain, instead of a generic "add a CNAME".
Most custom-domain connections die at the last mile: the end user opens a registrar panel they have never seen and has to guess which of three name fields to use, whether their provider can even hold a CNAME-like record at a root domain, and which provider-specific toggle silently breaks everything. This endpoint answers all three.
Domainee fingerprints the provider from the zone's public NS records, so
there is nothing to configure and no credentials involved.
Request
curl https://api.domainee.dev/v1/domains/$DOMAIN_ID/instructions \
-H "Authorization: Bearer $DOMAINEE_API_KEY"
Query parameters
| Param | Notes |
|---|---|
redirect_uri | Where the customer lands after approving a Domain Connect flow at their provider. Use it to bring them back into your app. |
state | Opaque value round-tripped through that redirect. Use it for CSRF protection and to identify the domain on return. |
Response — 200 OK
{
"instructions": {
"hostname": "acme.com",
"provider": { "id": "cloudflare", "name": "Cloudflare", "detected": true },
"apex": true,
"method": "a",
"methodReason": "At a root domain the A records route each visitor to the nearest region, which ALIAS or CNAME flattening cannot do.",
"records": [
{ "type": "A", "name": "@", "value": "75.2.52.197", "ttl": 300 },
{ "type": "A", "name": "@", "value": "3.33.255.144", "ttl": 300 }
],
"steps": [
"Sign in to Cloudflare at https://dash.cloudflare.com.",
"Open the DNS record editor: Select your domain > DNS > Records > Add record.",
"Add 2 A records with the name @, one for each address below. Add all 2: one alone works but leaves you with no redundancy.",
"Delete any existing A, AAAA or CNAME record with the name @, or it will answer alongside the new ones.",
"Save. We re-check every 5 minutes and issue the TLS certificate on the first request, so there is nothing else to do."
],
"quirks": [
"Set Proxy status to DNS only (the grey cloud). Leaving it proxied (orange) makes Cloudflare terminate TLS itself, and the certificate will never be issued."
],
"dashboardUrl": "https://dash.cloudflare.com"
}
}
Fields
| Field | Notes |
|---|---|
provider.id | Stable slug, e.g. cloudflare, route53, godaddy. unknown when nothing matched. |
provider.detected | false means the steps are generic. The record values are still correct. |
apex | true when the hostname is its own zone root. |
method | a or cname. Which one is right depends on both the position and the provider. |
methodReason | Why this method, in words you can show the end user. |
records | Exactly what to publish. name is already written the way this provider's form wants it. |
steps | Ordered, plain-language instructions. Safe to render as-is. |
quirks | Provider-specific gotchas. Show these; they are the difference between a connection that works and one that silently doesn't. |
The name field is the point
Providers disagree about how to write a record name, and this is the single
most common way a correct record ends up in the wrong place. A user who types
the full hostname into a form that already appends the zone gets
blog.acme.com.acme.com and no error message.
| Provider | Apex | Subdomain blog.acme.com |
|---|---|---|
| Cloudflare, GoDaddy, Namecheap | @ | blog |
| Route 53 | leave the record name blank | blog |
| Google Cloud DNS, Akamai | acme.com | blog.acme.com |
| Porkbun, Name.com, Dynadot | leave the host field blank | blog |
records[].name is already correct for the detected provider, so you can put
it straight into your UI without a lookup table of your own.
Why a separate endpoint
This does a live NS lookup, so it is not a field on the
Domain object. Listing 500 domains must not fan
out 500 DNS queries. Call it when you are about to show someone setup steps.
Errors
| Code | Status | When |
|---|---|---|
not_found | 404 | Domain doesn't exist in this workspace. |
Detection itself never fails. If the NS lookup returns nothing or the
nameservers match no known provider, provider.detected is false and you get
correct generic steps.
One-click setup with Domain Connect
Every response also carries a domainConnect object. When the customer's DNS
provider supports Domain Connect and has
adopted Domainee's template, they can approve a single screen at their own
provider instead of editing records by hand.
Status: not live yet. Domain Connect providers apply templates they sync from the shared Domain-Connect/Templates repository, and Domainee's are still awaiting review there. Until they are merged and synced,
supportedisfalsewithreason: "template_not_adopted"at every provider. The field is documented now so you can build against its final shape; wire upstepstoday andapplyUrlwill start appearing on its own, with no change on your side.
{
"domainConnect": {
"supported": true,
"providerId": "cloudflare.com",
"providerName": "Cloudflare",
"applyUrl": "https://dash.cloudflare.com/domainconnect/v2/domainTemplates/providers/domainee.dev/services/edge-apex/apply?domain=acme.com&ip1=...&redirect_uri=...",
"width": 750,
"height": 750
}
}
Send the customer to applyUrl, in a popup sized width × height or as a
full redirect. Their provider writes the records and returns them to your
redirect_uri with your state. No credentials reach Domainee and there is
nothing for the customer to revoke afterwards.
When it is unavailable you get a reason instead:
{ "domainConnect": { "supported": false, "reason": "no_discovery_record" } }
reason | Meaning |
|---|---|
no_discovery_record | The zone has no _domainconnect TXT record, so the provider doesn't support the protocol. |
settings_unavailable | Discovery record present but the provider's settings endpoint didn't answer. |
no_sync_support | The provider supports Domain Connect but not the synchronous flow. |
template_not_adopted | The provider supports the protocol but hasn't adopted Domainee's template yet. |
lookup_failed | The DNS lookup itself failed. Retry. |
Always render steps regardless. Domain Connect coverage is
roughly 40-50% of end customers at best, supported is false far more often
than it is true, and the manual steps are the path most of your customers
will actually take. Treat applyUrl as a shortcut you offer when it exists,
never as the only route.
MCP
The same thing is available to AI agents as the get_connect_instructions
tool, so an agent that just created a domain can walk the customer through
pointing it. See the MCP guide.