openapi: 3.1.0
info:
  title: 'Yūren-ja Jiaxing edition Buddhist Canon Database API'
  version: '1.0.0'
  summary: 'Public Web API for the Yūren-ja Jiaxing edition Buddhist Canon Database project: IIIF, DTS, JSON:API metadata, and full-text search.'
  description: |
    This document lists the public Web APIs of the **Yūren-ja Jiaxing edition Buddhist Canon Database** project, with usage instructions.

    ## API categories

    - **IIIF Presentation API** — Image collections, manifests, and annotations (IIIF 2.0 / 3.0)
    - **IIIF Content Search API** — Text search within a manifest (IIIF Search standard)
    - **DTS (Distributed Text Services)** — TEI/XML text delivery (DTS 1-alpha)
    - **JSON:API** — Drupal JSON:API metadata (title catalog, detailed catalog, volume metadata)
    - **Fulltext Search API** — Page-level search and word-count aggregation (used by the in-site UI)
    - **Internal endpoints** — Generic Search-UI bridge and others used internally

    ## Bulk metadata downloads

    In addition to per-record retrieval via the JSON:API, bulk Excel (xlsx) and CSV downloads are also provided.
    See the [dataset page](/dataset) for details.

    ## License

    The datasets are released under **CC BY 4.0**.
    Please credit "Yūren-ja Jiaxing edition Buddhist Canon Database" when reusing the data.
  contact:
    name: 'Yūren-ja Jiaxing edition Buddhist Canon Database'
    url: https://u-renja.toyobunko-lab.jp/
  license:
    name: CC BY 4.0
    url: https://creativecommons.org/licenses/by/4.0/

servers:
  - url: https://u-renja.toyobunko-lab.jp
    description: Production

tags:
  - name: IIIF Presentation
    description: |
      IIIF Presentation API endpoints (image collections and manifests).
      Compatible with Mirador, Universal Viewer, and other IIIF viewers.
  - name: IIIF Search
    description: IIIF Content Search API (full-text search within a manifest).
  - name: DTS
    description: |
      DTS (Distributed Text Services) 1-alpha compliant text delivery API.
      Browse with the [DTS Viewer](https://dts-viewer.vercel.app/ja/?url=https://u-renja.toyobunko-lab.jp/api/dts/collection).
  - name: JSON:API
    description: |
      Drupal JSON:API compatible metadata. Three indices are exposed: title catalog, detailed catalog, and volume metadata.
      Bulk downloads (xlsx/csv) are available on the [dataset page](/dataset).
  - name: Fulltext Search
    description: Full-text search API. Primarily used by the in-site search UI.
  - name: Internal
    description: Internal endpoints (basic auth, Search-UI bridge, etc.).

paths:
  # ============================================================
  # IIIF Presentation API
  # ============================================================
  /api/iiif/{version}/collection/top:
    get:
      tags: [IIIF Presentation]
      summary: Top-level IIIF Collection
      description: |
        Returns the top-level IIIF Collection for the Yūren-ja Jiaxing canon,
        containing the hierarchical category structure (sutras, vinaya, treatises, etc.).

        **Open in viewer:**
        - [Open in Image Annotator (kanzaki.com)](https://www.kanzaki.com/works/2016/pub/image-annotator?u=https://u-renja.toyobunko-lab.jp/api/iiif/2/collection/top)
        - Or paste this URL into Mirador / Universal Viewer or any IIIF-compatible viewer
      parameters:
        - $ref: '#/components/parameters/IIIFVersion'
      responses:
        '200':
          description: IIIF Collection
          content:
            application/json:
              schema:
                type: object
              examples:
                top:
                  summary: Top-level
                  externalValue: https://u-renja.toyobunko-lab.jp/api/iiif/2/collection/top

  /api/iiif/{version}/collection/{id}:
    get:
      tags: [IIIF Presentation]
      summary: Category IIIF Collection
      description: |
        Returns an IIIF Collection containing manifests under the specified category ID
        (e.g. a Qianziwen number).
      parameters:
        - $ref: '#/components/parameters/IIIFVersion'
        - name: id
          in: path
          required: true
          description: Category ID
          schema:
            type: string
          example: '001'
      responses:
        '200':
          description: IIIF Collection
          content:
            application/json:
              schema:
                type: object

  /api/iiif/{version}/collection/bib/{id}:
    get:
      tags: [IIIF Presentation]
      summary: Bibliographic IIIF Collection
      description: |
        Returns an IIIF Collection containing all volume manifests belonging to the
        specified bibliographic ID (sutra number).
      parameters:
        - $ref: '#/components/parameters/IIIFVersion'
        - name: id
          in: path
          required: true
          description: Bibliographic ID (sutra number)
          schema:
            type: string
          example: '0001'
      responses:
        '200':
          description: IIIF Collection
          content:
            application/json:
              schema:
                type: object

  /api/iiif/{version}/{id}/manifest:
    get:
      tags: [IIIF Presentation]
      summary: IIIF Manifest
      description: |
        Returns the IIIF Presentation Manifest for the specified volume ID
        (case–volume number, e.g. `069-05`). Includes the Canvas list (images) and metadata.

        **Open in viewer:**
        - [Open in Mirador](https://projectmirador.org/embed/?iiif-content=https://u-renja.toyobunko-lab.jp/api/iiif/2/069-05/manifest)
        - [Open in Universal Viewer](https://uv-v4.netlify.app/#?iiifManifestId=https://u-renja.toyobunko-lab.jp/api/iiif/2/069-05/manifest)
      parameters:
        - $ref: '#/components/parameters/IIIFVersion'
        - name: id
          in: path
          required: true
          description: "Case–volume number (e.g. `069-05`)"
          schema:
            type: string
          example: '069-05'
      responses:
        '200':
          description: IIIF Manifest (v2 or v3)
          content:
            application/json:
              schema:
                type: object

  /api/iiif/{version}/{id}/canvas/{page}/annotation:
    get:
      tags: [IIIF Presentation]
      summary: Canvas OCR annotation list
      description: |
        Returns the OCR text of the specified Canvas (page) as a IIIF AnnotationList,
        with coordinates. Useful for hit highlighting in search and region-level text access.
      parameters:
        - $ref: '#/components/parameters/IIIFVersion'
        - name: id
          in: path
          required: true
          description: Case–volume number
          schema:
            type: string
          example: '069-05'
        - name: page
          in: path
          required: true
          description: Page number (1-based)
          schema:
            type: integer
          example: 1
      responses:
        '200':
          description: IIIF AnnotationList
          content:
            application/json:
              schema:
                type: object

  # ============================================================
  # IIIF Content Search API
  # ============================================================
  /api/iiif-search/{version}/{id}:
    get:
      tags: [IIIF Search]
      summary: IIIF Content Search
      description: |
        IIIF Content Search API compliant text search within a manifest.
        Automatically called by IIIF viewers like Mirador when their search feature is used.

        - Pass the search term as the `q` parameter
        - The response is an AnnotationList containing hit positions as Annotations
      parameters:
        - $ref: '#/components/parameters/IIIFVersion'
        - name: id
          in: path
          required: true
          description: Case–volume number (manifest ID)
          schema:
            type: string
          example: '069-05'
        - name: q
          in: query
          required: false
          description: Search term
          schema:
            type: string
          example: '佛'
      responses:
        '200':
          description: IIIF AnnotationList (search hits)
          content:
            application/json:
              schema:
                type: object

  # ============================================================
  # DTS
  # ============================================================
  /api/dts:
    get:
      tags: [DTS]
      summary: DTS Entry Point
      description: |
        Entry point for the DTS API. Returns the URI templates for the
        `collection`, `navigation`, and `document` endpoints.

        **Open in viewer:**
        - [Open in DTS Viewer](https://dts-viewer.vercel.app/ja/?url=https://u-renja.toyobunko-lab.jp/api/dts/collection)
      responses:
        '200':
          description: DTS Entry Point
          content:
            application/json:
              schema:
                type: object

  /api/dts/collection:
    get:
      tags: [DTS]
      summary: DTS Collection
      description: |
        Returns the text collection. Members form a hierarchy
        (sutra → fascicle → page → line) and each member has a citation.

        **Open in viewer:**
        - [Open in DTS Viewer](https://dts-viewer.vercel.app/ja/?url=https://u-renja.toyobunko-lab.jp/api/dts/collection)
      parameters:
        - name: id
          in: query
          required: false
          description: Collection ID (IIIF `@id`). Defaults to the root collection.
          schema:
            type: string
      responses:
        '200':
          description: DTS Collection
          content:
            application/json:
              schema:
                type: object

  /api/dts/navigation:
    get:
      tags: [DTS]
      summary: DTS Navigation
      description: |
        Returns the citable units (text structure) of the specified resource.
      parameters:
        - name: resource
          in: query
          required: true
          description: Target resource IIIF `@id`
          schema:
            type: string
        - name: ref
          in: query
          required: false
          description: Citable unit reference
          schema:
            type: string
        - name: down
          in: query
          required: false
          description: Depth of hierarchy to return
          schema:
            type: integer
      responses:
        '200':
          description: DTS Navigation
          content:
            application/json:
              schema:
                type: object

  /api/dts/document:
    get:
      tags: [DTS]
      summary: DTS Document
      description: |
        Returns the TEI/XML body of the specified resource.
        Specifying `ref` returns a fragment.
      parameters:
        - name: resource
          in: query
          required: true
          description: Target resource IIIF `@id`
          schema:
            type: string
        - name: ref
          in: query
          required: false
          description: Citable unit reference (for fragment retrieval)
          schema:
            type: string
      responses:
        '200':
          description: TEI/XML body
          content:
            application/xml:
              schema:
                type: string

  # ============================================================
  # JSON:API
  # ============================================================
  /api/jsonapi/node/u_bib:
    get:
      tags: [JSON:API]
      summary: Title catalog (per sutra)
      description: |
        Returns the per-sutra title catalog metadata in Drupal JSON:API format.
        Includes translators, publication notes, dimensions, and so on.

        **Bulk download:** [Excel/CSV here](/dataset)
      parameters:
        - $ref: '#/components/parameters/PageLimit'
        - $ref: '#/components/parameters/PageOffset'
        - name: filter[field_id]
          in: query
          required: false
          description: Filter by sutra ID
          schema:
            type: string
      responses:
        '200':
          description: JSON:API response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JsonApiCollection'

  /api/jsonapi/node/u_detail:
    get:
      tags: [JSON:API]
      summary: Detailed catalog (per volume)
      description: |
        Returns the per-volume detailed catalog metadata in Drupal JSON:API format.
        Includes colophon, qianziwen, seals, and other detailed fields.

        **Bulk download:** [Excel/CSV here](/dataset)
      parameters:
        - $ref: '#/components/parameters/PageLimit'
        - $ref: '#/components/parameters/PageOffset'
        - name: filter[field_u_tsuban]
          in: query
          required: false
          description: Filter by serial number
          schema:
            type: string
      responses:
        '200':
          description: JSON:API response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JsonApiCollection'

  /api/jsonapi/node/u_satsu:
    get:
      tags: [JSON:API]
      summary: Volume (satsu) metadata
      description: |
        Returns metadata of physical structure (compartment, box, level, volume) and
        cover information per volume.

        **Bulk download:** [Excel/CSV here](/dataset)
      parameters:
        - $ref: '#/components/parameters/PageLimit'
        - $ref: '#/components/parameters/PageOffset'
        - name: filter[field_u_parent_str]
          in: query
          required: false
          description: Filter by parent (case)
          schema:
            type: string
      responses:
        '200':
          description: JSON:API response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JsonApiCollection'

  /api/jsonapi/node/u_image_detail_field:
    get:
      tags: [JSON:API]
      summary: Image detail fields
      description: |
        Returns per-image detail fields (regions, supplementary information, etc.).
        Used primarily by the item page image detail view.
      parameters:
        - $ref: '#/components/parameters/PageLimit'
        - $ref: '#/components/parameters/PageOffset'
        - name: filter[field_u_parent]
          in: query
          required: false
          description: Filter by parent ID
          schema:
            type: integer
      responses:
        '200':
          description: JSON:API response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JsonApiCollection'

  # ============================================================
  # Fulltext Search
  # ============================================================
  /api/fulltext-search:
    post:
      tags: [Fulltext Search]
      summary: Full-text search (page level)
      description: |
        Internal POST endpoint called by the in-site full-text search UI.
        Queries the `u_pages_v3` index in Elasticsearch and returns results with facets.

        To call directly from outside, send a body like the example below.

        > **Note:** This endpoint is shaped for internal UI needs and does not provide
        > a stable API contract.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FulltextSearchRequest'
            example:
              state:
                searchTerm: 佛
                current: 1
                resultsPerPage: 20
                filters: []
      responses:
        '200':
          description: Search results
          content:
            application/json:
              schema:
                type: object

  /api/fulltext-search/word-count:
    post:
      tags: [Fulltext Search]
      summary: Full-text search word-count aggregation
      description: |
        Returns the total number of occurrences of the search term in the result set.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FulltextSearchRequest'
      responses:
        '200':
          description: Aggregation result
          content:
            application/json:
              schema:
                type: object
                properties:
                  wordCount:
                    type: integer

  # ============================================================
  # Internal
  # ============================================================
  /api/{index}/search:
    get:
      tags: [Internal]
      summary: Generic ES search bridge
      description: |
        Generic Elasticsearch bridge for the Search-UI library.
        Used internally by the in-site React search UI.
      parameters:
        - name: index
          in: path
          required: true
          description: Elasticsearch index name
          schema:
            type: string
      responses:
        '200':
          description: ES result
          content:
            application/json:
              schema:
                type: object
    post:
      tags: [Internal]
      summary: Generic ES search bridge (Search-UI shape)
      description: |
        Receives `state` and `queryConfig` from Search-UI, converts them to ES queries,
        and returns the result.
      parameters:
        - name: index
          in: path
          required: true
          description: Elasticsearch index name
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                state:
                  type: object
                queryConfig:
                  type: object
      responses:
        '200':
          description: ES result (Search-UI shape)
          content:
            application/json:
              schema:
                type: object

  /api/{index}/item/{id}:
    get:
      tags: [Internal]
      summary: Single ES document fetch
      description: |
        Fetches a single document by `field_id` from the specified ES index.
      parameters:
        - name: index
          in: path
          required: true
          schema:
            type: string
        - name: id
          in: path
          required: true
          description: field_id value
          schema:
            type: string
      responses:
        '200':
          description: ES document
          content:
            application/json:
              schema:
                type: object

  /api/jsonapi/node/u_hako:
    get:
      tags: [Internal]
      summary: Case (hako) aggregation
      description: |
        Aggregates the number of volumes per parent case (`field_u_parent_str`).
        Currently unused by the frontend; an auxiliary endpoint.
      responses:
        '200':
          description: JSON:API response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JsonApiCollection'

  /api/openapi/{locale}:
    get:
      tags: [Internal]
      summary: OpenAPI spec delivery
      description: |
        Meta-endpoint that returns this very OpenAPI 3.1 YAML document, localized.
        Placeholders like `Yūren-ja Jiaxing edition Buddhist Canon Database` in the template are substituted server-side.
      parameters:
        - name: locale
          in: path
          required: true
          description: Locale (`ja` or `en`)
          schema:
            type: string
            enum: ['ja', 'en']
          example: en
      responses:
        '200':
          description: OpenAPI YAML
          content:
            application/yaml:
              schema:
                type: string

  /api/auth:
    get:
      tags: [Internal]
      summary: Basic auth endpoint
      description: |
        Internal endpoint for basic authentication (used in staging environments).
        Not intended for direct use by end users.
      responses:
        '401':
          description: "401 Unauthorized (returns `WWW-Authenticate: Basic`)"

components:
  parameters:
    IIIFVersion:
      name: version
      in: path
      required: true
      description: IIIF Presentation API version (`2` or `3`)
      schema:
        type: string
        enum: ['2', '3']
      example: '2'
    PageLimit:
      name: page[limit]
      in: query
      required: false
      description: >-
        Number of records per page (default 50, max 3000). The server clamps the
        value to 3000 to stay within the Cloudflare Worker resource limit.
      schema:
        type: integer
        default: 50
        maximum: 3000
        # example is the value used by the smoke test (scripts/test-openapi-endpoints.js).
        example: 50
    PageOffset:
      name: page[offset]
      in: query
      required: false
      description: Offset
      schema:
        type: integer
        default: 0

  schemas:
    JsonApiCollection:
      type: object
      description: Drupal JSON:API compatible collection response
      properties:
        jsonapi:
          type: object
          properties:
            version:
              type: string
              example: '1.0'
        meta:
          type: object
          properties:
            total:
              type: integer
              description: Total number of records
              example: 2390
        data:
          type: array
          items:
            type: object
            properties:
              type:
                type: string
                example: node--u_satsu
              id:
                type: string
                example: '069-05'
              attributes:
                type: object
                additionalProperties: true

    FulltextSearchRequest:
      type: object
      required: [state]
      properties:
        state:
          type: object
          properties:
            searchTerm:
              type: string
              example: '佛'
            current:
              type: integer
              default: 1
            resultsPerPage:
              type: integer
              default: 20
            filters:
              type: array
              items:
                type: object
            sortField:
              type: string
            sortDirection:
              type: string
              enum: [asc, desc]
            facets:
              type: object
