{
  "openapi": "3.1.1",
  "info": {
    "title": "Batch Image Resizer & Cropper API",
    "version": "1.0.0",
    "summary": "Resize or crop up to 30 images at once: exact size, fit, percentage or 1:1, 4:5, 16:9, 9:16.",
    "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://batchimageresizer.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Resize or crop up to 30 images at once: exact size, fit, percentage or 1:1, 4:5, 16:9, 9:16.",
        "description": "Upload one image or a ZIP with up to 30 images and resize them all the same way: to an exact size (cropped to fill, with a chosen focus point), to fit within a box, by percentage, or cropped to a social-media aspect ratio. Choose the output format and quality and download everything as one ZIP with a per-file report.\n\nMethodology:\n- Each file in the upload (or ZIP) is identified by its content; files that are not supported images are listed as skipped. The ZIP is read in memory with limits (30 images, 50 MB each, no encrypted or ZIP64 archives).\n- Every image is first turned upright using its EXIF orientation. Fit: scaled so it fits inside the box (never enlarged unless allowed). Exact size: scaled to cover the box, then cropped around the chosen focus. Percentage: scaled by the factor. Aspect ratio: the largest centred (or focused) crop with that ratio, then optionally fitted into the box.\n- Scaling uses ImageMagick's default high-quality filter (Lanczos when shrinking photos, Mitchell when enlarging). WebP is encoded with cwebp and AVIF with avifenc when installed.\n- Metadata is removed unless you keep it; when kept, exiftool copies it back and resets the orientation tag. The ZIP is built with fixed timestamps so the same input gives the same file.\n\nCropping removes parts of the picture; check the preview before publishing.",
        "tags": [
          "media"
        ],
        "x-mcp-tool": "batch_image_resizer",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchImageResizerInput"
              },
              "examples": {
                "example1": {
                  "summary": "Two images in a ZIP → 1:1 squares, WebP",
                  "value": {
                    "images": {
                      "filename": "photos.zip",
                      "contentType": "application/zip",
                      "contentBase64": "UEsDBBQAAAgAAAAAIQAvZbbGVAAAAFQAAAAHAAAAcmVkLnBuZ4lQTkcNChoKAAAADUlIRFIAAAAoAAAAHgEDAAAAZBPOngAAAANQTFRF0EBArFYGfwAAAAxJREFUCNdjYBh6AAAAtAABXnGQvwAAAABJRU5ErkJgglBLAwQUAAAIAAAAACEA+zRozFQAAABUAAAACQAAAGdyZWVuLnBuZ4lQTkcNChoKAAAADUlIRFIAAAAeAAAAKAEDAAAA093YUQAAAANQTFRFQKBADztNYwAAAAxJREFUCNdjYBgeAAAAyAABfGNpfQAAAABJRU5ErkJgglBLAQIUAxQAAAgAAAAAIQAvZbbGVAAAAFQAAAAHAAAAAAAAAAAAAACkgQAAAAByZWQucG5nUEsBAhQDFAAACAAAAAAhAPs0aMxUAAAAVAAAAAkAAAAAAAAAAAAAAKSBeQAAAGdyZWVuLnBuZ1BLBQYAAAAAAgACAGwAAAD0AAAAAAA="
                    },
                    "mode": "aspect",
                    "aspect": "1:1",
                    "format": "webp",
                    "quality": 80
                  }
                },
                "example2": {
                  "summary": "Single image → exactly 32×32 px",
                  "value": {
                    "images": {
                      "filename": "banner.png",
                      "contentType": "image/png",
                      "contentBase64": "iVBORw0KGgoAAAANSUhEUgAAADAAAAAgEAIAAACLJrAIAAADy0lEQVRo3mNUUHBwaGhgGAU4AAuzDIsqi+xoQIwG0GgA0SqApFnUWGRGAwJvCmIdTUH4Aoh1QLIY1zXW7h8rTGfLLb+517lejefcfFNduZ83/EUec5t91Hhj9vUG/9EziY/C1d33Jt4uNtp1eu6jFxo3v2b/4uR4Rk93Mpo+jnWZ60Ufy9jVmFN+m8fWm8zcbptWbtG90YlUE2b8OT4z4PPSzefme/D9NP3jwOpJ8wCy+J+YuTCd1tZI3+NreGW64kn0zIqrLHZMXn/7KTHtj8K/eOYv4X1LpTrnPwv6pCJ6hYYBZCOd2re0hXYWSEryWr0+vep0FEvBRlqYH+a+jG2Cw/Orn/eIutCqkFajTRnEepz5x+/XKyZGHi66xsDA4EibCFimHDG/5KRX0KKuBUd+zf+7k3XukGkHRSrqlWz8yPSAsf7fbRrWMtOYov98j+TWP7F19xLPS6ohzNRvB6lSux3EqcJa/MMzaY1h/bK79Cn+E78aci533Wh9+5V/8zeT39ac4YO6mjc0kRK/xs3AwBBN36aDkaiU4VXV4zLPVC1kqZrFqN1QtC2RbTp2g+EoAwODCD0DyGaO3Jzjbad2v1K1zRnUZZBeldiMi28ZGBiy6JuC9LvEZlw4yezL4spiNahrMcG3HHnvbtA/gIS7OJa84WS+z3KcxW9Qp6APE3/GCWuLmHAwvKRrAH0I/akjHM68jOUNC9WyNgsLDQLo6qb3s0xT7BkkL2+hawBdKf9gYOrGIsOyleX1oK7Fzhx61+DSam8u6bXFmp4BdDrx7Xc3WWYZVlUWjkHdDrqZ/yXR4g8DA51zGMMNmc8PrBYws7JosKQP6jLoTwFDJh/DOv2nW/KXBqVLn5lI8/bQhpynF3N1fysyNfC1MDMwqQ6JvtieitePU7cHyUpfnJjJ4MUgwTCdFrb8Nfl/mjVyl8ebvLSlzFwsuSx/qdybT184f/p9CdrFrcgTtuePHrZUqfvZs9PC/JoVN/ccPPbm1y9+uSCaDHdk7V2857EprbOAyDe2uodzGyqVhey+MF5maPhHUUPuH99/A5aahgN3/x/a8vbD74OyrDQcD8q9ufzh8yAGugBWT0bRn+ZOtYJnph/1ThDm6txEqglbmd6eq/DYl/EhL+PS74J/N9ktaO1mxoKva9he5zPQHbDvZ+z5+lTFlMP7SKb+FO41W0JVT3LkHS7hO8Gi8TLwk9OfW+Lzb+f+mG9jfOngtwwfuTtHvm+yWfJj8/8QHhZ6upOxRHCD9vsehlGApx00Oi9G73bQaACNBtAogAEAmBNP2iSWACUAAAAASUVORK5CYII="
                    },
                    "mode": "cover",
                    "width": 32,
                    "height": 32,
                    "gravity": "center",
                    "format": "jpeg",
                    "quality": 85
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchImageResizerResult"
                }
              }
            }
          },
          "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"
                }
              }
            }
          }
        }
      },
      "BatchImageResizerInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "images": {
            "title": "Image or ZIP of images",
            "description": "One image (JPG, PNG, WebP, AVIF, GIF, TIFF, HEIC) or a ZIP with up to 30 images, max 50 MB per image and 150 MB in total. Your browser form sends one file, so put several images into a ZIP. Upload as {filename, contentType?, contentBase64}. Max 153600 KiB. Accepts .zip,application/zip,.jpg,.jpeg,.png,.webp,.avif,.gif,.tif,.tiff,.heic,.heif,image/jpeg,image/png,image/webp,image/avif,image/gif,image/tiff,image/heic,image/heif.",
            "type": "object",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          },
          "mode": {
            "title": "Resize mode",
            "type": "string",
            "enum": [
              "fit",
              "cover",
              "percent",
              "aspect"
            ],
            "default": "fit"
          },
          "width": {
            "title": "Width",
            "description": "Needed for \"Exact size\"; for \"Fit\" give width, height or both.",
            "type": "integer",
            "minimum": 1,
            "maximum": 10000,
            "x-unit": "px"
          },
          "height": {
            "title": "Height",
            "type": "integer",
            "minimum": 1,
            "maximum": 10000,
            "x-unit": "px"
          },
          "percent": {
            "title": "Percentage",
            "description": "Only for \"Scale by percentage\".",
            "type": "number",
            "minimum": 1,
            "maximum": 400,
            "default": 50,
            "x-unit": "%"
          },
          "aspect": {
            "title": "Aspect ratio",
            "description": "Only for \"Crop to aspect ratio\".",
            "type": "string",
            "enum": [
              "1:1",
              "4:5",
              "16:9",
              "9:16",
              "3:2",
              "4:3"
            ],
            "default": "1:1"
          },
          "gravity": {
            "title": "Crop focus",
            "description": "Which part of the image is kept when cropping.",
            "type": "string",
            "enum": [
              "center",
              "north",
              "south",
              "west",
              "east",
              "northwest",
              "northeast",
              "southwest",
              "southeast"
            ],
            "default": "center"
          },
          "upscale": {
            "title": "Allow enlarging small images",
            "type": "boolean",
            "default": false
          },
          "format": {
            "title": "Output format",
            "type": "string",
            "enum": [
              "same",
              "jpeg",
              "png",
              "webp",
              "avif"
            ],
            "default": "same"
          },
          "quality": {
            "title": "Quality",
            "description": "For JPG, WebP and AVIF.",
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 85
          },
          "keepMetadata": {
            "title": "Keep EXIF metadata (camera, date, GPS)",
            "type": "boolean",
            "default": false
          }
        },
        "required": [
          "images"
        ]
      },
      "BatchImageResizerResult": {
        "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": [
              "mode",
              "images",
              "processed",
              "failed",
              "skipped",
              "outputFormat",
              "totalOriginalBytes",
              "totalOutputBytes",
              "files",
              "artifact",
              "preview"
            ],
            "properties": {
              "mode": {
                "type": "string"
              },
              "images": {
                "type": "integer"
              },
              "processed": {
                "type": "integer"
              },
              "failed": {
                "type": "integer"
              },
              "skipped": {
                "type": "integer"
              },
              "outputFormat": {
                "type": "string"
              },
              "totalOriginalBytes": {
                "type": "integer"
              },
              "totalOutputBytes": {
                "type": "integer"
              },
              "files": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "name",
                    "output",
                    "status",
                    "inputFormat",
                    "originalWidth",
                    "originalHeight",
                    "width",
                    "height",
                    "originalBytes",
                    "outputBytes"
                  ],
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "output": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "ok",
                        "failed",
                        "skipped"
                      ]
                    },
                    "reason": {
                      "type": "string"
                    },
                    "inputFormat": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "originalWidth": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "originalHeight": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "width": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "height": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "originalBytes": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "outputBytes": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    }
                  }
                }
              },
              "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)."
                  }
                },
                "description": "ZIP of all results, or the single image when only one was uploaded."
              },
              "preview": {
                "type": [
                  "object",
                  "null"
                ],
                "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)."
                  }
                },
                "description": "First result as an image preview (only for ZIP outputs)."
              }
            }
          },
          "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"
              }
            }
          }
        }
      }
    }
  }
}