# Domains API

> Add your own domain to a project, get the DNS records to publish, verify ownership, and check where it points and whether HTTPS is working.

Source: https://docs.fortly.com/api/domains

A custom domain takes two DNS records. A TXT record proves the domain is yours; a CNAME sends its traffic to the project. A domain is stored unverified and serves nothing until the TXT record is found, so nobody can claim a domain they do not control. For the walkthrough, see [Custom domains](https://docs.fortly.com/domains).

- `GET /projects/{name}/domains` — A project's domains, with the records to publish
- `POST /projects/{name}/domains` — Add a domain
- `POST /projects/{name}/domains/{host}/verify` — Check the TXT record and start serving
- `DELETE /projects/{name}/domains/{host}` — Remove a domain
- `GET /domains` — Every domain across your projects

The records, for `shop.example.com` on the project `my-app-ada`:

```text
TXT    _fortly.shop.example.com    fortly-verify=4c1d…    # proves it is yours
CNAME  shop.example.com            my-app-ada.fortly.com  # sends traffic here
```

At the apex of a domain (`example.com` itself) a CNAME is not allowed; use your DNS provider's ALIAS or ANAME record if it has one.

## List a project's domains

- `GET /projects/{name}/domains` — A project's domains, with the records to publish

Each domain, whether it is verified, where its DNS points and the state of its certificate. This route looks up DNS for each domain, so it is slower than most.

- `name` (string, required) — Path parameter. The project's `name`.

**curl**

```bash
curl https://api.fortly.com/projects/my-app-ada/domains \
  -H "Authorization: Bearer $FORTLY_TOKEN"
```

**JavaScript**

```js
const res = await fetch("https://api.fortly.com/projects/my-app-ada/domains", {
  headers: { Authorization: `Bearer ${process.env.FORTLY_TOKEN}` },
});
const { domains } = await res.json();
```

```json
{
  "domains": [
    {
      "url": "https://shop.example.com",
      "host": "shop.example.com",
      "verified": true,
      "created_at": 1791820800,
      "record_name": "_fortly.shop.example.com",
      "record_short": "_fortly.shop",
      "record_value": "fortly-verify=4c1d9e2a7b3f4c8d9e0a1b2c3d4e5f6a",
      "points_to": "my-app-ada.fortly.com",
      "pointing": "here",
      "nameservers": [],
      "certificate": "active"
    }
  ],
  "custom_domains_ready": true
}
```

- `record_name` (string, optional) — Where to publish the TXT record, in full.
- `record_short` (string | null, optional) — The same name relative to the zone, which is what most DNS forms ask for. `null` when there is no shorter form.
- `record_value` (string, optional) — The TXT record's value.
- `points_to` (string, optional) — The CNAME target: the project's host.
- `pointing` (string, optional) — `unchecked` until the domain is verified; then `here` (the CNAME points at this project), `elsewhere`, `proxied` (served through your own Cloudflare zone), or `unknown` (no CNAME visible, as with a flattened apex record).
- `nameservers` (string[], optional) — The domain's nameservers, so you know where to make the change. Empty once the domain is verified and pointing here.
- `certificate` (string, optional) — HTTPS for the domain: `none`, `pending`, `active`, `failed`, or `external` when your own Cloudflare zone terminates TLS.
- `custom_domains_ready` (boolean, optional) — `false` when the platform cannot issue certificates for custom domains right now, so a domain added here would fail HTTPS.

## Add a domain

- `POST /projects/{name}/domains` — Add a domain

Adds a domain, unverified, and returns the TXT record to publish.

- `name` (string, required) — Path parameter. The project's `name`.
- `host` (string, required) — The hostname, such as `shop.example.com`. Lower-cased; a trailing dot is dropped. Needs at least one dot; letters, digits and hyphens only; no wildcards.

**curl**

```bash
curl -X POST https://api.fortly.com/projects/my-app-ada/domains \
  -H "Authorization: Bearer $FORTLY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"host":"shop.example.com"}'
```

**JavaScript**

```js
const res = await fetch("https://api.fortly.com/projects/my-app-ada/domains", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.FORTLY_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ host: "shop.example.com" }),
});
const { record_name, record_value } = await res.json();
```

Answers `201`:

```json
{
  "host": "shop.example.com",
  "verified": false,
  "record_name": "_fortly.shop.example.com",
  "record_value": "fortly-verify=4c1d9e2a7b3f4c8d9e0a1b2c3d4e5f6a"
}
```

| Status | When |
| --- | --- |
| `400` | Not a valid hostname, or a name under `fortly.com`, which Fortly gives out itself. |
| `403` | Your account has as many custom domains as its plan allows. The count is across all your projects, verified or not. |
| `409` | The domain is already on a project. |

## Verify a domain

- `POST /projects/{name}/domains/{host}/verify` — Check the TXT record and start serving

Looks up the TXT record now. When it is found the domain is marked verified, starts serving at once, and a certificate is requested for it.

- `name` (string, required) — Path parameter. The project's `name`.
- `host` (string, required) — Path parameter. The domain.

**curl**

```bash
curl -X POST https://api.fortly.com/projects/my-app-ada/domains/shop.example.com/verify \
  -H "Authorization: Bearer $FORTLY_TOKEN"
```

**JavaScript**

```js
const res = await fetch(
  "https://api.fortly.com/projects/my-app-ada/domains/shop.example.com/verify",
  { method: "POST", headers: { Authorization: `Bearer ${process.env.FORTLY_TOKEN}` } },
);
const { verified, certificate, why } = await res.json();
```

When the record is found:

```json
{ "host": "shop.example.com", "verified": true, "certificate": "pending" }
```

When it is not found yet, the answer is still `200`, with the reason and the record again:

```json
{
  "host": "shop.example.com",
  "verified": false,
  "why": "That record is not visible yet. DNS changes can take a few minutes.",
  "record_name": "_fortly.shop.example.com",
  "record_short": "_fortly.shop",
  "record_value": "fortly-verify=4c1d9e2a7b3f4c8d9e0a1b2c3d4e5f6a"
}
```

| Status | When |
| --- | --- |
| `404` | The domain is not on this project. |
| `502` | DNS could not be asked. This is not the same as the record being missing; try again. |

Verifying proves ownership; it does not send traffic. Publish the CNAME as well, then check `pointing` on [List a project's domains](#list-a-projects-domains).

## Remove a domain

- `DELETE /projects/{name}/domains/{host}` — Remove a domain

Stops serving the domain and releases its certificate.

- `name` (string, required) — Path parameter. The project's `name`.
- `host` (string, required) — Path parameter. The domain.

**curl**

```bash
curl -X DELETE https://api.fortly.com/projects/my-app-ada/domains/shop.example.com \
  -H "Authorization: Bearer $FORTLY_TOKEN"
```

**JavaScript**

```js
await fetch("https://api.fortly.com/projects/my-app-ada/domains/shop.example.com", {
  method: "DELETE",
  headers: { Authorization: `Bearer ${process.env.FORTLY_TOKEN}` },
});
```

Answers `204 No Content`. A domain can be removed only by the account that added it; for anyone else, and for a domain that is not on the project, the answer is `404` with `that domain is not on this project`.

## List all domains

- `GET /domains` — Every domain across your projects

Every custom domain on every project you can access.

**curl**

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

**JavaScript**

```js
const res = await fetch("https://api.fortly.com/domains", {
  headers: { Authorization: `Bearer ${process.env.FORTLY_TOKEN}` },
});
const { domains } = await res.json();
```

```json
{
  "domains": [
    {
      "project": "my-app-ada",
      "url": "https://shop.example.com",
      "host": "shop.example.com",
      "verified": true,
      "created_at": 1791820800,
      "points_to": "my-app-ada.fortly.com",
      "certificate": "active"
    }
  ]
}
```
