Skip to content

Server image uploads

POST /server-assets/upload replaces or clears the server’s icon or banner image. Admin tools use it to change the server’s branding. The server name and other settings change through server_update instead.

Send multipart/form-data:

const form = new FormData();
form.append("asset", "icon");
form.append("file", imageFile);
form.append("validator_key", handshake.validator_key);
form.append("validator", validator);
const response = await fetch(new URL("/server-assets/upload", serverUrl), {
method: "POST",
body: form,
});
Field Required Description
asset Yes icon or banner.
file Unless clearing The new image. 1 byte to 10 MB, at most 8192 pixels on each side and 25 million pixels in total.
clear No Send 1 to remove the image instead of replacing it. file is then ignored.
validator_key, validator For Rotur accounts The connection’s validator_key and a fresh Rotur validator for it.
local_upload_token For server accounts The local_upload_token from ready. When present, the validator fields are ignored.

Authentication works the same way as attachment uploads.

The server decodes the image and saves it as PNG under a new random file name. Animation is lost. If saving the setting fails, the server deletes the new file.

Success returns HTTP 200 with the current image URLs:

{
"icon": "https://chat.example.com/server-assets/7f1c2d3e-4b5a-6978-8a9b-0c1d2e3f4a5b.png",
"banner": ""
}

An empty string means that image is cleared.

Every signed-in connection, including yours, then receives a server_update event:

{
"cmd": "server_update",
"name": "Example Chat",
"icon": "https://chat.example.com/server-assets/7f1c2d3e-4b5a-6978-8a9b-0c1d2e3f4a5b.png",
"banner": ""
}

The server also writes a server_update entry to the audit log.

The account must be a current member with manage_server. The owner and administrators have it.

Uploads count against the same per-minute limit as attachment uploads. An account in timeout can’t upload.

Failures return an HTTP status and {"error": "..."}.

Status error When
400 Asset must be icon or banner asset is missing or has another value.
400 An image file is required No file and no clear=1.
400 Choose a valid image no larger than 8192 pixels per side The image size can’t be read, a side is over 8192 pixels, or it has more than 25 million pixels.
400 Unsupported image format The image can’t be decoded.
401 validator_key and validator are required for authentication No proof was sent.
401 Invalid credentials Rotur rejected the validator, or the account doesn’t exist on this server.
401 Invalid local upload token The server account token is wrong or expired.
403 Access denied The account is banned.
403 Manage server permission required The account isn’t a member or lacks manage_server.
413 Server images must be between 1 byte and 10 MB The file is empty or too big.
415 Content-Type must be multipart/form-data The body isn’t multipart.
429 Upload rate limit exceeded Too many uploads this minute, or the account is in timeout.
500 Unable to save server image The file couldn’t be written.
502 Failed to validate credentials, Invalid authentication response The server couldn’t check the validator with Rotur.

A failure while saving the new setting returns HTTP 400 with the configuration error text.

GET /server-assets/{name} returns a stored image. The handshake’s server.icon and server.banner link here. A missing file, or a name containing a path, returns 404 with Server asset not found.