{
  "openapi": "3.1.1",
  "info": {
    "title": "Batch Watermark Studio API",
    "version": "1.0.0",
    "summary": "Add a text or logo watermark to up to 30 images at once: position, tiling, opacity and angle.",
    "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://batchwatermarkstudio.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Add a text or logo watermark to up to 30 images at once: position, tiling, opacity and angle.",
        "description": "Stamps a text watermark (choice of installed fonts, size, colour, opacity, angle) or your logo (PNG with transparency, scale, opacity) onto one image or a ZIP of up to 30 images, in a corner, the centre or tiled across the whole picture. Download all results as one ZIP and check the preview of the first image.\n\nMethodology:\n- Text is rendered by ImageMagick (label:) with a TrueType font installed on the server, in the chosen colour with the opacity applied to the colour's alpha channel, then rotated. The text is escaped so it is always drawn literally.\n- Logos are decoded from their content (PNG, WebP, JPG, GIF, AVIF), scaled to the given share of each image's width, their alpha channel multiplied by the opacity, then rotated.\n- Each image is first turned upright from its EXIF orientation. The watermark is composited (\"over\") at the chosen position with a margin relative to the shorter side, or repeated as a tile pattern with gaps of 6% of the shorter side around each copy.\n- Sizes are relative to each image, so mixed resolutions get proportionally equal watermarks. Metadata is removed from the outputs; the ZIP uses fixed timestamps.\n\nVisible watermarks deter casual copying but can be cropped or retouched away.",
        "tags": [
          "media"
        ],
        "x-mcp-tool": "batch_watermark_studio",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchWatermarkStudioInput"
              },
              "examples": {
                "example1": {
                  "summary": "Text watermark bottom right",
                  "value": {
                    "images": {
                      "filename": "photo.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": "text",
                    "text": "© Example",
                    "size": 12,
                    "color": "#ffffff",
                    "opacity": 70,
                    "position": "southeast",
                    "format": "png"
                  }
                },
                "example2": {
                  "summary": "Logo tiled at 30° with 40 % opacity",
                  "value": {
                    "images": {
                      "filename": "photo.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": "logo",
                    "logo": {
                      "filename": "logo.png",
                      "contentType": "image/png",
                      "contentBase64": "iVBORw0KGgoAAAANSUhEUgAAABgAAAAQAQMAAAA26i3WAAAABlBMVEX4+PgWUvBKqxVnAAAAEklEQVQI12NgQAP8/z/gxWgAAPdHD/H1HpIcAAAAAElFTkSuQmCC"
                    },
                    "size": 20,
                    "opacity": 40,
                    "angle": 30,
                    "position": "tile",
                    "format": "jpeg",
                    "quality": 85
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchWatermarkStudioResult"
                }
              }
            }
          },
          "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"
                }
              }
            }
          }
        }
      },
      "BatchWatermarkStudioInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "images": {
            "title": "Image or ZIP of images",
            "description": "One image or a ZIP with up to 30 images (JPG, PNG, WebP, AVIF, GIF, TIFF, HEIC), max 50 MB each and 150 MB in total. 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": "Watermark type",
            "type": "string",
            "enum": [
              "text",
              "logo"
            ],
            "default": "text"
          },
          "text": {
            "title": "Watermark text",
            "description": "For text watermarks. Line breaks are replaced by spaces.",
            "type": "string",
            "maxLength": 200,
            "default": "© Your Name"
          },
          "font": {
            "title": "Font",
            "type": "string",
            "enum": [
              "dejavu-sans",
              "dejavu-sans-bold",
              "dejavu-serif",
              "dejavu-sans-mono",
              "liberation-sans",
              "liberation-sans-bold",
              "liberation-serif",
              "liberation-serif-italic",
              "liberation-mono"
            ],
            "default": "dejavu-sans"
          },
          "size": {
            "title": "Size",
            "description": "Text: letter height as % of the shorter image side. Logo: logo width as % of the image width.",
            "type": "number",
            "minimum": 1,
            "maximum": 80,
            "default": 6,
            "x-unit": "%"
          },
          "color": {
            "title": "Text colour",
            "description": "Hex colour, e.g. #ffffff (white) or #000000 (black).",
            "type": "string",
            "maxLength": 7,
            "default": "#ffffff"
          },
          "opacity": {
            "title": "Opacity",
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 50,
            "x-unit": "%"
          },
          "angle": {
            "title": "Angle",
            "description": "Rotation of the watermark; e.g. -30 for a diagonal tile pattern.",
            "type": "integer",
            "minimum": -90,
            "maximum": 90,
            "default": 0,
            "x-unit": "°"
          },
          "position": {
            "title": "Position",
            "type": "string",
            "enum": [
              "center",
              "north",
              "south",
              "west",
              "east",
              "northwest",
              "northeast",
              "southwest",
              "southeast",
              "tile"
            ],
            "default": "southeast"
          },
          "margin": {
            "title": "Margin",
            "description": "Distance from the edge as % of the shorter side (ignored for centre and tiling).",
            "type": "number",
            "minimum": 0,
            "maximum": 25,
            "default": 3,
            "x-unit": "%"
          },
          "logo": {
            "title": "Logo (for logo watermarks)",
            "description": "PNG or WebP with a transparent background works best, max 10 MB. Upload as {filename, contentType?, contentBase64}. Max 10240 KiB. Accepts .png,.webp,.jpg,.jpeg,.gif,.avif,image/png,image/webp,image/jpeg,image/gif,image/avif.",
            "type": "object",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          },
          "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": 88
          }
        },
        "required": [
          "images"
        ]
      },
      "BatchWatermarkStudioResult": {
        "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",
              "position",
              "opacity",
              "angle",
              "files",
              "artifact",
              "preview"
            ],
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "text",
                  "logo"
                ]
              },
              "images": {
                "type": "integer"
              },
              "processed": {
                "type": "integer"
              },
              "failed": {
                "type": "integer"
              },
              "skipped": {
                "type": "integer"
              },
              "position": {
                "type": "string"
              },
              "opacity": {
                "type": "integer"
              },
              "angle": {
                "type": "integer"
              },
              "font": {
                "type": "string"
              },
              "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)."
                  }
                }
              },
              "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)."
                  }
                }
              }
            }
          },
          "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"
              }
            }
          }
        }
      }
    }
  }
}