Files
The Files API provides a multipart upload workflow for securely uploading binary files, such as audio recordings and
cover artwork. Once assembled, the API returns a permanent fileUrl that you can reference in other catalog endpoint
payloads.
Files uploaded through these endpoints can only be used with the V2.0 API endpoints. They are not compatible
with V1.0 API endpoints. Likewise, files uploaded via V1.0 endpoints (e.g., POST /media/image/upload) cannot be
referenced in V2.0 API payloads.
Upload Workflow
Uploading a file is a three-step process:
-
Create - Call POST
/files/v1/uploads/createto initialize the upload. The response provides anuploadId, the suggested total number ofparts, and a suggestedchunkSizeBytessized appropriately for the file’spurpose. -
Sign & upload each part - For every part number from
1toparts, call POST/files/v1/uploads/sign-partto receive a pre-signedurl, the required HTTPmethod(typicallyPUT), necessaryheaders, and an expiry timestamp.
- Optional Splitting (Recommended for Speed): Splitting your file into the suggested number of chunks allows you to sign and upload multiple parts in parallel, which significantly reduces total upload time.
- Single Upload (Alternative): Chunking is optional. If the API suggests 2 parts, you can simply sign part
1, upload the entire file to that single signed URL, and ignore part 2. - Time Limit: Pre-signed URLs are valid for exactly 20 minutes. If you attempt to upload to the URL after it
expires, it will return a
403 Forbiddenerror.
- Complete - Once your parts (or single full file) have been uploaded successfully, call
POST
/files/v1/uploads/completewith theuploadIdand an array of the uploaded part numbers.
- Note: The
partsarray only needs to contain the parts you actually uploaded (e.g., just[1]if you uploaded the whole file at once). - On success, the API returns the final assembled
fileUrl.
The uploadId is the file’s permanent ID. V2.0 API payloads reference uploaded files by a fileId GUID—for example
posterImageId on a track, or recordingVersions[].files[].fileId on a video. That fileId
is the uploadId returned in step 1, so keep it once the upload completes.
Content Validation
Beyond the content type and size checks performed at Create, some purposes inspect the file itself once it has been
assembled. These checks run during Complete Upload, which means a file that fails them is rejected only after its
bytes have been transferred, with a 400 naming the offending value.
| Purpose | Checks performed on the assembled file |
|---|---|
PosterImage | Minimum 1280 × 720, and a 16:9 aspect ratio within a 2% tolerance. |
Video | Minimum 1280 × 720; MP4 must carry H.264 or HEVC and MOV must carry ProRes; audio streams must be at least 256,000 bps and 48,000 Hz. See Videos for detail. |
Other purposes are stored as uploaded.
A file that fails these checks is not stored at all—the 400 is final, and the fileUrl that would have been returned
does not exist. Re-upload from Create with a corrected file rather than retrying Complete.
How Stored Files Are Named
The stored file is never named after the file you uploaded. The fileName you send at Create is kept as
metadata, but the object itself is named by the platform, and both the container and the object name depend on the
purpose:
| Purpose | Stored at |
|---|---|
Image, CoverImage, PosterImage | /images/{uploadId}/file.jpg |
Audio | /music/{uploadId}/file.wav or file.flac |
Video | /videos/{uploadId}/{uploadId} |
VideoCaption | /video-captions/{uploadId}/{uploadId} |
Each purpose names the object differently, and the pattern is not what you might expect:
- Images are always stored as
file.jpg, whatever format you uploaded. A GIF is served from afile.jpgURL withContent-Type: image/gif. The.jpgin the path is fixed and says nothing about the stored format—readContent-Typeinstead. - Audio keeps an extension matching the real format: a WAV is stored as
file.wav, a FLAC asfile.flac. - Videos and captions are stored under the
uploadIdwith no extension at all.
Because of this, never construct the final URL yourself, whether from your own file name or from the uploadId—always
use the fileUrl returned by POST /files/v1/uploads/complete.
The name you sent is not lost. It is what catalog endpoints echo back: the filename on a track’s or video’s
recordingVersions[].files[], for instance, is the fileName from your Create call, not the stored object name.
Reading File Metadata
A HEAD request against the returned fileUrl returns the original file name alongside the technical properties
extracted during processing, as x-ms-meta-* headers. These are listed in Access-Control-Expose-Headers, so browser
code can read them too.
HEAD /images/{uploadId}/file.jpg
Content-Type: image/jpeg
x-ms-meta-originalFileName: 4549767380582.jpg
x-ms-meta-uploadedAt: 2026-08-14T09:38:10.7059299Z
x-ms-meta-md5Hash: 9569F5F3927D12EE4DF90C5F6070EA76
x-ms-meta-width: 3000
x-ms-meta-height: 3000
x-ms-meta-aspectRatio: 1:1
x-ms-meta-format: MJPEG
originalFileName, uploadedAt and md5Hash are present on every upload. The remaining headers vary by purpose:
| Purpose | Additional metadata headers |
|---|---|
| Images | width height aspectRatio format |
Audio | sampleRate channels bitDepth bitrate duration |
Video | width height aspectRatio format resolution videoCodecProfile audioCodecProfile audioSampleRate audioBitRate audioChannels duration frameRate |
VideoCaption | encoding format |
PNG Conversion
PNG uploads are re-encoded to JPEG during Complete Upload. The stored bytes are the converted image and
Content-Type becomes image/jpeg, so a 1.4 MB PNG typically lands as a few hundred KB of JPEG.
PNG is the only image format converted this way. GIF, WEBP and BMP uploads are stored byte-for-byte as sent, keeping
their own content types—they simply sit at the same fixed file.jpg path as every other image.
x-ms-meta-originalFileName still records the name you sent, so a PNG original remains traceable. Note that
x-ms-meta-format reports the stored format, so it reads MJPEG after conversion rather than PNG.
The uploadId / fileId is unaffected by the conversion, so any reference you already stored (for example
posterImageId) remains valid.
Supported File Types & Size Limits
The purpose field determines which content types are accepted for a given upload, and each purpose has its own
maximum file size.
| Purpose | Max file size | Accepted content types |
|---|---|---|
Image, CoverImage, PosterImage | 10 MB | image/jpeg image/jpg image/png |
File, GenericFile | 25 MB | image/jpeg image/jpg image/png image/gif image/webp image/bmp audio/wav application/pdf application/msword application/vnd.openxmlformats-officedocument.wordprocessingml.document |
Video | 5 GB | video/mp4 video/mov video/quicktime |
Audio | 500 MB | audio/wav audio/flac audio/x-flac |
VideoCaption | 5 MB | application/ttml+xml text/scc (UTF-8 encoding only) |
These limits reflect the current configuration and may change over time. If a file exceeds the limit for its purpose,
POST /files/v1/uploads/create returns a 400 validation error.
Create Upload
/files/v1/uploads/create Initializes a new upload session. Returns the upload ID, the suggested chunk size, and the suggested number of parts.
Request Body *
object fileName string cover.jpg, recording.wav contentType string image/jpeg, image/png, audio/wav, audio/mpeg, video/mp4, application/pdf size integer purpose string Image, CoverImage, File, GenericFile, Video, Audio Responses
object uploadId string chunkSizeBytes integer parts integer /files/v1/uploads/create curl -X POST "https://platform.revelator.com/files/v1/uploads/create" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"fileName": "midnight-tides-cover",
"contentType": "image/jpeg",
"size": 4194304,
"purpose": "CoverImage"
}' curl -X POST "https://platform.revelator.com/files/v1/uploads/create" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"fileName": "midnight-tides-cover",
"contentType": "image/jpeg",
"size": 4194304,
"purpose": "CoverImage"
}' Example response 200
{
"uploadId": "d9fad28c-c3d5-459b-9aa4-2e64ab679924",
"chunkSizeBytes": 2097152,
"parts": 2
} {
"uploadId": "d9fad28c-c3d5-459b-9aa4-2e64ab679924",
"chunkSizeBytes": 2097152,
"parts": 2
} Sign Part
/files/v1/uploads/sign-part Returns a pre-signed URL for uploading a file part. PUT the binary chunk directly to the returned URL using the provided headers.
Request Body *
object uploadId string partNumber integer Responses
object method string url string headers object expiresAtUtc string (date-time) /files/v1/uploads/sign-part curl -X POST "https://platform.revelator.com/files/v1/uploads/sign-part" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"uploadId": "d9fad28c-c3d5-459b-9aa4-2e64ab679924",
"partNumber": 1
}' curl -X POST "https://platform.revelator.com/files/v1/uploads/sign-part" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"uploadId": "d9fad28c-c3d5-459b-9aa4-2e64ab679924",
"partNumber": 1
}' Example response 200
{
"method": "PUT",
"url": "https://revelatorstage.blob.core.windows.net/temp-uploads/...",
"headers": {
"Content-Type": "application/octet-stream",
"x-ms-blob-type": "BlockBlob"
},
"expiresAtUtc": "2026-03-17T10:18:53.7017793Z"
} {
"method": "PUT",
"url": "https://revelatorstage.blob.core.windows.net/temp-uploads/...",
"headers": {
"Content-Type": "application/octet-stream",
"x-ms-blob-type": "BlockBlob"
},
"expiresAtUtc": "2026-03-17T10:18:53.7017793Z"
} Complete Upload
/files/v1/uploads/complete Finalizes the upload workflow after your parts (or single file) have been uploaded to their signed URLs. Returns the permanent file URL.
Request Body *
object uploadId string parts integer[] Responses
object fileUrl string /files/v1/uploads/complete curl -X POST "https://platform.revelator.com/files/v1/uploads/complete" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"uploadId": "dad5931c-c728-4dfb-bda5-bd67a1905303",
"parts": [
2,
1
]
}' curl -X POST "https://platform.revelator.com/files/v1/uploads/complete" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"uploadId": "dad5931c-c728-4dfb-bda5-bd67a1905303",
"parts": [
2,
1
]
}' Example response 200
{
"fileUrl": "https://revelatorstage.blob.core.windows.net/images/00000000-0000-0000-0000-000000000000/file.jpg"
} {
"fileUrl": "https://revelatorstage.blob.core.windows.net/images/00000000-0000-0000-0000-000000000000/file.jpg"
}