Upload
File upload and download for photos.
File upload and download for photos. Unlike tasks/notes where attachments are children of the main entity, in photos the Photo record IS the file entity — it owns chunks directly via photo_chunk. Binary content is uploaded directly to cloud storage through presigned URLs.
Access control. The caller must own the photo or belong to an album that contains it. Downloads are allowed for any album role (editor or viewer); uploads, chunk confirmation and finalize require the editor role — a viewer can view a shared photo but cannot replace its contents.
Upload flow
- Create a photo record via photos.photos.create. The server returns the assigned
idwithstatus: "pending" - Call requestUploadUrl to get a presigned upload URL for each part
- PUT the encrypted chunk body to the presigned URL (standard HTTP PUT, not WebSocket)
- Call confirmChunk for each uploaded part — pass the
etagfrom the S3 PUT response - Call finalizeUpload to assemble parts and mark the photo as
ready
For thumbnails repeat steps 2–4 with chunk_type: "thumbnail_512" (single chunk). Thumbnails do not require a separate finalize call.
requestUploadUrl
Request a presigned upload URL for a single chunk/part.
WebSocket
Request
{
"jsonrpc": "2.0",
"method": "photos.upload.requestUploadUrl",
"params": {
"file_id": "1920438291738",
"object_key": "a1/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"chunk_type": "file"
},
"id": 30
}
| Param | Type | Required | Description |
|---|---|---|---|
file_id |
string |
✅ | Photo record id |
object_key |
string |
✅ | Client-generated object key for storage (format: <uuid[:2]>/<uuid>) |
chunk_type |
string |
"file" (default) or "thumbnail_512" |
Response
{
"jsonrpc": "2.0",
"result": {
"object_key": "a1/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"upload_url": "https://storage.example.com/enbox-photos-files/a1/a1b2c3d4-...?X-Amz-..."
},
"id": 30
}
confirmChunk
Confirm that a chunk/part was successfully uploaded. For multipart uploads, pass the etag returned by S3 in the PUT response header.
WebSocket
Request
{
"jsonrpc": "2.0",
"method": "photos.upload.confirmChunk",
"params": {
"file_id": "1920438291738",
"object_key": "a1/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"chunk_index": 0,
"chunk_type": "file",
"etag": "\"d41d8cd98f00b204e9800998ecf8427e\""
},
"id": 31
}
| Param | Type | Required | Description |
|---|---|---|---|
file_id |
string |
✅ | Photo record id |
object_key |
string |
✅ | Object key used during upload |
chunk_index |
integer |
0-based part order. Default 0 |
|
chunk_type |
string |
"file" (default) or "thumbnail_512" |
|
etag |
string |
S3 ETag from the PUT response. Required for multipart file chunks |
For chunk_type: "thumbnail_512", the server creates a Chunk record and sets thumbnail_512_id on the photo automatically.
Response
{
"jsonrpc": "2.0",
"result": {
"chunk_id": "1920438291770",
"chunk_index": 0,
"size_bytes": 3145728
},
"id": 31
}
finalizeUpload
Complete the multipart upload and mark the photo as ready. The server assembles all confirmed parts into a single S3 object, creates the PhotoChunk record, and sets status to "ready".
WebSocket
Request
{
"jsonrpc": "2.0",
"method": "photos.upload.finalizeUpload",
"params": {
"file_id": "1920438291738"
},
"id": 32
}
| Param | Type | Required | Description |
|---|---|---|---|
file_id |
string |
✅ | Photo record id |
Response
{
"jsonrpc": "2.0",
"result": {
"file_id": "1920438291738",
"status": "ready"
},
"id": 32
}
requestDownloadUrls
Request presigned download URLs for specific chunks of a photo.
WebSocket
Request
{
"jsonrpc": "2.0",
"method": "photos.upload.requestDownloadUrls",
"params": {
"file_id": "1920438291738",
"chunk_indexes": [0],
"chunk_type": "file"
},
"id": 33
}
| Param | Type | Required | Description |
|---|---|---|---|
file_id |
string |
✅ | Photo record id |
chunk_indexes |
integer[] |
✅ | 0-based indexes of chunks to download |
chunk_type |
string |
"file" (default) or "thumbnail_512" |
Response
{
"jsonrpc": "2.0",
"result": [
{
"chunk_index": 0,
"download_url": "https://storage.example.com/enbox-photos-files/a1/a1b2c3d4-...?X-Amz-...",
"size_bytes": 3145728
}
],
"id": 33
}
For thumbnails (chunk_type: "thumbnail_512") use chunk_indexes: [0]. The result contains a single entry or an empty array if no thumbnail exists.