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

  1. Create a photo record via photos.photos.create. The server returns the assigned id with status: "pending"
  2. Call requestUploadUrl to get a presigned upload URL for each part
  3. PUT the encrypted chunk body to the presigned URL (standard HTTP PUT, not WebSocket)
  4. Call confirmChunk for each uploaded part — pass the etag from the S3 PUT response
  5. 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.