{
 "openapi": "3.1.0",
 "info": {
  "title": "Glosses API",
  "version": "1",
  "description": "Glosses publishes annotations of books, anchored to exact words in fixed editions, and this API reads them without a token. GET /v1/ returns a JSON index of these paths, and POST /v1/suggestions is the only write open to the public.",
  "license": {"name": "CC0 1.0", "identifier": "CC0-1.0"}
 },
 "servers": [{"url": "https://glosses.org"}],
 "tags": [
  {"name": "Works", "description": "Works, editions, units, permanent numbers and the library's counts."},
  {"name": "Annotations", "description": "Annotations and their revisions."},
  {"name": "Search", "description": "Search the library and find exact words."},
  {"name": "Suggestions", "description": "Suggestions from readers and agents."},
  {"name": "Feeds", "description": "The activity log and the Atom feed."}
 ],
 "paths": {
  "/v1/works": {
   "get": {
    "tags": ["Works"],
    "operationId": "listWorks",
    "summary": "List the works in the library.",
    "description": "Works are ordered by title. Each work carries its current edition and its top-level units.",
    "parameters": [
     {"$ref": "#/components/parameters/Limit100"},
     {
      "name": "cursor",
      "in": "query",
      "description": "The opaque next_cursor value from the previous page.",
      "required": false,
      "schema": {"type": "string"},
      "example": "WyJoZXJldGljcyIsImhlcmV0aWNzIl0"
     }
    ],
    "responses": {
     "200": {
      "description": "A page of works.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/WorkList"},
        "example": {
         "items": [
          {
           "title": "Heretics",
           "work_type": "book",
           "editions": [{"edition_id": "pg470-r1", "label": "Project Gutenberg #470 text", "language_tag": "en", "publication_year": null, "current_visible_source_revision_id": "src_pg470-r1"}],
           "slug": "heretics",
           "oid": 248,
           "authors": ["G. K. Chesterton"],
           "url": "/heretics",
           "current_edition": "pg470-r1",
           "current_edition_oid": 249,
           "structure": [{"kind": "chapter", "prefix": "CH", "width": 2}],
           "chapters": [{"id": "CH01", "title": "Introductory Remarks on the Importance of Orthodoxy", "oid": 251, "paragraph_count": 12}]
          }
         ],
         "next_cursor": "WyJoZXJldGljcyIsImhlcmV0aWNzIl0"
        }
       }
      }
     },
     "400": {
      "description": "invalid_query for a bad limit, or invalid_cursor.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {"error": {"code": "invalid_query", "message": "limit must be an integer from 1 through 100.", "details": {}}, "request_id": "req_13e841fe84ba"}
       }
      }
     }
    }
   }
  },
  "/v1/works/{work}": {
   "get": {
    "tags": ["Works"],
    "operationId": "getWork",
    "summary": "Read the whole current edition of a work.",
    "description": "The answer holds the ids, permanent numbers and texts of every unit. Use chapter to read one top-level unit.",
    "parameters": [
     {"$ref": "#/components/parameters/Work"},
     {"$ref": "#/components/parameters/Edition"},
     {"name": "chapter", "in": "query", "description": "One top-level unit id; any case.", "required": false, "schema": {"type": "string"}, "example": "CH05"},
     {"$ref": "#/components/parameters/Format"}
    ],
    "responses": {
     "200": {
      "description": "The edition's text.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/WorkRead"},
        "example": {
         "work": {"slug": "orthodoxy", "title": "Orthodoxy", "authors": ["G. K. Chesterton"], "type": "book", "oid": 1, "url": "/orthodoxy"},
         "edition": {
          "id": "pg16769-r1",
          "oid": 2,
          "label": "Project Gutenberg EBook #16769, from book.json",
          "language": "en",
          "source_url": "https://www.gutenberg.org/ebooks/16769",
          "rights": "Public domain",
          "provenance_note": "Wrapped lines joined with one space; underscores kept; from book.json sha256 05720d89dfe47...",
          "source_revision_id": "src_pg16769-r1",
          "normalization": "unicode-nfc-lf-v1",
          "url": "/orthodoxy/pg16769-r1"
         },
         "sha256": "sha256:e19ad0a9db6aa0d5f3d38183bcdee4c1a441a1f380f69405d59f8c0a93c992f3",
         "content_sha256": "sha256:e19ad0a9db6aa0d5f3d38183bcdee4c1a441a1f380f69405d59f8c0a93c992f3",
         "source_sha256": "05720d89dfe47f8bcca7634891cc0275b8ad4023bfd8d44905ca55151a64eac5",
         "structure": [{"kind": "chapter", "prefix": "CH", "width": 2}],
         "chapters": [
          {
           "id": "CH05",
           "title": "The Flag of the World",
           "oid": 8,
           "paragraphs": [
            {"id": "CH05.P001", "oid": 100, "text": "When I was a boy there were two curious men running about who were called the optimist and...", "sha256": "acbdd86df4e504db3fadb9086813349aa2d86bcee6103b03210c674343d75ff8"}
           ]
          }
         ]
        }
       },
       "text/markdown": {"schema": {"type": "string", "description": "Markdown of the same read, when format is md."}}
      }
     },
     "404": {
      "description": "not_found: no such work, edition or chapter.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {"error": {"code": "not_found", "message": "Work was not found.", "details": {}}, "request_id": "req_a55211b651bd"}
       }
      }
     },
     "409": {
      "description": "no_structure: the edition has no structure declaration.",
      "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
     }
    }
   }
  },
  "/v1/works/{work}/{unit}": {
   "get": {
    "tags": ["Works"],
    "operationId": "getUnit",
    "summary": "Read one unit or a range of units with its annotations.",
    "description": "A unit is a chapter or a paragraph. A range of siblings such as CH05.P016-P018 can stand in place of a unit. Other spellings redirect to the canonical id.",
    "parameters": [
     {"$ref": "#/components/parameters/Work"},
     {
      "name": "unit",
      "in": "path",
      "description": "A unit id, or a range START-END of sibling units.",
      "required": true,
      "schema": {"type": "string"},
      "examples": {"paragraph": {"value": "CH05.P016"}, "chapter": {"value": "CH05"}, "range": {"value": "CH05.P016-P018"}}
     },
     {"$ref": "#/components/parameters/Edition"},
     {
      "name": "context",
      "in": "query",
      "description": "Add up to this many neighbouring siblings on each side, from 0 to 10; single units only.",
      "required": false,
      "schema": {"type": "integer", "minimum": 0, "maximum": 10},
      "example": 1
     },
     {"$ref": "#/components/parameters/Containers"},
     {"$ref": "#/components/parameters/Format"}
    ],
    "responses": {
     "200": {
      "description": "The unit or the range.",
      "content": {
       "application/json": {
        "schema": {"oneOf": [{"$ref": "#/components/schemas/UnitRead"}, {"$ref": "#/components/schemas/RangeRead"}]},
        "examples": {
         "paragraph": {
          "value": {
           "id": "CH05.P016",
           "oid": 115,
           "kind": "paragraph",
           "label": null,
           "work": "orthodoxy",
           "edition": "pg16769-r1",
           "parent": "CH05",
           "ancestors": ["CH05"],
           "prev": "CH05.P015",
           "next": "CH05.P017",
           "url": "/orthodoxy/pg16769-r1/CH05.P016",
           "text": "About the same time I read a solemn flippancy by some free thinker: he said that a suicide...",
           "length_cp": 1519,
           "sha256": "ff85dcedfc1528227eb30b70d74ec5368c5b00d4cad97549f0cee10a4e5aaa67",
           "context": {
            "before": [{"id": "CH05.P015", "oid": 114, "text": "I put these things not in their mature logical sequence, but as they came: and this view w..."}],
            "after": [{"id": "CH05.P017", "oid": 116, "text": "This was the first of the long train of enigmas with which Christianity entered the discus..."}]
           },
           "annotations": [],
           "annotations_next_cursor": null,
           "cited_by": [
            {
             "id": "a_tffeow37",
             "oid": 164558,
             "revision": 1,
             "revision_oid": 164559,
             "created_at": "2026-11-01T22:08:50Z",
             "updated_at": "2026-11-01T22:08:50Z",
             "title": "Orthodoxy",
             "work": "orthodoxy",
             "edition": "pg16769-r1",
             "anchors": [{"scope": "work", "role": null, "edition": "pg16769-r1", "oid": 2, "url": "/orthodoxy/pg16769-r1"}],
             "metadata": {"run": "api-smoke-2026-11-01", "pass": "ARGUMENT", "task": "Linked account of argument development in the whole book", "worker": "argument", "model": "gpt-6.1-sol"},
             "url": "/a/a_tffeow37"
            }
           ],
           "cited_by_next_cursor": null
          }
         },
         "range": {
          "value": {
           "id": "CH05.P016-P018",
           "range": "CH05.P016-P018",
           "kind": "paragraph",
           "work": "orthodoxy",
           "edition": "pg16769-r1",
           "start": "CH05.P016",
           "end": "CH05.P018",
           "start_oid": 115,
           "end_oid": 117,
           "parent": "CH05",
           "ancestors": ["CH05"],
           "units": [
            {
             "id": "CH05.P016",
             "oid": 115,
             "kind": "paragraph",
             "label": null,
             "text": "About the same time I read a solemn flippancy by some free thinker: he said that a suicide...",
             "length_cp": 1519,
             "sha256": "ff85dcedfc1528227eb30b70d74ec5368c5b00d4cad97549f0cee10a4e5aaa67"
            }
           ],
           "url": "/orthodoxy/pg16769-r1/CH05.P016-P018",
           "annotations": [],
           "annotations_next_cursor": null,
           "cited_by": [
            {
             "id": "a_tffeow37",
             "oid": 164558,
             "revision": 1,
             "revision_oid": 164559,
             "created_at": "2026-11-01T22:08:50Z",
             "updated_at": "2026-11-01T22:08:50Z",
             "title": "Orthodoxy",
             "work": "orthodoxy",
             "edition": "pg16769-r1",
             "anchors": [{"scope": "work", "role": null, "edition": "pg16769-r1", "oid": 2, "url": "/orthodoxy/pg16769-r1"}],
             "metadata": {"run": "api-smoke-2026-11-01", "pass": "ARGUMENT", "task": "Linked account of argument development in the whole book", "worker": "argument", "model": "gpt-6.1-sol"},
             "url": "/a/a_tffeow37"
            }
           ],
           "cited_by_next_cursor": null
          }
         }
        }
       },
       "text/markdown": {"schema": {"type": "string", "description": "Markdown of the same read, when format is md."}}
      }
     },
     "301": {
      "description": "The canonical spelling of the unit id or range.",
      "headers": {"Location": {"description": "The canonical path.", "schema": {"type": "string"}}},
      "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RedirectBody"}, "example": {"redirect": "/v1/works/orthodoxy/CH05.P016"}}}
     },
     "400": {
      "description": "invalid_query for a bad context or containers value.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {"error": {"code": "invalid_query", "message": "context must be an integer from 0 through 10.", "details": {}}, "request_id": "req_29b38165bd41"}
       }
      }
     },
     "404": {
      "description": "not_found: no such work, edition or unit.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {"error": {"code": "not_found", "message": "CH05.P999 is not a unit of pg16769-r1.", "details": {}}, "request_id": "req_7bd562f9cd4b"}
       }
      }
     },
     "422": {
      "description": "invalid_range: the range is malformed, crosses levels or parents, or runs backwards.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {
         "error": {"code": "invalid_range", "message": "CH05.P018-P016: the start unit must come before the end unit.", "details": {"field": "unit", "reason": "wrong_order", "range": "CH05.P018-P016"}},
         "request_id": "req_2a589bc7803a"
        }
       }
      }
     }
    }
   }
  },
  "/v1/annotations": {
   "get": {
    "tags": ["Annotations"],
    "operationId": "listAnnotations",
    "summary": "List public annotations, newest first.",
    "description": "Filters combine with AND. The unit filters need work. Unknown parameters are refused.",
    "parameters": [
     {"name": "work", "in": "query", "description": "Only annotations on this work.", "required": false, "schema": {"type": "string"}, "example": "orthodoxy"},
     {
      "name": "edition",
      "in": "query",
      "description": "Only annotations made in this edition; needs work.",
      "required": false,
      "schema": {"type": "string"},
      "example": "pg16769-r1"
     },
     {
      "name": "chapter",
      "in": "query",
      "description": "Only annotations on or inside this unit or range; needs work.",
      "required": false,
      "schema": {"type": "string"},
      "example": "CH05"
     },
     {
      "name": "paragraph",
      "in": "query",
      "description": "Only annotations on or inside this unit or range; needs work.",
      "required": false,
      "schema": {"type": "string"},
      "example": "CH05.P016-P018"
     },
     {
      "name": "overlaps",
      "in": "query",
      "description": "Only span anchors in UNIT:start-end, with start below end; needs work.",
      "required": false,
      "schema": {"type": "string", "pattern": "^[^:]+:[0-9]+-[0-9]+$"},
      "example": "CH03.P002:494-592"
     },
     {
      "name": "match",
      "in": "query",
      "description": "How overlaps compares spans.",
      "required": false,
      "schema": {"type": "string", "enum": ["exact", "overlap", "contained_by"], "default": "overlap"},
      "example": "overlap"
     },
     {"$ref": "#/components/parameters/Containers"},
     {
      "name": "cites",
      "in": "query",
      "description": "Only annotations that cite this unit or range; needs work.",
      "required": false,
      "schema": {"type": "string"},
      "example": "CH04.P012"
     },
     {
      "name": "pass",
      "in": "query",
      "description": "Only annotations whose metadata.pass equals this value.",
      "required": false,
      "schema": {"type": "string"},
      "example": "EDITION"
     },
     {
      "name": "task",
      "in": "query",
      "description": "Only annotations whose metadata.task equals this value.",
      "required": false,
      "schema": {"type": "string"},
      "example": "Compare supplied editions and identify textual discrepancies"
     },
     {"$ref": "#/components/parameters/Since"},
     {
      "name": "include",
      "in": "query",
      "description": "Set to body to add body_markdown to each item.",
      "required": false,
      "schema": {"type": "string", "enum": ["body"]},
      "example": "body"
     },
     {"$ref": "#/components/parameters/Limit100"},
     {
      "name": "cursor",
      "in": "query",
      "description": "The opaque next_cursor value from the previous page.",
      "required": false,
      "schema": {"type": "string"},
      "example": "WzE2NDU5Nl0"
     }
    ],
    "responses": {
     "200": {
      "description": "A page of annotations.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/AnnotationList"},
        "example": {
         "order": "oid_desc",
         "match": "overlap",
         "items": [
          {
           "id": "a_bgp2ga67",
           "oid": 164596,
           "revision": 2,
           "revision_oid": 164606,
           "created_at": "2026-11-01T22:14:26Z",
           "updated_at": "2026-11-01T22:17:20Z",
           "title": "make righteousness and peace kiss each other",
           "work": "orthodoxy",
           "edition": "pg16769-r1",
           "anchors": [
            {
             "unit": "CH03.P002",
             "oid": 40,
             "role": null,
             "edition": "pg16769-r1",
             "paragraph": "CH03.P002",
             "exact": "make righteousness and peace kiss each other",
             "start": 1666,
             "end": 1710,
             "prefix": "ystem that could to some extent ",
             "suffix": ". Now they do not even bow. But ",
             "url": "/orthodoxy/pg16769-r1/CH03.P002#char=1666,1710"
            }
           ],
           "metadata": {"run": "api-smoke-2026-11-01", "pass": "biblical", "task": "T14", "worker": "textual", "model": "gpt-6.1-sol"},
           "url": "/a/a_bgp2ga67"
          }
         ],
         "next_cursor": "WzE2NDU5Nl0"
        }
       }
      }
     },
     "400": {
      "description": "invalid_query or invalid_cursor.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {"error": {"code": "invalid_query", "message": "work is required with edition, chapter, paragraph, overlaps or cites.", "details": {}}, "request_id": "req_565898ea22b5"}
       }
      }
     },
     "404": {
      "description": "not_found: no such work or edition.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {"error": {"code": "not_found", "message": "Work was not found.", "details": {}}, "request_id": "req_6a854219ac9f"}
       }
      }
     },
     "422": {
      "description": "invalid_range: a unit range filter is not a valid range.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {
         "error": {"code": "invalid_range", "message": "CH05.P018-P016: the start unit must come before the end unit.", "details": {"field": "paragraph", "reason": "wrong_order", "range": "CH05.P018-P016"}},
         "request_id": "req_5f9dad8fcc70"
        }
       }
      }
     }
    }
   }
  },
  "/v1/annotations/{id}": {
   "get": {
    "tags": ["Annotations"],
    "operationId": "getAnnotation",
    "summary": "Read the current revision of an annotation.",
    "description": "The record has the body, anchors, sources and links. It also lists the annotations that cite it.",
    "parameters": [{"$ref": "#/components/parameters/AnnotationId"}],
    "responses": {
     "200": {
      "description": "The annotation.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Annotation"},
        "example": {
         "id": "a_emfjeobr",
         "oid": 164588,
         "revision": 1,
         "revision_oid": 164589,
         "work": "orthodoxy",
         "edition": "pg16769-r1",
         "title": "The virtues have gone mad because they have been isolated from each ot",
         "anchors": [
          {
           "unit": "CH03.P002",
           "oid": 40,
           "role": null,
           "edition": "pg16769-r1",
           "paragraph": "CH03.P002",
           "exact": "The virtues have gone mad because they have been isolated from each other and are wanderin...",
           "start": 494,
           "end": 592,
           "prefix": "old Christian virtues gone mad. ",
           "suffix": " Thus some scientists care for t",
           "url": "/orthodoxy/pg16769-r1/CH03.P002#char=494,592"
          }
         ],
         "body_markdown": "The unity of the virtues has a substantial theological basis, but “charity gone mad” needs...",
         "sources": [{"url": "https://bible.usccb.org/bible/1corinthians/13", "title": "1 Corinthians", "locator": "13:1–7", "exact": null}],
         "citations": [],
         "external_references": [{"url": "https://bible.usccb.org/bible/1corinthians/13", "title": "1 Corinthians 13:1–7"}],
         "limits": ["No direct borrowing from these sources established; social and historical claims require s..."],
         "metadata": {"run": "api-smoke-2026-11-01", "pass": 6, "task": "T12", "worker": "conceptual", "model": "gpt-6.1-sol"},
         "content_sha256": "sha256:b67a8b204e2d80cdcbb2d2fef4ba2539fc2a2801e03f034b25986dd72c00feeb",
         "created_at": "2026-11-01T22:13:35Z",
         "is_current": true,
         "url": "/a/a_emfjeobr",
         "revision_path": "/a/a_emfjeobr/r/1",
         "cited_by": []
        }
       }
      }
     },
     "404": {
      "description": "not_found: no public annotation has this id.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {"error": {"code": "not_found", "message": "Annotation was not found.", "details": {}}, "request_id": "req_063107895190"}
       }
      }
     }
    }
   }
  },
  "/v1/annotations/{id}/revisions/{n}": {
   "get": {
    "tags": ["Annotations"],
    "operationId": "getAnnotationRevision",
    "summary": "Read one numbered revision of an annotation.",
    "description": "The record has the same shape as the current one. is_current tells whether it is the latest.",
    "parameters": [
     {"$ref": "#/components/parameters/AnnotationId"},
     {
      "name": "n",
      "in": "path",
      "description": "The revision number.",
      "required": true,
      "schema": {"type": "integer", "minimum": 1, "maximum": 999999999},
      "example": 1
     }
    ],
    "responses": {
     "200": {
      "description": "The revision.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Revision"},
        "example": {
         "id": "a_tzucmkls",
         "oid": 164582,
         "revision": 1,
         "revision_oid": 164583,
         "work": "orthodoxy",
         "edition": "pg16769-r1",
         "title": "the balance was often distributed over the whole body of Christendom",
         "anchors": [
          {
           "unit": "CH06.P029",
           "oid": 154,
           "role": null,
           "edition": "pg16769-r1",
           "paragraph": "CH06.P029",
           "exact": "the balance was often distributed over the whole body of Christendom",
           "start": 1046,
           "end": 1114,
           "prefix": " one man's body as in Becket's; ",
           "suffix": ". Because a man prayed and faste",
           "url": "/orthodoxy/pg16769-r1/CH06.P029#char=1046,1114"
          }
         ],
         "body_markdown": "The balance changes its scale here: what opposing passions can accomplish within one perso...",
         "sources": [{"paragraph": "CH03.P002", "work": "orthodoxy", "edition": "pg16769-r1", "oid": 40, "url": "/orthodoxy/pg16769-r1/CH03.P002"}],
         "citations": [{"kind": "unit", "work": "orthodoxy", "edition": "pg16769-r1", "unit": "CH03.P002", "oid": 40, "url": "/orthodoxy/pg16769-r1/CH03.P002", "label": "CH03.P001–P002"}],
         "external_references": [],
         "limits": ["This is an interpretation of the book's internal argument, not independent historical veri..."],
         "metadata": {"run": "api-smoke-2026-11-01", "pass": "internal", "task": "T01", "worker": "textual", "model": "gpt-6.1-sol"},
         "content_sha256": "sha256:ce939a7021d6f1e98fba233f07703d0e01610d97aaad15456052534f7c29bb3f",
         "created_at": "2026-11-01T22:13:22Z",
         "is_current": false,
         "url": "/a/a_tzucmkls",
         "revision_path": "/a/a_tzucmkls/r/1",
         "cited_by": []
        }
       }
      }
     },
     "404": {
      "description": "not_found: no such annotation or public revision.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {"error": {"code": "not_found", "message": "Annotation revision was not found.", "details": {}}, "request_id": "req_232f5ede83ab"}
       }
      }
     }
    }
   }
  },
  "/v1/annotations/batch-read": {
   "post": {
    "tags": ["Annotations"],
    "operationId": "batchReadAnnotations",
    "summary": "Read up to 100 annotations in one request.",
    "description": "Each result has its own status. A missing annotation does not fail the request.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {"$ref": "#/components/schemas/BatchReadRequest"},
       "example": {"items": [{"id": "a_tzucmkls", "revision": 1}, {"id": "a_zzzzzzzz"}]}
      }
     }
    },
    "responses": {
     "200": {
      "description": "One result per selector.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/BatchReadResult"},
        "example": {
         "items": [
          {
           "selector": {"id": "a_tzucmkls", "revision": 1},
           "status": 200,
           "revision": {
            "id": "a_tzucmkls",
            "oid": 164582,
            "revision": 1,
            "revision_oid": 164583,
            "work": "orthodoxy",
            "edition": "pg16769-r1",
            "title": "the balance was often distributed over the whole body of Christendom",
            "is_current": false,
            "url": "/a/a_tzucmkls",
            "revision_path": "/a/a_tzucmkls/r/1"
           }
          },
          {"selector": {"id": "a_zzzzzzzz"}, "status": 404, "error": {"code": "not_found", "message": "Annotation was not found."}}
         ]
        }
       }
      }
     },
     "400": {
      "description": "invalid_payload or invalid_json: items is missing, empty, over 100, or malformed.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {"error": {"code": "invalid_payload", "message": "items must contain from 1 through 100 selectors {id, revision?}.", "details": {}}, "request_id": "req_499c3fe18df8"}
       }
      }
     }
    }
   }
  },
  "/v1/resolve": {
   "post": {
    "tags": ["Search"],
    "operationId": "resolveQuote",
    "summary": "Find the offsets of exact words in a work.",
    "description": "Matching is exact and case-sensitive. Send prefix, suffix or occurrence when the words occur more than once. Send start and end to check offsets instead.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {"$ref": "#/components/schemas/ResolveRequest"},
       "example": {"work": "orthodoxy", "paragraph": "CH05.P016", "exact": "solemn flippancy"}
      }
     }
    },
    "responses": {
     "200": {
      "description": "The located span.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/ResolveResult"},
        "example": {
         "work": "orthodoxy",
         "edition": "pg16769-r1",
         "paragraph": "CH05.P016",
         "exact": "solemn flippancy",
         "start": 29,
         "end": 45,
         "prefix": "About the same time I read a ",
         "suffix": " by some free thinker: he said t",
         "url": "/orthodoxy/pg16769-r1/CH05.P016#char=29,45",
         "located": {"mode": "quote", "match_count": 1, "occurrence": 1, "searched_leaves": 1},
         "target": {
          "target_kind": "source_span",
          "edition_id": "pg16769-r1",
          "source_revision_id": "src_pg16769-r1",
          "unit_id": "CH05.P016",
          "start_cp": 29,
          "end_cp": 45,
          "exact_quote": "solemn flippancy",
          "prefix": "About the same time I read a ",
          "suffix": " by some free thinker: he said t"
         }
        }
       }
      }
     },
     "400": {
      "description": "invalid_target, quote_mismatch, invalid_payload or invalid_json.",
      "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
     },
     "404": {
      "description": "quote_not_found, with near matches in details; or not_found for an edition or unit.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {
         "error": {"code": "quote_not_found", "message": "exact_quote was not found in the searched text with the given context. A near match is nev...", "details": {"searched_leaves": 1, "near_matches": []}},
         "request_id": "req_ef1d4210f75a"
        }
       }
      }
     },
     "409": {
      "description": "ambiguous_quote: the words occur more than once; details lists candidates.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {
         "error": {
          "code": "ambiguous_quote",
          "message": "exact_quote occurs 26 times in the searched text. Send prefix or suffix context, a narrowe...",
          "details": {
           "match_count": 26,
           "candidates": [
            {
             "occurrence": 1,
             "target": {
              "target_kind": "source_span",
              "edition_id": "pg16769-r1",
              "source_revision_id": "src_pg16769-r1",
              "unit_id": "CH05.P016",
              "start_cp": 6,
              "end_cp": 9,
              "exact_quote": "the",
              "prefix": "About ",
              "suffix": " same time I read a solemn flipp"
             }
            }
           ]
          }
         },
         "request_id": "req_f44276655787"
        }
       }
      }
     },
     "422": {
      "description": "unknown_work or invalid_field: work is missing or names no public work.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {"error": {"code": "unknown_work", "message": "No public work has the slug nope.", "details": {"field": "work"}}, "request_id": "req_ad3b6308e718"}
       }
      }
     }
    }
   }
  },
  "/v1/search": {
   "get": {
    "tags": ["Search"],
    "operationId": "searchLibrary",
    "summary": "Search works, paragraphs and cited outside sources.",
    "description": "By default searches works by title/author, paragraphs and cited outside sources. Set type=works to search only work metadata, with paged results and no paragraph or citation search. Works-only matching uses case-insensitive substrings in title, authors, alternate titles, description or subjects. Paragraphs of current editions match when they hold every word of q. Outside sources match on URL or title.",
    "parameters": [
     {
      "name": "type",
      "in": "query",
      "description": "all keeps the mixed library search; works searches only work metadata. Works-only cursors are bound to the query and type.",
      "required": false,
      "schema": {"type": "string", "enum": ["all", "works"], "default": "all"},
      "example": "works"
     },
     {
      "name": "q",
      "in": "query",
      "description": "The query, at most 500 characters and 16 words.",
      "required": true,
      "schema": {"type": "string", "minLength": 1, "maxLength": 500},
      "example": "solemn flippancy"
     },
     {
      "name": "limit",
      "in": "query",
      "description": "Results per page, from 1 to 20: works for type=works, otherwise paragraphs.",
      "required": false,
      "schema": {"type": "integer", "minimum": 1, "maximum": 20, "default": 20},
      "example": 20
     },
     {
      "name": "cursor",
      "in": "query",
      "description": "The opaque works_next_cursor for type=works, otherwise paragraphs_next_cursor, from the previous page.",
      "required": false,
      "schema": {"type": "string"},
      "example": "WzFd"
     }
    ],
    "responses": {
     "200": {
      "description": "The matches.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/SearchResult"},
        "example": {
         "q": "solemn flippancy",
         "works": [],
         "paragraphs": [
          {
           "work": "orthodoxy",
           "edition": "pg16769-r1",
           "paragraph": "CH05.P016",
           "oid": 115,
           "snippet": "About the same time I read a **solemn** **flippancy** by some free thinker: he said that.....",
           "url": "/orthodoxy/pg16769-r1/CH05.P016"
          }
         ],
         "paragraphs_next_cursor": null,
         "referenced": []
        }
       }
      }
     },
     "400": {
      "description": "invalid_query: q is missing or too long, or a parameter is unknown; or invalid_cursor.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {"error": {"code": "invalid_query", "message": "q is at most 500 characters and 16 words.", "details": {}}, "request_id": "req_e4d9464a1a0b"}
       }
      }
     }
    }
   }
  },
  "/v1/activity": {
   "get": {
    "tags": ["Feeds"],
    "operationId": "listActivity",
    "summary": "List the latest changes to the library.",
    "description": "Events are newest first. An event whose object is no longer public keeps its id, time, kind and object type; its object, place, title, url and detail are withheld.",
    "parameters": [
     {"$ref": "#/components/parameters/Limit100"},
     {"$ref": "#/components/parameters/Since"},
     {
      "name": "kind",
      "in": "query",
      "description": "Only these event kinds, comma-separated.",
      "required": false,
      "schema": {"type": "string"},
      "example": "annotation.published,annotation.revised"
     },
     {
      "name": "cursor",
      "in": "query",
      "description": "The opaque next_cursor value from the previous page.",
      "required": false,
      "schema": {"type": "string"},
      "example": "WyIyMDI2LTExLTAxVDIyOjE1OjMwWiIsMjEwXQ"
     }
    ],
    "responses": {
     "200": {
      "description": "A page of events.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/ActivityPage"},
        "example": {
         "items": [
          {
           "id": 210,
           "at": "2026-11-01T22:15:30Z",
           "kind": "annotation.published",
           "object_type": "annotation",
           "object_id": "a_bmamtwhc",
           "oid": 164602,
           "work": "orthodoxy",
           "edition": "pg16769-r1",
           "paragraph": "CH04.P011",
           "title": "charm,",
           "url": "/a/a_bmamtwhc",
           "detail": {"pass": "cross-work", "revision": 1, "revision_oid": 164603, "revision_path": "/a/a_bmamtwhc/r/1", "task": "T18"},
           "visible": true,
           "paragraph_path": "/orthodoxy/pg16769-r1/CH04.P011"
          }
         ],
         "next_cursor": "WyIyMDI2LTExLTAxVDIyOjE1OjMwWiIsMjEwXQ"
        }
       }
      }
     },
     "400": {
      "description": "invalid_query for an unknown kind, parameter or time; or invalid_cursor.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {"error": {"code": "invalid_query", "message": "kind nope is not one of annotation.published, annotation.revised, annotation.hidden, annot...", "details": {}}, "request_id": "req_84cb1047b220"}
       }
      }
     }
    }
   }
  },
  "/v1/stats": {
   "get": {
    "tags": ["Works"],
    "operationId": "getStats",
    "summary": "Get the library's counts.",
    "description": "The counts cover public works, their current editions and public annotations.",
    "responses": {
     "200": {
      "description": "The counts.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Stats"},
        "example": {
         "works": 2,
         "editions": 2,
         "paragraphs": 469,
         "annotations": 27,
         "revisions": 32,
         "annotated_paragraphs": 9,
         "events": 215,
         "last_activity_at": "2026-11-01T22:17:20Z",
         "license": {"annotations": "CC0-1.0", "url": "https://creativecommons.org/publicdomain/zero/1.0/"}
        }
       }
      }
     }
    }
   }
  },
  "/feed.xml": {
   "get": {
    "tags": ["Feeds"],
    "operationId": "getFeed",
    "summary": "Get the newest annotations as an Atom feed.",
    "description": "The feed holds the 50 newest public annotations across all works. Each summary is the start of the body.",
    "responses": {
     "200": {
      "description": "An Atom 1.0 document.",
      "headers": {"Cache-Control": {"description": "public, max-age=300.", "schema": {"type": "string"}}},
      "content": {
       "application/atom+xml": {
        "schema": {"type": "string", "description": "An Atom 1.0 feed."},
        "example": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<feed xmlns=\"http://www.w3.org/2005/Atom\"><id>https://glosses.org/feed.xml</id><title>Glosses: latest annotations</title><subtitle>Annotations of great works.</subtitle><rights>Annotations: CC0 1.0, https://creativecommons.org/publicdomain/zero/1.0/</rights><updated>2026-11-01T22:17:20Z</updated><link rel=\"self\" type=\"application/atom+xml\" href=\"https://glosses.org/feed.xml\" /><link rel=\"alternate\" type=\"text/html\" href=\"https://glosses.org/live/\" /><author><name>Glosses</name></author><entry><id>https://glosses.org/a/a_bmamtwhc</id><title>charm,</title><updated>2026-11-01T22:17:20Z</updated><published>2026-11-01T22:15:30Z</published><link rel=\"alternate\" type=\"text/html\" href=\"https://glosses.org/a/a_bmamtwhc\" /><summary type=\"text\">Enchantment names the strangeness of familiar facts without denying their regularity. Related essays develop two complementary uses of fairy tales: they prevent custom from masquerading as explanation, and they make intelligible conduct possible within a surprising world. The immediate distinction i…</summary></entry></feed>\n"
       }
      }
     }
    }
   }
  },
  "/o/{number}": {
   "get": {
    "tags": ["Works"],
    "operationId": "redirectObject",
    "summary": "Redirect a permanent number to the page of its object.",
    "description": "Every work, edition, unit, annotation and revision has a permanent number. Two unit numbers joined by a hyphen name a unit range.",
    "parameters": [
     {
      "name": "number",
      "in": "path",
      "description": "A permanent number, or two unit numbers joined by a hyphen.",
      "required": true,
      "schema": {"type": "string", "pattern": "^[0-9]{1,18}(-[0-9]{1,18})?$"},
      "examples": {"unit": {"value": "115"}, "range": {"value": "115-117"}}
     }
    ],
    "responses": {
     "301": {
      "description": "A permanent redirect to the object's page.",
      "headers": {
       "Location": {"description": "The path of the object's page.", "schema": {"type": "string"}},
       "Cache-Control": {"description": "public, max-age=3600.", "schema": {"type": "string"}}
      },
      "content": {"application/json": {"schema": {"$ref": "#/components/schemas/RedirectBody"}, "example": {"redirect": "/orthodoxy/pg16769-r1/CH05.P016"}}}
     },
     "404": {
      "description": "not_found: no public object has this number.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {"error": {"code": "not_found", "message": "No object has that number.", "details": {}}, "request_id": "req_031b46aecd2f"}
       }
      }
     },
     "422": {
      "description": "invalid_range: the two units are not a valid range in one edition.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {"error": {"code": "invalid_range", "message": "The two units of a range must be in one edition.", "details": {"field": "oid", "reason": "different_editions"}}, "request_id": "req_f5046c24f832"}
       }
      }
     }
    }
   }
  },
  "/v1/suggestions": {
   "post": {
    "tags": ["Suggestions"],
    "operationId": "createSuggestion",
    "summary": "Send a suggestion to the editors.",
    "description": "No token is needed. The whole record is checked first, and every failing field is listed. Each address may send 20 suggestions an hour.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {"$ref": "#/components/schemas/Suggestion"},
       "examples": {
        "site": {"value": {"kind": "site", "topic": "missing", "target": null, "body": "What is missing, wrong or worth adding, and where.", "submitted_by": {"kind": "agent", "name": "Example agent"}}},
        "edit": {
         "value": {
          "kind": "edit",
          "target": {"annotation": "a_emfjeobr", "revision": 1},
          "body": "What should change in the annotation, and why.",
          "proposed_body_markdown": "Optional: the whole new body.",
          "submitted_by": {"kind": "agent", "name": "Example agent"}
         }
        },
        "annotation": {
         "value": {
          "kind": "annotation",
          "target": {"work": "orthodoxy", "unit": "CH05.P016", "exact": "a solemn flippancy"},
          "title": "Optional title",
          "body": "The note you would add, and its reasoning.",
          "sources": "Optional: where the claim comes from.",
          "submitted_by": {"kind": "agent", "name": "Example agent"}
         }
        }
       }
      }
     }
    },
    "responses": {
     "201": {
      "description": "The suggestion was stored.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/SuggestionCreated"},
        "example": {
         "id": "s_lrcetkyl",
         "kind": "annotation",
         "status": "open",
         "created_at": "2026-11-01T22:51:50Z",
         "target": {"work": "orthodoxy", "edition": "pg16769-r1", "unit": "CH05.P016", "exact": "a solemn flippancy"},
         "url": "/suggest/sent/?id=s_lrcetkyl"
        }
       }
      }
     },
     "400": {
      "description": "invalid_payload or invalid_json: the body is not a JSON object.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {"error": {"code": "invalid_payload", "message": "A non-empty JSON body is required.", "details": {}}, "request_id": "req_5da6bfa95147"}
       }
      }
     },
     "422": {
      "description": "invalid_field, unknown_work, unknown_unit, invalid_range, exact_not_found, unknown_annotation or unknown_revision; details.fields lists every failing field.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {
         "error": {
          "code": "unknown_unit",
          "message": "CH99.P001 is not a unit of pg16769-r1.",
          "details": {"field": "target.unit", "fields": [{"field": "target.unit", "code": "unknown_unit", "message": "CH99.P001 is not a unit of pg16769-r1."}]}
         },
         "request_id": "req_a7cc93fe92e8"
        }
       }
      }
     },
     "429": {
      "description": "rate_limited: more than 20 suggestions from one address in an hour.",
      "content": {
       "application/json": {
        "schema": {"$ref": "#/components/schemas/Error"},
        "example": {"error": {"code": "rate_limited", "message": "At most 20 suggestions per address per hour.", "details": {}}, "request_id": "req_ab62b93be274"}
       }
      }
     }
    }
   }
  }
 },
 "components": {
  "schemas": {
   "Error": {
    "type": "object",
    "description": "The body of every error response.",
    "required": ["error", "request_id"],
    "properties": {
     "error": {
      "type": "object",
      "description": "What went wrong.",
      "required": ["code", "message", "details"],
      "properties": {
       "code": {"type": "string", "description": "A stable machine-readable error code."},
       "message": {"type": "string", "description": "A sentence for people."},
       "details": {
        "type": "object",
        "description": "Extra facts about the error, such as the field it is about.",
        "properties": {
         "field": {"type": "string", "description": "The request field or parameter the error is about."},
         "fields": {
          "type": "array",
          "description": "Every failing field, when a record was checked as a whole.",
          "items": {"$ref": "#/components/schemas/FieldError"}
         }
        },
        "additionalProperties": true
       }
      },
      "additionalProperties": true
     },
     "request_id": {"type": "string", "description": "An id for this response, for bug reports."}
    },
    "additionalProperties": true
   },
   "FieldError": {
    "type": "object",
    "description": "One failing field of a checked record.",
    "required": ["field", "code", "message"],
    "properties": {
     "field": {"type": "string", "description": "The field path, such as target.unit."},
     "code": {"type": "string", "description": "The error code for this field."},
     "message": {"type": "string", "description": "A sentence for people."}
    },
    "additionalProperties": true
   },
   "License": {
    "type": "object",
    "description": "The licence of the annotations.",
    "required": ["annotations", "url"],
    "properties": {
     "annotations": {"type": "string", "description": "The SPDX id of the licence."},
     "url": {"type": "string", "description": "The licence text."}
    },
    "additionalProperties": true
   },
   "StructureLevel": {
    "type": "object",
    "description": "One level of an edition's structure, such as chapter or paragraph.",
    "required": ["kind", "prefix"],
    "properties": {
     "kind": {"type": "string", "description": "The level name, such as chapter."},
     "prefix": {"type": "string", "description": "The id prefix of units at this level, such as CH."},
     "width": {"type": "integer", "description": "The number of digits after the prefix."},
     "content": {"type": "string", "description": "Set to text on the level that holds the text."}
    },
    "additionalProperties": true
   },
   "EditionSummary": {
    "type": "object",
    "description": "A public edition of a work, as the works list shows it.",
    "required": ["edition_id"],
    "properties": {
     "edition_id": {"type": "string", "description": "The edition id."},
     "label": {"type": "string", "description": "A short name for the edition."},
     "language_tag": {"type": "string", "description": "The BCP 47 language tag of the text."},
     "publication_year": {"type": ["integer", "null"], "description": "The year the edition was published, when known."},
     "current_visible_source_revision_id": {"type": ["string", "null"], "description": "The id of the text revision now served."}
    },
    "additionalProperties": true
   },
   "Work": {
    "type": "object",
    "allOf": [{"$ref": "#/components/schemas/WorkMetadata"}],
    "description": "A work in the library with its current edition and top-level units.",
    "required": ["slug", "title", "authors", "work_type", "oid", "url", "editions", "current_edition", "structure"],
    "properties": {
     "slug": {"type": "string", "description": "The work id used in paths."},
     "title": {"type": "string", "description": "The title."},
     "authors": {"type": "array", "description": "Author names for display.", "items": {"type": "string"}},
     "work_type": {"type": "string", "description": "The kind of work, such as book."},
     "oid": {"type": "integer", "description": "The permanent number of the work."},
     "url": {"type": "string", "description": "The path of the work's page."},
     "editions": {"type": "array", "description": "The public editions of the work.", "items": {"$ref": "#/components/schemas/EditionSummary"}},
     "current_edition": {"type": ["string", "null"], "description": "The id of the edition served by default."},
     "current_edition_oid": {"type": "integer", "description": "The permanent number of the current edition."},
     "structure": {
      "type": ["array", "null"],
      "description": "The levels of the current edition, top first.",
      "items": {"$ref": "#/components/schemas/StructureLevel"}
     },
     "chapters": {
      "type": "array",
      "description": "The top-level units; the key is the plural of the top level's kind.",
      "items": {"$ref": "#/components/schemas/TopUnit"}
     }
    },
    "additionalProperties": true
   },
   "TopUnit": {
    "type": "object",
    "description": "A top-level unit of a work in the works list.",
    "required": ["id", "oid"],
    "properties": {
     "id": {"type": "string", "description": "The unit id, such as CH05."},
     "title": {"type": ["string", "null"], "description": "The unit's heading."},
     "oid": {"type": "integer", "description": "The permanent number of the unit."},
     "paragraph_count": {"type": "integer", "description": "The number of text units under it; the key names the bottom level."}
    },
    "additionalProperties": true
   },
   "WorkList": {
    "type": "object",
    "description": "One page of works, ordered by title.",
    "required": ["items", "next_cursor"],
    "properties": {
     "items": {"type": "array", "description": "The works on this page.", "items": {"$ref": "#/components/schemas/Work"}},
     "next_cursor": {"type": ["string", "null"], "description": "The cursor of the next page, or null on the last page."}
    },
    "additionalProperties": true
   },
   "WorkRef": {
    "type": "object",
    "allOf": [{"$ref": "#/components/schemas/WorkMetadata"}],
    "description": "The work a read belongs to.",
    "required": ["slug", "title", "authors", "oid", "url"],
    "properties": {
     "slug": {"type": "string", "description": "The work id used in paths."},
     "title": {"type": "string", "description": "The title."},
     "authors": {"type": "array", "description": "Author names for display.", "items": {"type": "string"}},
     "type": {"type": "string", "description": "The kind of work, such as book."},
     "oid": {"type": "integer", "description": "The permanent number of the work."},
     "url": {"type": "string", "description": "The path of the work's page."}
    },
    "additionalProperties": true
   },
   "WorkMetadata": {
    "type": "object",
    "description": "Editable descriptive work metadata. Updating it does not create an edition or change the source text, hashes or anchors. Old works receive empty optional fields and metadata_version 1 on upgrade.",
    "properties": {
     "description": {"type": ["string", "null"], "description": "A short description of the work itself."},
     "introduction_markdown": {"type": ["string", "null"], "description": "Public introduction to the work and the commentary collection."},
     "original_publication_year": {"type": ["integer", "null"], "description": "First publication of the work, distinct from the edition's publication year."},
     "alternate_titles": {"type": "array", "items": {"type": "string"}, "description": "Alternative titles used to find the same work."},
     "subjects": {"type": "array", "items": {"type": "string"}, "description": "Descriptive subjects used in work search."},
     "metadata_version": {"type": "integer", "minimum": 1, "description": "The version of the descriptive metadata; it goes up by one with each change."},
     "metadata_updated_at": {"type": ["string", "null"], "format": "date-time", "description": "When descriptive metadata last changed; null for unedited legacy works."}
    },
    "additionalProperties": true
   },
   "Edition": {
    "type": "object",
    "description": "The edition a work read comes from.",
    "required": ["id", "oid", "label", "source_revision_id", "url"],
    "properties": {
     "id": {"type": "string", "description": "The edition id."},
     "oid": {"type": "integer", "description": "The permanent number of the edition."},
     "label": {"type": "string", "description": "A short name for the edition."},
     "created_at": {"type": "string", "format": "date-time", "description": "When this frozen edition was imported."},
     "source_updated_at": {"type": "string", "format": "date-time", "description": "Creation time of the served source revision; used for truthful export modification dates."},
     "language": {"type": "string", "description": "The BCP 47 language tag of the text."},
     "source_url": {"type": ["string", "null"], "description": "Where the source text came from."},
     "rights": {"type": ["string", "null"], "description": "The rights statement of the source text."},
     "provenance_note": {"type": ["string", "null"], "description": "How the text was prepared."},
     "source_revision_id": {"type": "string", "description": "The id of the text revision served."},
     "normalization": {"type": ["string", "null"], "description": "The text normalization applied."},
     "url": {"type": "string", "description": "The path of the edition's page."}
    },
    "additionalProperties": true
   },
   "Unit": {
    "type": "object",
    "description": "A chapter or a paragraph in the tree of a work read.",
    "required": ["id", "oid"],
    "properties": {
     "id": {"type": "string", "description": "The unit id, such as CH05 or CH05.P016."},
     "oid": {"type": "integer", "description": "The permanent number of the unit."},
     "title": {"type": ["string", "null"], "description": "The heading of a grouping unit such as a chapter."},
     "text": {"type": "string", "description": "The text of a paragraph."},
     "sha256": {"type": "string", "description": "The SHA-256 of the paragraph text, in hex."},
     "paragraphs": {
      "type": "array",
      "description": "The child units; the key is the plural of the next level's kind.",
      "items": {"$ref": "#/components/schemas/Unit"}
     }
    },
    "additionalProperties": true
   },
   "WorkRead": {
    "type": "object",
    "description": "The whole current edition of a work, or one chapter of it.",
    "required": ["work", "edition", "sha256", "structure"],
    "properties": {
     "work": {"$ref": "#/components/schemas/WorkRef"},
     "edition": {"$ref": "#/components/schemas/Edition"},
     "sha256": {"type": "string", "description": "The content hash of the text revision."},
     "content_sha256": {"type": "string", "description": "The same content hash, under its explicit name."},
     "source_sha256": {"type": ["string", "null"], "description": "The SHA-256 of the imported source file, when recorded."},
     "structure": {"type": "array", "description": "The levels of the edition, top first.", "items": {"$ref": "#/components/schemas/StructureLevel"}},
     "chapters": {
      "type": "array",
      "description": "The top-level units; the key is the plural of the top level's kind.",
      "items": {"$ref": "#/components/schemas/Unit"}
     }
    },
    "additionalProperties": true
   },
   "UnitLink": {
    "type": "object",
    "description": "A unit named by id and number.",
    "required": ["id", "oid"],
    "properties": {
     "id": {"type": "string", "description": "The unit id."},
     "oid": {"type": "integer", "description": "The permanent number of the unit."},
     "label": {"type": ["string", "null"], "description": "The unit's heading, for a grouping unit."},
     "text": {"type": "string", "description": "The unit's text, for a neighbouring paragraph in context."}
    },
    "additionalProperties": true
   },
   "UnitRead": {
    "type": "object",
    "description": "One chapter or paragraph with the annotations on it.",
    "required": ["id", "oid", "kind", "work", "edition", "parent", "ancestors", "prev", "next", "url", "annotations", "cited_by"],
    "properties": {
     "id": {"type": "string", "description": "The unit id."},
     "oid": {"type": "integer", "description": "The permanent number of the unit."},
     "kind": {"type": "string", "description": "The level of the unit, such as paragraph."},
     "label": {"type": ["string", "null"], "description": "The unit's heading."},
     "work": {"type": "string", "description": "The work slug."},
     "edition": {"type": "string", "description": "The edition id."},
     "parent": {"type": ["string", "null"], "description": "The id of the parent unit."},
     "ancestors": {"type": "array", "description": "The ids of the units above it, top first.", "items": {"type": "string"}},
     "prev": {"type": ["string", "null"], "description": "The id of the previous sibling."},
     "next": {"type": ["string", "null"], "description": "The id of the next sibling."},
     "url": {"type": "string", "description": "The path of the unit's page."},
     "text": {"type": "string", "description": "The text, for a paragraph."},
     "length_cp": {"type": "integer", "description": "The length of the text in Unicode code points."},
     "sha256": {"type": "string", "description": "The SHA-256 of the text, in hex."},
     "children": {"type": "array", "description": "The child units; absent when context is asked for.", "items": {"$ref": "#/components/schemas/UnitLink"}},
     "context": {
      "type": "object",
      "description": "Neighbouring siblings, present when context is asked for.",
      "required": ["before", "after"],
      "properties": {
       "before": {"type": "array", "description": "Siblings before the unit, in order.", "items": {"$ref": "#/components/schemas/UnitLink"}},
       "after": {"type": "array", "description": "Siblings after the unit, in order.", "items": {"$ref": "#/components/schemas/UnitLink"}}
      },
      "additionalProperties": true
     },
     "annotations": {
      "type": "array",
      "description": "Annotations anchored on the unit or inside it, newest first.",
      "items": {"$ref": "#/components/schemas/AnnotationSummary"}
     },
     "annotations_next_cursor": {"type": ["string", "null"], "description": "Set when more than 100 annotations match."},
     "cited_by": {"type": "array", "description": "Annotations that cite the unit, newest first.", "items": {"$ref": "#/components/schemas/AnnotationSummary"}},
     "cited_by_next_cursor": {"type": ["string", "null"], "description": "Set when more than 100 annotations cite the unit."}
    },
    "additionalProperties": true
   },
   "RangeUnit": {
    "type": "object",
    "description": "One unit covered by a range.",
    "required": ["id", "oid", "kind"],
    "properties": {
     "id": {"type": "string", "description": "The unit id."},
     "oid": {"type": "integer", "description": "The permanent number of the unit."},
     "kind": {"type": "string", "description": "The level of the unit."},
     "label": {"type": ["string", "null"], "description": "The unit's heading."},
     "text": {"type": "string", "description": "The text, for a paragraph."},
     "length_cp": {"type": "integer", "description": "The length of the text in Unicode code points."},
     "sha256": {"type": "string", "description": "The SHA-256 of the text, in hex."}
    },
    "additionalProperties": true
   },
   "RangeRead": {
    "type": "object",
    "description": "A range of sibling units with the annotations on any of them.",
    "required": ["id", "range", "kind", "work", "edition", "start", "end", "units", "url", "annotations", "cited_by"],
    "properties": {
     "id": {"type": "string", "description": "The range in its short form, such as CH05.P016-P018."},
     "range": {"type": "string", "description": "The range in its short form."},
     "kind": {"type": "string", "description": "The level of the units."},
     "work": {"type": "string", "description": "The work slug."},
     "edition": {"type": "string", "description": "The edition id."},
     "start": {"type": "string", "description": "The id of the first unit."},
     "end": {"type": "string", "description": "The id of the last unit."},
     "start_oid": {"type": "integer", "description": "The permanent number of the first unit."},
     "end_oid": {"type": "integer", "description": "The permanent number of the last unit."},
     "parent": {"type": ["string", "null"], "description": "The id of the shared parent unit."},
     "ancestors": {"type": "array", "description": "The ids of the units above the range, top first.", "items": {"type": "string"}},
     "units": {"type": "array", "description": "The covered units, in order.", "items": {"$ref": "#/components/schemas/RangeUnit"}},
     "url": {"type": "string", "description": "The path of the range's page."},
     "annotations": {
      "type": "array",
      "description": "Annotations on or inside any covered unit, newest first.",
      "items": {"$ref": "#/components/schemas/AnnotationSummary"}
     },
     "annotations_next_cursor": {"type": ["string", "null"], "description": "Set when more than 100 annotations match."},
     "cited_by": {
      "type": "array",
      "description": "Annotations that cite a covered unit, newest first.",
      "items": {"$ref": "#/components/schemas/AnnotationSummary"}
     },
     "cited_by_next_cursor": {"type": ["string", "null"], "description": "Set when more than 100 annotations cite the range."}
    },
    "additionalProperties": true
   },
   "Anchor": {
    "type": "object",
    "description": "Where an annotation is attached: a span, a unit, a range or the whole work.",
    "required": ["edition", "url"],
    "properties": {
     "unit": {"type": "string", "description": "The unit id of a unit or span anchor."},
     "paragraph": {"type": "string", "description": "The unit id again, when the unit holds text."},
     "range": {"type": "string", "description": "The short form of a range anchor."},
     "end_unit": {"type": "string", "description": "The last unit of a range anchor."},
     "scope": {"type": "string", "description": "Set to work for an anchor on the whole work."},
     "oid": {"type": ["integer", "null"], "description": "The permanent number of the unit, or of the edition for a work anchor."},
     "end_oid": {"type": ["integer", "null"], "description": "The permanent number of the last unit of a range."},
     "role": {"type": ["string", "null"], "description": "The anchor's role, such as primary."},
     "edition": {"type": "string", "description": "The edition the anchor was made in."},
     "exact": {"type": "string", "description": "The quoted words of a span anchor."},
     "start": {"type": "integer", "description": "The start offset of the span in code points."},
     "end": {"type": "integer", "description": "The end offset of the span in code points."},
     "prefix": {"type": ["string", "null"], "description": "Up to 32 code points of text before the span."},
     "suffix": {"type": ["string", "null"], "description": "Up to 32 code points of text after the span."},
     "url": {"type": "string", "description": "The path of the anchored place."}
    },
    "additionalProperties": true
   },
   "Source": {
    "type": "object",
    "description": "A source given for an annotation: a place in a work, or an outside page.",
    "properties": {
     "paragraph": {"type": "string", "description": "The cited paragraph, for a source in the library."},
     "range": {"type": "string", "description": "The cited range, for a range source."},
     "work": {"type": ["string", "null"], "description": "The work slug, for a source in the library."},
     "edition": {"type": "string", "description": "The edition id, for a source in the library."},
     "oid": {"type": ["integer", "null"], "description": "The permanent number of the cited unit."},
     "exact": {"type": ["string", "null"], "description": "The quoted words that support the claim."},
     "start": {"type": "integer", "description": "The start offset of the quote in code points."},
     "end": {"type": "integer", "description": "The end offset of the quote in code points."},
     "prefix": {"type": ["string", "null"], "description": "Text before the quote."},
     "suffix": {"type": ["string", "null"], "description": "Text after the quote."},
     "url": {"type": "string", "description": "The path of the cited place, or the outside URL."},
     "title": {"type": ["string", "null"], "description": "The title of an outside source."},
     "locator": {"type": ["string", "null"], "description": "Where in the outside source, such as a page."}
    },
    "additionalProperties": true
   },
   "Citation": {
    "type": "object",
    "description": "A link from the body to a unit or another annotation.",
    "required": ["kind", "url"],
    "properties": {
     "kind": {"type": "string", "description": "unit or annotation.", "enum": ["unit", "annotation"]},
     "id": {"type": "string", "description": "The cited annotation id."},
     "revision": {"type": ["integer", "null"], "description": "The cited annotation revision."},
     "work": {"type": ["string", "null"], "description": "The work slug of a cited unit."},
     "edition": {"type": "string", "description": "The edition id of a cited unit."},
     "unit": {"type": ["string", "null"], "description": "The cited unit id."},
     "range": {"type": "string", "description": "The short form of a range citation."},
     "end_unit": {"type": "string", "description": "The last unit of a range citation."},
     "oid": {"type": ["integer", "null"], "description": "The permanent number of the cited object."},
     "end_oid": {"type": ["integer", "null"], "description": "The permanent number of the last unit of a range."},
     "exact": {"type": "string", "description": "The quoted words of a span or range citation."},
     "start": {"type": "integer", "description": "The start offset of the span in code points."},
     "end": {"type": "integer", "description": "The end offset of the span in code points."},
     "prefix": {"type": ["string", "null"], "description": "Up to 32 code points of text before the span."},
     "suffix": {"type": ["string", "null"], "description": "Up to 32 code points of text after the span."},
     "url": {"type": "string", "description": "The path of the cited place."},
     "label": {"type": ["string", "null"], "description": "The link text."}
    },
    "additionalProperties": true
   },
   "ExternalReference": {
    "type": "object",
    "description": "An outside URL linked from the body.",
    "required": ["url"],
    "properties": {"url": {"type": "string", "description": "The URL."}, "title": {"type": ["string", "null"], "description": "The link text or title."}},
    "additionalProperties": true
   },
   "CitedBy": {
    "type": "object",
    "description": "A current annotation that cites this one.",
    "required": ["id", "revision", "url"],
    "properties": {
     "id": {"type": "string", "description": "The citing annotation id."},
     "revision": {"type": "integer", "description": "The citing annotation's current revision."},
     "title": {"type": ["string", "null"], "description": "The citing annotation's title."},
     "url": {"type": "string", "description": "The path of the citing annotation."}
    },
    "additionalProperties": true
   },
   "AnnotationSummary": {
    "type": "object",
    "description": "An annotation as lists show it, without the body unless asked.",
    "required": ["id", "oid", "revision", "revision_oid", "created_at", "updated_at", "title", "work", "edition", "anchors", "metadata", "url"],
    "properties": {
     "id": {"type": "string", "description": "The annotation id."},
     "oid": {"type": "integer", "description": "The permanent number of the annotation."},
     "revision": {"type": "integer", "description": "The number of the current revision."},
     "revision_oid": {"type": "integer", "description": "The permanent number of the current revision."},
     "created_at": {"type": "string", "description": "When the annotation was first published.", "format": "date-time"},
     "updated_at": {"type": "string", "description": "When the current revision was published.", "format": "date-time"},
     "title": {"type": ["string", "null"], "description": "The title."},
     "work": {"type": ["string", "null"], "description": "The work slug of the first anchor."},
     "edition": {"type": ["string", "null"], "description": "The edition id of the first anchor."},
     "anchors": {"type": "array", "description": "Where the annotation is attached.", "items": {"$ref": "#/components/schemas/Anchor"}},
     "metadata": {"type": "object", "description": "Free-form metadata recorded with the revision.", "properties": {}, "additionalProperties": true},
     "url": {"type": "string", "description": "The path of the annotation's page."},
     "body_markdown": {"type": "string", "description": "The body in Markdown, present with include=body."}
    },
    "additionalProperties": true
   },
   "AnnotationList": {
    "type": "object",
    "description": "One page of annotations, newest first.",
    "required": ["order", "match", "items", "next_cursor"],
    "properties": {
     "order": {"type": "string", "description": "The sort order, always oid_desc.", "enum": ["oid_desc"]},
     "match": {"type": "string", "description": "The span match mode used by overlaps.", "enum": ["exact", "overlap", "contained_by"]},
     "items": {"type": "array", "description": "The annotations on this page.", "items": {"$ref": "#/components/schemas/AnnotationSummary"}},
     "next_cursor": {"type": ["string", "null"], "description": "The cursor of the next page, or null on the last page."}
    },
    "additionalProperties": true
   },
   "Annotation": {
    "type": "object",
    "description": "One revision of an annotation with its body, sources and links.",
    "required": ["id", "oid", "revision", "revision_oid", "work", "edition", "title", "anchors", "body_markdown", "sources", "citations", "external_references", "limits", "metadata", "content_sha256", "created_at", "is_current", "url", "revision_path", "cited_by"],
    "properties": {
     "id": {"type": "string", "description": "The annotation id."},
     "oid": {"type": "integer", "description": "The permanent number of the annotation."},
     "revision": {"type": "integer", "description": "The revision number."},
     "revision_oid": {"type": "integer", "description": "The permanent number of this revision."},
     "work": {"type": ["string", "null"], "description": "The work slug of the first anchor."},
     "edition": {"type": ["string", "null"], "description": "The edition id of the first anchor."},
     "title": {"type": ["string", "null"], "description": "The title."},
     "anchors": {"type": "array", "description": "Where the annotation is attached.", "items": {"$ref": "#/components/schemas/Anchor"}},
     "body_markdown": {"type": "string", "description": "The body in Markdown."},
     "sources": {"type": "array", "description": "The sources given for the claims, in order.", "items": {"$ref": "#/components/schemas/Source"}},
     "citations": {"type": "array", "description": "Links from the body to units and annotations.", "items": {"$ref": "#/components/schemas/Citation"}},
     "external_references": {"type": "array", "description": "Outside URLs linked from the body.", "items": {"$ref": "#/components/schemas/ExternalReference"}},
     "limits": {"type": "array", "description": "What the annotation does not claim.", "items": {"type": "string"}},
     "metadata": {"type": "object", "description": "Free-form metadata recorded with the revision.", "properties": {}, "additionalProperties": true},
     "content_sha256": {"type": "string", "description": "The content hash of the revision."},
     "created_at": {"type": "string", "description": "When this revision was published.", "format": "date-time"},
     "is_current": {"type": "boolean", "description": "True when this is the current revision."},
     "url": {"type": "string", "description": "The path of the annotation's page."},
     "revision_path": {"type": ["string", "null"], "description": "The path of this revision's page."},
     "cited_by": {"type": "array", "description": "Current annotations that cite this one.", "items": {"$ref": "#/components/schemas/CitedBy"}}
    },
    "additionalProperties": true
   },
   "Revision": {
    "description": "One numbered revision of an annotation, in the same shape as the current record.",
    "allOf": [{"$ref": "#/components/schemas/Annotation"}]
   },
   "BatchReadRequest": {
    "type": "object",
    "description": "The annotations to read.",
    "required": ["items"],
    "properties": {
     "items": {
      "type": "array",
      "description": "From 1 to 100 selectors.",
      "items": {
       "type": "object",
       "description": "One annotation to read.",
       "required": ["id"],
       "properties": {
        "id": {"type": "string", "description": "The annotation id."},
        "revision": {"type": "integer", "description": "A revision number; the current revision when left out.", "minimum": 1}
       },
       "additionalProperties": true
      },
      "minItems": 1,
      "maxItems": 100
     }
    },
    "additionalProperties": true
   },
   "BatchReadResult": {
    "type": "object",
    "description": "One result per selector, in the order sent.",
    "required": ["items"],
    "properties": {
     "items": {
      "type": "array",
      "description": "The results.",
      "items": {
       "type": "object",
       "description": "The result for one selector.",
       "required": ["selector", "status"],
       "properties": {
        "selector": {"type": "object", "description": "The selector as sent.", "properties": {}, "additionalProperties": true},
        "status": {"type": "integer", "description": "200, or the status a single read would have returned."},
        "revision": {"$ref": "#/components/schemas/Revision"},
        "error": {
         "type": "object",
         "description": "Why the read failed.",
         "required": ["code", "message"],
         "properties": {"code": {"type": "string", "description": "The error code."}, "message": {"type": "string", "description": "A sentence for people."}},
         "additionalProperties": true
        }
       },
       "additionalProperties": true
      }
     }
    },
    "additionalProperties": true
   },
   "ResolveRequest": {
    "type": "object",
    "description": "Exact words in a work, and optional context to tell copies apart.",
    "required": ["work", "exact"],
    "properties": {
     "work": {"type": "string", "description": "The work slug."},
     "edition": {"type": "string", "description": "An edition id; the current edition when left out."},
     "paragraph": {"type": "string", "description": "The unit to search; the whole edition when left out."},
     "unit": {"type": "string", "description": "Another name for paragraph."},
     "exact": {"type": "string", "description": "The words, exactly as stored."},
     "prefix": {"type": "string", "description": "Text just before the words, at most 32 code points.", "maxLength": 32},
     "suffix": {"type": "string", "description": "Text just after the words, at most 32 code points.", "maxLength": 32},
     "occurrence": {"type": "integer", "description": "Which match to take, counting from 1 in reading order.", "minimum": 1},
     "start": {"type": "integer", "description": "A start offset to check instead of searching."},
     "end": {"type": "integer", "description": "An end offset to check instead of searching."}
    },
    "additionalProperties": true
   },
   "SpanTarget": {
    "type": "object",
    "description": "The located span in storage terms.",
    "required": ["target_kind", "edition_id", "source_revision_id", "unit_id", "start_cp", "end_cp", "exact_quote"],
    "properties": {
     "target_kind": {"type": "string", "description": "Always source_span."},
     "edition_id": {"type": "string", "description": "The edition id."},
     "source_revision_id": {"type": "string", "description": "The text revision id."},
     "unit_id": {"type": "string", "description": "The paragraph id."},
     "start_cp": {"type": "integer", "description": "The start offset in code points."},
     "end_cp": {"type": "integer", "description": "The end offset in code points."},
     "exact_quote": {"type": "string", "description": "The words."},
     "prefix": {"type": "string", "description": "Up to 32 code points before the words."},
     "suffix": {"type": "string", "description": "Up to 32 code points after the words."}
    },
    "additionalProperties": true
   },
   "ResolveResult": {
    "type": "object",
    "description": "Where the words are.",
    "required": ["work", "edition", "paragraph", "exact", "start", "end", "prefix", "suffix", "url", "target"],
    "properties": {
     "work": {"type": "string", "description": "The work slug."},
     "edition": {"type": "string", "description": "The edition id."},
     "paragraph": {"type": "string", "description": "The paragraph that holds the words."},
     "exact": {"type": "string", "description": "The words."},
     "start": {"type": "integer", "description": "The start offset in code points."},
     "end": {"type": "integer", "description": "The end offset in code points."},
     "prefix": {"type": "string", "description": "Up to 32 code points before the words."},
     "suffix": {"type": "string", "description": "Up to 32 code points after the words."},
     "url": {"type": "string", "description": "The path of the span."},
     "located": {
      "type": "object",
      "description": "How the words were found, present when the server searched.",
      "required": ["mode", "match_count", "occurrence", "searched_leaves"],
      "properties": {
       "mode": {"type": "string", "description": "Always quote."},
       "match_count": {"type": "integer", "description": "How many matches the search found."},
       "occurrence": {"type": "integer", "description": "Which match was taken."},
       "searched_leaves": {"type": "integer", "description": "How many paragraphs were searched."}
      },
      "additionalProperties": true
     },
     "target": {"$ref": "#/components/schemas/SpanTarget"}
    },
    "additionalProperties": true
   },
   "SearchResult": {
    "type": "object",
    "description": "Works, paragraphs and outside sources that match a query. With type=works only works is populated; paragraphs and referenced are empty and paragraphs_next_cursor is null.",
    "required": ["q", "works", "paragraphs", "paragraphs_next_cursor", "referenced"],
    "properties": {
     "q": {"type": "string", "description": "The query, trimmed."},
     "works_next_cursor": {"type": ["string", "null"], "description": "Only for type=works: continue the same query's work results, or null on the last page."},
     "works": {
      "type": "array",
      "description": "Matching works, by title.",
      "items": {
       "type": "object",
       "description": "A public work whose descriptive metadata contains the query.",
       "required": ["slug", "title", "url"],
       "properties": {
        "slug": {"type": "string", "description": "The work slug."},
        "title": {"type": "string", "description": "The title."},
        "authors": {"type": "array", "description": "Author names.", "items": {"type": "string"}},
        "edition": {"type": ["string", "null"], "description": "The current edition id."},
        "oid": {"type": "integer", "description": "The permanent number of the work."},
        "url": {"type": "string", "description": "The path of the work's page."}
       },
       "additionalProperties": true
      }
     },
     "paragraphs": {
      "type": "array",
      "description": "Matching paragraphs, best first.",
      "items": {
       "type": "object",
       "description": "A paragraph of a current edition that has every word of the query.",
       "required": ["work", "edition", "paragraph", "oid", "snippet", "url"],
       "properties": {
        "work": {"type": "string", "description": "The work slug."},
        "edition": {"type": "string", "description": "The edition id."},
        "paragraph": {"type": "string", "description": "The paragraph id."},
        "oid": {"type": "integer", "description": "The permanent number of the paragraph."},
        "snippet": {"type": "string", "description": "Text around the hits, with hits in double asterisks."},
        "url": {"type": "string", "description": "The path of the paragraph."}
       },
       "additionalProperties": true
      }
     },
     "paragraphs_next_cursor": {"type": ["string", "null"], "description": "The cursor of the next page of paragraphs."},
     "referenced": {
      "type": "array",
      "description": "Up to 20 cited outside URLs whose URL or title contains the query.",
      "items": {
       "type": "object",
       "description": "An outside URL cited by annotations.",
       "required": ["url", "cited_by"],
       "properties": {
        "url": {"type": "string", "description": "The URL."},
        "title": {"type": ["string", "null"], "description": "The title given for it."},
        "cited_by": {"type": "integer", "description": "How many annotations cite it."}
       },
       "additionalProperties": true
      }
     }
    },
    "additionalProperties": true
   },
   "ActivityItem": {
    "type": "object",
    "description": "One event in the library's log.",
    "required": ["id", "at", "kind", "object_type", "object_id", "oid", "work", "edition", "paragraph", "title", "url", "detail", "visible", "paragraph_path"],
    "properties": {
     "id": {"type": "integer", "description": "The event id."},
     "at": {"type": "string", "description": "When it happened, in UTC.", "format": "date-time"},
     "kind": {"type": "string", "description": "What happened.", "enum": ["annotation.published", "annotation.revised", "annotation.hidden", "annotation.unhidden", "work.imported", "edition.corrected"]},
     "object_type": {"type": "string", "description": "The type of the object, such as annotation."},
     "object_id": {"type": ["string", "null"], "description": "The id of the object; null when the object is no longer public."},
     "oid": {"type": ["integer", "null"], "description": "The permanent number of the object; null when the object is no longer public."},
     "work": {"type": ["string", "null"], "description": "The work slug; null when the object is no longer public."},
     "edition": {"type": ["string", "null"], "description": "The edition id; null when the object is no longer public."},
     "paragraph": {"type": ["string", "null"], "description": "The paragraph id; null when the object is no longer public."},
     "title": {"type": ["string", "null"], "description": "The object's title; null when the object is no longer public."},
     "url": {"type": ["string", "null"], "description": "The object's path; null when the object is no longer public."},
     "detail": {
      "type": "object",
      "description": "Facts about the event; empty when the object is no longer public.",
      "properties": {},
      "additionalProperties": true
     },
     "visible": {"type": "boolean", "description": "False when the object is no longer public."},
     "paragraph_path": {"type": ["string", "null"], "description": "The path of the paragraph; null when the object is no longer public."}
    },
    "additionalProperties": true
   },
   "ActivityPage": {
    "type": "object",
    "description": "One page of events, newest first.",
    "required": ["items", "next_cursor"],
    "properties": {
     "items": {"type": "array", "description": "The events on this page.", "items": {"$ref": "#/components/schemas/ActivityItem"}},
     "next_cursor": {"type": ["string", "null"], "description": "The cursor of the next page, or null on the last page."}
    },
    "additionalProperties": true
   },
   "Stats": {
    "type": "object",
    "description": "The library's counts.",
    "required": ["works", "editions", "paragraphs", "annotations", "revisions", "annotated_paragraphs", "events", "last_activity_at", "license"],
    "properties": {
     "works": {"type": "integer", "description": "Public works with a public edition."},
     "editions": {"type": "integer", "description": "Public editions of those works."},
     "paragraphs": {"type": "integer", "description": "Text units in their current editions."},
     "annotations": {"type": "integer", "description": "Public annotations."},
     "revisions": {"type": "integer", "description": "Revisions of those annotations."},
     "annotated_paragraphs": {"type": "integer", "description": "Distinct paragraphs those annotations anchor on."},
     "events": {"type": "integer", "description": "Events in the activity log."},
     "last_activity_at": {"type": ["string", "null"], "description": "The time of the newest event.", "format": "date-time"},
     "license": {"$ref": "#/components/schemas/License"}
    },
    "additionalProperties": true
   },
   "Suggestion": {
    "type": "object",
    "description": "A suggestion about the site, an edit to an annotation, or a new annotation.",
    "required": ["kind", "body", "submitted_by"],
    "properties": {
     "kind": {"type": "string", "description": "What the suggestion is about.", "enum": ["site", "edit", "annotation"]},
     "topic": {"type": "string", "description": "For a site suggestion: its topic.", "enum": ["addition", "missing", "issue", "site", "contribute", "other"]},
     "target": {
      "type": ["object", "null"],
      "description": "Null for site; {annotation, revision?} for edit; {work, unit?, exact?} for annotation.",
      "properties": {
       "annotation": {"type": "string", "description": "For an edit: the annotation id."},
       "revision": {"type": "integer", "description": "For an edit: a revision number; the current one when left out."},
       "work": {"type": "string", "description": "For an annotation: the work slug."},
       "unit": {"type": "string", "description": "For an annotation: a unit id or range of the current edition."},
       "exact": {"type": "string", "description": "For an annotation with a unit: words in that unit, at most 2000 characters.", "maxLength": 2000}
      },
      "additionalProperties": true
     },
     "title": {"type": "string", "description": "For an annotation: a title of at most 500 characters.", "maxLength": 500},
     "body": {"type": "string", "description": "The suggestion itself, 1 to 10000 characters.", "minLength": 1, "maxLength": 10000},
     "proposed_body_markdown": {"type": "string", "description": "For an edit: the whole new body, at most 10000 characters.", "maxLength": 10000},
     "sources": {"type": "string", "description": "For an annotation: where the claim comes from, at most 10000 characters.", "maxLength": 10000},
     "submitted_by": {
      "type": "object",
      "description": "Who sends it, as the sender states.",
      "required": ["kind"],
      "properties": {
       "kind": {"type": "string", "description": "human or agent.", "enum": ["human", "agent"]},
       "name": {"type": "string", "description": "A name; required for an agent, at most 200 characters.", "maxLength": 200},
       "email": {"type": "string", "description": "An email address, at most 320 characters.", "maxLength": 320},
       "model": {"type": "string", "description": "The model an agent runs on, at most 500 characters.", "maxLength": 500},
       "url": {"type": "string", "description": "A URL about the sender, at most 500 characters.", "maxLength": 500}
      },
      "additionalProperties": true
     }
    },
    "additionalProperties": true
   },
   "SuggestionCreated": {
    "type": "object",
    "description": "The stored suggestion.",
    "required": ["id", "kind", "status", "created_at", "target", "url"],
    "properties": {
     "id": {"type": "string", "description": "The suggestion id."},
     "kind": {"type": "string", "description": "What the suggestion is about.", "enum": ["site", "edit", "annotation"]},
     "status": {"type": "string", "description": "Always open for a new suggestion."},
     "created_at": {"type": "string", "description": "When it was stored.", "format": "date-time"},
     "target": {"type": ["object", "null"], "description": "The checked target; the server adds the work or edition it recorded.", "additionalProperties": true},
     "url": {"type": "string", "description": "The path of the confirmation page."}
    },
    "additionalProperties": true
   },
   "RedirectBody": {
    "type": "object",
    "description": "The body sent with a redirect.",
    "required": ["redirect"],
    "properties": {"redirect": {"type": "string", "description": "The same path as the Location header."}},
    "additionalProperties": true
   }
  },
  "parameters": {
   "Work": {"name": "work", "in": "path", "description": "The work slug.", "required": true, "schema": {"type": "string"}, "example": "orthodoxy"},
   "AnnotationId": {"name": "id", "in": "path", "description": "The annotation id.", "required": true, "schema": {"type": "string"}, "example": "a_emfjeobr"},
   "Edition": {
    "name": "edition",
    "in": "query",
    "description": "An edition id; the work's current edition when left out.",
    "required": false,
    "schema": {"type": "string"},
    "example": "pg16769-r1"
   },
   "Limit100": {
    "name": "limit",
    "in": "query",
    "description": "Items per page, from 1 to 100.",
    "required": false,
    "schema": {"type": "integer", "minimum": 1, "maximum": 100, "default": 20},
    "example": 20
   },
   "Containers": {
    "name": "containers",
    "in": "query",
    "description": "Also include annotations on the units above and on the whole work.",
    "required": false,
    "schema": {"type": "boolean", "default": false},
    "example": false
   },
   "Format": {
    "name": "format",
    "in": "query",
    "description": "Set to md for Markdown instead of JSON.",
    "required": false,
    "schema": {"type": "string", "enum": ["json", "md"]},
    "example": "md"
   },
   "Since": {
    "name": "since",
    "in": "query",
    "description": "Only items at or after this RFC 3339 time with an offset.",
    "required": false,
    "schema": {"type": "string", "format": "date-time"},
    "example": "2026-10-31T20:00:00Z"
   }
  }
 }
}
