Files
bookhoard/docs/developer/api/media-items/create_media_item.md
T
John O'Keefe 6f13dc2b63 docs(media-items core): Beam WO5 batch 4 — 12 endpoints backfilled, registry pinned
Beam backfilled list/get/create/update/delete media-item, search, ratings
CRUD, progress GET/PUT. Beam correctly identified create/update/delete as
jwt+admin (admin group in the router — verified).

Claude registry: 12 entries. Notable pins:
- get_media_item: hand-built ~44-key map pinned verbatim from the handler
- create/update return the full database.MediaItems row (74 columns) —
  examples merged up to the real shape via gate-driven merge script
- search returns []SearchMediaItemsRow (sqlc row — Real type pinned)
- rating scale is 1-10 int (odd = half-star); GET returns {rating: null} when
  absent; update reuses the create handler (201)
- progress GET: 25 base keys + 4 CONDITIONAL position-authority keys
  (css_selector/anchor_href/char_offset/context_text — added by the handler
  for convertible formats with a valid CFI); PUT progress binds
  sync.SaveProgressRequest and returns database.ReadingProgress
- Beam's examples were missing entirely on 4 files (list/search/progress
  GET/PUT) — relabeled existing fences or inserted contract-shaped blocks

Gate-driven merge scripts (object + array variants) brought all examples to
the exact handler key sets. make test 9/9.
2026-10-24 16:09:38 -04:00

6.0 KiB

method, path, auth, area
method path auth area
POST /api/media-items jwt+admin media-items

Create Media Item

Create a new media item (Admin only).

Endpoint: POST /api/media-items Auth: Required (Admin only) Content-Type: application/json

Prerequisites

Before creating media items, the library must have at least one folder configured:

  1. Create a library: POST /api/libraries
  2. Add folder(s) to the library: POST /api/libraries/{library_id}/folders
  3. Then create media items: POST /api/media-items

See Library API documentation for more details.

Request Body

Field Type Required Description
library_id string (UUID) Yes Library UUID to add the media item to
title string Yes Media item title (1-500 characters)
author string No Author name
isbn string No ISBN number
description string No Description or summary
file_path string Yes Path to the media file
file_size integer Yes Size of the file in bytes
mime_type string Yes MIME type of the file
cover_image_path string No Path to the cover image
series string No Series name
series_number integer No Number in the series
tags array of strings No Tags (auto-normalized)
asin string No Amazon ASIN
date_published string No Publication date
publisher string No Publisher name
contributors array of strings No Contributors (auto-normalized)

Tag/Contributor Normalization

Tags and contributors are automatically normalized:

  • Tags: Titlecased, punctuation preserved, case-insensitive deduplication
  • Contributors: Original casing and punctuation preserved, case-insensitive deduplication
  • Search fields: Auto-generated for case-insensitive search

Example Request

{
  "library_id": "550e8400-e29b-41d4-a716-446655440000",
  "title": "Example Book Title",
  "author": "Jane Doe",
  "isbn": "978-0-123456-78-9",
  "description": "A great book about technology",
  "file_path": "/library/books/example.epub",
  "file_size": 1048576,
  "mime_type": "application/epub+zip",
  "series": "Tech Series",
  "series_number": 1,
  "tags": ["science-fiction", "technology", "ACME CORP."],
  "publisher": "O'Reilly Media",
  "contributors": ["John Smith", "ACME CORP."]
}

Response (201 Created)

{
  "id": "550e8400-e29b-41d4-a716-446655440001",
  "library_id": "550e8400-e29b-41d4-a716-446655440000",
  "title": "Example Book Title",
  "author": "Jane Doe",
  "isbn": "978-0-123456-78-9",
  "description": "A great book about technology",
  "file_path": "/library/books/example.epub",
  "file_size": 1048576,
  "mime_type": "application/epub+zip",
  "series": "Tech Series",
  "series_number": 1,
  "tags": [
    "Science-Fiction",
    "Technology",
    "ACME CORP."
  ],
  "tags_search": [
    "science fiction",
    "technology",
    "acme corp"
  ],
  "publisher": "O'Reilly Media",
  "contributors": [
    "John Smith",
    "ACME CORP."
  ],
  "contributors_search": [
    "john smith",
    "acme corp"
  ],
  "created_at": "2026-01-31T10:00:00Z",
  "updated_at": "2026-01-31T10:00:00Z",
  "abridged": null,
  "added_by_admin_id": null,
  "age_rating": null,
  "alternate_info": null,
  "archived_at": null,
  "asin": null,
  "audio_metadata": null,
  "authors": null,
  "authors_search": null,
  "chapter_count": null,
  "chapter_metadata": null,
  "community_rating": null,
  "copyright_year": null,
  "cover_image_path": null,
  "date_published": null,
  "edition": null,
  "entitlement_id": null,
  "explicit": null,
  "file_sha256": null,
  "format_group": null,
  "format_mimetype": null,
  "genre": null,
  "goodreads_id": null,
  "google_books_id": null,
  "has_fixed_layout": null,
  "hash_confidence": null,
  "imported_at": null,
  "imprint": null,
  "is_black_and_white": null,
  "is_reflowable": null,
  "kobo_content_id": null,
  "kobo_metadata": null,
  "language": null,
  "library_type_name": null,
  "manga_type": null,
  "metadata_notes": null,
  "metadata_overrides": null,
  "missing_scan_count": null,
  "narrators": null,
  "narrators_search": null,
  "openlibrary_id": null,
  "opf_identifier": null,
  "opf_uuid": null,
  "page_count": null,
  "publishers": null,
  "publishers_search": null,
  "reading_direction": null,
  "revision_number": null,
  "scan_information": null,
  "series_count": null,
  "story_arc": null,
  "subtitle": null,
  "summary": null,
  "total_characters": null,
  "volume": null,
  "web_url": null
}

Error Responses

Code Description
400 Invalid request data OR library has no folders
401 Invalid or expired token
403 User is not an admin
404 Library not found

400 - Library Has No Folders

When attempting to create a media item in a library that has no folders configured:

{
  "error": "Cannot add media items to a library with no folders. Please add at least one folder to the library first."
}

Solution: Add a folder to the library first:

POST /api/libraries/{library_id}/folders
{
  "folder_path": "/path/to/library/folder"
}