@@ -178,22 +178,23 @@ DELETE /_emdash/api/content/:collection/:id
178178### List Media
179179
180180``` http
181- GET /_emdash/api/media
181+ GET /_emdash/api/media?includeUsage=1
182182```
183183
184184#### Parameters
185185
186- | Parameter | Type | Description |
187- | ---------- | -------- | ---------------------------- |
188- | ` cursor ` | ` string ` | Pagination cursor |
189- | ` limit ` | ` number ` | Items per page (default: 20) |
190- | ` mimeType ` | ` string ` | Filter by MIME type prefix |
186+ | Parameter | Type | Description |
187+ | -------------- | -------- | -------------------------------------------------------------- |
188+ | ` cursor ` | ` string ` | Opaque pagination cursor |
189+ | ` limit ` | ` number ` | Items per page, from 1 to 100 (default: 50) |
190+ | ` mimeType ` | ` string ` | Filter by one or more comma-separated MIME types |
191+ | ` q ` | ` string ` | Case-insensitive filename search |
192+ | ` includeUsage ` | ` 1 ` | Include a coverage-aware ` usage ` summary on every returned item |
191193
192194#### Response
193195
194196``` json
195197{
196- "success" : true ,
197198 "data" : {
198199 "items" : [
199200 {
@@ -203,8 +204,15 @@ GET /_emdash/api/media
203204 "size" : 102400 ,
204205 "width" : 1920 ,
205206 "height" : 1080 ,
206- "url" : " https://cdn.example.com/photo.jpg" ,
207- "createdAt" : " 2025-01-24T12:00:00Z"
207+ "url" : " /_emdash/api/media/file/uploads/photo.jpg" ,
208+ "createdAt" : " 2025-01-24T12:00:00Z" ,
209+ "usage" : {
210+ "count" : 3 ,
211+ "coverage" : {
212+ "scope" : " all_content_collections" ,
213+ "status" : " complete"
214+ }
215+ }
208216 }
209217 ],
210218 "nextCursor" : " eyJpZCI6..."
@@ -215,9 +223,99 @@ GET /_emdash/api/media
215223### Get Media
216224
217225``` http
218- GET /_emdash/api/media/:id
226+ GET /_emdash/api/media/:id?includeUsage=1
227+ ```
228+
229+ ` includeUsage ` is optional on both list and get. Its only accepted value is ` 1 ` . When omitted,
230+ the ` usage ` property is omitted and the server does not run usage queries.
231+
232+ ### Usage Summaries
233+
234+ ` usage.count ` is the number of distinct active EmDash content rows or locales whose selected
235+ current indexed source references the media item. Repeated references and multiple source
236+ variants for the same content entry count once. Trashed content does not count.
237+
238+ A numeric count can reveal draft-like content. It is returned only when a session user has
239+ ` content:read_drafts ` , or when an API token has ` admin ` scope and its associated user also has
240+ that permission. Other media readers receive ` usage.count: null ` ; this is a successful redacted
241+ response, not an error.
242+
243+ Every requested summary includes aggregate coverage for all currently registered content
244+ collections:
245+
246+ | Status | Meaning |
247+ | ---------- | --------------------------------------------------------------------- |
248+ | ` complete ` | Every registered collection has current, completed usage coverage |
249+ | ` never ` | No registered collection has completed an initial usage repair |
250+ | ` running ` | A usage repair is currently running |
251+ | ` partial ` | Coverage is mixed or only part of the registered scope was indexed |
252+ | ` failed ` | Coverage failed across the registered scope |
253+ | ` stale ` | Indexed coverage is outdated |
254+ | ` unknown ` | Stored coverage contains a state this version does not recognize |
255+
256+ Only ` complete ` supports a scoped complete-zero statement within the EmDash-managed fields
257+ described below. Counts with any other status are indexed projections and may over-report or
258+ under-report. Even complete results are advisory during concurrent writes; usage reads are not a
259+ transactional lock and must not be used as a deletion guarantee.
260+
261+ ### Get Media Usage Details
262+
263+ ``` http
264+ GET /_emdash/api/media/:id/usage?limit=50&cursor=...
265+ ```
266+
267+ This endpoint requires ` media:read ` and ` content:read_drafts ` . Token-authenticated callers also
268+ require ` admin ` scope; token scope does not bypass the associated user's permissions.
269+
270+ ` limit ` controls content entry groups per page, from 1 to 100 (default: 50). Pagination never
271+ splits the sources or occurrences for one returned entry group.
272+
273+ ``` json
274+ {
275+ "data" : {
276+ "items" : [
277+ {
278+ "collection" : " posts" ,
279+ "contentId" : " 01CONTENT..." ,
280+ "title" : " Launch notes" ,
281+ "slug" : " launch-notes" ,
282+ "locale" : " en" ,
283+ "status" : " published" ,
284+ "scheduledAt" : null ,
285+ "deletedAt" : null ,
286+ "sources" : [
287+ {
288+ "variant" : " columns" ,
289+ "occurrences" : [
290+ {
291+ "fieldSlug" : " hero" ,
292+ "fieldPath" : " hero" ,
293+ "occurrenceIndex" : 0 ,
294+ "referenceType" : " image_field"
295+ }
296+ ]
297+ }
298+ ]
299+ }
300+ ],
301+ "nextCursor" : " eyJvcmRlclZhbHVlIjoicG9zdHMiLCJpZCI6IjAxLi4uIn0" ,
302+ "coverage" : {
303+ "scope" : " all_content_collections" ,
304+ "status" : " complete"
305+ }
306+ }
307+ }
219308```
220309
310+ Authorized details include active and trashed entries. A non-null ` deletedAt ` identifies a
311+ trashed entry. Sources are ` columns ` or ` draft_overlay ` ; occurrences identify the supported field
312+ and path without exposing internal index metadata.
313+
314+ Media usage covers local media references in top-level image and file fields, repeater image
315+ fields, and Portable Text image blocks managed by EmDash content collections. It does not scan
316+ custom code, rendered HTML, settings, menus, widgets, plugin-private data, external sites, or
317+ provider-only assets.
318+
221319### Create Media
222320
223321``` http
0 commit comments