# Extensions and templates: Mosaic API reference

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

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

**Content and transfer** REST API

## Extensions and templates

A library to attach from. Nothing here is applied to a profile on its own.

- [GETList the extension library](#list-extensions)
- [POSTRegister an extension](#add-extension)
- [PATCHUpdate an extension](#update-extension)
- [DELETERemove from the library](#delete-extension)
- [GETList presets](#list-templates)
- [POSTAuthor a preset](#create-template)
- [PATCHChange a preset](#update-template)
- [POSTSave a profile as a preset](#template-from-profile)
- [DELETEDelete a preset](#delete-template)

## [List the extension library](#list-extensions)

GET`/api/v1/extensions`

Extensions registered for use, not extensions currently attached to anything.

#### Query parameters

| Field | Type | What it is |
| --- | --- | --- |
| `projectId` | `string` | Restrict to one project. |

#### Returns

`{ total, extensions: ExtensionEntry[] }`

## [Register an extension](#add-extension)

POST`/api/v1/extensions`

Adds an unpacked extension folder to the library so profiles can attach it.

#### Body

| Field | Type | What it is |
| --- | --- | --- |
| `name`required | `string` | Display name. |
| `path`required | `string` | Absolute path to the unpacked folder. |
| `projectId` | `string` | Owning project. |

#### Returns

The created `ExtensionEntry`.

## [Update an extension](#update-extension)

PATCH`/api/v1/extensions/:id`

Rename it or re-point it at a different folder.

#### Path parameters

| Field | Type | What it is |
| --- | --- | --- |
| `id`required | `string` | The record’s id. |

#### Body

`Partial<ExtensionEntry>`. name, path.

#### Returns

The updated entry.

## [Remove from the library](#delete-extension)

DELETE`/api/v1/extensions/:id`

Removes the entry. Profiles already referencing the path keep working.

#### Path parameters

| Field | Type | What it is |
| --- | --- | --- |
| `id`required | `string` | The record’s id. |

#### Returns

`{ deleted: true }`

## [List presets](#list-templates)

GET`/api/v1/templates`

Saved sets of profile settings used as a starting point for new profiles. A preset with a null `projectId` is available everywhere. Each preset describes either a desktop or a phone — read off the `os` in its own `fingerprintOptions`, never stored beside them — and applying one kind to the other produces a machine that is neither, because a phone’s screen, touch points, GPU and capability record come from a captured device record rather than the desktop catalogue.

#### Query parameters

| Field | Type | What it is |
| --- | --- | --- |
| `projectId` | `string` | Return only the presets usable in this project, which includes the global ones. |
| `kind` | `"desktop" \| "mobile"` | Only presets describing this kind of machine. Anything else is a `400` naming the field, because a filter that is silently ignored looks exactly like a filter that matched everything. |

#### Returns

`{ total, templates: ProfileTemplate[] }`

#### Errors

- `400` — `kind` is neither "desktop" nor "mobile".

## [Author a preset](#create-template)

POST`/api/v1/templates`

Builds a preset from scratch: what every profile made from it starts with. Stores settings, never an identity — a preset carries the *options* a fingerprint is drawn from and each profile draws its own, because two profiles sharing one is two profiles a site reads as one. To learn one off a profile you already have, use `POST /templates/from-profile` instead. An unknown field is a `400` naming it, rather than a preset that silently does less than it was asked to.

#### Body

| Field | Type | What it is |
| --- | --- | --- |
| `name`required | `string` | Preset name. It cannot be blank, and it cannot later be emptied — a preset with no name is a row nobody can pick. |
| `description` | `string` | What it is for. Shown beside the name wherever a preset is offered. |
| `projectId` | `string \| null` | Owning project. `null` makes it available in every project. |
| `fingerprintOptions` | `object` | What each profile’s identity is drawn *from* — `os`, `brand`, `timezone`, `languages`. Never a fingerprint: every profile made from this preset draws its own, because two profiles sharing one is two profiles a site reads as one. The `os` here is also what decides whether the preset describes a desktop or a phone. |
| `privacy` | `PrivacyConfig` | Privacy switches, as on a profile. |
| `startOptions` | `StartOptions` | Launch mode and window geometry, as on a profile. |
| `startPages` | `string[]` | The tabs each profile opens on launch. |
| `extensions` | `string[]` | Absolute paths to unpacked extensions to attach. |
| `tags` | `string[]` | Tags every profile made from this preset wears. Any name not seen before gets a colour. |
| `bookmarks` | `{ title, url }[]` | Bookmarks in the bar of the first window. An `http://` or `https://` address is required — anything else is a `400` naming the row, because a bookmark a browser cannot open is a row that does nothing. A missing title shows as the URL, which is what Chromium does. |
| `note` | `string` | The profile’s note, pre-filled. Markdown — the same field the companion and the editor both edit. |
| `proxy` | `{ providerId, pick } \| null` | A **provider** to mint a fresh proxy from, per profile — never an address. One gateway line mints a different session, and so a different exit, for each profile; an address stored here would put every profile the preset ever makes on one IP, which is the fault the whole provider design exists to prevent. A pasted line or an inline host is refused by name. `pick` is `{ country, state, city, minutes }` in that seller’s own words, and a pick the vendor cannot spell is refused rather than dropped — a dropped token authenticates and puts the profile somewhere else without saying so. |

#### Returns

The created `ProfileTemplate`.

#### Errors

- `400` — `name` is blank, a field is unknown, a bookmark has no http(s) URL, `proxy` is an address rather than `{ providerId, pick }`, the named provider does not exist, or the pick names a place that seller cannot spell.

## [Change a preset](#update-template)

PATCH`/api/v1/templates/:id`

PATCH-shaped: an absent field is no change. `proxy: null` takes the provider off. There was no route for this at all — a preset could only be created and deleted, so editing one meant deleting it and rebuilding it from memory.

#### Path parameters

| Field | Type | What it is |
| --- | --- | --- |
| `id`required | `string` | The record’s id. |

#### Body

| Field | Type | What it is |
| --- | --- | --- |
| `name` | `string` | New name. Sending it blank is a `400` rather than a preset with no name. |
| `description` | `string` | What it is for. Shown beside the name wherever a preset is offered. |
| `projectId` | `string \| null` | Owning project. `null` makes it available in every project. |
| `fingerprintOptions` | `object` | What each profile’s identity is drawn *from* — `os`, `brand`, `timezone`, `languages`. Never a fingerprint: every profile made from this preset draws its own, because two profiles sharing one is two profiles a site reads as one. The `os` here is also what decides whether the preset describes a desktop or a phone. |
| `privacy` | `PrivacyConfig` | Privacy switches, as on a profile. |
| `startOptions` | `StartOptions` | Launch mode and window geometry, as on a profile. |
| `startPages` | `string[]` | The tabs each profile opens on launch. |
| `extensions` | `string[]` | Absolute paths to unpacked extensions to attach. |
| `tags` | `string[]` | Tags every profile made from this preset wears. Any name not seen before gets a colour. |
| `bookmarks` | `{ title, url }[]` | Bookmarks in the bar of the first window. An `http://` or `https://` address is required — anything else is a `400` naming the row, because a bookmark a browser cannot open is a row that does nothing. A missing title shows as the URL, which is what Chromium does. |
| `note` | `string` | The profile’s note, pre-filled. Markdown — the same field the companion and the editor both edit. |
| `proxy` | `{ providerId, pick } \| null` | A **provider** to mint a fresh proxy from, per profile — never an address. One gateway line mints a different session, and so a different exit, for each profile; an address stored here would put every profile the preset ever makes on one IP, which is the fault the whole provider design exists to prevent. A pasted line or an inline host is refused by name. `pick` is `{ country, state, city, minutes }` in that seller’s own words, and a pick the vendor cannot spell is refused rather than dropped — a dropped token authenticates and puts the profile somewhere else without saying so. |

#### Returns

The updated `ProfileTemplate`.

#### Errors

- `404` — no preset with that id.
- `400` — `name` was sent blank, a field is unknown, or the proxy provider or pick is one that cannot work.

## [Save a profile as a preset](#template-from-profile)

POST`/api/v1/templates/from-profile`

Builds a preset out of an existing profile: its privacy switches, start options and pages, extensions, tags, bookmarks and note are copied, and its fingerprint is reduced to the options that produced it — platform, browser, timezone, languages. The identity itself is deliberately not copied, so profiles made from the preset come out of the same population without any two sharing a fingerprint. Cookies and browser data never travel. Nor does the **proxy**: what transfers is the *provider* it was minted from, if any, so every profile made from the preset mints its own session and its own exit. A pasted proxy has no provider and contributes nothing here, which is the honest outcome rather than a silent copy. To author a preset rather than learn one, use `POST /templates` — that is the door that takes bookmarks, a default note and a provider as things you choose.

#### Body

| Field | Type | What it is |
| --- | --- | --- |
| `profileId`required | `string` | The profile to learn from. |
| `name` | `string` | Preset name. Defaults to the profile name plus “ preset”. |
| `description` | `string` | What the preset is for; shown in the chooser. |
| `projectId` | `string \| null` | Owning project. `null` makes it available in every project. Defaults to the profile’s own project. |

#### Returns

The created `ProfileTemplate`.

#### Errors

- 400 — profileId is missing.
- 404 — no such profile.

## [Delete a preset](#delete-template)

DELETE`/api/v1/templates/:id`

Removes the preset. Profiles already made from it are untouched.

#### Path parameters

| Field | Type | What it is |
| --- | --- | --- |
| `id`required | `string` | The record’s id. |

#### Returns

`{ deleted: true }`

## 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.
