{"components":{"schemas":{"APIInfo":{"properties":{"api":{"type":"string"},"docs":{"type":"string"},"openapi":{"type":"string"}},"type":"object"},"Category":{"properties":{"color":{"type":"string"},"id":{"type":"integer"},"is_builtin":{"type":"boolean"},"name":{"type":"string"}},"type":"object"},"CreateImageResponse":{"description":"Bare Image on success; wrapped in {image, tag_warnings?, autotag?} when any tag failed or an auto-tag job was started.","properties":{"autotag":{"description":"Human-readable status about the auto-tag job","nullable":true,"type":"string"},"image":{"$ref":"#/components/schemas/Image"},"tag_warnings":{"items":{"type":"string"},"nullable":true,"type":"array"}},"type":"object"},"DuplicateImageResponse":{"description":"Returned when the posted file's SHA-256 is already known. The existing image is returned with the pushed tags and provenance merged in. A multipart upload's redundant copy is discarded; a JSON path-reference at a new path is recorded as an alias.","properties":{"alias_added":{"description":"True when the posted path was recorded as an alias of the existing image (JSON path-reference); false when a redundant multipart upload was discarded","type":"boolean"},"image":{"$ref":"#/components/schemas/Image"},"merge":{"$ref":"#/components/schemas/MergeSummary"},"tag_warnings":{"items":{"type":"string"},"nullable":true,"type":"array"}},"type":"object"},"EnrichResponse":{"properties":{"merge":{"$ref":"#/components/schemas/MergeSummary"},"tag_warnings":{"items":{"type":"string"},"nullable":true,"type":"array"},"verified":{"description":"False when verify was requested but the source reported no md5","type":"boolean"}},"type":"object"},"Error":{"properties":{"code":{"type":"string"},"error":{"type":"string"}},"type":"object"},"Gallery":{"properties":{"active":{"description":"True for the gallery used when no ?gallery= selector is given","type":"boolean"},"images":{"description":"Visible (non-missing) image count","type":"integer"},"name":{"type":"string"},"tags":{"description":"Non-alias tag count","type":"integer"}},"type":"object"},"Image":{"properties":{"aliases":{"items":{"type":"string"},"type":"array"},"annotations":{"description":"Positional note boxes pulled per source, in original-image pixel coordinates. Omitted when empty.","items":{"properties":{"body":{"type":"string"},"h":{"type":"integer"},"w":{"type":"integer"},"x":{"type":"integer"},"y":{"type":"integer"}},"type":"object"},"type":"array"},"auto_tagged_at":{"format":"date-time","nullable":true,"type":"string"},"canonical_path":{"type":"string"},"collection":{"description":"Home collection label - mirrors the first entry of 'collections'. Empty string when unset. Filtered by the search keyword 'collection:' (exact match). Settable on create and via PATCH /images/{id}.","type":"string"},"collection_order":{"description":"1-based position within the home collection. Null when unset. Settable on create and via PATCH /images/{id}.","nullable":true,"type":"integer"},"collections":{"description":"Every collection the image belongs to. The scalar collection / collection_order fields mirror the home membership. Managed through the web UI; the API sets only the home via collection / collection_order.","items":{"properties":{"name":{"type":"string"},"order":{"description":"1-based position within this collection. Null when unset.","nullable":true,"type":"integer"}},"type":"object"},"type":"array"},"file_size":{"type":"integer"},"file_type":{"type":"string"},"height":{"nullable":true,"type":"integer"},"id":{"type":"integer"},"ingested_at":{"format":"date-time","type":"string"},"is_favorited":{"type":"boolean"},"is_inbox":{"description":"true = needs triage (sits in the inbox); false = curated. New ingests default to true.","type":"boolean"},"is_missing":{"type":"boolean"},"note":{"description":"Operator's freeform note. Never written by a push, enrich, or import.","type":"string"},"origin":{"description":"How the image got into the gallery: 'ingest' for watcher/sync, 'upload' for the web UI, or any caller-supplied string (app name, URL...) set via POST /images with 'via'","type":"string"},"original_source":{"description":"Operator's image-level original source URL. Never written by a push, enrich, or import.","type":"string"},"page_count":{"description":"Number of pages for cbz / manga rows; null on every other file type.","nullable":true,"type":"integer"},"phash":{"description":"16-char hex perceptual hash; null until the phash backfill or ingest has populated it.","nullable":true,"type":"string"},"sha256":{"type":"string"},"source":{"description":"Site label of the primary (first) entry of 'sources'. Empty string when the image has no origin. Filtered by the search keyword 'source:' which matches any of the image's sources. Settable on create and via PATCH /images/{id}.","type":"string"},"source_type":{"description":"Generation-tool source: 'a1111', 'comfyui', 'a1111,comfyui', or 'none'. Filtered by the search keyword 'ai:'.","type":"string"},"sources":{"description":"Every origin the image came from. The scalar source / url fields mirror the primary (first) origin. Managed through the web UI; the API sets only the primary via source / url.","items":{"properties":{"commentary":{"description":"Artist commentary pulled from (or hand-entered for) this origin; omitted when empty.","type":"string"},"original":{"description":"Upstream artist source the post declared (usually a URL, newline-joined when several); omitted when empty.","type":"string"},"post_id":{"description":"Upstream post id; omitted for a manually-added origin.","type":"string"},"similarity":{"description":"Best similarity-service score (0-100) a lookup matched this origin with; omitted for an exact or manual origin. A matched origin's refetches skip the md5 verify.","type":"number"},"site":{"description":"Site label, e.g. \"danbooru\"; empty for a url-only origin.","type":"string"},"url":{"type":"string"}},"type":"object"},"type":"array"},"tags":{"items":{"$ref":"#/components/schemas/Tag"},"type":"array"},"thumbnail_url":{"type":"string"},"url":{"description":"URL of the primary (first) entry of 'sources'. Empty string when the image has no origin. Must start with http:// or https://. Settable on create and via PATCH /images/{id}.","type":"string"},"width":{"nullable":true,"type":"integer"}},"type":"object"},"ImageRelations":{"description":"Declared relations centred on one image. duplicate_group / alternate_group are null when the image is not part of a group; the four edge slots are null when no edge exists.","properties":{"alternate_group":{"nullable":true,"properties":{"id":{"type":"integer"},"member_ids":{"items":{"type":"integer"},"type":"array"}},"type":"object"},"derivative_source":{"description":"id of the source image when this image is a derivative; null otherwise.","nullable":true,"type":"integer"},"derivatives":{"items":{"type":"integer"},"type":"array"},"duplicate_group":{"nullable":true,"properties":{"id":{"type":"integer"},"member_ids":{"items":{"type":"integer"},"type":"array"},"original_image_id":{"type":"integer"}},"type":"object"},"version_child":{"description":"id of the newer revision; null when this image is the chain leaf.","nullable":true,"type":"integer"},"version_parent":{"description":"id of the older revision; null when this image is the chain root.","nullable":true,"type":"integer"}},"type":"object"},"Implication":{"properties":{"implied_category":{"type":"string"},"implied_id":{"type":"integer"},"implied_name":{"type":"string"},"parent_id":{"type":"integer"}},"type":"object"},"MergeSummary":{"description":"What a duplicate push or enrich folded into the existing image. The merge reconciles the source's own tag slice to the pushed set; tags owned by the operator, the auto-tagger, or another source are never touched.","properties":{"rating_filled":{"description":"True when the push supplied a rating and the image had none; an existing rating is never displaced","type":"boolean"},"source_added":{"type":"boolean"},"tags_added":{"type":"integer"},"tags_removed":{"description":"Tags this source contributed earlier and no longer lists","type":"integer"}},"type":"object"},"PaginatedImages":{"properties":{"limit":{"type":"integer"},"page":{"type":"integer"},"results":{"items":{"$ref":"#/components/schemas/Image"},"type":"array"},"total":{"type":"integer"}},"type":"object"},"PaginatedTags":{"properties":{"limit":{"type":"integer"},"page":{"type":"integer"},"results":{"items":{"$ref":"#/components/schemas/TagRow"},"type":"array"},"total":{"type":"integer"}},"type":"object"},"Tag":{"properties":{"category":{"type":"string"},"confidence":{"nullable":true,"type":"number"},"is_auto":{"type":"boolean"},"name":{"type":"string"},"tagger_name":{"description":"Provenance identifier: auto-tagger subfolder name when is_auto, caller-supplied 'via' (e.g. app name) when manual, null for UI-driven user adds","nullable":true,"type":"string"}},"type":"object"},"TagArray":{"items":{"$ref":"#/components/schemas/Tag"},"type":"array"},"TagRow":{"properties":{"category":{"type":"string"},"color":{"type":"string"},"id":{"type":"integer"},"is_alias":{"type":"boolean"},"last_used_at":{"description":"ISO 8601; most recent application to an image. Absent when never applied.","type":"string"},"name":{"type":"string"},"origin":{"description":"Creation provenance label: 'user', a booru site, 'ptr', an auto-tagger name, an import label. Empty on rows predating the column.","type":"string"},"usage_count":{"type":"integer"}},"type":"object"}},"securitySchemes":{"bearerAuth":{"bearerFormat":"token","description":"Named, scoped token from Settings -\u003e Authentication. Scopes: read (GET), write (POST/PATCH), delete (DELETE); a token missing the scope gets 403 insufficient_scope.","scheme":"bearer","type":"http"}}},"info":{"description":"REST API for monbooru image library","title":"Monbooru API","version":"1.1.0"},"openapi":"3.0.3","paths":{"/":{"get":{"operationId":"apiInfo","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIInfo"}}},"description":"API metadata"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"API disabled (no token configured)"}},"summary":"API info"}},"/categories":{"get":{"operationId":"listCategories","parameters":[{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Category"},"type":"array"}}},"description":"Categories"}},"summary":"List tag categories"},"post":{"operationId":"createCategory","parameters":[{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"color":{"description":"#rgb or #rrggbb; defaults to #888888 when blank.","type":"string"},"name":{"description":"Lowercase letters, digits, underscore, or hyphen. Search-filter keywords are reserved.","type":"string"}},"required":["name"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Category"}}},"description":"Created category"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid or reserved name, or invalid colour"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"A category with this name already exists"}},"summary":"Create a category"}},"/categories/{id}":{"delete":{"operationId":"deleteCategory","parameters":[{"description":"Category ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"action":{"description":"'move' (default; reparent the category's tags) or 'delete_all' (drop the tags too).","type":"string"},"target_id":{"description":"Move target category id; defaults to general when omitted.","type":"integer"}},"type":"object"}}},"required":false},"responses":{"204":{"description":"Deleted"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Built-in category, or an unknown action"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Category not found"}},"summary":"Delete a category"},"patch":{"operationId":"patchCategory","parameters":[{"description":"Category ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"color":{"description":"#rgb or #rrggbb. Allowed on built-in categories.","type":"string"},"name":{"description":"New name. Built-in categories refuse a rename.","type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Category"}}},"description":"Updated category"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No fields, invalid colour, reserved/invalid name, or a built-in rename"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Category not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"A category with the new name already exists"}},"summary":"Rename and/or recolor a category"}},"/galleries":{"get":{"operationId":"listGalleries","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Gallery"},"type":"array"}}},"description":"Galleries with counts and the active flag"}},"summary":"List configured galleries"}},"/images":{"post":{"operationId":"createImage","parameters":[{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"autotag":{"description":"Kick off an auto-tag job on the new image","type":"boolean"},"collection":{"description":"Optional collection label (images.series). Written on the new row; on a duplicate-SHA push added as a membership that never displaces the existing home.","type":"string"},"collection_order":{"description":"Optional 1-based position within the collection. Requires a non-empty collection in the same request.","type":"integer"},"commentary":{"description":"Optional artist commentary for the pushed source (\u003c=10000 chars).","type":"string"},"folder":{"description":"Destination subfolder for relative paths","type":"string"},"md5":{"description":"Optional md5 the source claims for the file (\u003c=64 chars). Recorded on the origin row as an audit trail; never a dedup key.","type":"string"},"notes":{"description":"Optional positional note boxes ({x, y, w, h, body}) for the pushed source.","items":{"type":"object"},"type":"array"},"original":{"description":"Optional upstream artist source the post declared (\u003c=2048 chars, newline-joined when several). Recorded on the same origin as 'source'.","type":"string"},"parent_url":{"description":"Optional canonical URL of the post this post declares as its parent. Must start with http:// or https://. Recorded on the same origin as 'source'; when the parent's post lands in the gallery too, the pair is linked as a derivative relation.","type":"string"},"path":{"description":"Path to a file already on disk. Absolute paths are used verbatim; relative paths are resolved under gallery/\u003cfolder\u003e when folder is set, otherwise under the gallery root. WARNING: absolute paths give a token holder read access to anything the monbooru process can stat.","type":"string"},"source":{"description":"Optional site label for the image's origin (site name, scraper...). Recorded as a source; on a duplicate-SHA push its tags and provenance merge into the existing image instead of being discarded.","type":"string"},"tagger_name":{"description":"Optional auto-tagger name","type":"string"},"tags":{"items":{"type":"string"},"type":"array"},"url":{"description":"Optional canonical web URL the image came from. Must start with http:// or https://. Recorded on the same origin as 'source'.","type":"string"},"via":{"description":"Optional caller-supplied identifier. Stored as images.origin and attached to each initial tag. Blank defaults images.origin to 'ingest' for JSON path-reference mode.","type":"string"}},"required":["path"],"type":"object"}},"multipart/form-data":{"schema":{"properties":{"autotag":{"description":"Set to \"true\" to kick off an auto-tag job on the new image","type":"string"},"collection":{"description":"Optional collection label (images.series). Written on the new row; on a duplicate-SHA push added as a membership that never displaces the existing home.","type":"string"},"collection_order":{"description":"Optional 1-based position within the collection. Requires a non-empty collection in the same request.","type":"string"},"commentary":{"description":"Optional artist commentary for the pushed source (\u003c=10000 chars).","type":"string"},"file":{"description":"Image or video file","format":"binary","type":"string"},"folder":{"description":"Destination subfolder under the gallery root; missing directories are created. Leave blank for the gallery root.","type":"string"},"md5":{"description":"Optional md5 the source claims for the file (\u003c=64 chars). Recorded on the origin row as an audit trail; never a dedup key.","type":"string"},"notes":{"description":"Optional JSON-encoded array of positional note boxes ({x, y, w, h, body}) for the pushed source.","type":"string"},"original":{"description":"Optional upstream artist source the post declared (\u003c=2048 chars, newline-joined when several). Recorded on the same origin as 'source'.","type":"string"},"parent_url":{"description":"Optional canonical URL of the post this post declares as its parent. Must start with http:// or https://. Recorded on the same origin as 'source'; when the parent's post lands in the gallery too, the pair is linked as a derivative relation.","type":"string"},"source":{"description":"Optional site label for the image's origin (site name, scraper...). Recorded as a source; on a duplicate-SHA push its tags and provenance merge into the existing image instead of being discarded.","type":"string"},"tagger_name":{"description":"Optional auto-tagger name; when set with autotag, restricts the job to that tagger","type":"string"},"tags":{"description":"JSON-encoded array of tag names","type":"string"},"url":{"description":"Optional canonical web URL the image came from. Must start with http:// or https://. Recorded on the same origin as 'source'.","type":"string"},"via":{"description":"Optional caller-supplied identifier (app name, URL...). Stored as images.origin and attached to each initial tag via image_tags.tagger_name. Blank defaults images.origin to 'upload' for multipart mode.","type":"string"}},"required":["file"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DuplicateImageResponse"}}},"description":"Duplicate SHA-256: existing image returned with the pushed tags and provenance merged; alias_added is true only when a JSON path-reference recorded a new alias"},"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateImageResponse"}}},"description":"Image created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid request or unsupported file type"},"413":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"File exceeds max size"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Ingest failure"}},"summary":"Add an image"}},"/images/search":{"get":{"operationId":"searchImages","parameters":[{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}},{"description":"Search query (tag list, filters, wildcards)","in":"query","name":"q","required":false,"schema":{"type":"string"}},{"description":"Sort field: newest, filesize, order, random","in":"query","name":"sort","required":false,"schema":{"type":"string"}},{"description":"Sort order: asc, desc","in":"query","name":"order","required":false,"schema":{"type":"string"}},{"description":"Random-sort seed for stable pagination when sort=random","in":"query","name":"seed","required":false,"schema":{"type":"string"}},{"description":"Page number (1-based)","in":"query","name":"page","required":false,"schema":{"type":"string"}},{"description":"Results per page (max 200)","in":"query","name":"limit","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedImages"}}},"description":"Paginated image list"}},"summary":"Search images"}},"/images/{id}":{"delete":{"operationId":"deleteImage","parameters":[{"description":"Image ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}},{"description":"Remove containing folder if empty after deletion","in":"query","name":"delete_empty_folder","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted (folder also removed)"},"204":{"description":"Deleted"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"summary":"Delete image from library"},"get":{"operationId":"getImage","parameters":[{"description":"Image ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Image"}}},"description":"Image metadata"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"summary":"Get image metadata"},"patch":{"operationId":"patchImage","parameters":[{"description":"Image ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"description":"Any subset of the editable fields. An absent or null field is left unchanged; an empty string clears a text field. Clearing collection also clears collection_order unless one is supplied in the same request; to clear collection_order on its own, clear the collection.","properties":{"collection":{"description":"Collection label (images.series). Empty clears.","type":"string"},"collection_order":{"description":"1-based position within the collection. Requires a non-empty collection (incoming or already stored).","type":"integer"},"is_favorited":{"type":"boolean"},"is_inbox":{"description":"true = sits in the inbox (needs triage); false = curated.","type":"boolean"},"source":{"description":"Operator-edited provenance label (\u003c=200 chars). Empty clears.","type":"string"},"url":{"description":"Canonical web URL (\u003c=2048 chars, http:// or https://). Empty clears.","type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Image"}}},"description":"Updated image"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid request (bad url, order without a collection, or no editable fields supplied)"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Relabel collides with another origin the image already carries"}},"summary":"Edit image fields"}},"/images/{id}/enrich":{"post":{"description":"Metadata-only counterpart of a duplicate push, used by monloader's source refetch. Shares the duplicate-push merge semantics: the origin upserts, the source's own tag slice reconciles to the pushed set, and empty url / md5 / commentary / original / notes leave the stored values be.","operationId":"enrichImage","parameters":[{"description":"Image ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"commentary":{"type":"string"},"notes":{"description":"Positional note boxes ({x, y, w, h, body}); a non-empty array replaces the set this origin contributed.","items":{"type":"object"},"type":"array"},"original":{"description":"Upstream artist source the post declared (\u003c=2048 chars, newline-joined when several). A non-empty value overwrites the stored one.","type":"string"},"parent_url":{"description":"Canonical URL of the post this post declares as its parent. Recorded on the origin row; when both sides are in the gallery the pair is linked as a derivative relation. Empty keeps the stored value.","type":"string"},"similarity":{"description":"Similarity-service score (0-100) when the post was found by image similarity rather than an exact hash. Recorded on the origin row; a matched origin's refetches skip the md5 verify. 0 or absent keeps the stored score.","type":"number"},"source":{"description":"Site label of the origin being refetched","type":"string"},"source_md5":{"description":"md5 the source claims (\u003c=64 chars). Compared against the stored file when verify is set; recorded on the origin row.","type":"string"},"tags":{"items":{"type":"string"},"type":"array"},"url":{"type":"string"},"verify":{"description":"When true and source_md5 is non-empty, the stored file is hashed and compared first; a mismatch applies nothing and returns 409 - unless the origin is similarity-matched, whose file differs by design.","type":"boolean"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrichResponse"}}},"description":"Metadata applied"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Hash mismatch: the source no longer serves this file; nothing was changed. Not raised for a similarity-matched origin"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"A merge write failed (recorded as an error fetch status)"}},"summary":"Apply fetched metadata to an existing image"}},"/images/{id}/fetch-status":{"post":{"description":"Lets monloader report a fetch that failed before it could enrich (unsupported URL, timeout, blocked) so the detail page's pending pill resolves instead of polling to its cap.","operationId":"reportFetchStatus","parameters":[{"description":"Image ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"message":{"description":"Operator-facing detail shown on the image's fetch pill","type":"string"},"state":{"description":"Outcome state, e.g. \"error\"","type":"string"}},"required":["state"],"type":"object"}}},"required":true},"responses":{"200":{"description":"Recorded"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing state"}},"summary":"Report a source-fetch outcome"}},"/images/{id}/file":{"get":{"operationId":"getImageFile","parameters":[{"description":"Image ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}},"description":"Original file bytes"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"summary":"Download original image/video bytes"}},"/images/{id}/page/{n}":{"get":{"operationId":"getMangaPage","parameters":[{"description":"Image ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"1-based page number","in":"path","name":"n","required":true,"schema":{"type":"string"}},{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}},"description":"Page bytes (lazily extracted from the archive)"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid page number"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not a manga row, or page out of range"}},"summary":"Download a manga page (cbz rows)"}},"/images/{id}/page/{n}/thumb":{"get":{"operationId":"getMangaPageThumb","parameters":[{"description":"Image ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"1-based page number","in":"path","name":"n","required":true,"schema":{"type":"string"}},{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"image/jpeg":{"schema":{"format":"binary","type":"string"}}},"description":"Page thumbnail (JPEG)"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid page number"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not a manga row, or page out of range"}},"summary":"Download a manga page thumbnail (cbz rows)"}},"/images/{id}/relations":{"get":{"operationId":"getImageRelations","parameters":[{"description":"Image ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageRelations"}}},"description":"Declared relations"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Image not found"}},"summary":"Get declared relations for an image"}},"/images/{id}/tags":{"delete":{"operationId":"removeImageTags","parameters":[{"description":"Image ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"tags":{"description":"Tag names to remove","items":{"type":"string"},"type":"array"}},"required":["tags"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagArray"}}},"description":"Updated tag list"}},"summary":"Remove tags from image"},"get":{"operationId":"listImageTags","parameters":[{"description":"Image ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagArray"}}},"description":"Image tag list"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"summary":"List image tags"},"post":{"operationId":"addImageTags","parameters":[{"description":"Image ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"tags":{"description":"Tag names to add","items":{"type":"string"},"type":"array"},"via":{"description":"Optional caller-supplied identifier attached to each added tag (app name, URL...); stored in image_tags.tagger_name so the detail page can surface which third party contributed them","type":"string"}},"required":["tags"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/TagArray"},{"properties":{"tag_warnings":{"items":{"type":"string"},"type":"array"},"tags":{"$ref":"#/components/schemas/TagArray"}},"type":"object"}]}}},"description":"Bare TagArray on success; wrapped in {tags, tag_warnings} when any tag failed validation."}},"summary":"Add tags to image"}},"/images/{id}/thumbnail":{"get":{"operationId":"getImageThumbnail","parameters":[{"description":"Image ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"image/jpeg":{"schema":{"format":"binary","type":"string"}}},"description":"JPEG thumbnail"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Image or thumbnail not found"}},"summary":"Download the static thumbnail"}},"/relations":{"delete":{"operationId":"removeRelation","parameters":[{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"a":{"description":"Required for version / derivative / not_related; either order is accepted.","type":"integer"},"b":{"description":"Pair partner for a.","type":"integer"},"group_id":{"description":"Required for dissolve_dup / dissolve_alt / promote_original.","type":"integer"},"image_id":{"description":"Required for duplicate / alternate (the member to unlink); the new original for promote_original.","type":"integer"},"root_id":{"description":"Required for dissolve_version / dissolve_derivative; any chain or tree member id (the walker locates the root).","type":"integer"},"type":{"description":"One of: duplicate, alternate, version, derivative, not_related, dissolve_dup, dissolve_alt, dissolve_version, dissolve_derivative, promote_original","type":"string"}},"required":["type"],"type":"object"}}},"required":true},"responses":{"204":{"description":"Removed (idempotent)"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid request"}},"summary":"Remove a declared relation"},"post":{"operationId":"addRelation","parameters":[{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"a":{"description":"Left image id. For directed types (version, derivative), `a` is the parent / source (older revision, derived-from).","type":"integer"},"b":{"description":"Right image id. For directed types, the child / derivative.","type":"integer"},"direction":{"description":"Optional `ba` swaps left/right; default `ab` treats `a` as the parent/source side.","type":"string"},"type":{"description":"One of: duplicate, alternate, version, derivative, not_related","type":"string"}},"required":["type","a","b"],"type":"object"}}},"required":true},"responses":{"201":{"description":"Relation created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid request (self-relation, unknown type, missing ids)"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Pair carries a conflicting relation already; remove it first."}},"summary":"Declare a relation"}},"/tags":{"get":{"operationId":"listTags","parameters":[{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}},{"description":"Prefix filter","in":"query","name":"q","required":false,"schema":{"type":"string"}},{"description":"Filter by category name","in":"query","name":"category","required":false,"schema":{"type":"string"}},{"description":"Sort field (usage, alpha)","in":"query","name":"sort","required":false,"schema":{"type":"string"}},{"description":"Page number","in":"query","name":"page","required":false,"schema":{"type":"string"}},{"description":"Results per page (default 100, max 500)","in":"query","name":"limit","required":false,"schema":{"type":"string"}},{"description":"Hide zero-usage non-alias tags ('0' to opt out). Default surfaces them so the API total matches the /tags page total.","in":"query","name":"show_zero","required":false,"schema":{"type":"string"}},{"description":"Filter by stored creation origin ('user', a booru site, 'ptr', an auto-tagger name, ...). The legacy 'alias' value narrows to alias rows. Empty (default) returns every tag.","in":"query","name":"origin","required":false,"schema":{"type":"string"}},{"description":"Structural filter: 'tag' (non-alias rows), 'alias' (alias rows). Empty (default) returns both.","in":"query","name":"type","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedTags"}}},"description":"Paginated tag list"}},"summary":"List tags"},"post":{"operationId":"createTag","parameters":[{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"category":{"description":"Category name; defaults to general.","type":"string"},"name":{"type":"string"}},"required":["name"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagRow"}}},"description":"The tag (created, or the existing row when the name already exists in the category)"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing name, unknown category, or invalid tag name"}},"summary":"Create a tag (get-or-create)"}},"/tags/aliases":{"post":{"operationId":"createAlias","parameters":[{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"canonical_id":{"description":"The tag this alias resolves to.","type":"integer"},"category":{"description":"Category for the alias; defaults to general.","type":"string"},"name":{"description":"Alias name.","type":"string"}},"required":["name","canonical_id"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagRow"}}},"description":"The alias row"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing fields or an invalid alias target"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Canonical tag not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The name already names a tag with image_tags rows; merge it instead"}},"summary":"Create an alias"}},"/tags/merge":{"post":{"operationId":"mergeTags","parameters":[{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"alias_id":{"description":"Tag to retire; becomes an alias of canonical_id and its image_tags move onto it.","type":"integer"},"canonical_id":{"description":"Surviving tag.","type":"integer"}},"required":["alias_id","canonical_id"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagRow"}}},"description":"The canonical tag"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Self-merge, missing ids, or a merge into an alias"}},"summary":"Merge one tag into another"}},"/tags/{id}":{"delete":{"operationId":"deleteTag","parameters":[{"description":"Tag ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted (rating-category rows are usage-stripped; the catalog row stays)"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Tag not found"}},"summary":"Delete a tag"},"patch":{"operationId":"patchTag","parameters":[{"description":"Tag ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"category":{"description":"Move to this category by name.","type":"string"},"name":{"description":"New name.","type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagRow"}}},"description":"Updated tag"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No fields supplied, or a rating-tag edit"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Tag not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"A tag with the new name already exists in the target category"}},"summary":"Rename a tag and/or move it to another category"}},"/tags/{id}/implications":{"get":{"operationId":"listImplications","parameters":[{"description":"Parent tag ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Implication"},"type":"array"}}},"description":"Direct implications"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Parent tag not found"}},"summary":"List a tag's implications"},"post":{"description":"Declares parent -\u003e implied. The edge is effective immediately for future tag adds; the historical fan-out across images already carrying the parent (the web background job) is not run from the API.","operationId":"addImplication","parameters":[{"description":"Parent tag ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"implied_id":{"description":"Existing tag id the parent implies. Both sides must be canonical (non-alias).","type":"integer"}},"required":["implied_id"],"type":"object"}}},"required":true},"responses":{"200":{"description":"Edge already declared (no-op)"},"201":{"description":"Edge created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing implied_id, self-implication, or an alias on either side"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Parent or implied tag not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Edge would close a cycle"}},"summary":"Declare an implication"}},"/tags/{id}/implications/{impliedID}":{"delete":{"description":"Drops the edge only; the image-side sweep of rows implied solely by this edge is the web background job and is not run from the API.","operationId":"removeImplication","parameters":[{"description":"Parent tag ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Implied tag ID","in":"path","name":"impliedID","required":true,"schema":{"type":"string"}},{"description":"Target gallery name; omit for the active gallery (also accepted as X-Monbooru-Gallery header)","in":"query","name":"gallery","required":false,"schema":{"type":"string"}}],"responses":{"204":{"description":"Removed"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Edge not found"}},"summary":"Remove an implication"}}},"security":[{"bearerAuth":[]}],"servers":[{"description":"This server","url":"https://booru.grinev.dev/api/v1"}]}
