API Endpoints
All endpoints are mounted at /api/library.
CRUD Endpoints
| Method | Route | Description |
|---|---|---|
| GET | /api/library |
List all photos (id, filename, thumbnail_name, md5_hash, caption, created_at, file_size_bytes, width, height) |
| POST | /api/library/upload |
Upload photo (multipart, field: photo). Computes MD5, checks for duplicate, generates thumbnail |
| POST | /api/library/save-from-message |
Save from conversation. Body: { chatId, messageId }. Downloads media from WhatsApp message |
| GET | /api/library/:id/file |
Serve full-size photo |
| GET | /api/library/:id/thumb |
Serve thumbnail |
| PATCH | /api/library/:id |
Update caption. Body: { caption } |
| DELETE | /api/library/:id |
Delete photo, thumbnail, and DB record |
Send Endpoint
| Method | Route | Description |
|---|---|---|
| POST | /api/library/:id/send |
Send photo to chat. Body: { chatId, caption? } |
Send flow:
- Load photo record from DB
- Read file from disk
- Create
MessageMediafrom file path - Call
client.sendMessage(chatId, media)via whatsapp-web.js - Record in
photo_library_sends: library_photo_id, message_wa_id, chat_wa_id - Return the sent message ID
Purge System
The purge removes a photo from every conversation it was sent to, using “delete for me” (recipients keep their copy).
Purge Preview
| Method | Route | Description |
|---|---|---|
| POST | /api/library/:id/purge-preview |
Dry run — returns affected chats and message counts |
Returns: { chats: [{ chatId, messageCount }], totalMessages }
Purge Execution
| Method | Route | Description |
|---|---|---|
| POST | /api/library/:id/purge |
Delete from all conversations |
Dual-Approach Algorithm
The purge uses two methods to find all instances:
- Tracked sends — query
photo_library_sendsfor all messages sent from this library photo - Hash scan fallback — query
media_filestable for matchingmd5_hashto catch photos saved before the library existed or sent manually
For each found message:
- Load the WhatsApp message via
client.getMessageById(msg_wa_id) - Call
msg.delete(false)— “delete for me” (does NOT delete for recipient) - Delete from
messagestable in DB - Delete from
media_filestable if present - Clean up
photo_library_sendsrecords
Returns: { deletedMessages, failedMessages, affectedChats }
Frontend Integration
PhotoLibraryPage (/library)
- Responsive grid of photo thumbnails
- Click photo → full-size lightbox viewer
- Per-photo actions: Download, Send, Purge, Delete
- Upload button with drag-and-drop support
- Toast notifications for all operations
Send Modal (within PhotoLibraryPage)
- Search-enabled contact picker
- Shows contact avatars and names from the chat list
- Sends via
libraryAPI.send(photoId, chatId)
Purge Flow (within PhotoLibraryPage)
- Click Purge → calls purge-preview
- Modal shows affected chats and message counts
- Confirm → calls purge endpoint
- Result toast: “Deleted X messages from Y chats”
LibraryPickerModal (from MessageComposer)
- Triggered by Library icon button in the message composer
- 4-column thumbnail grid
- Click to select → sends to current chat