{
  "openapi": "3.1.1",
  "info": {
    "title": "Retro Pixel Art Batch Processor API",
    "version": "1.0.0",
    "summary": "Apply one retro palette and effect recipe to a ZIP of images.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://fliplineretrobatch.usefulweb.app"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Apply one retro palette and effect recipe to a ZIP of images.",
        "description": "Convert up to eight images into consistent pixel-art assets with shared colors, dithering and adjustments. Download PNGs, editable pixel projects and the resolved recipe in one ZIP.\n\nMethodology:\n- Auto-orient and convert the source to sRGB, then sample native output pixels using nearest-neighbor fit, centered fill or stretch. Images are limited to 4 MB and 2 MP.\n- Apply RGB offsets, brightness and contrast, gamma, then saturation. Palette matching uses squared sRGB distance with first-entry tie-breaking. Automatic palettes select frequent adjusted colors.\n- Ordered dithering uses a 4 × 4 Bayer bias. Serpentine Floyd–Steinberg distributes errors with 7/16, 3/16, 5/16 and 1/16 weights; transparent pixels neither receive nor spread errors. Strength zero equals no dithering.\n- Alpha below the threshold becomes transparent. PNG integer scaling repeats pixels exactly. Dithering runs on native pixels and does not change with preview zoom or export scale.\n- ZIP limits: 16 entries, eight supported images, 4 MB per entry, 16 MB uncompressed input and 8 MP of decoded images total. Unsupported files are reported. ZIP order defines the first image.\n- If the palette is automatic, derive it from the first adjusted image and reuse it for every image. The recipe stored in the output resolves the palette to exact HEX values. Output names have numbered prefixes to avoid collisions.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "flipline_retro_batch",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FliplineRetroBatchInput"
              },
              "examples": {
                "example1": {
                  "summary": "Two sprites with one moss palette",
                  "value": {
                    "images": {
                      "filename": "images.zip",
                      "contentType": "application/zip",
                      "contentBase64": "UEsDBBQAAAgAAAAAIQATAmpkXQAAAF0AAAAHAAAAb25lLnBuZ4lQTkcNChoKAAAADUlIRFIAAAAgAAAAIAEDAAAASbTotwAAAAZQTFRF/////wAA61rnkwAAABJJREFUCNdjYCAW/P9PXYI4AAD3Jx/hgKZB+AAAAABJRU5ErkJgglBLAwQUAAAIAAAAACEAEwJqZF0AAABdAAAABwAAAHR3by5wbmeJUE5HDQoaCgAAAA1JSERSAAAAIAAAACABAwAAAEm06LcAAAAGUExURf////8AAOta55MAAAASSURBVAjXY2AgFvz/T12COAAA9ycf4YCmQfgAAAAASUVORK5CYIJQSwECFAAUAAAIAAAAACEAEwJqZF0AAABdAAAABwAAAAAAAAAAAAAAAAAAAAAAb25lLnBuZ1BLAQIUABQAAAgAAAAAIQATAmpkXQAAAF0AAAAHAAAAAAAAAAAAAAAAAIIAAAB0d28ucG5nUEsFBgAAAAACAAIAagAAAAQBAAAAAA=="
                    },
                    "width": 8,
                    "height": 8,
                    "scale": 2,
                    "palette": "#172c2f,#416350,#93ab69,#e3e6b1",
                    "dither": "bayer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FliplineRetroBatchResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "FliplineRetroBatchInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "images": {
            "title": "ZIP of PNG, JPEG or WebP images",
            "type": "object",
            "description": "Upload as {filename, contentType?, contentBase64}. Max 15625 KiB. Accepts .zip.",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          },
          "width": {
            "title": "Pixel canvas width",
            "type": "integer",
            "minimum": 1,
            "maximum": 128,
            "default": 32
          },
          "height": {
            "title": "Pixel canvas height",
            "type": "integer",
            "minimum": 1,
            "maximum": 128,
            "default": 32
          },
          "colors": {
            "title": "Automatic palette colors",
            "type": "integer",
            "minimum": 1,
            "maximum": 32,
            "default": 16
          },
          "palette": {
            "title": "Custom HEX, GPL or JASC-PAL palette (optional)",
            "type": "string",
            "maxLength": 16384,
            "default": ""
          },
          "alpha": {
            "title": "Alpha threshold",
            "type": "integer",
            "minimum": 1,
            "maximum": 255,
            "default": 128
          },
          "scale": {
            "title": "PNG integer scale",
            "type": "integer",
            "minimum": 1,
            "maximum": 8,
            "default": 8
          },
          "fit": {
            "title": "Image fit",
            "type": "string",
            "enum": [
              "stretch",
              "contain",
              "cover"
            ],
            "default": "stretch"
          },
          "dither": {
            "title": "Dithering",
            "type": "string",
            "enum": [
              "none",
              "bayer",
              "floyd"
            ],
            "default": "none"
          },
          "strength": {
            "title": "Dither strength (%)",
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "default": 75
          },
          "brightness": {
            "title": "Brightness",
            "type": "number",
            "minimum": -100,
            "maximum": 100,
            "default": 0
          },
          "contrast": {
            "title": "Contrast",
            "type": "number",
            "minimum": -100,
            "maximum": 100,
            "default": 0
          },
          "gamma": {
            "title": "Gamma",
            "type": "number",
            "minimum": 0.2,
            "maximum": 3,
            "default": 1
          },
          "saturation": {
            "title": "Saturation (%)",
            "type": "number",
            "minimum": 0,
            "maximum": 200,
            "default": 100
          },
          "red": {
            "title": "Red shift (RGB units)",
            "type": "number",
            "minimum": -64,
            "maximum": 64,
            "default": 0
          },
          "green": {
            "title": "Green shift (RGB units)",
            "type": "number",
            "minimum": -64,
            "maximum": 64,
            "default": 0
          },
          "blue": {
            "title": "Blue shift (RGB units)",
            "type": "number",
            "minimum": -64,
            "maximum": 64,
            "default": 0
          }
        },
        "required": [
          "images"
        ]
      },
      "FliplineRetroBatchResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "artifact",
              "files",
              "recipe"
            ],
            "properties": {
              "artifact": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "artifactId",
                  "filename",
                  "mime",
                  "bytes",
                  "sha256",
                  "path"
                ],
                "properties": {
                  "artifactId": {
                    "type": "string"
                  },
                  "filename": {
                    "type": "string"
                  },
                  "mime": {
                    "type": "string"
                  },
                  "bytes": {
                    "type": "integer"
                  },
                  "sha256": {
                    "type": "string"
                  },
                  "path": {
                    "type": "string",
                    "description": "Download path on the same site (valid for about 2 hours)."
                  }
                }
              },
              "files": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "name",
                    "source",
                    "width",
                    "height"
                  ],
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "source": {
                      "type": "string"
                    },
                    "width": {
                      "type": "integer"
                    },
                    "height": {
                      "type": "integer"
                    }
                  }
                }
              },
              "recipe": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "format",
                  "version",
                  "settings"
                ],
                "properties": {
                  "format": {
                    "const": "flipline-retro"
                  },
                  "version": {
                    "const": 1
                  },
                  "settings": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "width",
                      "height",
                      "scale",
                      "fit",
                      "colors",
                      "palette",
                      "dither",
                      "strength",
                      "brightness",
                      "contrast",
                      "gamma",
                      "saturation",
                      "red",
                      "green",
                      "blue",
                      "alpha"
                    ],
                    "properties": {
                      "width": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 128
                      },
                      "height": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 128
                      },
                      "scale": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 8
                      },
                      "fit": {
                        "enum": [
                          "contain",
                          "cover",
                          "stretch"
                        ]
                      },
                      "colors": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 32
                      },
                      "palette": {
                        "type": "array",
                        "maxItems": 32,
                        "items": {
                          "type": "string",
                          "pattern": "^#[a-fA-F0-9]{6}$"
                        }
                      },
                      "dither": {
                        "enum": [
                          "none",
                          "bayer",
                          "floyd"
                        ]
                      },
                      "strength": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 100
                      },
                      "brightness": {
                        "type": "number",
                        "minimum": -100,
                        "maximum": 100
                      },
                      "contrast": {
                        "type": "number",
                        "minimum": -100,
                        "maximum": 100
                      },
                      "gamma": {
                        "type": "number",
                        "minimum": 0.2,
                        "maximum": 3
                      },
                      "saturation": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 200
                      },
                      "red": {
                        "type": "number",
                        "minimum": -64,
                        "maximum": 64
                      },
                      "green": {
                        "type": "number",
                        "minimum": -64,
                        "maximum": 64
                      },
                      "blue": {
                        "type": "number",
                        "minimum": -64,
                        "maximum": 64
                      },
                      "alpha": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 255
                      }
                    }
                  }
                },
                "description": "A settings-only retro conversion recipe. No images are embedded. Empty palette selects frequent adjusted image colors; batch exports resolve it from the first image and share that palette. Dithering runs in native output pixel coordinates, independent of display zoom and export scale."
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}