Stashr docs
Uploads
Uploads always belong to a project. Choose a project in the dashboard or send a project slug when uploading from an integration.
Dashboard Uploads
Use dashboard uploads when a person is adding or organizing files manually.
Choose:
- project
- optional folder
- file
- title
- alt text for images
- tags
API Uploads
Use API uploads when a site, app, CMS, build script, or backend service needs to send files into Stashr.
const baseUrl = "https://stashr.dev";
const apiKey = process.env.STASHR_API_KEY;
const projectId = process.env.STASHR_PROJECT_ID || "";
const folderId = process.env.STASHR_FOLDER_ID || "";
const assetId = process.env.STASHR_ASSET_ID || "";
const authHeaders = { Authorization: "Bearer " + apiKey };import { readFile } from "node:fs/promises";
const metadata = {
"projectSlug": "website",
"folderPath": "brand/launch",
"title": "Hero image",
"altText": "Dashboard preview on a laptop",
"tags": ["hero", "launch"],
"storeOriginalImages": false
};
const form = new FormData();
form.set("metadata", JSON.stringify(metadata));
form.set("file", new Blob([await readFile("./hero.png")], { type: "image/png" }), "hero.png");
const response = await fetch(baseUrl + "/api/v1/assets", {
method: "POST",
headers: authHeaders,
body: form
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());The request uses multipart/form-data because it contains a binary file. The metadata itself is one JSON part, so it stays a normal structured object rather than a series of individual form assignments. Sending the file inside an application/json request would require inefficient base64 encoding.
Upload Fields
| Field | Status | Meaning |
|---|---|---|
file | Required | Binary file to upload. Maximum size: 100 MB. |
metadata | Required for new integrations | JSON object containing the fields below. Existing flat multipart fields remain supported for compatibility. |
metadata.projectSlug | Conditional | Project slug. Required unless metadata.projectId is supplied. |
metadata.projectId | Conditional | Project ID. Required unless metadata.projectSlug is supplied. |
metadata.folderPath | Optional | Folder inside the project. Defaults to the project root. |
metadata.title | Optional | Human-friendly asset title, up to 160 characters. |
metadata.altText | Optional | Image description, up to 500 characters. |
metadata.tags | Optional | Array of up to 24 search and grouping labels. Defaults to an empty array. |
metadata.storeOriginalImages | Optional | Image-only boolean override. If omitted, the workspace upload setting is used. |
Use either the metadata JSON part or the legacy flat metadata fields, not both. Only file and one project identifier are fundamentally required.
The upload response includes an asset id, project slug, public path, metadata, and delivery URL.
deliveryUrl and publicPath are relative paths. Prefix the returned value with https://stashr.dev and do not construct the filename yourself. Optimized images can have a changed extension or variant suffix.
The maximum file size is 100 MB. Uploads are also checked against workspace storage and plan media limits.
Manage Uploaded Assets
Use GET /api/v1/assets/{assetId} for metadata, PATCH to change metadata, move, or rename the asset, and DELETE to remove it. Use GET /api/v1/assets/lookup?projectSlug=website&path=… when a stored project path is available.
For project-wide automation, list with:
/api/v1/assets?projectSlug=website&recursive=true&page=1&limit=100
See API recipes for complete examples.
Supported Media
Free accounts can upload images and PDFs.
Hobby and Pro accounts can upload common images, PDFs, audio, video, text, CSV, Office documents, JSON, ZIP, and generic binary files.
Image Optimization On Upload
Images are optimized during upload by ManxMedia using the workspace WebP quality from Settings. The workspace can default uploads to keep originals, and each dashboard or API upload can override that choice. Keeping originals stores original and optimized variants in the selected folder. The original keeps its source format, the optimized copy uses the default profile, and API responses group both files into one logical asset.
Non-image assets are stored as accepted by the media service.