{"openapi":"3.1.0","info":{"title":"Heron Tools API","version":"1.1.0","description":"Programmatic access to selected heron.tools utilities, including stateless image and PDF processing."},"servers":[{"url":"https://api.heron.tools"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"Pass the Heron API key as a Bearer token."}},"schemas":{"HealthResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]},"service":{"type":"string","enum":["heron-tools-api"]},"version":{"type":"string","enum":["v1"]}},"required":["status","service","version"]},"meta":{"$ref":"#/components/schemas/RequestMeta"}},"required":["data","meta"]},"RequestMeta":{"type":"object","properties":{"requestId":{"type":"string","example":"5a8d83c3-4d9e-43a7-8d94-3f9356881269"}},"required":["requestId"]},"SlugResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"slug":{"type":"string"},"inputLength":{"type":"integer","minimum":0},"slugLength":{"type":"integer","minimum":0},"wordCount":{"type":"integer","minimum":0}},"required":["slug","inputLength","slugLength","wordCount"]},"meta":{"$ref":"#/components/schemas/RequestMeta"}},"required":["data","meta"]},"Problem":{"type":"object","properties":{"type":{"type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"code":{"type":"string"},"detail":{"type":"string"},"requestId":{"type":"string"},"issues":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["type","title","status","code","detail","requestId"]},"SlugRequest":{"type":"object","properties":{"text":{"type":"string","maxLength":10000},"delimiter":{"type":"string","maxLength":8},"lowercase":{"type":"boolean"},"dedupeDelimiter":{"type":"boolean"}},"required":["text"]},"UnitConversionResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"input":{"type":"number"},"output":{"type":"number"},"formattedOutput":{"type":"string"},"summary":{"type":"string"},"from":{"type":"string","enum":["meter","kilometer","centimeter","millimeter","inch","foot","yard","mile","gram","kilogram","milligram","ounce","pound","stone","liter","milliliter","cubic-meter","teaspoon","tablespoon","fluid-ounce","cup","pint","quart","gallon","celsius","fahrenheit","kelvin"],"example":"meter"},"to":{"type":"string","enum":["meter","kilometer","centimeter","millimeter","inch","foot","yard","mile","gram","kilogram","milligram","ounce","pound","stone","liter","milliliter","cubic-meter","teaspoon","tablespoon","fluid-ounce","cup","pint","quart","gallon","celsius","fahrenheit","kelvin"],"example":"meter"}},"required":["input","output","formattedOutput","summary","from","to"]},"meta":{"$ref":"#/components/schemas/RequestMeta"}},"required":["data","meta"]},"UnitConversionRequest":{"type":"object","properties":{"value":{"type":"number"},"from":{"type":"string","enum":["meter","kilometer","centimeter","millimeter","inch","foot","yard","mile","gram","kilogram","milligram","ounce","pound","stone","liter","milliliter","cubic-meter","teaspoon","tablespoon","fluid-ounce","cup","pint","quart","gallon","celsius","fahrenheit","kelvin"],"example":"meter"},"to":{"type":"string","enum":["meter","kilometer","centimeter","millimeter","inch","foot","yard","mile","gram","kilogram","milligram","ounce","pound","stone","liter","milliliter","cubic-meter","teaspoon","tablespoon","fluid-ounce","cup","pint","quart","gallon","celsius","fahrenheit","kelvin"],"example":"meter"}},"required":["value","from","to"]},"ExchangeRateResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"date":{"type":"string"},"base":{"type":"string"},"quote":{"type":"string"},"rate":{"type":"number","exclusiveMinimum":0}},"required":["date","base","quote","rate"]},"meta":{"$ref":"#/components/schemas/RequestMeta"}},"required":["data","meta"]},"ImageInfoResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"format":{"type":"string"},"fileSize":{"type":"integer","minimum":0},"width":{"type":"integer","exclusiveMinimum":0},"height":{"type":"integer","exclusiveMinimum":0}},"required":["format"]},"meta":{"$ref":"#/components/schemas/RequestMeta"}},"required":["data","meta"]},"PdfInfoResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"fileName":{"type":"string"},"fileSize":{"type":"integer","minimum":0},"pageCount":{"type":"integer","exclusiveMinimum":0},"title":{"type":"string"},"author":{"type":"string"},"subject":{"type":"string"},"keywords":{"type":"string"},"creator":{"type":"string"},"producer":{"type":"string"},"creationDate":{"type":"string"},"modificationDate":{"type":"string"},"encrypted":{"type":"boolean"}},"required":["fileName","fileSize","pageCount","encrypted"]},"meta":{"$ref":"#/components/schemas/RequestMeta"}},"required":["data","meta"]}},"parameters":{}},"paths":{"/v1/health":{"get":{"tags":["System"],"summary":"Check API health","responses":{"200":{"description":"The API is available.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/v1/text/slug":{"post":{"tags":["Text"],"summary":"Generate a URL-friendly slug","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlugRequest"}}}},"responses":{"200":{"description":"The generated slug and summary statistics.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlugResponse"}}}},"401":{"description":"The API key is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"413":{"description":"The request body is too large.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request body is invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/conversions/unit":{"post":{"tags":["Conversions"],"summary":"Convert a value between compatible units","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnitConversionRequest"}}}},"responses":{"200":{"description":"The converted value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnitConversionResponse"}}}},"401":{"description":"The API key is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"413":{"description":"The request body is too large.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The units are unsupported or incompatible.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/exchange-rates/latest":{"get":{"tags":["Exchange rates"],"summary":"Get the latest exchange rate for a currency pair","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","pattern":"^[A-Za-z]{3}$","example":"USD"},"required":true,"name":"base","in":"query"},{"schema":{"type":"string","pattern":"^[A-Za-z]{3}$","example":"USD"},"required":true,"name":"quote","in":"query"}],"responses":{"200":{"description":"The latest rate reported by Frankfurter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExchangeRateResponse"}}}},"401":{"description":"The API key is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The currency pair is invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"The upstream exchange-rate service failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/images/info":{"post":{"tags":["Images"],"summary":"Read image dimensions and format","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","maxLength":255,"example":"document.pdf"},"required":false,"name":"x-filename","in":"header"}],"requestBody":{"required":true,"content":{"image/avif":{"schema":{"type":"string","format":"binary"}},"image/gif":{"schema":{"type":"string","format":"binary"}},"image/heic":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}},"image/png":{"schema":{"type":"string","format":"binary"}},"image/svg+xml":{"schema":{"type":"string","format":"binary"}},"image/webp":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"Detected image information.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageInfoResponse"}}}},"400":{"description":"The file body could not be read.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The API key is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"413":{"description":"The file request is too large.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"415":{"description":"The file type is unsupported.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The file or operation options are invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/images/transform":{"post":{"tags":["Images"],"summary":"Resize, crop, rotate, compress, or convert an image","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":12000},"required":false,"name":"width","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":12000},"required":false,"name":"height","in":"query"},{"schema":{"type":"string","enum":["scale-down","contain","pad","squeeze","cover","crop"]},"required":false,"name":"fit","in":"query"},{"schema":{"type":"string","enum":["auto","center","entropy","face","left","right","top","bottom"]},"required":false,"name":"gravity","in":"query"},{"schema":{"type":"string","pattern":"^#[\\da-fA-F]{3,8}$"},"required":false,"name":"background","in":"query"},{"schema":{"type":["number","null"]},"required":false,"name":"rotate","in":"query"},{"schema":{"type":"string","enum":["h","v","hv"]},"required":false,"name":"flip","in":"query"},{"schema":{"type":"string","enum":["avif","jpeg","png","webp"],"default":"webp"},"required":false,"name":"format","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":85},"required":false,"name":"quality","in":"query"},{"schema":{"type":"string","maxLength":255,"example":"document.pdf"},"required":false,"name":"x-filename","in":"header"}],"requestBody":{"required":true,"content":{"image/avif":{"schema":{"type":"string","format":"binary"}},"image/gif":{"schema":{"type":"string","format":"binary"}},"image/heic":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}},"image/png":{"schema":{"type":"string","format":"binary"}},"image/svg+xml":{"schema":{"type":"string","format":"binary"}},"image/webp":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"The transformed image bytes.","content":{"image/avif":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}},"image/png":{"schema":{"type":"string","format":"binary"}},"image/webp":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"The file body could not be read.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The API key is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"413":{"description":"The file request is too large.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"415":{"description":"The file type is unsupported.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The file or operation options are invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/pdfs/info":{"post":{"tags":["PDF"],"summary":"Read PDF page count and document metadata","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","maxLength":255,"example":"document.pdf"},"required":false,"name":"x-filename","in":"header"}],"requestBody":{"required":true,"content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"PDF information and metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PdfInfoResponse"}}}},"400":{"description":"The file body could not be read.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The API key is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"413":{"description":"The file request is too large.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"415":{"description":"The file type is unsupported.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The file or operation options are invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/pdfs/merge":{"post":{"tags":["PDF"],"summary":"Merge PDF files in upload order","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"files":{"anyOf":[{"type":"string","format":"binary"},{"type":"array","items":{"type":"string","format":"binary"}}]}},"required":["files"]}}}},"responses":{"200":{"description":"The merged PDF.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"The file body could not be read.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The API key is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"413":{"description":"The file request is too large.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"415":{"description":"The file type is unsupported.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The file or operation options are invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/pdfs/extract":{"post":{"tags":["PDF"],"summary":"Extract selected PDF pages","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":2000},"required":true,"name":"pages","in":"query"},{"schema":{"type":"string","maxLength":255,"example":"document.pdf"},"required":false,"name":"x-filename","in":"header"}],"requestBody":{"required":true,"content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"A PDF containing the selected pages.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"The file body could not be read.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The API key is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"413":{"description":"The file request is too large.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"415":{"description":"The file type is unsupported.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The file or operation options are invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/pdfs/delete":{"post":{"tags":["PDF"],"summary":"Delete selected PDF pages","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":2000},"required":true,"name":"pages","in":"query"},{"schema":{"type":"string","maxLength":255,"example":"document.pdf"},"required":false,"name":"x-filename","in":"header"}],"requestBody":{"required":true,"content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"A PDF without the selected pages.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"The file body could not be read.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The API key is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"413":{"description":"The file request is too large.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"415":{"description":"The file type is unsupported.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The file or operation options are invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/pdfs/rotate":{"post":{"tags":["PDF"],"summary":"Rotate selected PDF pages","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","maxLength":2000},"required":false,"name":"pages","in":"query"},{"schema":{"type":"number"},"required":true,"name":"angle","in":"query"},{"schema":{"type":"string","maxLength":255,"example":"document.pdf"},"required":false,"name":"x-filename","in":"header"}],"requestBody":{"required":true,"content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"The rotated PDF.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"The file body could not be read.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The API key is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"413":{"description":"The file request is too large.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"415":{"description":"The file type is unsupported.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The file or operation options are invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/pdfs/split":{"post":{"tags":["PDF"],"summary":"Split PDF ranges into a ZIP archive","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":2000},"required":true,"name":"ranges","in":"query"},{"schema":{"type":"string","maxLength":255,"example":"document.pdf"},"required":false,"name":"x-filename","in":"header"}],"requestBody":{"required":true,"content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"A ZIP archive containing one PDF per range.","content":{"application/zip":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"The file body could not be read.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The API key is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"413":{"description":"The file request is too large.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"415":{"description":"The file type is unsupported.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The file or operation options are invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/pdfs/compress":{"post":{"tags":["PDF"],"summary":"Losslessly repack a PDF","description":"Rewrites the PDF with object streams. This preserves page content and does not rasterize pages.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string","enum":["true","false"],"default":"false"},"required":false,"name":"clearMetadata","in":"query"},{"schema":{"type":"string","maxLength":255,"example":"document.pdf"},"required":false,"name":"x-filename","in":"header"}],"requestBody":{"required":true,"content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"The repacked PDF.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"The file body could not be read.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The API key is missing or invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"413":{"description":"The file request is too large.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"415":{"description":"The file type is unsupported.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The file or operation options are invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}},"webhooks":{}}