{
  "openapi": "3.0.3",
  "info": {
    "title": "Jinero API",
    "version": "1.0.0",
    "description": "Free public API for jinero.online design tools — fonts, icons, colors, palettes, code, SVG and dev utilities. No key required (60 req/min per IP); send an API token for 600 req/min.",
    "contact": { "name": "Jinero", "url": "https://jinero.online" },
    "license": { "name": "Terms", "url": "https://jinero.online/about" }
  },
  "servers": [
    { "url": "https://jinero.online/api/v1", "description": "Production" }
  ],
  "tags": [
    { "name": "Fonts" },
    { "name": "Icons" },
    { "name": "Colors" },
    { "name": "Palettes" },
    { "name": "Code" },
    { "name": "SVG" },
    { "name": "Dev" }
  ],
  "components": {
    "securitySchemes": {
      "ApiToken": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Token",
        "description": "Optional. Personal API token from your dashboard — raises the limit to 600 req/min. Bearer header is also accepted."
      }
    },
    "parameters": {
      "page": { "name": "page", "in": "query", "schema": { "type": "integer", "minimum": 1 }, "description": "Page number." },
      "perPage": { "name": "per_page", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100 }, "description": "Results per page." },
      "sort": { "name": "sort", "in": "query", "schema": { "type": "string", "enum": ["asc", "desc"] }, "description": "Sort direction." }
    },
    "responses": {
      "RateLimited": { "description": "Too many requests." },
      "NotFound": { "description": "Resource not found." },
      "ValidationError": { "description": "Invalid parameters." }
    }
  },
  "security": [ {}, { "ApiToken": [] } ],
  "paths": {
    "/fonts": {
      "get": {
        "tags": ["Fonts"],
        "summary": "Search fonts",
        "operationId": "searchFonts",
        "parameters": [
          { "name": "name", "in": "query", "schema": { "type": "string" }, "description": "Fuzzy name match." },
          { "name": "category", "in": "query", "schema": { "type": "string" }, "description": "Comma-separated, e.g. 'serif,sans'." },
          { "name": "langs", "in": "query", "schema": { "type": "string" }, "description": "Comma-separated codes, ALL required, e.g. 'latin,cyrillic'." },
          { "name": "variable", "in": "query", "schema": { "type": "boolean" } },
          { "name": "monospace", "in": "query", "schema": { "type": "boolean" } },
          { "name": "styles_min", "in": "query", "schema": { "type": "integer" } },
          { "name": "styles_max", "in": "query", "schema": { "type": "integer" } },
          { "name": "order", "in": "query", "schema": { "type": "string", "enum": ["likes", "downloads", "views", "name", "created_at"] } },
          { "$ref": "#/components/parameters/sort" },
          { "$ref": "#/components/parameters/perPage" },
          { "$ref": "#/components/parameters/page" }
        ],
        "responses": { "200": { "description": "Paginated font families." }, "429": { "$ref": "#/components/responses/RateLimited" } }
      }
    },
    "/fonts/css": {
      "get": {
        "tags": ["Fonts"],
        "summary": "Generate @font-face CSS (Google-Fonts-compatible)",
        "operationId": "getFontsCss",
        "parameters": [
          { "name": "family", "in": "query", "required": true, "schema": { "type": "string" }, "description": "e.g. 'inter:wght@400,700' or variable range 'inter:wght@300..900'. Repeat the param for multiple families." },
          { "name": "display", "in": "query", "schema": { "type": "string", "enum": ["swap", "auto", "block", "fallback", "optional"] } }
        ],
        "responses": { "200": { "description": "CSS", "content": { "text/css": { "schema": { "type": "string" } } } }, "404": { "$ref": "#/components/responses/NotFound" } }
      }
    },
    "/fonts/{slug}": {
      "get": {
        "tags": ["Fonts"],
        "summary": "Get a font family",
        "operationId": "getFont",
        "parameters": [ { "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } } ],
        "responses": { "200": { "description": "Font family with styles, axes, license and URLs." }, "404": { "$ref": "#/components/responses/NotFound" } }
      }
    },
    "/fonts/{slug}/files": {
      "get": {
        "tags": ["Fonts"],
        "summary": "List font files",
        "operationId": "getFontFiles",
        "parameters": [ { "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } } ],
        "responses": { "200": { "description": "Flat list of files (weight/italic/format/url/size)." }, "404": { "$ref": "#/components/responses/NotFound" } }
      }
    },
    "/fonts/{slug}/similar": {
      "get": {
        "tags": ["Fonts"],
        "summary": "Visually similar families",
        "operationId": "getSimilarFonts",
        "parameters": [ { "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } } ],
        "responses": { "200": { "description": "Ranked similar families (cosine over recognizer embeddings)." }, "404": { "$ref": "#/components/responses/NotFound" } }
      }
    },
    "/fonts/{slug}/download": {
      "get": {
        "tags": ["Fonts"],
        "summary": "Download family as ZIP",
        "operationId": "downloadFont",
        "parameters": [ { "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } } ],
        "responses": { "200": { "description": "ZIP archive (all styles + fonts.css).", "content": { "application/zip": { "schema": { "type": "string", "format": "binary" } } } } }
      }
    },
    "/fonts/recognize": {
      "post": {
        "tags": ["Fonts"],
        "summary": "Recognize a font from an image",
        "operationId": "recognizeFont",
        "description": "Heavy AI endpoint — tightly rate limited (3 req/min).",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": ["image"],
                "properties": {
                  "image": { "type": "string", "format": "binary", "description": "Tight crop, jpg/png/webp/bmp, ≤8 MB." },
                  "sample_text": { "type": "string", "maxLength": 160 },
                  "top_k": { "type": "integer", "minimum": 3, "maximum": 20 }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Ranked matches with scores." }, "429": { "$ref": "#/components/responses/RateLimited" } }
      }
    },
    "/icons": {
      "get": {
        "tags": ["Icons"],
        "summary": "Search icons",
        "operationId": "searchIcons",
        "parameters": [
          { "name": "name", "in": "query", "schema": { "type": "string" }, "description": "Synonym-aware search." },
          { "name": "package", "in": "query", "schema": { "type": "string" }, "description": "Package slug." },
          { "name": "style", "in": "query", "schema": { "type": "string" } },
          { "name": "tags", "in": "query", "schema": { "type": "string" } },
          { "name": "kind", "in": "query", "schema": { "type": "string" } },
          { "name": "aesthetic", "in": "query", "schema": { "type": "string" } },
          { "name": "order", "in": "query", "schema": { "type": "string" } },
          { "$ref": "#/components/parameters/sort" },
          { "name": "per_page", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 200 } },
          { "$ref": "#/components/parameters/page" }
        ],
        "responses": { "200": { "description": "Paginated icons." } }
      }
    },
    "/icons/packages": {
      "get": { "tags": ["Icons"], "summary": "List icon packages", "operationId": "listIconPackages", "responses": { "200": { "description": "Icon packages." } } }
    },
    "/icons/{id}": {
      "get": {
        "tags": ["Icons"],
        "summary": "Get an icon",
        "operationId": "getIcon",
        "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } } ],
        "responses": { "200": { "description": "Icon metadata." }, "404": { "$ref": "#/components/responses/NotFound" } }
      }
    },
    "/icons/{id}/svg": {
      "get": {
        "tags": ["Icons"],
        "summary": "Get icon SVG",
        "operationId": "getIconSvg",
        "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } } ],
        "responses": { "200": { "description": "SVG markup.", "content": { "image/svg+xml": { "schema": { "type": "string" } } } } }
      }
    },
    "/colors/contrast": {
      "get": {
        "tags": ["Colors"],
        "summary": "WCAG contrast ratio",
        "operationId": "checkContrast",
        "parameters": [
          { "name": "fg", "in": "query", "required": true, "schema": { "type": "string", "maxLength": 32 } },
          { "name": "bg", "in": "query", "required": true, "schema": { "type": "string", "maxLength": 32 } }
        ],
        "responses": { "200": { "description": "Ratio + AA/AAA pass/fail." } }
      }
    },
    "/colors/shades": {
      "get": {
        "tags": ["Colors"],
        "summary": "Tints & shades",
        "operationId": "getColorShades",
        "parameters": [
          { "name": "hex", "in": "query", "required": true, "schema": { "type": "string", "maxLength": 32 } },
          { "name": "step", "in": "query", "schema": { "type": "integer", "enum": [5, 10, 20, 25] } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 20 } }
        ],
        "responses": { "200": { "description": "Base color + tints + shades." } }
      }
    },
    "/colors/extract": {
      "post": {
        "tags": ["Colors"],
        "summary": "Extract a palette from an image",
        "operationId": "extractColors",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "image": { "type": "string", "format": "binary", "description": "jpg/png/webp/gif/bmp, ≤10 MB." },
                  "image_url": { "type": "string", "format": "uri" },
                  "count": { "type": "integer", "minimum": 2, "maximum": 16 },
                  "mode": { "type": "string", "enum": ["balanced", "vibrant", "muted"] }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Extracted colors." } }
      }
    },
    "/palettes": {
      "get": {
        "tags": ["Palettes"],
        "summary": "Search palettes",
        "operationId": "searchPalettes",
        "parameters": [
          { "name": "name", "in": "query", "schema": { "type": "string" } },
          { "name": "tone", "in": "query", "schema": { "type": "string", "enum": ["light", "dark", "mixed"] } },
          { "name": "temperature", "in": "query", "schema": { "type": "string", "enum": ["warm", "cool", "neutral"] } },
          { "name": "mood", "in": "query", "schema": { "type": "string" } },
          { "name": "harmony", "in": "query", "schema": { "type": "string" } },
          { "name": "color_count", "in": "query", "schema": { "type": "integer", "minimum": 2, "maximum": 10 } },
          { "name": "tags[]", "in": "query", "schema": { "type": "array", "items": { "type": "string" } }, "description": "Tag slugs — ALL must match." },
          { "name": "order", "in": "query", "schema": { "type": "string", "enum": ["newest", "popular", "name"] } },
          { "$ref": "#/components/parameters/perPage" },
          { "$ref": "#/components/parameters/page" }
        ],
        "responses": { "200": { "description": "Paginated palettes." } }
      }
    },
    "/palettes/{id}": {
      "get": {
        "tags": ["Palettes"],
        "summary": "Get a palette",
        "operationId": "getPalette",
        "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } } ],
        "responses": { "200": { "description": "Palette with colors and metadata." }, "404": { "$ref": "#/components/responses/NotFound" } }
      }
    },
    "/code/minify": {
      "post": {
        "tags": ["Code"],
        "summary": "Minify or beautify code",
        "operationId": "minifyCode",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["code", "type"],
                "properties": {
                  "code": { "type": "string" },
                  "type": { "type": "string", "enum": ["js", "css", "html", "svg", "json", "xml", "auto"] },
                  "mode": { "type": "string", "enum": ["minify", "beautify"] },
                  "keep_license": { "type": "boolean" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Transformed code + sizes." } }
      }
    },
    "/code/detect": {
      "post": {
        "tags": ["Code"],
        "summary": "Detect code language",
        "operationId": "detectCode",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["code"], "properties": { "code": { "type": "string" } } } } } },
        "responses": { "200": { "description": "Detected type." } }
      }
    },
    "/code/convert": {
      "post": {
        "tags": ["Code"],
        "summary": "Convert between formats",
        "operationId": "convertCode",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["code", "from", "to"],
                "properties": {
                  "code": { "type": "string" },
                  "from": { "type": "string" },
                  "to": { "type": "string" },
                  "options": { "type": "object" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Converted output." } }
      }
    },
    "/code/converters": {
      "get": { "tags": ["Code"], "summary": "List converters", "operationId": "listCodeConverters", "responses": { "200": { "description": "Available converters + option schemas." } } }
    },
    "/svg/optimize": {
      "post": {
        "tags": ["SVG"],
        "summary": "Optimize SVG",
        "operationId": "optimizeSvg",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["svg"],
                "properties": {
                  "svg": { "type": "string" },
                  "preset": { "type": "string", "enum": ["safe", "balanced", "aggressive"] }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Optimized SVG + sizes." } }
      }
    },
    "/svg/to-datauri": {
      "post": {
        "tags": ["SVG"],
        "summary": "SVG → data URI",
        "operationId": "svgToDataUri",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "svg": { "type": "string" },
                  "url": { "type": "string", "format": "uri" },
                  "encoding": { "type": "string", "enum": ["utf8", "base64"] },
                  "quotes": { "type": "string", "enum": ["single", "double"] }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "data: URI + CSS-ready string." } }
      }
    },
    "/svg/presets": {
      "get": { "tags": ["SVG"], "summary": "List SVG presets", "operationId": "listSvgPresets", "responses": { "200": { "description": "Optimization presets." } } }
    },
    "/dev/scss-mixins": {
      "get": {
        "tags": ["Dev"],
        "summary": "List SCSS mixins",
        "operationId": "listScssMixins",
        "parameters": [
          { "name": "group", "in": "query", "schema": { "type": "string" } },
          { "name": "search", "in": "query", "schema": { "type": "string" } },
          { "name": "fields", "in": "query", "schema": { "type": "string", "enum": ["summary", "full"] } }
        ],
        "responses": { "200": { "description": "Mixin catalog." } }
      }
    },
    "/dev/scss-mixins/{id}": {
      "get": {
        "tags": ["Dev"],
        "summary": "Get a SCSS mixin",
        "operationId": "getScssMixin",
        "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } } ],
        "responses": { "200": { "description": "Mixin source + usage." }, "404": { "$ref": "#/components/responses/NotFound" } }
      }
    },
    "/dev/clamp": {
      "get": {
        "tags": ["Dev"],
        "summary": "Generate fluid clamp()",
        "operationId": "generateClamp",
        "parameters": [
          { "name": "min_fs", "in": "query", "required": true, "schema": { "type": "number" } },
          { "name": "max_fs", "in": "query", "required": true, "schema": { "type": "number" } },
          { "name": "min_vw", "in": "query", "schema": { "type": "number" } },
          { "name": "max_vw", "in": "query", "schema": { "type": "number" } },
          { "name": "unit", "in": "query", "schema": { "type": "string", "enum": ["px", "rem", "both"] } },
          { "name": "root", "in": "query", "schema": { "type": "number" } }
        ],
        "responses": { "200": { "description": "clamp() expression." } }
      }
    }
  }
}
