CustomDomain · Schema
NameserverMigration
CompanyCustom DomainsDNSSSLEdge ProxySaaS InfrastructureWhite LabelMCP
Properties
| Name | Type | Description |
|---|---|---|
| id | string | |
| connection_id | string | |
| zone | string | The registrable domain whose nameservers move. |
| state | string | `pending`: target `custom` waits for the platform's zone. `copying`: the zone is being created and the records copied; a copy whose write to the DNS servers failed is retried, never switched to. `need |
| path | string | |
| target | string | |
| dns_host | string | The DNS host of target `dns_host` (an adapter id such as `cloudflare`). |
| provider | string | Where the nameservers are changed: the registrar's provider id (`registrar.provider`), or, when `registrar` is null, the DNS host the domain used, as a guess (then `next_step` says "where the domain i |
| registrar | object | Who the domain is registered with (RDAP, or the account or key the caller brought); null when unknown. |
| old_dns_host | string | The DNS host the domain used when the move started (a provider id such as `cloudflare`). |
| old_nameservers | array | What the registry listed when the move started; a rollback sets these. |
| new_nameservers | array | |
| observed_nameservers | array | What the registry listed at the last check. |
| waiting_for | string | |
| next_step | string | One sentence for a person. |
| next_action | object | The one request to press next, read from the move alone (the same for every caller), so a card can show the button `next_step` only describes. `null` when nothing is waiting for a press: the move is c |
| failure_code | string | `changed_elsewhere`: the registrar listed other nameservers than the ones the move started from, so the change did not overwrite them. |
| failure_detail | string | |
| records_copied | integer | |
| email_records | integer | How many of the copied records carry mail (MX, SPF, DKIM, DMARC). |
| email_kinds | array | |
| copy_source | string | How the records were copied. `api`: the whole zone, read through a key we hold at the DNS host the domain used (a kept sign in or a connected account there); proven complete. `discovery`: read from pu |
| copy_warnings | array | What a person should check before the switch: records left out (a Cloudflare proxied record answers with Cloudflare's addresses, not the origin), lookups that went unanswered, forwarding that stays at |
| copy_reviewed_at | string | When a person confirmed a copy that is not proven complete. |
| ds_present | boolean | The registry published a DS record (DNSSEC was on) during the move. |
| ds_removed_at | string | |
| ds_cleared_at | string | |
| can_rollback | boolean | |
| rollback_path | string | How the move back was made: `api` (the registrar's API, done), `agent` (the person signs in to the registrar in the cloud browser with `purpose: nameservers` and approves it), `manual` (`next_step` sa |
| rollback_targets | array | The move backs possible now; `previous` is left out once the old nameservers no longer answer for the zone. |
| rollback_default | string | Where a move back without `rollback_target` goes. |
| rollback_target | string | Where the move back went. |
| rollback_set_at | string | When the move back was made at the registrar; absent while a person or the cloud browser still has it to do. |
| old_dns_serves | booleannull | Whether the old nameservers still answered for the zone when last asked (once a day while active, and at a move back); null when not asked yet. |
| consent | object | |
| actor | string | |
| created_at | string | |
| set_at | string | When the nameservers were changed at the registrar (or first seen changed). |
| active_at | string | |
| failed_at | string | |
| rolled_back_at | string | |
| drifted_at | string | |
| checked_at | string | |
| next_check_at | string | |
| updated_at | string |
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://artifacts.apis.io/customdomain-ai/json-schema/customdomain-ai-nameserver-migration-schema.json",
"title": "NameserverMigration",
"x-generated": "2026-11-04",
"x-method": "derived",
"x-generator": "derive-json-schema.py",
"x-source": "openapi/customdomain-ai-openapi.yml#/components/schemas/NameserverMigration",
"type": "object",
"required": [
"id",
"zone",
"state",
"path",
"target",
"registrar",
"old_nameservers",
"new_nameservers",
"observed_nameservers",
"records_copied",
"email_records",
"email_kinds",
"copy_warnings",
"ds_present",
"can_rollback",
"rollback_targets",
"old_dns_serves",
"next_action",
"created_at",
"updated_at"
],
"properties": {
"id": {
"type": "string"
},
"connection_id": {
"type": "string"
},
"zone": {
"type": "string",
"description": "The registrable domain whose nameservers move."
},
"state": {
"type": "string",
"enum": [
"pending",
"copying",
"needs_review",
"needs_dnssec_off",
"waiting_ds",
"setting",
"propagating",
"active",
"failed",
"rolled_back"
],
"description": "`pending`: target `custom` waits for the platform's zone. `copying`: the zone is being created and the records copied; a copy whose write to the DNS servers failed is retried, never switched to. `needs_review`: the copy is not proven complete (it was read from public DNS, which can't list a zone, or some records could not be carried over); a person checks `copy_warnings` and confirms with `copy_reviewed`. `needs_dnssec_off`: DNSSEC must be turned off at the registrar first. `waiting_ds`: DNSSEC is off; waiting for the registry to stop publishing the DS record and its TTL to pass. `setting`: the nameservers are being changed (`waiting_for` says by whom). `propagating`: changed; waiting for the registry to list every new nameserver (checked every 5 minutes for 2 hours, then hourly up to 48 hours). `active`: the registry lists them (re-checked every 6 hours; a move away is `failed` with `delegation_drift`). `failed`: see `failure_code`. `rolled_back`: moved back."
},
"path": {
"type": "string",
"enum": [
"api",
"agent",
"manual"
]
},
"target": {
"type": "string",
"enum": [
"managed",
"dns_host",
"custom"
]
},
"dns_host": {
"type": "string",
"description": "The DNS host of target `dns_host` (an adapter id such as `cloudflare`)."
},
"provider": {
"type": "string",
"description": "Where the nameservers are changed: the registrar's provider id (`registrar.provider`), or, when `registrar` is null, the DNS host the domain used, as a guess (then `next_step` says \"where the domain is registered\" and names no company)."
},
"registrar": {
"description": "Who the domain is registered with (RDAP, or the account or key the caller brought); null when unknown.",
"oneOf": [
{
"$ref": "#/$defs/NameserverRegistrar"
},
{
"type": "null"
}
]
},
"old_dns_host": {
"type": "string",
"description": "The DNS host the domain used when the move started (a provider id such as `cloudflare`)."
},
"old_nameservers": {
"type": "array",
"items": {
"type": "string"
},
"description": "What the registry listed when the move started; a rollback sets these."
},
"new_nameservers": {
"type": "array",
"items": {
"type": "string"
}
},
"observed_nameservers": {
"type": "array",
"items": {
"type": "string"
},
"description": "What the registry listed at the last check."
},
"waiting_for": {
"type": "string",
"enum": [
"zone_ready",
"zone_served",
"copy_review",
"dnssec_off",
"ds_clear",
"credential",
"registrar_confirm",
"browser_session",
"registrar",
"delegation"
]
},
"next_step": {
"type": "string",
"description": "One sentence for a person."
},
"next_action": {
"description": "The one request to press next, read from the move alone (the same for every caller), so a card can show the button `next_step` only describes. `null` when nothing is waiting for a press: the move is copying, waiting for the registry, waiting for a person at the registrar, DNSSEC or the copy review (`copy_reviewed`), or over. Because it is the same for every caller, it can name a request the caller who reads it is refused. Four cases: `sign_in` points at `POST /v1/connections/{id}/browser:start`, which runs only from a customer's browser or the console and refuses every agent token and every API key without the console's client label (`403 browser_customer_present_only`); `retry` is refused `403 ns_not_offered` to an agent token while the application's DNS hosting is off; `retry` is refused `403 forbidden` to a widget token when the setting is off; and `retry` of a move made with a connected account is refused `403 forbidden` to an API key whose member may not use that account (another member's). A caller in one of those cases tells the person (`next_step` says what to do) and does not send the request. `rollback` is open to all three callers whatever the setting says.",
"oneOf": [
{
"$ref": "#/$defs/NameserverNextAction"
},
{
"type": "null"
}
]
},
"failure_code": {
"type": "string",
"enum": [
"zone_copy_failed",
"zone_not_served",
"dnssec_timeout",
"ds_removal_failed",
"credential_refused",
"registrar_refused",
"registrar_locked",
"changed_elsewhere",
"agent_failed",
"delegation_timeout",
"delegation_drift",
"internal"
],
"description": "`changed_elsewhere`: the registrar listed other nameservers than the ones the move started from, so the change did not overwrite them."
},
"failure_detail": {
"type": "string"
},
"records_copied": {
"type": "integer"
},
"email_records": {
"type": "integer",
"description": "How many of the copied records carry mail (MX, SPF, DKIM, DMARC)."
},
"email_kinds": {
"type": "array",
"items": {
"type": "string",
"enum": [
"mx",
"spf",
"dkim",
"dmarc"
]
}
},
"copy_source": {
"type": "string",
"enum": [
"api",
"discovery",
"existing_zone"
],
"description": "How the records were copied. `api`: the whole zone, read through a key we hold at the DNS host the domain used (a kept sign in or a connected account there); proven complete. `discovery`: read from public DNS at the names domains commonly use; a person confirms it. `existing_zone`: the platform's zone already held records, which were kept."
},
"copy_warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "What a person should check before the switch: records left out (a Cloudflare proxied record answers with Cloudflare's addresses, not the origin), lookups that went unanswered, forwarding that stays at the old host."
},
"copy_reviewed_at": {
"type": "string",
"format": "date-time",
"description": "When a person confirmed a copy that is not proven complete."
},
"ds_present": {
"type": "boolean",
"description": "The registry published a DS record (DNSSEC was on) during the move."
},
"ds_removed_at": {
"type": "string",
"format": "date-time"
},
"ds_cleared_at": {
"type": "string",
"format": "date-time"
},
"can_rollback": {
"type": "boolean"
},
"rollback_path": {
"type": "string",
"enum": [
"api",
"agent",
"manual",
"none"
],
"description": "How the move back was made: `api` (the registrar's API, done), `agent` (the person signs in to the registrar in the cloud browser with `purpose: nameservers` and approves it), `manual` (`next_step` says what to set or choose), `none` (nothing had changed yet)."
},
"rollback_targets": {
"type": "array",
"items": {
"type": "string",
"enum": [
"previous",
"registrar_default"
]
},
"description": "The move backs possible now; `previous` is left out once the old nameservers no longer answer for the zone."
},
"rollback_default": {
"type": "string",
"enum": [
"previous",
"registrar_default"
],
"description": "Where a move back without `rollback_target` goes."
},
"rollback_target": {
"type": "string",
"enum": [
"previous",
"registrar_default"
],
"description": "Where the move back went."
},
"rollback_set_at": {
"type": "string",
"format": "date-time",
"description": "When the move back was made at the registrar; absent while a person or the cloud browser still has it to do."
},
"old_dns_serves": {
"type": [
"boolean",
"null"
],
"description": "Whether the old nameservers still answered for the zone when last asked (once a day while active, and at a move back); null when not asked yet."
},
"consent": {
"type": "object",
"required": [
"id",
"version",
"at"
],
"properties": {
"id": {
"type": "string"
},
"version": {
"type": "string"
},
"at": {
"type": "string",
"format": "date-time"
}
}
},
"actor": {
"type": "string",
"enum": [
"end_user",
"api_key",
"console",
"agent"
]
},
"created_at": {
"type": "string",
"format": "date-time"
},
"set_at": {
"type": "string",
"format": "date-time",
"description": "When the nameservers were changed at the registrar (or first seen changed)."
},
"active_at": {
"type": "string",
"format": "date-time"
},
"failed_at": {
"type": "string",
"format": "date-time"
},
"rolled_back_at": {
"type": "string",
"format": "date-time"
},
"drifted_at": {
"type": "string",
"format": "date-time"
},
"checked_at": {
"type": "string",
"format": "date-time"
},
"next_check_at": {
"type": "string",
"format": "date-time"
},
"updated_at": {
"type": "string",
"format": "date-time"
}
},
"$defs": {
"NameserverNextAction": {
"type": "object",
"required": [
"kind",
"method",
"path"
],
"description": "A request that moves a nameserver migration on, with the connection's id filled in. The caller adds what it always adds to that route.",
"properties": {
"kind": {
"type": "string",
"enum": [
"sign_in",
"retry",
"rollback"
],
"description": "`sign_in`: the move waits for the cloud browser (state `needs_dnssec_off` or `setting` on path `agent`, or a move back whose `rollback_path` is `agent` and not yet made), so the person signs in to their registrar and approves what will change. The request is the registrar sign in's start. `retry`: the move failed before anything was changed at the registrar (`can_rollback` is false); the start call with this `body` begins the same move again, on the same path and, for `api` with a connected account, with that account, where a start with no body picks them afresh (a move made with a key sent once keeps no account: its retry is `{path: api}` and the key is sent again; the end user's start also sends `consent`). `rollback`: the move failed after the registrar was changed or asked (`can_rollback` is true), and moving back is the safe way out, to where a move back without a choice goes (`rollback_default`)."
},
"method": {
"type": "string",
"enum": [
"POST"
]
},
"path": {
"type": "string",
"description": "The route: `/v1/connections/{id}/browser:start` for `sign_in`, `/v1/connections/{id}/nameservers:migrate` for `retry`, `/v1/connections/{id}/nameservers:rollback` for `rollback`."
},
"body": {
"type": "object",
"additionalProperties": true,
"description": "The fields the route needs beyond the ones the caller always sends. `sign_in`: `purpose` `nameservers` and the registrar's `provider` (the caller adds `return_origin` and `consent_version`). `rollback`: `rollback_target`. `retry`: the move's `path`, and for `api` with a connected account that account's `provider_account_id` (a widget token reads the path only: it may not name an account; a move made with a key sent once has none, so only `{path: api}`), absent when the move recorded no path. A caller who may not use that account is refused `403 forbidden`."
}
}
},
"NameserverRegistrar": {
"type": "object",
"description": "The company a domain is registered with, where its nameservers are changed. Looked up over RDAP at the TLD's registry (found through the IANA bootstrap file) and matched to a provider by its IANA Registrar ID, so a domain registered at GoDaddy with its DNS at Cloudflare is changed at GoDaddy.",
"required": [
"name",
"source"
],
"properties": {
"name": {
"type": "string",
"description": "The brand (\"GoDaddy\"), the reseller the registry names, or RDAP's own name."
},
"provider": {
"type": "string",
"description": "Our provider id for it, when we route to it (`godaddy`)."
},
"iana_id": {
"type": "integer",
"description": "The IANA Registrar ID, when the registry gives one."
},
"url": {
"type": "string",
"description": "The registrar's website."
},
"source": {
"type": "string",
"enum": [
"rdap",
"request",
"account",
""
],
"description": "`rdap`: looked up. `request`: the caller named it with a key or an account. `account`: RDAP said nothing, and a connected account read the domain's registration. Empty: RDAP named a registrar we do not route to (`name` only)."
}
}
}
}
}
Work with this as data
Every JSON Schema here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for schemas
4 MCP tools reach this
find_json_schemasBrowse and filter every JSON Schema in the catalog.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.
Call it yourself
curl for this page
This JSON Schema
curl "https://apis.io/api/v1/json-schemas/customdomain-ai-nameserver-migration"
All schemas
curl "https://apis.io/api/v1/json-schemas?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.