Immich Integration for the Photo Widget
1. Context & Objective
Gladys will soon have a Photo Widget for the dashboard (box.type = 'photo') that displays
a slideshow from a manually entered list of URLs ({ url, caption }). The images are
retrieved via a server proxy (GET /api/v1/dashboard/photo/proxy?url=) to remain
accessible remotely (Gladys Plus).
Objective: Allow the user to automatically populate this slideshow from an Immich server
(self-hosted photo manager).
The user connects their Immich once, then selects a dynamic source (an album,
or their memories “on this day”); the widget displays the corresponding photos and
refreshes itself.
Scope v1
- Sources: Album of choice + Memories “on this day”
- Image quality: preview (~1440px)
- Architecture: full Immich service (config page + authenticated proxy)
2. The Immich API — What It Enables
2.1 Authentication
- All requests include the header
x-api-key: <key>. - The key is generated in Immich: Account Settings → API Keys. Minimum required
permissions:album.read,asset.read,memory.read. - Base URL = Immich server address, e.g.,
http://192.168.1.20:2283. All paths
below are prefixed by/api.
Key point: The current Gladys proxy (
dashboard.getPhoto.js) makes a GET without
headers → it cannot talk to Immich. Therefore, a dedicated Immich proxy is needed that
injectsx-api-keyand points to the configured base URL.
2.2 Choosing Which Photos to Display (Sources)
| Source | Endpoint | Useful Response |
|---|---|---|
| List of albums | GET /api/albums |
[{ id, albumName, assetCount, albumThumbnailAssetId, shared }] — used to populate the album selector |
| Contents of an album | GET /api/albums/{id} |
{ albumName, assets: [{ id, type, originalFileName, fileCreatedAt, exifInfo }] } |
| Memories “on this day” | GET /api/memories |
[{ id, type:"on_this_day", memoryAt, data:{ year }, assets:[{ id, ... }] }] — a group per year on the same date |
2.3 Retrieving the Image File of an Asset
Each photo is identified by a UUID asset.id. Three renditions:
| Variant | Endpoint | Usage |
|---|---|---|
| Preview (~1440px) |
GET /api/assets/{id}/thumbnail?size=preview |
Good quality/size trade-off for a slideshow |
| Thumbnail | GET /api/assets/{id}/thumbnail?size=thumbnail |
Small thumbnail (timeline) |
| Original | GET /api/assets/{id}/original |
Max quality, potentially large files (>5 MB) |
Response = binary image/*. The Immich proxy converts it to the format already expected by
the widget: "<contentType>;base64,<data>".
2.4 Asset Fields Usable for Captions
originalFileName, fileCreatedAt / localDateTime, and exifInfo (description, city,
dateTimeOriginal). Allows auto-generating a caption (e.g., “Rome — August 12, 2019”).
3. Expected Functional Behavior
3.1 Connecting the Integration (Once)
- New Immich card in the list of integrations.
- Config page asking for Server URL + API Key.
- “Test Connection” button → calls
GET /api/albumsto validate URL + key, and
returns a clear error if failed (unreachable URL, 401 invalid key).
3.2 Widget Configuration (By the User, When Editing the Dashboard)
The Photo Widget gains a choice of source mode:
- Manual URLs — current behavior, unchanged (full backward compatibility).
- Immich — Album — a dropdown lists the albums (via
GET /api/albums);
the user selects one. - Immich — Memories — displays photos returned by
GET /api/memories
(“X years ago, on this day”).
Existing options are preserved and applicable to all modes: framing (cover/contain),
slideshow interval, showing/hiding captions, widget title.
For Immich modes, the caption can be auto-generated from the asset metadata (date + location) instead of manually entered.
3.3 Display (Runtime)
- On opening, the widget resolves the Immich source to a list of assets (album or memories),
then displays each image in preview via the authenticated Immich proxy. - Slideshow: automatic scrolling according to the interval, forward/backward navigation and
indicators (already present inPhotoBox.jsx). - Image cache in memory + preloading of the next image (already present), reused as is.
- Refreshing the list: The list of assets (especially “memories,” which changes
every day) is periodically re-queried / when the widget is mounted, to be decided.
3.4 Edge Cases & Functional Decisions to Validate
- Empty album / empty memories for the day → explicit empty state (message), no error.
- Videos in an album (
asset.type = VIDEO) → filtered (onlyIMAGEkept) or
display their poster? → To be decided (proposal: ignore videos in v1). - Large albums → limit the number of loaded assets (e.g., ceiling + possible random order)
to avoid thousands of entries? → To be decided. - Display order: chronological (by
fileCreatedAt), reverse, or random?
→ To be decided (proposal: most recent first). - 5 MB proxy limit: preview remains well below the limit, so kept. Only to be reviewed if
we add the “original” or video mode later.
4. API Sources (Reference)
- Immich API Doc: API | Immich · endpoints: Immich - API Documentation
- Albums:
getAllAlbums,getAlbumInfo· Memories:searchMemories(GET /api/memories) - Image:
viewAsset/GET /api/assets/{id}/thumbnail?size=preview - Random (ref.):
POST /api/search/random· Metadata:POST /api/search/metadata
5. Open Questions Before Implementation
- Videos in albums: ignore or display a poster? (proposal: ignore in v1)
- Display order: reverse chronological, chronological, or random?
- Limit on the number of photos per source (performance)?
- Caption: auto-generated (date + location) by default, or no caption for Immich?

