# Files API

> List, upload, rename and delete the files a project stores, which are served at /_files/ on the project's address without starting your app.

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

A project can store files, such as uploads, images and documents. They live in object storage and are served by the platform at `/_files/{path}` on the project's address, so an idle project still serves them without starting. For how they are served, see [Files](https://docs.fortly.com/files).

- `GET /projects/{name}/files` — Every stored file
- `POST /projects/{name}/files/{path}` — Upload a file, replacing one at the same path
- `POST /projects/{name}/rename-file/{path}` — Move a file to a new path
- `DELETE /projects/{name}/files/{path}` — Delete a file

## File paths

`{path}` is the rest of the URL after `files/` or `rename-file/`, and may contain slashes: `avatars/ada.png`. Percent-encode each segment, not the slashes between them. A leading `/` is ignored and `.` segments are dropped. A path that is empty, longer than 512 characters, contains a line break or uses `..` to step outside the project is refused with `400`.

Files are served with a type chosen from the extension: common image, font, audio, video, PDF, text, CSV, JSON and ZIP types. Anything else, HTML included, is served as `application/octet-stream`, so an upload never runs as a page on your project's origin.

## List files

- `GET /projects/{name}/files` — Every stored file

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

**curl**

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

**JavaScript**

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

```json
{
  "files": [
    {
      "path": "avatars/ada.png",
      "bytes": 48213,
      "modified": 1791820800,
      "url": "https://my-app-ada.fortly.com/_files/avatars/ada.png"
    }
  ]
}
```

`modified` is Unix seconds, or `null` when storage does not report it. `url` is where visitors fetch the file.

## Upload a file

- `POST /projects/{name}/files/{path}` — Upload a file, replacing one at the same path

The body is the file's bytes, sent raw, up to 64 MB. A file already at this path is replaced, and the edge cache for the project is cleared so the new one is served.

- `name` (string, required) — Path parameter. The project's `name`.
- `path` (string, required) — Path parameter. Where to store it, such as `avatars/ada.png`. See [File paths](#file-paths).

**curl**

```bash
curl -X POST https://api.fortly.com/projects/my-app-ada/files/avatars/ada.png \
  -H "Authorization: Bearer $FORTLY_TOKEN" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @ada.png
```

**JavaScript**

```js
import { readFile } from "node:fs/promises";

const res = await fetch("https://api.fortly.com/projects/my-app-ada/files/avatars/ada.png", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.FORTLY_TOKEN}`,
    "Content-Type": "application/octet-stream",
  },
  body: await readFile("ada.png"),
});
const { path, bytes } = await res.json();
```

Answers `201`:

```json
{
  "path": "avatars/ada.png",
  "bytes": 48213,
  "url": "http://my-app-ada.fortly.com/_files/avatars/ada.png"
}
```

`path` is the cleaned path the file was stored under. The `url` in this response is always built as `http://{host}/_files/{path}`. For the address visitors should use, with the project's real scheme, take `url` from [List files](#list-files) or [Rename a file](#rename-a-file).

| Status | When |
| --- | --- |
| `400` | The body is empty (`that file is empty`), or the path is refused. |
| `413` | The file is over 64 MB. |

## Rename a file

- `POST /projects/{name}/rename-file/{path}` — Move a file to a new path

Moves a stored file to a new path. The file is copied to the new path first and the old one is deleted only after that, so a failure never loses it.

- `name` (string, required) — Path parameter. The project's `name`.
- `path` (string, required) — Path parameter. The file's current path.
- `to` (string, required) — Body. The new path, under the same [rules](#file-paths). A file already there is replaced.

**curl**

```bash
curl -X POST https://api.fortly.com/projects/my-app-ada/rename-file/avatars/ada.png \
  -H "Authorization: Bearer $FORTLY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"to":"avatars/ada-lovelace.png"}'
```

**JavaScript**

```js
const res = await fetch(
  "https://api.fortly.com/projects/my-app-ada/rename-file/avatars/ada.png",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.FORTLY_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ to: "avatars/ada-lovelace.png" }),
  },
);
const file = await res.json();
```

```json
{
  "path": "avatars/ada-lovelace.png",
  "bytes": 48213,
  "url": "https://my-app-ada.fortly.com/_files/avatars/ada-lovelace.png"
}
```

| Status | When |
| --- | --- |
| `400` | A path is refused, or `to` is the file's current path (`that is already its name`). |
| `404` | No file at `{path}` (`no such file`). |

## Delete a file

- `DELETE /projects/{name}/files/{path}` — Delete a file

Deletes the file and clears the edge cache for the project.

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

**curl**

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

**JavaScript**

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

Answers `204 No Content`. Deleting a path that holds no file also answers `204`.
