Photos

Encrypted photo and video library with album grouping and tagging.

Encrypted photo and video library with album grouping and tagging. User-facing content (filenames, EXIF metadata, media classification) is encrypted client-side before reaching the server. Structural metadata used for timeline sorting, smart scopes, and storage quotas (dates, flags, file sizes) is stored in the clear.

Each photo can belong to multiple albums simultaneously (Apple Photos / Google Photos semantics). Deleting an album removes only the membership — photos themselves are not deleted.

All methods use the JSON-RPC 2.0 protocol over WebSocket. See Enbox API for transport details, error codes, query operators, and encryption model.

Fields

Field Type Required Description
id string Primary key. Read-only, auto-generated
owner_id string Owner user id. Read-only
owner_wrapped_key string Content key wrapped by owner’s account key (base64)
size_bytes integer Logical plaintext file size in bytes
status string Upload state. Read-only. Values: pending (uploading) · ready (finalized)
taken_at string? ISO 8601 EXIF DateTimeOriginal. Used for timeline sort. null for files without EXIF date
favorite boolean Favorites smart scope flag. Default false
hidden boolean Hidden smart scope flag (excluded from Library). Default false
trashed boolean Trash smart scope / soft-delete flag. Default false
thumbnail_512_id string? Chunk id of the encrypted 512 px thumbnail. Read-only. null until uploaded
enc_name string Encrypted filename. Plain string after decryption (e.g. "IMG_2847.HEIC")
enc_mime_type string Encrypted MIME type. Plain string after decryption (e.g. "image/jpeg", "video/mp4")
enc_overview string? Encrypted media overview. See PhotoOverview. null until set
enc_metadata string? Encrypted EXIF / location / camera details. See PhotoMetadata. null until set
chunk_count integer Number of uploaded chunks. Read-only, computed
created_at string ISO 8601 creation timestamp. Read-only
updated_at string ISO 8601 last update timestamp. Read-only
photo_album PhotoAlbum[] Album assignments
photo_tag string[] Assigned tag IDs
photo_chunk PhotoChunk[] Ordered chunk references. Returned in detail view

Filterable: favorite · hidden · trashed

Default ordering: -taken_at, -created_at

PhotoAlbum

Junction entry linking a photo to an album with an optional wrapped key for shared-album access.

Field Type Required Description
id string Junction row id. Read-only. Include when updating existing assignments
album_id string Album id
album_wrapped_key string? Content key wrapped by album key. Set when album is shared

PhotoChunk

Ordered chunk reference for a photo. Created by confirmChunk.

Field Type Required Description
id string Primary key. Read-only
photo_id string Parent Photo id
chunk_index integer 0-based chunk order
chunk_id string Reference to the stored encrypted blob

Encrypted field schemas

Each enc_* column is an AES-GCM blob. After decryption the plaintext is a JSON value whose structure is documented below.

Danger: The server cannot validate encrypted field structures. Writing an incorrect schema will produce corrupted data for all clients. Follow the type definitions below exactly.

PhotoOverview

Decrypted content of enc_overview. Object. Loaded in every list/grid view for media-type badges and smart scope filtering.

Field Type Required Allowed values Description
media_type string photo · video · selfie · live · portrait · panorama · screenshot · burst · animated Media classification
duration float Video length in seconds. 0 for still images

Warning: Both fields are required. Set duration to 0 for non-video media types.

PhotoMetadata

Decrypted content of enc_metadata. Object. Loaded only on the detail / info screen.

Field Type Required Description
width integer Image/video width in pixels
height integer Image/video height in pixels
camera string Camera model (e.g. "iPhone 15 Pro", "Canon EOS R5"). Empty string if unknown
exposure string Exposure summary (e.g. "f/1.8 1/120s ISO 64"). Empty string if unknown
location GeoLocation? GPS location. null if not available
people string[] Detected person names (from face recognition). Empty array if none
caption string User-entered or AI-generated caption. Empty string if none

Warning: All fields are required and must be present in the JSON object. Use "" (empty string) for text fields without a value, [] for the people array, and null for location when GPS data is not available.

GeoLocation

Nested object within PhotoMetadata. Nullable — the location field may be null.

Field Type Required Description
name string Reverse-geocoded place name (e.g. "Paris, France"). Empty string if unknown
lat float Latitude in decimal degrees
lng float Longitude in decimal degrees

find

List photos with filtering, sorting, and pagination.

WebSocket

Request

{
  "jsonrpc": "2.0",
  "method": "photos.photos.find",
  "params": {
    "filter": { "trashed": { "$eq": false }, "hidden": { "$eq": false } },
    "sort": [{ "field": "taken_at", "dir": "desc" }],
    "limit": 100,
    "fields": [
      "id", "size_bytes", "status", "taken_at", "favorite", "hidden", "trashed",
      "thumbnail_512_id", "chunk_count",
      "enc_name", "enc_mime_type", "enc_overview",
      "owner_id", "owner_wrapped_key",
      "photo_album.id", "photo_album.album_id", "photo_album.album_wrapped_key",
      "photo_tag.tag_id"
    ]
  },
  "id": 1
}

Response

{
  "jsonrpc": "2.0",
  "result": {
    "items": [
      {
        "id": "1920438291738",
        "size_bytes": 3145728,
        "status": "ready",
        "taken_at": "2026-05-10T14:32:00Z",
        "favorite": true,
        "hidden": false,
        "trashed": false,
        "thumbnail_512_id": "1920438291780",
        "chunk_count": 1,
        "enc_name": "<base64>",
        "enc_mime_type": "<base64>",
        "enc_overview": "<base64>",
        "owner_id": "42",
        "owner_wrapped_key": "<base64>",
        "photo_album": [
          { "id": "1920438291950", "album_id": "1920438291900", "album_wrapped_key": null }
        ],
        "photo_tag": ["1920438291800"]
      }
    ],
    "total": 1284
  },
  "id": 1
}

findOneById

Fetch a single photo with full detail including metadata, chunks, albums, and tags.

WebSocket

Request

{
  "jsonrpc": "2.0",
  "method": "photos.photos.findOneById",
  "params": {
    "id": "1920438291738",
    "fields": [
      "id", "size_bytes", "status", "taken_at", "favorite", "hidden", "trashed",
      "thumbnail_512_id", "chunk_count", "created_at", "updated_at",
      "enc_name", "enc_mime_type", "enc_overview", "enc_metadata",
      "owner_id", "owner_wrapped_key",
      "photo_album.id", "photo_album.album_id", "photo_album.album_wrapped_key",
      "photo_tag.tag_id",
      "photo_chunk.id", "photo_chunk.chunk_index", "photo_chunk.chunk_id"
    ]
  },
  "id": 2
}

Response

{
  "jsonrpc": "2.0",
  "result": {
    "id": "1920438291738",
    "size_bytes": 3145728,
    "status": "ready",
    "taken_at": "2026-05-10T14:32:00Z",
    "favorite": true,
    "hidden": false,
    "trashed": false,
    "thumbnail_512_id": "1920438291780",
    "chunk_count": 1,
    "created_at": "2026-05-10T15:00:00Z",
    "updated_at": "2026-05-10T15:01:00Z",
    "enc_name": "<base64>",
    "enc_mime_type": "<base64>",
    "enc_overview": "<base64>",
    "enc_metadata": "<base64>",
    "owner_id": "42",
    "owner_wrapped_key": "<base64>",
    "photo_album": [
      { "id": "1920438291950", "album_id": "1920438291900", "album_wrapped_key": null }
    ],
    "photo_tag": ["1920438291800"],
    "photo_chunk": [
      { "id": "1920438291770", "chunk_index": 0, "chunk_id": "1920438291771" }
    ]
  },
  "id": 2
}

create

Create a new photo record. The server assigns id, created_at, and updated_at. After creation, upload binary content via the Upload service.

WebSocket

Request

{
  "jsonrpc": "2.0",
  "method": "photos.photos.create",
  "params": {
    "document": {
      "size_bytes": 3145728,
      "taken_at": "2026-05-10T14:32:00Z",
      "enc_name": "<base64>",
      "enc_mime_type": "<base64>",
      "enc_overview": "<base64>",
      "enc_metadata": "<base64>",
      "owner_id": "42",
      "owner_wrapped_key": "<base64>",
      "photo_album": [
        { "album_id": "1920438291900" }
      ],
      "photo_tag": ["1920438291800"]
    }
  },
  "id": 3
}

Response

{
  "jsonrpc": "2.0",
  "result": {
    "id": "1920438291738",
    "size_bytes": 3145728,
    "status": "pending",
    "enc_name": "<base64>",
    "created_at": "2026-05-10T15:00:00Z"
  },
  "id": 3
}

updateOneById

Partial update — only specified fields change; omitted fields are untouched. Nested collections are updated automatically: items with id are updated, items without id are created, items missing from the array are deleted.

WebSocket

Request

{
  "jsonrpc": "2.0",
  "method": "photos.photos.updateOneById",
  "params": {
    "id": "1920438291738",
    "document": {
      "favorite": true,
      "enc_overview": "<base64-new>",
      "photo_album": [
        { "id": "1920438291950", "album_id": "1920438291900" },
        { "album_id": "1920438291910" }
      ]
    }
  },
  "id": 4
}

Response

{
  "jsonrpc": "2.0",
  "result": {
    "id": "1920438291738",
    "favorite": true,
    "enc_overview": "<base64-new>",
    "updated_at": "2026-05-13T18:45:00Z"
  },
  "id": 4
}

replaceOneById

Full replacement — all writable fields must be provided. Unlike updateOneById, omitted fields are reset to defaults. Nested collections (photo_album, photo_tag, photo_chunk) are not accepted in this method — use updateOneById to modify them.

WebSocket

Request

{
  "jsonrpc": "2.0",
  "method": "photos.photos.replaceOneById",
  "params": {
    "id": "1920438291738",
    "document": {
      "size_bytes": 3145728,
      "taken_at": "2026-05-10T14:32:00Z",
      "favorite": true,
      "hidden": false,
      "trashed": false,
      "enc_name": "<base64>",
      "enc_mime_type": "<base64>",
      "enc_overview": "<base64>",
      "enc_metadata": "<base64>"
    }
  },
  "id": 4
}

Response

{
  "jsonrpc": "2.0",
  "result": {
    "id": "1920438291738",
    "enc_name": "<base64>",
    "updated_at": "2026-05-13T18:45:00Z"
  },
  "id": 4
}

update

Batch update multiple photos in a single transaction (all-or-nothing). See Batch operations.

WebSocket

Request

{
  "jsonrpc": "2.0",
  "method": "photos.photos.update",
  "params": {
    "items": [
      { "id": "1920438291738", "document": { "favorite": true } },
      { "id": "1920438291739", "document": { "favorite": true } }
    ]
  },
  "id": 5
}

Response

{
  "jsonrpc": "2.0",
  "result": {
    "affected": 2,
    "items": [
      { "id": "1920438291738", "favorite": true },
      { "id": "1920438291739", "favorite": true }
    ]
  },
  "id": 5
}

deleteOneById

Delete a photo. All album/tag assignments and chunk references are deleted with it.

WebSocket

Request

{
  "jsonrpc": "2.0",
  "method": "photos.photos.deleteOneById",
  "params": {
    "id": "1920438291738"
  },
  "id": 6
}

Response

{
  "jsonrpc": "2.0",
  "result": {
    "id": "1920438291738",
    "enc_name": "<base64>"
  },
  "id": 6
}

delete

Batch delete photos in a single transaction (all-or-nothing). See Batch operations.

WebSocket

Request

{
  "jsonrpc": "2.0",
  "method": "photos.photos.delete",
  "params": {
    "items": [
      { "id": "1920438291738" },
      { "id": "1920438291739" }
    ]
  },
  "id": 7
}

Response

{
  "jsonrpc": "2.0",
  "result": {
    "affected": 2,
    "items": [
      { "id": "1920438291738" },
      { "id": "1920438291739" }
    ]
  },
  "id": 7
}