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.
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.