SocialMate Local API
Media
Browse the media archive, stream files and thumbnails, trigger downloads, and clean up.
SocialMate keeps a local archive of media seen across your chats. Browse it, pull files and
thumbnails through the tunnel, trigger downloads, and clean up. File paths are never exposed —
each item instead carries a stable apiPath you can fetch.
/v1/accounts/{id}/mediaFiltered, paginated archive list. Query params: chatId, type,
state, direction, search, sort
(newest|oldest|biggest|smallest),
limit (max 500), offset, pinnedOnly,
downloadedOnly. Each row includes apiPath and
thumbnailApiPath (or null until downloadable).
curl "http://localhost:3456/v1/accounts/ACCOUNT_ID/media?type=image&downloadedOnly=true&limit=50" \
-H "x-api-key: YOUR_KEY"
{ "data": [ { "id": "med_1", "type": "image", "state": "downloaded", "apiPath": "/v1/accounts/a1b2c3/media/med_1/file", "thumbnailApiPath": "/v1/accounts/a1b2c3/media/med_1/thumbnail" } ], "pagination": { "limit": 50, "offset": 0, "total": 1 } }
/v1/accounts/{id}/media/statsCounts and total size by type/state for the account's archive.
curl http://localhost:3456/v1/accounts/ACCOUNT_ID/media/stats \
-H "x-api-key: YOUR_KEY"
{ "data": { "total": 128, "downloaded": 110, "pending": 18, "totalBytes": 10485760, "byType": [ { "type": "image", "count": 90, "bytes": 8000000, "downloaded": 80 }, { "type": "video", "count": 20, "bytes": 2400000, "downloaded": 18 }, { "type": "document", "count": 18, "bytes": 85760, "downloaded": 12 } ] } }
/v1/accounts/{id}/media/{mediaId}One item's metadata (no filesystem path). 404 if it isn't on this account.
curl http://localhost:3456/v1/accounts/ACCOUNT_ID/media/med_1 \
-H "x-api-key: YOUR_KEY"
{ "data": { "id": "med_1", "type": "image", "state": "downloaded", "sizeBytes": 81920, "apiPath": "/v1/accounts/a1b2c3/media/med_1/file" } }
/v1/accounts/{id}/media/{mediaId}/fileStream the raw file. Honours Range (206 partial content) for video
scrubbing. Returns 409 with the current state if the file isn't
downloaded yet. This is a binary response — not wrapped in the JSON envelope.
curl http://localhost:3456/v1/accounts/ACCOUNT_ID/media/med_1/file \
-H "x-api-key: YOUR_KEY" -o photo.jpg
/v1/accounts/{id}/media/{mediaId}/thumbnailA JPEG thumbnail (cached an hour). 404 if none is available. Binary JPEG response.
curl http://localhost:3456/v1/accounts/ACCOUNT_ID/media/med_1/thumbnail \
-H "x-api-key: YOUR_KEY" -o thumb.jpg
/v1/accounts/{id}/media/{mediaId}/downloadForce a media item to download so it's locally cached before you fetch it.
curl -X POST http://localhost:3456/v1/accounts/ACCOUNT_ID/media/med_1/download \
-H "x-api-key: YOUR_KEY"
{ "data": { "success": true, "state": "queued" } }
A freshly forced item reports state: "queued" (or downloading if a
download is already in flight); poll GET …/media/{mediaId} until state is
downloaded and apiPath is non-null before fetching the file.
/v1/accounts/{id}/media/{mediaId}/contextAgent Memory. Cache the description or transcript your AI produced for
this media (image / video / voice note / document), so the same file is never re-analyzed. The
cached text then rides GET /messages and GET /ai-context (a media turn
reads as [image: <what it shows>]) and fires a media.context_updated
webhook. SocialMate ships no model and never generates this — it is a passive
store, like the WhatsApp caption. Works for received and sent media. Pass overwrite:false
to get a 409 instead of replacing existing context. To find items still needing
analysis, list media with ?hasContext=false (each item also carries a
needsContext flag).
curl -X PUT http://localhost:3456/v1/accounts/ACCOUNT_ID/media/med_1/context \
-H "x-api-key: YOUR_KEY" -H "Content-Type: application/json" \
-d '{ "context": "A signed invoice #A-1001, total $420, due 2026-08-01.", "source": "your-vision-model" }'
{ "data": { "success": true, "media": { "id": "med_1", "state": "downloaded",
"apiPath": "/v1/accounts/ACCOUNT_ID/media/med_1/file",
"aiContext": "A signed invoice #A-1001, total $420, due 2026-08-01.",
"aiContextSource": "your-vision-model", "needsContext": false } } }
/v1/accounts/{id}/media/{mediaId}Remove the cached file for an item.
curl -X DELETE http://localhost:3456/v1/accounts/ACCOUNT_ID/media/med_1 \
-H "x-api-key: ADMIN_KEY"
{ "data": { "success": true } }
Download queue & cleanup
/v1/media/queueThe media-downloader's current status (in-flight, pending, failed).
curl http://localhost:3456/v1/media/queue \
-H "x-api-key: YOUR_KEY"
{ "data": { "active": 1, "pending": 4, "failed": 0 } }
Webhook timing. Webhooks that carry media — message.received,
message.sent, and media.discovered — are held until the download
settles (usually a few seconds, with a ~2-minute backstop), so their media slice
arrives state: "downloaded" with a ready-to-fetch local apiPath —
never the WhatsApp URL. See
Webhook events.
/v1/media/cleanupRun archive cleanup now (an ops convenience — Free users rely on the app's scheduled cleanup). Pro only.
curl -X POST http://localhost:3456/v1/media/cleanup \
-H "x-api-key: ADMIN_KEY"
{ "data": { "timeEvictions": 30, "quotaEvictions": 12, "bytesFreed": 5242880 } }
timeEvictions are files aged out past the retention window;
quotaEvictions are the LRU evictions made to get back under the storage quota.