{
  "openapi": "3.0.0",
  "info": {
    "title": "YoBook API",
    "description": "API for accessing digital textbooks, educational resources, audio links, and grouped question-paper collections for Nepal.",
    "version": "1.0.0",
    "contact": {
      "name": "YoBook API Contributors",
      "url": "https://github.com/bitnepal"
    },
    "x-api-policy": {
      "compatibility": "Backward compatible changes are preferred within v1.",
      "deprecation": "Deprecated fields/endpoints will be announced in docs before removal.",
      "sunset_notice": "Breaking removals target a minimum 90-day notice."
    }
  },
  "servers": [
    {
      "url": "http://localhost:5000",
      "description": "Local development server"
    },
    {
      "url": "https://yobook-api.vercel.app",
      "description": "Public production server"
    }
  ],
  "paths": {
    "/api/books": {
      "get": {
        "summary": "Retrieve a list of books",
        "description": "Get a paginated list of books with various filters.",
        "parameters": [
          {
            "": "#/components/parameters/q"
          },
          {
            "": "#/components/parameters/source"
          },
          {
            "": "#/components/parameters/grade"
          },
          {
            "": "#/components/parameters/subject"
          },
          {
            "": "#/components/parameters/language"
          },
          {
            "": "#/components/parameters/category"
          },
          {
            "": "#/components/parameters/page"
          },
          {
            "": "#/components/parameters/limit"
          },
          {
            "": "#/components/parameters/full"
          }
        ],
        "responses": {
          "200": {
            "description": "A successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookListResponse"
                }
              }
            }
          },
          "400": {
            "": "#/components/responses/BadRequest"
          },
          "429": {
            "": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/search": {
      "get": {
        "summary": "Search books",
        "description": "Dedicated search endpoint. Supports q, source, grade, subject, language, category, page, limit, and full query parameters.",
        "responses": {
          "200": {
            "description": "A successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookListResponse"
                }
              }
            }
          },
          "400": {
            "": "#/components/responses/BadRequest"
          },
          "429": {
            "": "#/components/responses/TooManyRequests"
          }
        },
        "parameters": [
          {
            "": "#/components/parameters/q"
          },
          {
            "": "#/components/parameters/source"
          },
          {
            "": "#/components/parameters/grade"
          },
          {
            "": "#/components/parameters/subject"
          },
          {
            "": "#/components/parameters/language"
          },
          {
            "": "#/components/parameters/category"
          },
          {
            "": "#/components/parameters/page"
          },
          {
            "": "#/components/parameters/limit"
          },
          {
            "": "#/components/parameters/full"
          }
        ]
      }
    },
    "/api/course-materials": {
      "get": {
        "summary": "List course materials",
        "description": "Course Materials only. Supports q, source, grade, subject, language, page, limit, and full query parameters.",
        "responses": {
          "200": {
            "description": "A successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookListResponse"
                }
              }
            }
          },
          "400": {
            "": "#/components/responses/BadRequest"
          },
          "429": {
            "": "#/components/responses/TooManyRequests"
          }
        },
        "parameters": [
          {
            "": "#/components/parameters/q"
          },
          {
            "": "#/components/parameters/source"
          },
          {
            "": "#/components/parameters/grade"
          },
          {
            "": "#/components/parameters/subject"
          },
          {
            "": "#/components/parameters/language"
          },
          {
            "": "#/components/parameters/category"
          },
          {
            "": "#/components/parameters/page"
          },
          {
            "": "#/components/parameters/limit"
          },
          {
            "": "#/components/parameters/full"
          }
        ]
      }
    },
    "/api/textbooks": {
      "get": {
        "summary": "List textbooks",
        "description": "Student textbook records across CEHRD, NCERT, and textbook-labeled catalog records. Supports q, source, grade, subject, language, page, limit, and full query parameters.",
        "responses": {
          "200": {
            "description": "A successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookListResponse"
                }
              }
            }
          },
          "400": {
            "": "#/components/responses/BadRequest"
          },
          "429": {
            "": "#/components/responses/TooManyRequests"
          }
        },
        "parameters": [
          {
            "": "#/components/parameters/q"
          },
          {
            "": "#/components/parameters/source"
          },
          {
            "": "#/components/parameters/grade"
          },
          {
            "": "#/components/parameters/subject"
          },
          {
            "": "#/components/parameters/language"
          },
          {
            "": "#/components/parameters/category"
          },
          {
            "": "#/components/parameters/page"
          },
          {
            "": "#/components/parameters/limit"
          },
          {
            "": "#/components/parameters/full"
          }
        ]
      }
    },
    "/api/teacher-guides": {
      "get": {
        "summary": "List teacher guides",
        "description": "Teacher guide and teaching manual records across sources. Supports q, grade, subject, language, page, limit, and full query parameters.",
        "responses": {
          "200": {
            "description": "A successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookListResponse"
                }
              }
            }
          },
          "400": {
            "": "#/components/responses/BadRequest"
          },
          "429": {
            "": "#/components/responses/TooManyRequests"
          }
        },
        "parameters": [
          {
            "": "#/components/parameters/q"
          },
          {
            "": "#/components/parameters/source"
          },
          {
            "": "#/components/parameters/grade"
          },
          {
            "": "#/components/parameters/subject"
          },
          {
            "": "#/components/parameters/language"
          },
          {
            "": "#/components/parameters/category"
          },
          {
            "": "#/components/parameters/page"
          },
          {
            "": "#/components/parameters/limit"
          },
          {
            "": "#/components/parameters/full"
          }
        ]
      }
    },
    "/api/curriculum": {
      "get": {
        "summary": "List curriculum books",
        "description": "Curriculum records across sources. Supports q, grade, subject, language, page, limit, and full query parameters.",
        "responses": {
          "200": {
            "description": "A successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookListResponse"
                }
              }
            }
          },
          "400": {
            "": "#/components/responses/BadRequest"
          },
          "429": {
            "": "#/components/responses/TooManyRequests"
          }
        },
        "parameters": [
          {
            "": "#/components/parameters/q"
          },
          {
            "": "#/components/parameters/source"
          },
          {
            "": "#/components/parameters/grade"
          },
          {
            "": "#/components/parameters/subject"
          },
          {
            "": "#/components/parameters/language"
          },
          {
            "": "#/components/parameters/category"
          },
          {
            "": "#/components/parameters/page"
          },
          {
            "": "#/components/parameters/limit"
          },
          {
            "": "#/components/parameters/full"
          }
        ]
      }
    },
    "/api/ncert": {
      "get": {
        "summary": "List NCERT textbook collections",
        "description": "NCERT records only. Supports q, grade, subject, language, page, limit, and full query parameters.",
        "responses": {
          "200": {
            "description": "A successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookListResponse"
                }
              }
            }
          },
          "400": {
            "": "#/components/responses/BadRequest"
          },
          "429": {
            "": "#/components/responses/TooManyRequests"
          }
        },
        "parameters": [
          {
            "": "#/components/parameters/q"
          },
          {
            "": "#/components/parameters/source"
          },
          {
            "": "#/components/parameters/grade"
          },
          {
            "": "#/components/parameters/subject"
          },
          {
            "": "#/components/parameters/language"
          },
          {
            "": "#/components/parameters/category"
          },
          {
            "": "#/components/parameters/page"
          },
          {
            "": "#/components/parameters/limit"
          },
          {
            "": "#/components/parameters/full"
          }
        ]
      }
    },
    "/api/gradewise-audio": {
      "get": {
        "summary": "List grade-wise audio links",
        "description": "Returns Pustakalaya grade-wise audio grouped by grade, subject, and chapter. Supports optional grade and subject filters.",
        "parameters": [
          {
            "name": "grade",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Filter by grade number, such as 4"
          },
          {
            "name": "subject",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filter by subject name, such as English"
          }
        ],
        "responses": {
          "200": {
            "description": "Grade-wise audio data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "source": {
                          "type": "string"
                        },
                        "scrapedAt": {
                          "type": "string"
                        },
                        "stats": {
                          "type": "object"
                        },
                        "grades": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "grade": {
                                "type": "integer"
                              },
                              "subjects": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "subject": {
                                      "type": "string"
                                    },
                                    "chapters": {
                                      "type": "array",
                                      "items": {
                                        "type": "object",
                                        "properties": {
                                          "chapter": {
                                            "type": "integer"
                                          },
                                          "chapterName": {
                                            "type": "string"
                                          },
                                          "unit": {
                                            "type": "string"
                                          },
                                          "url": {
                                            "type": "string",
                                            "format": "uri"
                                          }
                                        }
                                      }
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/health": {
      "get": {
        "summary": "Health check",
        "description": "Returns basic API health and catalog counts.",
        "responses": {
          "200": {
            "description": "API health status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "status": {
                      "type": "string"
                    },
                    "books": {
                      "type": "integer"
                    },
                    "sources": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/books/{id}": {
      "get": {
        "summary": "Get a single book by ID",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The unique identifier of the book",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The requested book data",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookResponse"
                }
              }
            }
          },
          "404": {
            "": "#/components/responses/NotFound"
          },
          "429": {
            "": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/download": {
      "get": {
        "summary": "Proxy a catalog download for same-origin reading",
        "description": "Streams a download/read URL only when the URL is already present in the catalog, including top-level downloadUrl/readUrl values, chapterDownloadUrls[].downloadUrl, and nested question_papers[].readUrl/url values.",
        "parameters": [
          {
            "": "#/components/parameters/proxyUrl"
          }
        ],
        "responses": {
          "200": {
            "description": "Download stream",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "": "#/components/responses/BadRequest"
          },
          "403": {
            "": "#/components/responses/Forbidden"
          },
          "413": {
            "": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "": "#/components/responses/TooManyRequests"
          },
          "502": {
            "": "#/components/responses/BadGateway"
          }
        }
      }
    },
    "/api/audio": {
      "get": {
        "summary": "Proxy a catalog audio file for same-origin playback",
        "description": "Streams an audio URL only when the URL is already present as an audioUrl in the book catalog.",
        "parameters": [
          {
            "": "#/components/parameters/proxyUrl"
          }
        ],
        "responses": {
          "200": {
            "description": "Audio stream",
            "content": {
              "audio/mpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "": "#/components/responses/BadRequest"
          },
          "403": {
            "": "#/components/responses/Forbidden"
          },
          "413": {
            "": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "": "#/components/responses/TooManyRequests"
          },
          "502": {
            "": "#/components/responses/BadGateway"
          }
        }
      }
    },
    "/api/sources": {
      "get": {
        "summary": "List available data sources",
        "responses": {
          "200": {
            "description": "A list of data sources with statistics",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "source": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          },
                          "grades": {
                            "type": "array",
                            "items": {
                              "type": "integer"
                            }
                          },
                          "subjects": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/stats": {
      "get": {
        "summary": "Get overall collection statistics",
        "responses": {
          "200": {
            "description": "Statistics by grade, subject, source, and language",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "totalBooks": {
                          "type": "integer"
                        },
                        "byGrade": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "integer"
                          }
                        },
                        "bySubject": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "integer"
                          }
                        },
                        "bySource": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "integer"
                          }
                        },
                        "byLanguage": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "": "#/components/responses/TooManyRequests"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Book": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "author": {
            "type": "string"
          },
          "grade": {
            "type": "integer"
          },
          "subject": {
            "type": "string",
            "nullable": true
          },
          "language": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "curriculum": {
            "type": "string"
          },
          "source": {
            "type": "string"
          },
          "sourceUrl": {
            "type": "string"
          },
          "readUrl": {
            "type": "string"
          },
          "coverUrl": {
            "type": "string"
          },
          "downloadUrl": {
            "description": "Direct downloadable resource URL. Use a string for one file, or an array of URL strings when a source provides multiple formats.",
            "nullable": true,
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ]
          },
          "chapterDownloadUrls": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "object",
              "properties": {
                "chapter": {
                  "type": "string"
                },
                "downloadUrl": {
                  "type": "string"
                }
              }
            }
          },
          "audioUrl": {
            "type": "string",
            "nullable": true
          },
          "level": {
            "type": "integer",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "category": {
            "type": "string"
          },
          "scrapedAt": {
            "type": "string",
            "format": "date-time"
          },
          "collection_name": {
            "type": "string",
            "nullable": true,
            "description": "Collection label for grouped resources, such as Health Loksewa or Bank Exam Preparation."
          },
          "question_papers": {
            "type": "array",
            "nullable": true,
            "description": "Nested question-paper entries for grouped exam collections. Present on grouped records such as Question Bank Nepal and Shisir question-paper collections.",
            "items": {
              "$ref": "#/components/schemas/QuestionPaper"
            }
          }
        }
      },
      "BookSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "titleLocal": {
            "type": "string",
            "nullable": true
          },
          "author": {
            "type": "string"
          },
          "grade": {
            "type": "integer"
          },
          "subject": {
            "type": "string",
            "nullable": true
          },
          "language": {
            "type": "string"
          },
          "source": {
            "type": "string"
          },
          "coverUrl": {
            "type": "string"
          },
          "downloadUrl": {
            "description": "Direct downloadable resource URL. Can be one URL string or an array of URL strings.",
            "nullable": true,
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ]
          },
          "category": {
            "type": "string"
          },
          "audioUrl": {
            "type": "string",
            "nullable": true
          },
          "level": {
            "type": "integer",
            "nullable": true
          },
          "detailUrl": {
            "type": "string"
          },
          "collection_name": {
            "type": "string",
            "nullable": true,
            "description": "Collection label for grouped resources."
          },
          "questionPaperCount": {
            "type": "integer",
            "nullable": true,
            "description": "Number of nested question papers when this list item is a grouped question-paper collection."
          }
        }
      },
      "BookResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "data": {
            "$ref": "#/components/schemas/Book"
          }
        }
      },
      "BookListResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BookSummary"
            }
          },
          "meta": {
            "type": "object",
            "properties": {
              "total": {
                "type": "integer"
              },
              "page": {
                "type": "integer"
              },
              "limit": {
                "type": "integer"
              },
              "pages": {
                "type": "integer"
              },
              "detail": {
                "type": "string",
                "enum": [
                  "compact",
                  "full"
                ]
              }
            }
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": false
          },
          "error": {
            "type": "string"
          }
        },
        "required": [
          "success",
          "error"
        ]
      },
      "QuestionPaper": {
        "type": "object",
        "description": "A single paper inside a grouped question-paper collection.",
        "properties": {
          "title": {
            "type": "string"
          },
          "year": {
            "type": "string",
            "nullable": true
          },
          "readUrl": {
            "type": "string",
            "nullable": true,
            "description": "Readable/downloadable file URL for this paper. This URL is accepted by /api/download when present in the catalog."
          },
          "url": {
            "type": "string",
            "nullable": true,
            "description": "Legacy/alternate file URL used by some grouped sources. This URL is accepted by /api/download when present in the catalog."
          },
          "sourceUrl": {
            "type": "string",
            "nullable": true,
            "description": "Original source page for this paper, when available."
          },
          "coverUrl": {
            "type": "string",
            "nullable": true,
            "description": "Paper-level cover image, when available."
          },
          "fileSize": {
            "type": "string",
            "nullable": true
          }
        }
      }
    },
    "parameters": {
      "q": {
        "name": "q",
        "in": "query",
        "description": "Search query (title, subject, description, etc.)",
        "schema": {
          "type": "string"
        }
      },
      "source": {
        "name": "source",
        "in": "query",
        "description": "Filter by data source",
        "schema": {
          "type": "string",
          "enum": [
            "cehrd-learning",
            "cehrd-stories",
            "cehrd-nfe",
            "cehrd-audio",
            "pustakalaya-stories",
            "pustakalaya-reference",
            "pustakalaya-course",
            "pustakalaya-teaching",
            "pustakalaya-other-educational",
            "ncert-official",
            "cdc-library"
          ]
        }
      },
      "grade": {
        "name": "grade",
        "in": "query",
        "description": "Filter by grade (1-12)",
        "schema": {
          "type": "string"
        }
      },
      "subject": {
        "name": "subject",
        "in": "query",
        "description": "Filter by subject",
        "schema": {
          "type": "string"
        }
      },
      "language": {
        "name": "language",
        "in": "query",
        "description": "Filter by language (ne, en)",
        "schema": {
          "type": "string"
        }
      },
      "category": {
        "name": "category",
        "in": "query",
        "description": "Filter by category",
        "schema": {
          "type": "string"
        }
      },
      "page": {
        "name": "page",
        "in": "query",
        "description": "Page number",
        "schema": {
          "type": "integer",
          "default": 1,
          "minimum": 1
        }
      },
      "limit": {
        "name": "limit",
        "in": "query",
        "description": "Results per page",
        "schema": {
          "type": "integer",
          "default": 50,
          "minimum": 1,
          "maximum": 200
        }
      },
      "full": {
        "name": "full",
        "in": "query",
        "description": "Set to true/1 to return complete book records from list endpoint",
        "schema": {
          "type": "boolean",
          "default": false
        }
      },
      "proxyUrl": {
        "name": "url",
        "in": "query",
        "required": true,
        "description": "Upstream URL from a book record",
        "schema": {
          "type": "string",
          "format": "uri"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid request parameters",
        "content": {
          "application/json": {
            "schema": {
              "": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Forbidden": {
        "description": "URL is not part of the catalog or host is blocked",
        "content": {
          "application/json": {
            "schema": {
              "": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": {
              "": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limit exceeded",
        "content": {
          "application/json": {
            "schema": {
              "": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "BadGateway": {
        "description": "Unable to load upstream resource",
        "content": {
          "application/json": {
            "schema": {
              "": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "PayloadTooLarge": {
        "description": "Upstream content exceeds proxy size limit",
        "content": {
          "application/json": {
            "schema": {
              "": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "InternalServerError": {
        "description": "Internal server error",
        "content": {
          "application/json": {
            "schema": {
              "": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    }
  }
}
