CustomDomain · Schema

Connection

CompanyCustom DomainsDNSSSLEdge ProxySaaS InfrastructureWhite LabelMCP

Properties

Name Type Description
id string
application_id string
domain string
host string subdomain label; empty means the apex
provider_id string detected / applied DNS provider
setup_type object
status object
managed boolean `true` when the connection opted into **managed mode**: the control plane holds a durable, encrypted async grant and can re-apply (`POST /v1/connections/{id}:reapply`) or revert (`DELETE /v1/connectio
created_at string
sandbox boolean `true` when the connection belongs to a test mode application. Its DNS is simulated, nothing real is read, written or served, and it counts for nothing (no free domain, quota, billing or metering). Co
last_checked_at string When the propagation poller last examined this connection. Omitted until the first check.
www_redirect boolean Serve an apex↔www redirect. Echoed from create.
override_spf boolean Replace an existing SPF record instead of merging into it, where the provider supports it. Echoed from create.
validate_dmarc boolean Stored and echoed from create; not acted on yet.
validate_caa boolean Whether the Let's Encrypt CAA records are part of the desired record set. Echoed from create.
monitor boolean Whether the domain is enrolled in drift monitoring. Echoed from create, where it defaults to true when omitted, and honoured: the drift watch is registered when the connection goes live only if this i
batch_id string The multi-domain session this connection was created in. Omitted for a standalone connect.
end_user_ref string The integrator's own identifier for the end user who connected this domain. Set once at create and never changed: it binds widget tokens and saved access to the connection. Omitted for a console/direc
client_label string The name of the client or customer the domain is for, set at create or with `PATCH /connections/{id}`. Omitted when none is set. It is the platform's own note about the client: it is left out of every
retried_at string When a human (or the create-path resume, or real edge traffic) last asked for this connection to be re-verified. The retry clock is max(created_at, retried_at): a revived connection gets a fresh windo
routing object Where this domain points: its application's routing when the connection was created. A change to the application's routing moves it only while it is still waiting for DNS; once connected it keeps this
records_source string Present as `integrator` when the connection's desired record set was supplied through `PUT /v1/connections/{id}/records` rather than authored by us. Omitted for the usual server-synthesized or Domain
created_via string Where the connection was created, recorded once at create and never changed. `widget`: an end user's connect-scoped widget token (the embedded widget in your product). `console`: the CustomDomain™ con
secure_root_domain boolean The edge also issues/secures the registrable apex alongside this subdomain connection. Omitted (treated as `false`) when unset.
redirect_to string URL the edge 301s this host to (Power redirect target). Omitted when the connection is not a redirect.
power_root_path_access array URL paths the edge may static-serve at the root (Power root-path access). Omitted when none are configured.
error_code string The last failure reason recorded for this connection, from whichever path failed, not only the background poller. It is persisted by: the propagation poller / `:recheck` verification (e.g. `propagatio
error_message string Human-readable detail for `error_code`. Populated on the same paths and cleared alongside it. Omitted when there is none.
write_rail string The rail that last wrote this connection's records. Omitted when no rail wrote them (a manual setup, or a connection from before this was recorded). It decides what removing the domain can do with the
provider_account_id string The connected provider account whose credential wrote this connection's records and, when the account's access is `continuous`, keeps them (repairs and removal). Omitted when none.
removal_started_at string Set while the domain is being removed: the records are being taken out and the connection is deleted when that finishes. The domain no longer serves from this moment. Omitted otherwise.
dns_hosting object
View JSON Schema on GitHub

JSON Schema

customdomain-ai-connection-schema.json Raw ↑
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/customdomain-ai/json-schema/customdomain-ai-connection-schema.json",
  "title": "Connection",
  "x-generated": "2026-11-04",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/customdomain-ai-openapi.yml#/components/schemas/Connection",
  "type": "object",
  "required": [
    "id",
    "application_id",
    "domain",
    "setup_type",
    "status",
    "created_at"
  ],
  "properties": {
    "id": {
      "type": "string"
    },
    "application_id": {
      "type": "string"
    },
    "domain": {
      "type": "string"
    },
    "host": {
      "type": "string",
      "description": "subdomain label; empty means the apex"
    },
    "provider_id": {
      "type": "string",
      "description": "detected / applied DNS provider"
    },
    "setup_type": {
      "$ref": "#/$defs/SetupType"
    },
    "status": {
      "$ref": "#/$defs/ConnectionStatus"
    },
    "managed": {
      "type": "boolean",
      "description": "`true` when the connection opted into **managed mode**: the control plane holds a durable, encrypted async grant and can re-apply (`POST /v1/connections/{id}:reapply`) or revert (`DELETE /v1/connections/{id}`) the template server-side without re-consent. Omitted (treated as `false`) for the default use-once connection."
    },
    "created_at": {
      "type": "string",
      "format": "date-time"
    },
    "sandbox": {
      "type": "boolean",
      "description": "`true` when the connection belongs to a test mode application. Its DNS is simulated, nothing real is read, written or served, and it counts for nothing (no free domain, quota, billing or metering). Copied from the application at create and never changed. Omitted (treated as `false`) on a real connection."
    },
    "last_checked_at": {
      "type": "string",
      "format": "date-time",
      "description": "When the propagation poller last examined this connection. Omitted until the first check."
    },
    "www_redirect": {
      "type": "boolean",
      "description": "Serve an apex↔www redirect. Echoed from create."
    },
    "override_spf": {
      "type": "boolean",
      "description": "Replace an existing SPF record instead of merging into it, where the provider supports it. Echoed from create."
    },
    "validate_dmarc": {
      "type": "boolean",
      "description": "Stored and echoed from create; not acted on yet."
    },
    "validate_caa": {
      "type": "boolean",
      "description": "Whether the Let's Encrypt CAA records are part of the desired record set. Echoed from create."
    },
    "monitor": {
      "type": "boolean",
      "default": true,
      "description": "Whether the domain is enrolled in drift monitoring. Echoed from create, where it defaults to true when omitted, and honoured: the drift watch is registered when the connection goes live only if this is true. Connections created before the flag was honoured read true (they were monitored unconditionally, and were backfilled), so this is false only where a caller explicitly opted out."
    },
    "batch_id": {
      "type": "string",
      "description": "The multi-domain session this connection was created in. Omitted for a standalone connect."
    },
    "end_user_ref": {
      "type": "string",
      "description": "The integrator's own identifier for the end user who connected this domain. Set once at create and never changed: it binds widget tokens and saved access to the connection. Omitted for a console/direct connect, and from every answer a delegated agent token reads. The name of the client a domain is for is `client_label`."
    },
    "client_label": {
      "type": "string",
      "maxLength": 200,
      "description": "The name of the client or customer the domain is for, set at create or with `PATCH /connections/{id}`. Omitted when none is set. It is the platform's own note about the client: it is left out of every answer an end user's widget token or a delegated agent token reads, and an API key or an `app`-scoped token reads it."
    },
    "retried_at": {
      "type": "string",
      "format": "date-time",
      "description": "When a human (or the create-path resume, or real edge traffic) last asked for this connection to be re-verified. The retry clock is max(created_at, retried_at): a revived connection gets a fresh window from here, not from its original creation. Absent when it was never retried."
    },
    "routing": {
      "allOf": [
        {
          "$ref": "#/$defs/ConnectionRouting"
        }
      ],
      "description": "Where this domain points: its application's routing when the connection was created. A change to the application's routing moves it only while it is still waiting for DNS; once connected it keeps this until `POST /v1/connections/{id}:retarget` moves it. Omitted only for a connection created before this was recorded, which follows its application's current routing."
    },
    "records_source": {
      "type": "string",
      "enum": [
        "integrator"
      ],
      "description": "Present as `integrator` when the connection's desired record set was supplied through `PUT /v1/connections/{id}/records` rather than authored by us. Omitted for the usual server-synthesized or Domain Connect template-materialized set. Such a connection cannot use the Domain Connect rail, which applies our published templates at the provider."
    },
    "created_via": {
      "type": "string",
      "enum": [
        "",
        "widget",
        "console",
        "api",
        "agent",
        "mcp"
      ],
      "description": "Where the connection was created, recorded once at create and never changed. `widget`: an end user's connect-scoped widget token (the embedded widget in your product). `console`: the CustomDomain™ console. `api`: an API key, or an app-scoped token, from your server. `agent`: a delegated agent access token. `mcp`: the MCP server. Empty for connections created before this was recorded. A trusted server-side caller may name its surface with the `X-CustomDomain-Client` request header (`console` or `mcp`); the header is a label only and grants nothing."
    },
    "secure_root_domain": {
      "type": "boolean",
      "description": "The edge also issues/secures the registrable apex alongside this subdomain connection. Omitted (treated as `false`) when unset."
    },
    "redirect_to": {
      "type": "string",
      "format": "uri",
      "description": "URL the edge 301s this host to (Power redirect target). Omitted when the connection is not a redirect."
    },
    "power_root_path_access": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "URL paths the edge may static-serve at the root (Power root-path access). Omitted when none are configured."
    },
    "error_code": {
      "type": "string",
      "description": "The last failure reason recorded for this connection, from whichever path failed, not only the background poller. It is persisted by: the propagation poller / `:recheck` verification (e.g. `propagation_timeout`, `setup_incomplete`, `pending_expired`, `domain_already_connected`); the foreground apply on `POST /connections/{id}/apply` (e.g. `dns_write_failed`, `ProviderAuthenticationError`, `spf_merge_failed`, `apex_not_supported`, `forwarding_not_supported`, `porkbun_api_access_disabled`); the OAuth callback (which also records `forwarding_not_supported`); and the Domain Connect async callback (both e.g. `provider_unavailable`, `ProviderAuthenticationError`, `apply_failed`, `internal_error`, or the provider's own `error` parameter). Rejected request validation (`400`) is deliberately NOT recorded here: a malformed request is not a connection failure. Omitted when healthy or never failed; cleared when the connection next verifies."
    },
    "error_message": {
      "type": "string",
      "description": "Human-readable detail for `error_code`. Populated on the same paths and cleared alongside it. Omitted when there is none."
    },
    "write_rail": {
      "type": "string",
      "enum": [
        "api_key",
        "sign_in",
        "domain_connect",
        "domain_connect_async",
        "route53_role",
        "provider_account",
        "managed_dns"
      ],
      "description": "The rail that last wrote this connection's records. Omitted when no rail wrote them (a manual setup, or a connection from before this was recorded). It decides what removing the domain can do with the records (`GET /connections/{id}/removal`)."
    },
    "provider_account_id": {
      "type": "string",
      "description": "The connected provider account whose credential wrote this connection's records and, when the account's access is `continuous`, keeps them (repairs and removal). Omitted when none."
    },
    "removal_started_at": {
      "type": "string",
      "format": "date-time",
      "description": "Set while the domain is being removed: the records are being taken out and the connection is deleted when that finishes. The domain no longer serves from this moment. Omitted otherwise."
    },
    "dns_hosting": {
      "$ref": "#/$defs/ConnectionDnsHosting"
    }
  },
  "$defs": {
    "ConnectionDnsHosting": {
      "type": "object",
      "description": "Who runs the domain's DNS: a zone CustomDomain™ hosts, or a move to one. It is read from the managed DNS zone the connection is linked to and the workspace's nameserver moves, never from the connection itself, so it is on the list and on `GET /connections/{id}` and nowhere else a connection appears. A linked zone decides first (`active` is hosted, `awaiting_delegation` is moving, `failed` is failed). Without one, a nameserver move of the workspace on the domain or a parent of it decides (`active` is hosted, a move still under way is moving, one that failed because the domain's nameservers moved away again is failed; a rolled back or otherwise failed move counts for nothing). A test mode connection is always `none`.",
      "required": [
        "state"
      ],
      "properties": {
        "state": {
          "type": "string",
          "enum": [
            "none",
            "moving",
            "hosted",
            "failed"
          ],
          "description": "`none`: nothing of ours runs this domain's DNS (no other field is set). `moving`: the zone is being set up or the nameservers are being moved, and the domain is not served from it yet. `hosted`: the nameservers point at the zone, which is what the world resolves. `failed`: the zone never became active, or the domain moved its nameservers away again; someone has to look."
        },
        "by": {
          "type": "string",
          "enum": [
            "customdomain",
            "dns_host",
            "custom"
          ],
          "description": "Who runs the DNS: CustomDomain™'s own managed DNS (`customdomain`), our account at a DNS host we integrate with (`dns_host`; `provider` names it), or the platform's own list of nameservers (`custom`). Omitted when `state` is `none`."
        },
        "provider": {
          "type": "string",
          "description": "The DNS host's provider id when `by` is `dns_host`, for example `cloudflare`."
        },
        "zone": {
          "type": "string",
          "description": "The zone that is hosted or being set up: the connection's domain or a parent of it (`acme.com` for `shop.acme.com`)."
        },
        "since": {
          "type": "string",
          "format": "date-time",
          "description": "When the domain reached this state: the zone's activation for `hosted`, the start of the move for `moving`, the failure for `failed`."
        }
      }
    },
    "ConnectionRouting": {
      "type": "object",
      "required": [
        "mode",
        "set_at"
      ],
      "properties": {
        "mode": {
          "type": "string",
          "enum": [
            "edge",
            "custom"
          ],
          "description": "`edge`: records point at the CustomDomain™ edge. `custom`: at your own infrastructure."
        },
        "cname_target": {
          "type": "string",
          "description": "For `custom`: the hostname subdomains and ALIAS-capable roots point at."
        },
        "apex_ipv4": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "For `custom`: the addresses roots get on DNS providers with no ALIAS-style record."
        },
        "set_at": {
          "type": "string",
          "format": "date-time",
          "description": "When this routing was recorded for the domain."
        }
      }
    },
    "ConnectionStatus": {
      "type": "string",
      "enum": [
        "pending",
        "propagating",
        "live",
        "failed"
      ]
    },
    "SetupType": {
      "type": "string",
      "description": "How a connection reaches live.",
      "enum": [
        "automatic",
        "manual",
        "async",
        "mcp"
      ]
    }
  }
}

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.
All 92 tools →

Call it yourself

curl for this page
This JSON Schema
curl "https://apis.io/api/v1/json-schemas/customdomain-ai-connection"
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.