# Domains API endpoints

> Cloider API reference for domains, claim a free cloider.app subdomain or add a custom domain, check availability, list, get and delete domains with their records and routes.

These endpoints manage the domains of a Cloider account: **free subdomains** under `cloider.app` that Cloider hosts for you, and **custom domains** you own and point at Cloider's nameservers. A domain does nothing by itself: [DNS records](/api/endpoints/dns-records) say what its names resolve to, and [targets](/api/endpoints/targets) route its paths to an app, a cluster service or a bucket's static website over HTTPS.

All paths are relative to `https://cloider.com/api`. Domains are identified by their name, not by an id: `GET /domains/example.com`. See [Domains overview](/domains/overview).

## At a glance

| Method | Path | Access |
| --- | --- | --- |
| GET | [`/domains`](#list-domains) | Read |
| POST | [`/domains`](#add-a-domain) | Write |
| GET | [`/domains/availability`](#check-whether-a-free-subdomain-is-available) | Read |
| GET | [`/domains/:domain_name`](#get-a-domain) | Read |
| DELETE | [`/domains/:domain_name`](#delete-a-domain) | Write |

## The domain object

```json
{
  "domain_name": "example.com",
  "domain_type": "CustomDomain",
  "status": "Active",
  "created_at": "2026-09-20T13:05:41.880214Z"
}
```

| Field | Type | Description |
| --- | --- | --- |
| `domain_name` | string | The domain, in lower case. It is the domain's identifier in every endpoint |
| `domain_type` | string | `Subdomain` for a free subdomain such as `blog.cloider.app`, `CustomDomain` for your own domain |
| `status` | string | `Pending` (just added, nothing routed yet), `Active` (at least one target routes to it), `Suspended` (its last target was removed) or `Failed` |
| `created_at` | string | When the domain was added |

## List domains

```http
GET /domains
```

Returns every domain of the account, sorted by name in reverse alphabetical order.

**Access:** any token.

```bash
curl https://cloider.com/api/domains \
  -H "Authorization: Bearer $CLOIDER_TOKEN"
```

```json title="Response"
[
  {"domain_name": "example.com", "domain_type": "CustomDomain", "status": "Active", "created_at": "2026-09-20T13:05:41.880214Z"},
  {"domain_name": "blog.cloider.app", "domain_type": "Subdomain", "status": "Active", "created_at": "2026-10-01T09:20:33.102994Z"}
]
```

## Check whether a free subdomain is available

```http
GET /domains/availability?domain_name=:name
```

Checks whether a free subdomain can be claimed, with the same rules `POST /domains` and app creation apply. It claims nothing.

**Access:** any token.

| Query parameter | Required | Description |
| --- | --- | --- |
| `domain_name` | Yes | The full subdomain, for example `shop.cloider.app` |

```bash
curl "https://cloider.com/api/domains/availability?domain_name=shop.cloider.app" \
  -H "Authorization: Bearer $CLOIDER_TOKEN"
```

```json title="Response"
{"domain_name": "shop.cloider.app", "available": true, "reason": ""}
```

When `available` is `false`, `reason` says why, for example `This subdomain is already taken. Please try another one.`, `You already own this subdomain. Connect it to the app from Domains instead.` or a naming rule such as `Subdomain name cannot start with numbers`.

## Add a domain

```http
POST /domains
```

Claims a free subdomain or adds a custom domain to the account.

**Access:** Read & Write token.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `domain_name` | string | Yes | The full domain. Lower cased before it is checked |
| `domain_type` | string | Yes | `Subdomain` or `CustomDomain` |

**Free subdomains** are one label under `cloider.app` (`GET /options` lists the root as `subdomain_roots`): 2 to 63 characters of lower case letters, digits and `-`, starting with a letter and not starting or ending with `-`, and no dots of their own.

**Custom domains** are 3 to 253 characters of lower case letters, digits, dots and hyphens, with at least two labels. Domains under `cloider` are not allowed.

**Custom domain**

```bash
curl -X POST https://cloider.com/api/domains \
  -H "Authorization: Bearer $CLOIDER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"domain_name": "example.com", "domain_type": "CustomDomain"}'
```

**Free subdomain**

```bash
curl -X POST https://cloider.com/api/domains \
  -H "Authorization: Bearer $CLOIDER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"domain_name": "shop.cloider.app", "domain_type": "Subdomain"}'
```

```json title="Response"
{"domain_name": "example.com", "domain_type": "CustomDomain", "status": "Pending", "created_at": "2026-10-20T10:12:09.441027Z"}
```

Adding a domain the account already has returns the existing domain with `200`, so this request is safe to repeat. A new custom domain also gets an `ALIAS` record for the domain itself that points at `cloider.app` (and, once a target routes the domain to an app with a free subdomain, at that subdomain), unless it's under another of the account's custom domains; it's listed with the domain's [DNS records](/api/endpoints/dns-records). After adding a custom domain, set its nameservers at your registrar to the ones in `GET /options` `nameservers` (`ns1.cloider.com` to `ns4.cloider.com`); see [Nameservers](/domains/nameservers).

| Status | Field | Message |
| --- | --- | --- |
| `400` | `domain_name` | `Invalid domain format`, `Disallowed domain name`, `Domain name cannot have upper case letters`, `Domain name must not have consecutive dots`, `Domain name must contain only letters, numbers, dots, and hyphens`, `Domain name must not exceed 253 characters`, `Domain name is a public suffix shared by many owners, add your own domain under it, such as example.co.uk` |
| `400` | `domain_name` | `Invalid root domain name`, `Subdomain name cannot contain dots (multi-level subdomains not allowed)`, `Subdomain name cannot start with numbers`, `Subdomain name must be at least 2 characters` |
| `400` | `_` | `Invalid domain type specified` |
| `400` | `_` | `This subdomain is already taken. Please try another one.` |
| `400` | `_` | `The domain already created by a different user, if you own this domain contact support` |
| `400` | `_` | `This domain is under example.com, which belongs to another Cloider account. If you own example.com, contact support` |
| `400` | `_` | `A domain under this one belongs to another Cloider account. If you own example.com, contact support` |
| `400` | `_` | `Your account is suspended. Upgrade to a paid plan to continue using our services.` |
| `400` | `_` | `You have read-only access to this account, you can not create domains!` (a Restricted member) |
| `403` | `_` | `This API token is read only!` |

## Get a domain

```http
GET /domains/:domain_name
```

Returns one domain with its targets, newest first. For an app or bucket target, `namespace` and `service` are empty.

**Access:** any token.

```bash
curl https://cloider.com/api/domains/example.com \
  -H "Authorization: Bearer $CLOIDER_TOKEN"
```

```json title="Response (trimmed)"
{
  "domain_name": "example.com",
  "domain_type": "CustomDomain",
  "status": "Active",
  "created_at": "2026-09-20T13:05:41.880214Z",
  "targets": [
    {"hashed_id": "t4k9pz", "domain_name": "example.com", "target_type": "App", "app_id": "a7c2mq", "app_name": "web", "app_label": "web", "namespace": "", "service": "", "port": 8080, "path": "/", "target_path": "/"}
  ]
}
```

A domain that doesn't exist, or belongs to another account, answers `404 Not Found` (plain text). The domain's records have their own endpoint, [`GET /domains/example.com/dns-records`](/api/endpoints/dns-records#list-a-domains-records), and [`GET /targets?domain_name=example.com`](/api/endpoints/targets#list-a-domains-targets) lists the targets on their own.

## Delete a domain

```http
DELETE /domains/:domain_name
```

Deletes a domain together with all of its DNS records and targets. Apps, cluster services and bucket websites that were routed through it keep running, but are no longer reachable on it. This can't be undone, and a free subdomain becomes available to anyone.

**Access:** Read & Write token.

```bash
curl -X DELETE https://cloider.com/api/domains/example.com \
  -H "Authorization: Bearer $CLOIDER_TOKEN"
```

The response is the account's remaining domain list.

| Status | Message |
| --- | --- |
| `400` | `Operation failed, could not delete DNS records` and other `Operation failed, ...` messages when the deletion couldn't complete; nothing was deleted |
| `403` | `This API token is read only!`, `You have read-only access to this account, you can not delete domains!` |
| `404` | `Not Found` (plain text) |

See [Delete a domain](/domains/delete-a-domain).

## Related

- [DNS records](/api/endpoints/dns-records) and [targets](/api/endpoints/targets)
- [Free subdomains](/domains/free-subdomains) and [add a custom domain](/domains/add-a-custom-domain)
- [cloider domain](/cli/commands/domain) and the [cloider_domain](/terraform/resources/domain) Terraform resource
