openapi: 3.1.0
info:
  title: eLibri API
  version: "1"
  description: >
    API for fetching ONIX product metadata and managing synchronization queues.
    All endpoints require HTTP Digest authentication using API credentials
    (separate from personal login credentials).
  contact:
    email: tomek@elibri.com.pl

servers:
  - url: https://www.elibri.com.pl/api/v1

security:
  - digestAuth: []

components:
  securitySchemes:
    digestAuth:
      type: http
      scheme: digest

  headers:
    X-eLibri-API-ONIX-dialect:
      description: >
        Requested ONIX dialect version. Defaults to `3.0.2` if omitted.
      schema:
        type: string
        enum: ["3.0.1", "3.0.2"]
        default: "3.0.2"
    X-eLibri-API-pop-products-count:
      description: Number of products returned in the pop response (0 if queue was empty).
      schema:
        type: integer
    X-eLibri-API-pop-queue-name:
      description: Name of the queue that was popped (`meta` or `stocks`). Omitted when queue is empty.
      schema:
        type: string
        enum: [meta, stocks]

  schemas:
    ErrorResponse:
      type: object
      xml:
        name: error
      properties:
        message:
          type: string
      required: [message]

    ONIXMessage:
      description: >
        ONIX 3.0 XML message. The root element is `<ONIXMessage>` containing a
        `<Header>` and one or more `<Product>` elements.
      type: string
      format: xml

    QueuesResponse:
      type: object
      xml:
        name: queues
      properties:
        queue:
          type: array
          items:
            type: object
            xml:
              name: queue
            properties:
              name:
                type: string
                description: Queue name (e.g. `meta`, `stocks`)
                xml:
                  attribute: true
              products_count:
                type: integer
                description: Number of products pending in the queue
                xml:
                  attribute: true

    PublishersResponse:
      type: object
      xml:
        name: publishers
      properties:
        publisher:
          type: array
          items:
            type: object
            xml:
              name: publisher
            properties:
              id:
                type: integer
                xml:
                  attribute: true
              name:
                type: string

paths:
  /products/{record_reference}:
    get:
      summary: Fetch ONIX for a single product
      description: >
        Returns an ONIX XML message containing metadata for the product
        identified by its `RecordReference` (or ISBN-13). Access is restricted
        to products the API credential is authorised to view. It should be used only for
        test purposes, as every changed will be put to the queue anyway.
      operationId: getProduct
      parameters:
        - name: record_reference
          in: path
          required: true
          description: >
            The product's `RecordReference` (eLibri internal identifier) or a
            13-digit ISBN.
          schema:
            type: string
            example: "fdb04cfbf0ad4fe6905c326c29dfb17b"
        - name: X-eLibri-API-ONIX-dialect
          in: header
          required: false
          schema:
            type: string
            enum: ["3.0.1", "3.0.2"]
            default: "3.0.2"
          description: >
            ONIX dialect to use. `3.0.1` applies eLibri extensions via
            `ONIX::Mixer.transform`; `3.0.2` (default) is standard ONIX 3.0.
      responses:
        "200":
          description: ONIX XML message with the product record.
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/ONIXMessage"
              example: |
                <?xml version="1.0" encoding="UTF-8"?>
                <ONIXMessage release="3.0" xmlns="http://ns.editeur.org/onix/3.0/reference">
                  <Header>
                    <Sender><SenderName>eLibri</SenderName></Sender>
                  </Header>
                  <Product>
                    <RecordReference>fdb04cfbf0ad4fe6905c326c29dfb17b</RecordReference>
                    <!-- ... -->
                  </Product>
                </ONIXMessage>
        "403":
          description: Access denied — the credential does not have permission to view this product.
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Product not found.
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "412":
          description: Invalid value in `X-eLibri-API-ONIX-dialect` header.
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Daily request limit exceeded.
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /queues:
    get:
      summary: List available queues
      description: >
        Returns the queues available to the authenticated credential, along with
        the number of pending products in each queue.
      operationId: listQueues
      responses:
        "200":
          description: XML list of queues and their pending product counts.
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/QueuesResponse"
              example: |
                <?xml version="1.0" encoding="UTF-8"?>
                <queues>
                  <queue name="meta" products_count="42"/>
                  <queue name="stocks" products_count="5"/>
                </queues>

  /queues/refill_all:
    post:
      summary: Trigger full re-synchronisation
      description: >
        Enqueues all accessible products into the queues, effectively performing
        a full resync. The job runs asynchronously in the background. The
        response body is empty.
      operationId: refillAllQueues
      responses:
        "200":
          description: Job enqueued successfully. Response body is empty.

  /queues/{queue_name}/pop:
    post:
      summary: Pop products from a queue
      description: >
        Retrieves up to `count` (max 30, default 10) products from the named
        queue and returns them as an ONIX XML message. By default the products
        are removed from the queue after being returned (`:move` mode). Pass
        `testing=1` to read without removing, and `offset` to page through the
        queue in test mode.

        Metadata pop (`meta` queue) returns full ONIX records. Stocks pop
        (`stocks` queue) returns only `RecordReference` and stock/pricing data.

        Response headers report how many products were returned and which queue
        was popped.
      operationId: popQueue
      parameters:
        - name: queue_name
          in: path
          required: true
          description: Queue to pop from — `meta` (full metadata) or `stocks` (stock/price data).
          schema:
            type: string
            enum: [meta, stocks]
        - name: count
          in: query
          required: false
          description: Number of products to fetch. Must be between 1 and 30. Defaults to 10.
          schema:
            type: integer
            minimum: 1
            maximum: 30
            default: 10
        - name: testing
          in: query
          required: false
          description: >
            Pass `1` to enter testing mode — products are returned but **not**
            removed from the queue.
          schema:
            type: string
            enum: ["1"]
        - name: offset
          in: query
          required: false
          description: >
            Offset into the queue. Only meaningful when `testing=1`. Use to
            page through the queue without consuming it.
          schema:
            type: integer
            minimum: 0
            default: 0
        - name: X-eLibri-API-ONIX-dialect
          in: header
          required: false
          schema:
            type: string
            enum: ["3.0.1", "3.0.2"]
            default: "3.0.2"
      responses:
        "200":
          description: >
            ONIX XML message containing the popped products, or an empty body
            if the queue was empty.
          headers:
            X-eLibri-API-pop-products-count:
              $ref: "#/components/headers/X-eLibri-API-pop-products-count"
            X-eLibri-API-pop-queue-name:
              $ref: "#/components/headers/X-eLibri-API-pop-queue-name"
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/ONIXMessage"
        "404":
          description: Queue name not found.
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "412":
          description: Invalid value in `X-eLibri-API-ONIX-dialect` header (error code 1003).
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: >
            Either the daily request limit is exceeded, or a concurrent pop
            request is already in progress for this credential.
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /queues/{queue_name}/last_pop:
    get:
      summary: Replay the last pop
      description: >
        Returns the same ONIX data that was returned by the most recent
        successful `POST /queues/{queue_name}/pop` call, without modifying the
        queue. Useful for recovering after a failed processing run.
      operationId: lastPop
      parameters:
        - name: queue_name
          in: path
          required: true
          schema:
            type: string
            enum: [meta, stocks]
        - name: X-eLibri-API-ONIX-dialect
          in: header
          required: false
          schema:
            type: string
            enum: ["3.0.1", "3.0.2"]
            default: "3.0.2"
      responses:
        "200":
          description: ONIX XML message with the previously popped products.
          headers:
            X-eLibri-API-pop-products-count:
              $ref: "#/components/headers/X-eLibri-API-pop-products-count"
            X-eLibri-API-pop-queue-name:
              $ref: "#/components/headers/X-eLibri-API-pop-queue-name"
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/ONIXMessage"
        "412":
          description: No previous pop exists for this queue (error code 1002).
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /queues/{queue_name}/remove/{record_reference}:
    post:
      summary: Remove a product from a queue
      description: >
        Deletes the specified product from the named queue without returning its
        data. Use when you want to skip a product without consuming it via pop.
      operationId: removeFromQueue
      parameters:
        - name: queue_name
          in: path
          required: true
          schema:
            type: string
            enum: [meta, stocks]
        - name: record_reference
          in: path
          required: true
          description: The `RecordReference` of the product to remove.
          schema:
            type: string
      responses:
        "200":
          description: Product successfully removed from the queue.
        "404":
          description: Queue not found (error code 1001).
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "412":
          description: Product not found (error code 1002).
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /publishers:
    get:
      summary: List accessible publishers
      description: Returns all publishers the authenticated credential can access.
      operationId: listPublishers
      responses:
        "200":
          description: XML list of publishers.
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/PublishersResponse"

  /publishers/pdw:
    get:
      summary: List PDW-distributed publishers
      description: Returns publishers distributed via PDW.
      operationId: listPdwPublishers
      responses:
        "200":
          description: XML list of publishers.
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/PublishersResponse"

  /publishers/dressler:
    get:
      summary: List Dressler-distributed publishers
      description: Returns publishers distributed via Dressler
      operationId: listOlesiejukPublishers
      responses:
        "200":
          description: XML list of publishers.
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/PublishersResponse"

  /publishers/{publisher_id}/products:
    get:
      summary: List products for a publisher
      description: >
        Returns a brief list (id, title, RecordReference) of products belonging
        to the given publisher that the credential is authorised to access.
      operationId: listPublisherProducts
      parameters:
        - name: publisher_id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: XML list of products.
          content:
            application/xml:
              schema:
                type: object
                xml:
                  name: products
                properties:
                  product:
                    type: array
                    items:
                      type: object
                      xml:
                        name: product
                      properties:
                        id:
                          type: integer
                          xml:
                            attribute: true
                        record_reference:
                          type: string
                        title:
                          type: string
        "403":
          description: Access denied to this publisher.
          content:
            application/xml:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
