Upload images
/v1/file/uploadUploads up to 10 images in one multipart/form-data request and
returns a storage key for each. Use it to store reference images —
for example a background for outfit compositions — and then pass the
returned key wherever an image key is accepted, or display it through
the image delivery endpoint at https://stylor.ai/api/v1/image/{key}.
Accepted files
Files are read from every file part in the body regardless of the form field name; non-file fields are ignored. Only the first 10 file parts are read — any further files are silently dropped, not rejected. Each file must:
- declare one of
image/jpeg,image/png,image/webp,image/avifin the part'sContent-Type(a part without one is treated asapplication/octet-streamand rejected) - actually be that format (the first bytes are checked against the declared type's signature)
- be between 1 KB (1,024 bytes) and 10 MB (10,485,760 bytes)
Per-file results
Files are validated and stored independently, so one bad file does not
fail the request. The response always carries three keys: files for
the stored ones, failures for the rejected ones (each with the
original filename and an error), and a summary. The status tells
you at a glance how it went: 200 all stored, 207 some stored. When
none was stored the response is an error that still carries files,
failures and summary, with error: No files were stored. and a
status chosen from the per-file reasons: 415 when every file had an
unaccepted type or content, 413 when every file was too large, 422
for any other client-side mix, and 500 (Internal server error.)
when storage failed. Per-file error values are:
Unsupported file type: <type>. Allowed: image/jpeg, image/png, image/webp, image/avifFile content does not match declared type <type>— the bytes are not the declared formatFile too small to validate as <type>— too few bytes arrived to check the type's signature (under 12 for WebP, 8 for PNG and AVIF, 3 for JPEG)File exceeds maximum size of 10 MB— the upload was cut off at 10 MB and discardedFile too small. Minimum size is 1024 bytesFailed to process file— storage or record creation failedFile stream error— the connection dropped mid-file
Each stored file gets a UUID fileId and a key of the form
uploads/<fileId>.<ext>. Pixel dimensions are extracted where
possible and are null when they cannot be read.
This endpoint counts one request against your quota however many files it carries.
Request body
multipart/form-datarequiredOne or more image files. The field name is not significant — any file part is accepted — but file is conventional. Only the first 10 file parts are read.
Response
Files that were stored.
Unique id of the stored file. It is the UUID portion of key.
Object key of the form uploads/<fileId>.<ext>. Pass it to GET /v1/image/{key} or to any field that accepts an image key.
The original filename after sanitising (path stripped, unsafe characters removed, at most 255 characters). unnamed when none was sent.
The declared and verified MIME type.
image/jpegimage/pngimage/webpimage/avifSize in bytes.
Hex SHA-256 of the stored bytes, computed as the file streamed in. Compare it with a local hash to confirm the upload arrived intact.
Pixel width, or null if it could not be read.
Pixel height, or null if it could not be read.
Files that were rejected.
The sanitised original filename.
Why the file was rejected. One of the per-file messages listed in the endpoint description.
Counts for the batch.
Files processed (succeeded + failed). At most 10.
Number of entries in files.
Number of entries in failures.
failures.Files that were stored.
Unique id of the stored file. It is the UUID portion of key.
Object key of the form uploads/<fileId>.<ext>. Pass it to GET /v1/image/{key} or to any field that accepts an image key.
The original filename after sanitising (path stripped, unsafe characters removed, at most 255 characters). unnamed when none was sent.
The declared and verified MIME type.
image/jpegimage/pngimage/webpimage/avifSize in bytes.
Hex SHA-256 of the stored bytes, computed as the file streamed in. Compare it with a local hash to confirm the upload arrived intact.
Pixel width, or null if it could not be read.
Pixel height, or null if it could not be read.
Files that were rejected.
The sanitised original filename.
Why the file was rejected. One of the per-file messages listed in the endpoint description.
Counts for the batch.
Files processed (succeeded + failed). At most 10.
Number of entries in files.
Number of entries in failures.
Errors
24Every error is JSON with a single error string unless noted. Match on the status code; the message is for humans.
Failed to parse uploadThe multipart body could not be parsed — it is malformed or truncated, or the multipart/form-data header has no boundary parameter.
No files were stored.Every file's stream broke off mid-upload (File stream error). The body also carries files, failures and summary.
No files were stored.Every file exceeded 10 MB. The body also carries files (empty), failures and summary; read failures[].error.
{
"error": "No files were stored.",
"files": [],
"failures": [
{
"filename": "huge.png",
"error": "File exceeds maximum size of 10 MB"
}
],
"summary": {
"total": 1,
"succeeded": 0,
"failed": 1
}
}Content-Type must be multipart/form-dataThe request's Content-Type header is not multipart/form-data.
No files were stored.Every file declared an unaccepted type, or its bytes did not match the declared type. The body also carries files (empty), failures and summary; read failures[].error.
{
"error": "No files were stored.",
"files": [],
"failures": [
{
"filename": "notes.txt",
"error": "Unsupported file type: text/plain. Allowed: image/jpeg, image/png, image/webp, image/avif"
}
],
"summary": {
"total": 1,
"succeeded": 0,
"failed": 1
}
}No files providedThe request has no body, or the body parsed but contained no file parts.
No files were stored.Every file was rejected, for differing client-side reasons or because a file was under 1 KB. The body also carries files (empty), failures and summary; read failures[].error.
curl -X POST "https://stylor.ai/api/v1/file/upload" \
-H "Authorization: Bearer $STYLOR_API_KEY" \
-F "file=@./catalogue.csv"{
"files": [
{
"fileId": "6d1e5c0a-2b3f-4a8e-9c7d-0f1e2a3b4c5d",
"key": "uploads/6d1e5c0a-2b3f-4a8e-9c7d-0f1e2a3b4c5d.jpg",
"filename": "background.jpg",
"contentType": "image/jpeg",
"size": 482113,
"sha256": "9f2b6c1d4e8a7b3c0d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c",
"width": 1600,
"height": 1067
}
],
"failures": [],
"summary": {
"total": 1,
"succeeded": 1,
"failed": 0
}
}