Photo albums of a community. Every route sits behind the router-level feature gate requireFeature(albums): a plan without it gets 403 FEATURE_NOT_AVAILABLE, reads included. Tenant context comes from ?communityId= (or X-Tenant-Id / the custom domain).
Reads are optionalAuth and follow the community's read scope:
- An APPROVED member reads every album. Hidden albums are for albums:manage and albums:moderate only.
- A guest, or a signed-in non-member, reads only albums opted into guest preview, in a PUBLIC community whose plan includes albums.
- Anyone else gets 404.
Writes need an APPROVED member who isn't suspended or blocked from media. Editing ANY album needs albums:manage or albums:moderate. Anyone may edit their OWN album. albums:create lets you create and edit your own, never anyone else's.
14 endpoints.
| Method | Path | Summary |
|---|---|---|
GET | /api/albums | List albums |
POST | /api/albums | Create an album (optionally with photos) |
DELETE | /api/albums/{albumId} | Delete an album (owner, or albums:manage | albums:moderate) |
GET | /api/albums/{albumId} | One album |
PUT | /api/albums/{albumId} | Edit an album's details (owner, or albums:manage | albums:moderate) |
POST | /api/albums/{albumId}/hide | Hide an album (albums:manage | albums:moderate) |
DELETE | /api/albums/{albumId}/images | Remove one photo (owner, or albums:manage | albums:moderate) |
POST | /api/albums/{albumId}/images | Add photos (owner, or albums:manage | albums:moderate) |
DELETE | /api/albums/{albumId}/images/all | Remove every photo (owner, or albums:manage | albums:moderate) |
PUT | /api/albums/{albumId}/images/reorder | Reorder photos (owner, or albums:manage | albums:moderate) |
POST | /api/albums/{albumId}/like | Toggle your like |
GET | /api/albums/{albumId}/likes | Who liked an album |
POST | /api/albums/{albumId}/unhide | Unhide an album (albums:manage | albums:moderate) |
GET | /api/albums/preview/{slug}/{albumId} | Link-preview data for an album (no auth) |
GET /api/albumsAuth: optional — a token changes what you can see (see "Who may read what").
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
communityId | query | no | string (uuid) | — |
q | query | no | string | Search in title and description |
page | query | no | integer | — |
limit | query | no | integer | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | success, albums: [Album], pagination | — |
404 | The caller may not read this community's albums | — |
POST /api/albumsNeeds albums:manage or albums:create. The community is the one the guards checked. A body communityId that disagrees is refused with 400 COMMUNITY_MISMATCH. Photos count against the plan's storage and are refused over the cap. Up to 50 photos, 25 MB each. Only PUBLIC communities that opted albums into guest preview accept isGuestVisible=true.
Auth: Bearer JWT required.
Request body (required) — application/json
Type: CreateAlbumRequest
Responses
| Status | Description | Body |
|---|---|---|
201 | success, data: Album | — |
400 | title missing, COMMUNITY_MISMATCH, ALBUM_IMAGE_LIMIT, or guest preview not allowed | — |
403 | Not a member, missing permission, RESTRICTION_BLOCKED, FEATURE_NOT_AVAILABLE, or storage cap reached | — |
DELETE /api/albums/{albumId}Gives the photos' storage back and removes the album from the home feed. Auth: Bearer JWT required.
Responses
| Status | Description | Body |
|---|---|---|
200 | success, message | — |
GET /api/albums/{albumId}A hidden album, or one a guest may not preview, answers 404 exactly as a missing id does. Auth: optional — a token changes what you can see (see "Who may read what").
Responses
| Status | Description | Body |
|---|---|---|
200 | success, data: Album | — |
404 | Not found or not visible to this caller | — |
PUT /api/albums/{albumId}Auth: Bearer JWT required.
Request body — application/json
| Field | Type | Required | Description |
|---|---|---|---|
title | string | no | — |
description | string | no | — |
isGuestVisible | boolean | no | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | success, data: Album | — |
400 | NO_FIELDS_TO_UPDATE | — |
404 | ALBUM_NOT_FOUND | — |
POST /api/albums/{albumId}/hideA hidden album leaves every listing. It, its likes and its comment thread answer 404 to everyone but moderators. Auth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
albumId | path | yes | string (uuid) | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | success, data: Album | — |
404 | ALBUM_NOT_FOUND | — |
DELETE /api/albums/{albumId}/imagesAuth: Bearer JWT required.
Request body (required) — application/json
| Field | Type | Required | Description |
|---|---|---|---|
imageUrl | string | yes | The photo's url as stored |
Responses
| Status | Description | Body |
|---|---|---|
200 | success, data: Album — the cover becomes the first remaining photo | — |
404 | ALBUM_NOT_FOUND | — |
POST /api/albums/{albumId}/imagesAppended atomically, so concurrent uploads never lose photos. At most 50 per album. Counts against storage. Auth: Bearer JWT required.
Request body (required) — multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
albumImages | string (binary)[] | no | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | success, data: Album | — |
400 | No images | — |
404 | ALBUM_NOT_FOUND | — |
DELETE /api/albums/{albumId}/images/allAuth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
albumId | path | yes | string (uuid) | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | success, data: Album | — |
404 | ALBUM_NOT_FOUND | — |
PUT /api/albums/{albumId}/images/reorderimages must be an exact reordering of the album's current photos, matched
by filename (url for old photos). The server stores its own copy of each
photo, never the client's. The first photo becomes the cover.
Auth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
albumId | path | yes | string (uuid) | — |
Request body (required) — application/json
| Field | Type | Required | Description |
|---|---|---|---|
images | object[] | yes | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | success, data: Album | — |
400 | INVALID_IMAGE_ORDER | — |
404 | ALBUM_NOT_FOUND | — |
POST /api/albums/{albumId}/likeAny APPROVED member not blocked from liking, on an album they may open (a hidden one answers 404). The album's creator is notified while still a member. Auth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
albumId | path | yes | string (uuid) | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | success, data: { liked, likeCount } | — |
404 | Not found or hidden | — |
GET /api/albums/{albumId}/likesReadable exactly when the album is (same rule as GET /api/albums/albumId).
Auth: optional — a token changes what you can see (see "Who may read what").
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
albumId | path | yes | string (uuid) | — |
page | query | no | integer | — |
limit | query | no | integer | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | success, data: { likes: [{ userId, userName, userAvatar, likedAt }], … } — the community names | — |
404 | Not found or not visible to this caller | — |
POST /api/albums/{albumId}/unhideAuth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
albumId | path | yes | string (uuid) | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | success, data: Album | — |
404 | ALBUM_NOT_FOUND | — |
GET /api/albums/preview/{slug}/{albumId}Answers only for an album a guest may see (public community, albums in guest preview, album opted in). Everything else is 404. Auth: Bearer JWT required.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
slug | path | yes | string | — |
albumId | path | yes | string (uuid) | — |
Responses
| Status | Description | Body |
|---|---|---|
200 | success, data: { id, title, description, coverImage, imageCount, community… } | — |
404 | Not found or not public | — |