# Projects API

> List, create, configure and delete projects, check whether a name is free, and read the regions a project can be set to.

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

A project is one app with its own address, PostgreSQL database, files, secrets, domains and schedules. These routes manage the project itself; what it runs is on [Deploys](https://docs.fortly.com/api/deploys). Every route needs a [token](https://docs.fortly.com/api#authentication) except [List regions](#list-regions).

- `GET /projects` — Every project you can access
- `POST /projects` — Create a project
- `GET /projects/available` — Check whether a name is free
- `GET /projects/suggest` — Get a generated name nobody is using
- `PATCH /projects/{name}` — Change its region, start command or Dockerfile setting
- `DELETE /projects/{name}` — Delete it and everything in it
- `GET /regions` — The regions a project can be set to

## The project object

Every route that returns a project returns this shape.

- `name` (string, optional) — The project's identifier in every path and its hostname: the name you chose joined to your account's slug, such as `my-app-ada`.
- `label` (string, optional) — The name you chose, such as `my-app`.
- `host` (string, optional) — Its hostname.
- `url` (string, optional) — Where visitors reach it, scheme included.
- `state` (string, optional) — `running` while the project has a process of its own running, `stopped` otherwise. A static site, or a release served by the Fortly runtime, has no process of its own and reads `stopped` while it serves normally.
- `cache_enabled` (boolean, optional) — Whether responses are cached at the edge. On by default.
- `cache_ttl` (integer, optional) — Seconds a response is cached when it does not say. Default `60`.
- `region` (string, optional) — A region id from [List regions](#list-regions). New projects get `us-central`.
- `cmd` (string, optional) — The start command, as stored. Empty until a release or a [PATCH](#update-a-project) sets one.
- `ignore_dockerfile` (boolean, optional) — Whether the project's Dockerfile is ignored when it is built.
- `runtime` (string | null, optional) — What it is written in, guessed from the first word of `cmd`: `Node`, `Static`, `Python`, `Ruby`, `Go`, `PHP`, `Java` or `Rust`. `null` when not recognised.
- `db_bytes` (integer, optional) — Size of its database, in bytes.
- `files_count` (integer, optional) — Number of [stored files](/api/files).
- `files_bytes` (integer, optional) — Total size of stored files, in bytes.
- `live` (object | null, optional) — The release being served: `id`, `created_at`, `files`, `bytes`. `null` before the first release.
- `owner` (object | null, optional) — The account the project belongs to: `id` and `email`.

## List projects

- `GET /projects` — Every project you can access

Returns your own projects and those of every team you belong to, sorted by `name`.

**curl**

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

**JavaScript**

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

```json
{
  "projects": [
    {
      "name": "my-app-ada",
      "label": "my-app",
      "host": "my-app-ada.fortly.com",
      "url": "https://my-app-ada.fortly.com",
      "state": "running",
      "cache_enabled": true,
      "cache_ttl": 60,
      "region": "us-central",
      "cmd": "next start",
      "ignore_dockerfile": false,
      "runtime": null,
      "db_bytes": 8028160,
      "files_count": 12,
      "files_bytes": 3481920,
      "live": {
        "id": "dpl_3f9c2a71d04e4b8c9a6e5d1f2b7c8a90",
        "created_at": 1791820800,
        "files": 214,
        "bytes": 1893422
      },
      "owner": { "id": "8b1e4f0c2d6a4e7f9c3b5a1d0e2f4c6b", "email": "ada@example.com" }
    }
  ]
}
```

## Create a project

- `POST /projects` — Create a project

Creates an empty project with a database and an address. Nothing is served until its first [release](https://docs.fortly.com/api/deploys#upload-a-release).

- `name` (string, required) — 2 or more lower-case letters, digits and hyphens, not starting or ending with a hyphen. It becomes part of a hostname, so together with your account slug and a hyphen it must fit in 63 characters. Trimmed and lower-cased before it is checked.

**curl**

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

**JavaScript**

```js
const res = await fetch("https://api.fortly.com/projects", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.FORTLY_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ name: "my-app" }),
});
const project = await res.json(); // project.name is what every other route takes
```

Answers `201` with the new [project](#the-project-object):

```json
{
  "name": "my-app-ada",
  "label": "my-app",
  "host": "my-app-ada.fortly.com",
  "url": "https://my-app-ada.fortly.com",
  "state": "stopped",
  "cache_enabled": true,
  "cache_ttl": 60,
  "region": "us-central",
  "cmd": "",
  "ignore_dockerfile": false,
  "runtime": null,
  "db_bytes": 0,
  "files_count": 0,
  "files_bytes": 0,
  "live": null,
  "owner": { "id": "8b1e4f0c2d6a4e7f9c3b5a1d0e2f4c6b", "email": "ada@example.com" }
}
```

| Status | When |
| --- | --- |
| `400` | The name breaks a rule above, or is reserved by the platform (`api` is one). |
| `403` | Your plan's project limit is reached. Fortly Starter and Pro do not limit projects. |
| `409` | You already have a project with this name. The body includes a free `suggestion`. |
| `429` | Projects are being created too quickly on this account. See `Retry-After`. |

```json
{
  "error": "you already have a project called my-app",
  "suggestion": "my-app-2"
}
```

## Check a name

- `GET /projects/available` — Check whether a name is free

Says whether you could create a project with this name, and if not, a near one you could. It checks only your own projects; another account's `my-app` does not stand in your way.

- `name` (string, required) — Query parameter. The name to check.

**curl**

```bash
curl "https://api.fortly.com/projects/available?name=my-app" \
  -H "Authorization: Bearer $FORTLY_TOKEN"
```

**JavaScript**

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

```json
{
  "available": false,
  "suggestion": "my-app-2",
  "host": "my-app-2-ada.fortly.com"
}
```

`host` is the address the suggested name would be published at. A name that breaks the naming rules answers `200` with the reason instead:

```json
{
  "available": false,
  "problem": "a project name can use lower-case letters, digits and hyphens"
}
```

## Suggest a name

- `GET /projects/suggest` — Get a generated name nobody is using

Returns a generated name of two words and a number that none of your projects uses.

**curl**

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

**JavaScript**

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

```json
{ "name": "winter-harbor-24" }
```

## Update a project

- `PATCH /projects/{name}` — Change its region, start command or Dockerfile setting

Changes only the fields you send. Nothing is restarted.

- `name` (string, required) — Path parameter. The project's `name`.
- `region` (string, optional) — A region id from [List regions](#list-regions). Any other value is refused.
- `cmd` (string, optional) — The start command. It is recorded on every release that does not send its own [`x-fortly-cmd`](/api/deploys#upload-a-release). Static sites and releases on the Fortly runtime do not run it. An empty string clears it, and the command is then detected from each release.
- `ignore_dockerfile` (boolean, optional) — `true` to stop reading the project's Dockerfile when it is built.

Fortly runs the start command directly, not through a shell. A command containing `|`, `&`, `;`, `<`, `>`, a backtick, parentheses, quotes or `*` is refused with `400`, and so is one that starts with a `VAR=value` assignment. Put the shell part in a script your project ships and name the script here.

**curl**

```bash
curl -X PATCH https://api.fortly.com/projects/my-app-ada \
  -H "Authorization: Bearer $FORTLY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"cmd":"next start","region":"eu-west"}'
```

**JavaScript**

```js
const res = await fetch("https://api.fortly.com/projects/my-app-ada", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.FORTLY_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ cmd: "next start", region: "eu-west" }),
});
const project = await res.json();
```

Answers `200` with the updated [project](#the-project-object). In this response `files_count` and `files_bytes` are always `0`; read them from [List projects](#list-projects).

## Delete a project

- `DELETE /projects/{name}` — Delete it and everything in it

Stops the project and removes its releases, database, files, secrets, domains and schedules, including the copies in object storage. The name becomes free for anyone.

> **Warning:**
>
> There is no undo. [Export the database](https://docs.fortly.com/api/data#export-the-database) first if you might want it.

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

Only the account owner or a team admin can delete a project; a member gets `403`.

**curl**

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

**JavaScript**

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

Answers `204 No Content`. A `502` means object storage did not confirm the removal; send the request again.

## List regions

- `GET /regions` — The regions a project can be set to

The fixed list of region ids [Update a project](#update-a-project) accepts, and the default. Needs no token.

**curl**

```bash
curl https://api.fortly.com/regions
```

**JavaScript**

```js
const { regions, default: fallback } = await (await fetch("https://api.fortly.com/regions")).json();
```

```json
{
  "regions": [
    { "id": "us-central", "name": "US Central", "city": "Dallas", "country": "United States", "area": "North America" },
    { "id": "us-east", "name": "US East", "city": "Ashburn", "country": "United States", "area": "North America" }
  ],
  "default": "us-central"
}
```

The ids are `us-central`, `us-east`, `us-west`, `ca-central`, `sa-east`, `eu-west`, `eu-central`, `eu-north`, `uk-south`, `ap-south`, `ap-southeast`, `ap-northeast` and `au-southeast`.

> **Note:**
>
> A region is recorded on the project and every release inherits it. Changing it does not move or restart a running project.
