# Downloads: Mosaic API reference

> Downloads in Mosaic’s local REST API: 3 routes, each with its method, path, fields, what it returns and the errors it names.

Source: https://mosaicbrowser.com/docs/api/downloads

**A profile’s data** REST API

## Downloads

The files profiles have saved, when `downloadMode` is `"perProfile"`.

Mosaic has two answers for where a download lands. `shared` is what every browser does and what this app did for its whole life: one folder, because a download is a file the operator asked for and burying it per profile means a different unopenable folder each time. `perProfile` answers the question that never addressed — a downloaded file is **evidence of a profile**, and in `~/Downloads` it sits with every other profile’s files, in the first place anybody looks, in the search index, outliving the profile that made it. In that mode each profile writes into its own folder inside Mosaic’s data directory, its **file picker starts there too** (Chromium moves that preference when somebody browses elsewhere, and it is written again at the next launch, so the folder is where each session begins rather than a cage), the files go when the profile is purged, and these routes report and empty them.

**It is not encryption.** Chromium writes the bytes itself with its own file handle; nothing is between the network and the disk. What the mode buys is isolation from the home folder, a `.metadata_never_index` marker so Spotlight never indexes the contents, and a wipe that actually wipes. Anything stronger is FileVault.

In `shared` mode these routes still answer, and `mode` says so — they report nothing because the folder belongs to the operating system, and offering to empty somebody’s `~/Downloads` is not a thing this API will do.

- [GETWhat each profile has saved](#list-downloads)
- [DELETEEmpty one profile’s files](#clear-profile-downloads)
- [POSTEmpty several, by name](#clear-downloads)

## [What each profile has saved](#list-downloads)

GET`/api/v1/downloads`

One pass over the store. Every profile gets an entry whether or not it holds anything, so a caller can render a complete table; `orphans` are folders whose profile no longer exists, which nothing else in the product can name and which `all: true` below is the only way to remove. `.crdownload` parts are not counted — reporting a file somebody cannot open yet, at a size about to change, is worse than not reporting it.

#### Returns

`{ mode, root, entries: [{ profileId, name, path, files, bytes, newestAt }], orphans: [… with name: null], totalFiles, totalBytes }`

```
curl -H "Authorization: Bearer $KEY" "$BASE/downloads"
```

## [Empty one profile’s files](#clear-profile-downloads)

DELETE`/api/v1/downloads/:profileId`

Deletes the files and recreates the folder empty — a running browser resolved its download directory at startup, and a folder that vanishes underneath it makes the next download fail with a message about a path, which reads as a broken browser. The profile, its cookies and its history are untouched. There is no trash for these and nothing to restore from.

#### Path parameters

| Field | Type | What it is |
| --- | --- | --- |
| `profileId` | `string` | Profile id. A `404` if no such profile, rather than silently emptying a folder named by a typo. |

#### Returns

`{ profileId, files, bytes }` — what actually went.

#### Errors

- `404` — no profile with that id.

```
curl -X DELETE -H "Authorization: Bearer $KEY" "$BASE/downloads/$ID"
```

## [Empty several, by name](#clear-downloads)

POST`/api/v1/downloads/clear`

**An absent scope is a `400`.** This is the rule this repository paid 586 profiles for, on a route whose subject is somebody’s downloaded documents: `{}` is refused rather than read as the flattering "they must have meant all of them". `all: true` exists, has to be said out loud, and is the only form that also removes the orphaned folders — no list of profile ids can name those. Sending both is refused too, because they describe different sets. The report is per profile with the files and bytes that went, because one number over a list is not an answer.

#### Body

| Field | Type | What it is |
| --- | --- | --- |
| `ids` | `string[]` | Profile ids whose files to delete. At least one, unless `all`. |
| `all` | `boolean` | Every profile’s files **and** every orphaned folder. Cannot be combined with `ids`. |

#### Returns

`{ cleared: [{ profileId, files, bytes }], files, bytes }`

#### Errors

- `400` — neither `ids` nor `all: true`; nothing was deleted.
- `400` — both `ids` and `all: true`.

```
curl -X POST -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"ids":["'$ID'"]}' "$BASE/downloads/clear"
```

## Run the API on your own profiles

The REST API and the MCP server are part of Pro: $49 a month, with the first 14 days free. Free has every browser feature and no API key.

Mosaic is a desktop app

Email yourself the link and open it on your computer.
