# Domains · Management API

> Management API reference for domains: GET /sites/{id}/domains, PUT /sites/{id}/domains, GET /sites/{id}/domains/status.

> Markdown version of https://kanorio.com/en/developers/management-api/domains. Append `.md` to any developer docs URL for this format. Index: https://kanorio.com/llms.txt · MCP: https://app.kanorio.com/api/mcp

Base URL: `https://app.kanorio.com/api/v1` · [Errors](https://kanorio.com/en/developers/basics/errors) · [Pagination](https://kanorio.com/en/developers/basics/pagination)

### List domains

`GET /sites/{id}/domains`

- Stability: beta
- Permission: `console:view`
- Rate limit: default (120/minute per token)
- MCP tool: `domains_list`

List the custom domains bound to a website (primary first) with their verification status, all in one page. Errors: FORBIDDEN (also when the website does not exist).

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Website id. Get it from sites.list or from sites.create. |

**Response** (200)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | object[] | yes | — |
| `data[].hostname` | string | yes | — |
| `data[].kind` | string | yes | CUSTOM_PRIMARY (canonical) or CUSTOM_ALIAS (301 to primary). |
| `data[].isPrimary` | boolean | yes | — |
| `data[].status` | string | yes | PENDING until DNS and TLS are verified, then ACTIVE. |
| `hasMore` | boolean | yes | True when another page exists; pass nextCursor to fetch it. |
| `nextCursor` | string \| null | yes | Cursor for the next page, or null on the last page. |

**Errors**

| Code | Status | When |
| --- | --- | --- |
| `FORBIDDEN` | 403 | also when the website does not exist |

```bash
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/domains" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
```

```json
{
  "data": [
    {
      "hostname": "www.sunnybakery.example",
      "kind": "CUSTOM",
      "isPrimary": true,
      "status": "ACTIVE"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

### Bind domains

`PUT /sites/{id}/domains`

- Stability: beta
- Permission: `site:domains`
- Rate limit: default (120/minute per token)
- MCP tool: `domains_bind`

Set the complete list of custom domains for a website: the first hostname becomes the primary (canonical) domain, the rest become aliases that 301-redirect to it. Hostnames missing from the list are unbound; pass an empty list to remove all custom domains. After binding, the owner must point DNS (CNAME) at Kanorio; call domains.status to see the exact record and verification progress. Requires a paid plan. Errors: FORBIDDEN, PRO_REQUIRED, INVALID_FORMAT, BLOCKED, HIGH_RISK_TLD, LIMIT_REACHED (details.max), TAKEN (used by another site), READD_COOLDOWN (details.retryAfterSeconds).

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Website id. Get it from sites.list or from sites.create. |

**Request body**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `hostnames` | string[] | yes | Ordered hostnames without scheme, e.g. ["www.example.com", "example.com"]. First = primary. |

**Response** (200)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `domains` | object[] | yes | — |
| `domains[].hostname` | string | yes | — |
| `domains[].kind` | string | yes | CUSTOM_PRIMARY (canonical) or CUSTOM_ALIAS (301 to primary). |
| `domains[].isPrimary` | boolean | yes | — |
| `domains[].status` | string | yes | PENDING until DNS and TLS are verified, then ACTIVE. |
| `cloudflareErrors` | string[] | yes | Non-fatal provisioning errors; the binding is saved and will be reconciled. |
| `cooldowns` | object \| null | yes | Removed hostnames → seconds before they can be re-added. |

**Errors**

| Code | Status | When |
| --- | --- | --- |
| `FORBIDDEN` | 403 | — |
| `PRO_REQUIRED` | 403 | — |
| `INVALID_FORMAT` | 400 | — |
| `BLOCKED` | 422 | — |
| `HIGH_RISK_TLD` | 422 | — |
| `LIMIT_REACHED` | 403 | details.max |
| `TAKEN` | 409 | used by another site |
| `READD_COOLDOWN` | 409 | details.retryAfterSeconds |

```bash
curl -X PUT "https://app.kanorio.com/api/v1/sites/{id}/domains" \
  -H "Authorization: Bearer $KANORIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"hostnames":["www.sunnybakery.example","sunnybakery.example"]}'
```

```json
{
  "domains": [
    {
      "hostname": "www.sunnybakery.example",
      "kind": "CUSTOM",
      "isPrimary": true,
      "status": "PENDING"
    }
  ],
  "cloudflareErrors": [],
  "cooldowns": null
}
```

### Domain status

`GET /sites/{id}/domains/status`

- Stability: beta
- Permission: `site:domains`
- Rate limit: default (120/minute per token)
- MCP tool: `domains_status`

Re-check DNS/TLS for every PENDING custom domain right now and return the current list. Domains that are still PENDING include a `diagnoses[hostname]` entry explaining what record is missing (expected vs. observed). Also returns the primary binding. Poll every 15-60 seconds after domains.bind until status is ACTIVE; DNS propagation can take minutes to hours. Errors: FORBIDDEN.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Website id. Get it from sites.list or from sites.create. |

**Response** (200)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `domains` | object[] | yes | — |
| `domains[].hostname` | string | yes | — |
| `domains[].kind` | string | yes | CUSTOM_PRIMARY (canonical) or CUSTOM_ALIAS (301 to primary). |
| `domains[].isPrimary` | boolean | yes | — |
| `domains[].status` | string | yes | PENDING until DNS and TLS are verified, then ACTIVE. |
| `primary` | object \| null | yes | — |
| `primary.hostname` | string | yes | — |
| `primary.status` | string | yes | — |
| `diagnoses` | object | yes | — |

**Errors**

| Code | Status | When |
| --- | --- | --- |
| `FORBIDDEN` | 403 | — |

```bash
curl -X GET "https://app.kanorio.com/api/v1/sites/{id}/domains/status" \
  -H "Authorization: Bearer $KANORIO_TOKEN"
```

```json
{
  "domains": [
    {
      "hostname": "www.sunnybakery.example",
      "kind": "CUSTOM",
      "isPrimary": true,
      "status": "ACTIVE"
    }
  ],
  "primary": {
    "hostname": "www.sunnybakery.example",
    "status": "ACTIVE"
  },
  "diagnoses": {}
}
```
