---
title: "Self-hosted Storage"
description: "Replace the File Manager's API endpoints with your own, so listing, uploads and deletions run entirely on your infrastructure."
url: https://docs.topol.io/email-editor/guide/self-hosted-storage.html
---

# Self-hosted Storage

> **This feature is available with the Plugin for Business plan and higher.**
>
> If you're on the Plugin for Startup or Plugin Expansion plan, consider upgrading to access this feature. For more details, visit our [pricing page](https://topol.io/pricing) or contact support.

**Self-hosted storage swaps the File Manager's API endpoints for your own,** which keeps listing, uploads and deletions on your infrastructure while users keep the built-in File Manager interface.

If our integrated storage providers (Google Cloud Storage, Amazon S3, Cloudflare R2, DigitalOcean Spaces) don't meet your specific requirements, this is the way to connect your own.

> **WARNING**
>
> **Before implementing the endpoints, check [how to work with API endpoints.](https://docs.topol.io/email-editor/guide/how-to-work-with-your-api.html)**

## How it works

By defining a set of custom backend endpoints, you can connect our File Manager UI directly to your infrastructure for:

-   Listing files and folders
-   Uploading images
-   Creating new folders
-   Deleting files or folders
-   Saving edited images from the built-in image editor

**All API responses must follow the expected structure to work correctly within the editor.**

### Authentication and shared params

The `FOLDERS` URL can include a `{key}` placeholder, which is replaced with the API key. **Without the placeholder, the key is appended as a `key` query parameter instead**, so plan for one or the other.

Listing carries `hostname`, `userId` and `uuid`; folder creation and deletion additionally carry `id`. `userId` and `uuid` always hold the same value, so either one can be used.

## API endpoints

### List Images & Folders

Used when File Manager opens. This call is used for retrieving files and folders.

-   URL: `/{API.FOLDERS}`
-   Method: `GET`
-   Params: `path`, `userId`, `uuid`, `hostname`
-   Content-Type: `application/json`

**Response example:**

```json
[
  {
    "name": "filename.jpg",
    "date": "2024-12-01T14:23:00Z", // last-date-modified
    "size": "512000", // string
    "path": "/path/", // current directory path
    "type": "file", // "file" | "folder"
    "extension": ".jpg",
    "url": "https://url-to-image.com/image.jpeg"
},
{
    "name": "holiday-images",
    "path": "/",
    "type": "folder"
  }
]
```

A bare array is expected, though an `{ "items": [...] }` envelope is accepted too.

**`name`, `type` and `path` are required on every entry**, for folders as well as files. `url`, `size`, `extension` and `date` are optional, and a value of the wrong type is dropped rather than failing the entry. `size` in particular has to be a string, so a numeric one disappears from the UI.

To support deletion by storage key, return an `all` object carrying `OriginalKey`. That value is what the editor sends back as `key` when the item is deleted.

### Create New Folder

Used when user adds a new folder in the File Manager.

-   URL: `/{API.FOLDERS}`
-   Method: `POST`
-   Content-Type: `application/json`

**Body:**

```json
{
  "name": "new-folder",
  "path": "/"
}
```

**Response:**

HTTP `200` on success. The body is not read, so no particular shape is required.

### Delete Images or Folders

Used when user deletes selected images or folders. We automatically append `/delete` to the `FOLDERS API` path.

-   URL: `/{API.FOLDERS}/delete`
-   Method: `POST`
-   Content-Type: `application/json`

**Body:**

```json
[
  {
    "name": "filename.jpg",
    "type": "file",
    "path": "/",
    "key": "storage-key-from-all-OriginalKey"
  },
  {
    "name": "old-folder",
    "type": "folder",
    "path": "/",
    "key": null
  }
]
```

`key` is present on every entry, for files and folders alike, and is `null` whenever the listing carried no `all.OriginalKey`.

**Response:**

HTTP `200` or `204` on success (no body required).

### Image Upload

Used when user uploads a file via File Manager or when user drops an image onto an image block.

-   URL: `/{API.IMAGE_UPLOAD}`
-   Method: `POST`
-   Content-Type: `multipart/form-data`

**Body:** three form fields.

| field | value |
| --- | --- |
| `image` | The file itself, sent as a binary part with a filename |
| `path` | Target directory |
| `uuid` | User identifier |

**Response:**

```json
{
  "success": true,
  "name": "uploaded-image.jpg",
  "url": "https://your-domain.com/images/uploaded-image.jpg"
}
```

**`success` has to be `true`**, otherwise the editor treats the upload as incomplete. `name` becomes the file's canonical name in the File Manager, which lets you return a name adjusted for collisions or sanitization.

### Upload Image from Image Editor

Used when user saves an image from the integrated image editor.

-   URL: `/{API.IMAGE_EDITOR_UPLOAD}`
-   Method: `POST`
-   Content-Type: `application/json`

**Request:**

```json
{
  "content": "data:image/png;base64,...", // full base64 data URL
  "filename": "edited-image.png"
}
```

**Response:**

```json
{
  "url": "https://your-domain.com/images/edited-image.png"
}
```

Only `url` is read here, so no `success` flag is needed.
