DomaineeDocs
Domains

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.

Don't want to build this UI? The connect widget renders these instructions, the one-click button and the verification polling for you, from one <script> tag. Overview: Domain Connect API.

Request

curl https://api.domainee.dev/v1/domains/$DOMAIN_ID/instructions \
  -H "Authorization: Bearer $DOMAINEE_API_KEY"

Query parameters

ParamNotes
redirect_uriWhere the customer lands after approving a Domain Connect flow at their provider. Use it to bring them back into your app. See Returning to your app.
stateOpaque 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

FieldNotes
provider.idStable slug, e.g. cloudflare, route53, godaddy. unknown when nothing matched.
provider.detectedfalse means the steps are generic. The record values are still correct.
apextrue when the hostname is its own zone root.
methoda or cname. Which one is right depends on both the position and the provider.
methodReasonWhy this method, in words you can show the end user.
recordsExactly what to publish. name is already written the way this provider's form wants it.
stepsOrdered, plain-language instructions. Safe to render as-is.
quirksProvider-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.

ProviderApexSubdomain blog.acme.com
Cloudflare, GoDaddy, Namecheap@blog
Route 53leave the record name blankblog
Google Cloud DNS, Akamaiacme.comblog.acme.com
Porkbun, Name.com, Dynadotleave the host field blankblog

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

CodeStatusWhen
not_found404Domain 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.

Providers only apply templates they have synced from the shared Domain-Connect/Templates repository and onboarded one by one, so availability grows provider by provider.

ProviderOne-click
CloudflareLive for all accounts. Records are created DNS only.
Glauca DigitalLive.
IONOS, Vercel, WordPress.comSupport the protocol, not yet onboarded. reason: "template_not_adopted".
GoDaddyNot available. GoDaddy is not onboarding new templates directly.
Namecheap, Route 53, Porkbun, Squarespace, Wix and most othersDon't support the protocol. reason: "no_discovery_record".

You don't need to track this list. supported turns true on its own as providers adopt the template.

{
  "domainConnect": {
    "supported": true,
    "providerId": "cloudflare.com",
    "providerName": "Cloudflare",
    "providerDisplayName": "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.

Returning to your app

At Cloudflare, redirect_uri can be any URL on your own domain. After the customer approves, Cloudflare sends them there with your query string intact and state appended, for example https://app.example.com/domains?id=123&state=abc. Check state against the value you passed, then call POST /v1/domains/{id}/check so the domain verifies right away instead of on the next 5-minute check.

Domainee's templates list domainee.dev as their redirect domain. Cloudflare does not enforce that, but other providers may once they enable the templates, and would then reject an outside redirect_uri. If you open applyUrl in a popup, you can leave redirect_uri out and run the check when your page regains focus, which works at every provider. Don't wait for the popup to close: Cloudflare's approval page sets Cross-Origin-Opener-Policy, so popup.closed reads true straight away. The connect widget already handles this.

When it is unavailable you get a reason instead:

{ "domainConnect": { "supported": false, "reason": "no_discovery_record" } }
reasonMeaning
no_discovery_recordThe zone has no _domainconnect TXT record, so the provider doesn't support the protocol.
settings_unavailableDiscovery record present but the provider's settings endpoint didn't answer.
no_sync_supportThe provider supports Domain Connect but not the synchronous flow.
template_not_adoptedThe provider supports the protocol but hasn't adopted Domainee's template yet.
lookup_failedThe DNS lookup itself failed. Retry.

Always render steps regardless. One-click depends on where each customer's DNS is hosted, so for most integrations supported is false more often than it is true, and the manual steps are the path many 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.

On this page