A release-readiness QA pass over the whole product. The commits split into defects a user would hit and gates that were reporting green while measuring nothing. ## Fixes that change behaviour Rate limiting was bypassable on every install: TRUST_PROXY defaulted to true, so request.ip came from a client-set header and a forged X-Forwarded-For got past the login limiter. The default is now a private-network trust list. A transient Postgres outage stranded in-flight jobs, leaving finished output on disk with no row pointing at it. A reconciler now resolves those rows and adopts the bytes rather than dropping the work. A Redis connection that moved to a new address wedged every read-blocked consumer, so completions stopped signalling while health still answered 200. Socket timeouts plus subscriber pings recover it. Installing more than one AI bundle left the shared venv multi-versioned and silently broke three tools. The installer now reconciles distributions to one version each. Converting an image to JXL at quality 1 through 4 returned a 500, because libjxl 0.7 rejects the distance those values compute. The quality is floored at what the encoder honours. A missing ffmpeg was also reported to the user as a corrupt upload; it now says the engine is unavailable. RAW uploads reached an unpatched LibRaw on arm64, so it is built from source at 0.22.2, and the release scan was split so it can fail on an unfixed critical instead of hiding it behind ignore-unfixed. ## Gates that could not fail Two mutation lanes ran zero mutants because Stryker crawled the gitignored docs build; coverage discarded its whole report on any failing test; the lint gate skipped root tests, scripts, and two workspaces; and several generated matrices counted a host missing ffmpeg as a passing tool. Each now measures what it claims. Full evidence and the outstanding release items are tracked locally and are not part of this branch.
46 KiB
description
| description |
|---|
| Complete REST API reference. Tool endpoints, batch processing, pipelines, file library, authentication, teams, and admin operations. |
REST API Reference
Interactive API docs with request/response examples are available at http://localhost:1349/api/docs.
Machine-readable specs:
/api/v1/openapi.yaml- OpenAPI 3.1 spec/llms.txt- LLM-friendly summary/llms-full.txt- Complete LLM-friendly docs
Authentication
All endpoints require authentication unless AUTH_ENABLED=false.
Session Token
# Login
curl -X POST http://localhost:1349/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin"}'
# Returns: {"token":"<session-token>"}
# Use token (tool routes are POST multipart)
curl -X POST http://localhost:1349/api/v1/tools/image/resize \
-H "Authorization: Bearer <session-token>" \
-F "file=@photo.jpg" \
-F 'settings={"width":800}'
Sessions expire after 7 days (configurable via SESSION_DURATION_HOURS).
API Keys
# Create a key (returns key once - store it)
curl -X POST http://localhost:1349/api/v1/api-keys \
-H "Authorization: Bearer <session-token>" \
-H "Content-Type: application/json" \
-d '{"name":"my-script"}'
# Returns: {"key":"si_<96 hex chars>","id":"...","name":"my-script"}
# Use the key
curl -X POST http://localhost:1349/api/v1/tools/image/resize \
-H "Authorization: Bearer si_<your-key>" \
-F "file=@photo.jpg" \
-F 'settings={"width":800}'
Keys are prefixed si_ and stored as scrypt hashes - the raw key is shown once and never retrievable again.
Auth Endpoints
| Method | Path | Access | Description |
|---|---|---|---|
POST |
/api/auth/login |
Public | Login, get session token |
POST |
/api/auth/logout |
Auth | Destroy current session |
GET |
/api/auth/session |
Auth | Validate current session |
POST |
/api/auth/change-password |
Auth | Change own password (invalidates all other sessions + API keys) |
GET |
/api/auth/users |
Admin | List all users |
POST |
/api/auth/register |
Admin (users:manage; proposed-role authority) |
Create a new user |
PUT |
/api/auth/users/:id |
Admin (users:manage; target authority) |
Update user role or team |
POST |
/api/auth/users/:id/reset-password |
Admin (users:manage; target authority) |
Reset user's password |
DELETE |
/api/auth/users/:id |
Admin (users:manage; target authority) |
Delete a user |
GET |
/api/v1/config/auth |
Public | Check if authentication is enabled ({ authEnabled: bool }) |
POST |
/api/auth/mfa/enroll |
Auth | Start TOTP MFA enrollment. Requires the enterprise mfa feature |
POST |
/api/auth/mfa/verify |
Auth | Confirm MFA enrollment with a TOTP code |
POST |
/api/auth/mfa/complete |
Public | Complete a pending MFA login challenge |
POST |
/api/auth/mfa/disable |
Auth | Disable MFA for the current user |
POST |
/api/auth/users/:id/mfa/reset |
Admin (users:manage; target authority) |
Reset MFA for a user |
GET |
/api/auth/oidc/login |
Public | Start OIDC login when OIDC is enabled |
GET |
/api/auth/oidc/callback |
Public | OIDC authorization callback |
GET |
/api/auth/saml/metadata |
Public | SAML SP metadata XML when SAML is enabled |
GET |
/api/auth/saml/login |
Public | Start SAML login |
POST |
/api/auth/saml/callback |
Public | SAML assertion consumer service |
When MFA is enabled for a user, POST /api/auth/login returns {"requiresMfa":true,"mfaToken":"...","mfaRequired":true|false} instead of a session token. Send that mfaToken plus a TOTP or recovery code to /api/auth/mfa/complete.
Permissions
| Permission | Admin | User |
|---|---|---|
| Use tools | ✓ | ✓ |
| Own files/pipelines/API keys | ✓ | ✓ |
| See all users' files/pipelines/keys | ✓ | - |
| Write settings | ✓ | - |
| Manage users & teams | ✓ | - |
| Manage branding | ✓ | - |
Health Check
| Method | Path | Access | Description |
|---|---|---|---|
GET |
/api/v1/health |
Public | Basic health check. Returns {"status":"healthy","version":"..."} with 200, or {"status":"unhealthy"} with 503 if the database is unreachable. |
GET |
/api/v1/readyz |
Public | Readiness probe. Checks PostgreSQL, Redis, disk space, and S3 when configured. Returns 503 when the instance should not receive traffic. |
GET |
/api/v1/admin/health |
Admin (system:health) |
Detailed diagnostics including uptime, storage mode, database status, queue state, and GPU availability. |
Using Tools
Every tool follows the same pattern:
# Single file
curl -X POST http://localhost:1349/api/v1/tools/<section>/<toolId> \
-H "Authorization: Bearer <token>" \
-F "file=@input.jpg" \
-F 'settings={"width":800,"height":600}'
# Batch (returns ZIP)
curl -X POST http://localhost:1349/api/v1/tools/<section>/<toolId>/batch \
-H "Authorization: Bearer <token>" \
-F "files=@a.jpg" \
-F "files=@b.jpg" \
-F 'settings={...}'
<section> is one of image, video, audio, pdf, or files.
- Upload is
multipart/form-data. settingsis a JSON string with tool-specific options.clientJobIdis an optional form field for caller-supplied progress correlation.fileIdis an optional form field referencing an existing file library item. When present, the processed output is saved as a new version and the response includessavedFileId.- Fast tools usually return 200 JSON:
{"jobId":"...","downloadUrl":"/api/v1/download/<jobId>/<filename>","originalSize":1234,"processedSize":567}. Fetch the processed file fromdownloadUrl. - Any queued tool can return 202 JSON if it is long-running or exceeds the synchronous wait window:
{"jobId":"...","async":true}. Connect to SSE for progress, then download when complete (see Progress Tracking). - Batch routes return a ZIP archive streamed directly (with
X-Job-Idheader) for tools registered in the generic batch registry.
Tools Reference
Conversion Presets
The shared catalog includes 83 dedicated conversion preset endpoints such as jpg-to-png, mov-to-mp4, m4a-to-mp3, pdf-to-jpg, and excel-to-csv. Presets are first-class tool routes:
POST /api/v1/tools/<section>/<presetId>
Each preset locks the output format and delegates to a base tool such as convert, convert-video, extract-audio, convert-audio, image-to-pdf, pdf-to-image, svg-to-raster, or convert-spreadsheet. See Conversion Presets for the complete route table and optional settings.
Essentials
| Tool ID | Name | Key settings |
|---|---|---|
resize |
Resize | width, height, fit (cover/contain/fill/inside/outside), percentage, withoutEnlargement, plus 23 social media presets |
crop |
Crop | left, top, width, height, unit (px/percent) |
rotate |
Rotate & Flip | angle, horizontal (bool), vertical (bool) |
convert |
Convert | format (jpg/png/webp/avif/tiff/gif/heic/heif), quality |
compress |
Compress | mode (quality/targetSize), quality (1–100), targetSizeKb |
Optimization
| Tool ID | Name | Key settings |
|---|---|---|
optimize-for-web |
Optimize for Web | format (webp/jpeg/avif/png), quality, maxWidth, maxHeight, progressive, stripMetadata |
strip-metadata |
Strip Metadata | - |
edit-metadata |
Edit Metadata | title, description, author, copyright, keywords, gps (lat/lon), dateTime |
bulk-rename |
Bulk Rename | pattern (supports {n}, {date}, {original}), startIndex, padding |
image-to-pdf |
Image to PDF | pageSize (A4/Letter/...), orientation, margin, targetSize ({value, unit}) |
favicon |
Favicon Generator | padding, backgroundColor, borderRadius - generates all standard sizes |
Adjustments
| Tool ID | Name | Key settings |
|---|---|---|
adjust-colors |
Adjust Colors | brightness, contrast, exposure, saturation, temperature, tint, hue, sharpness, red, green, blue, effect (none/grayscale/sepia/invert) |
sharpening |
Sharpening | method (adaptive/unsharp-mask/high-pass), sigma, m1, m2, x1, y2, y3, amount, radius, threshold, strength, kernelSize (3/5), denoise (off/light/medium/strong) |
replace-color |
Replace Color | sourceColor, targetColor (replacement), makeTransparent, tolerance |
color-blindness |
Color Blindness Simulation | simulationType (protanopia/deuteranopia/tritanopia/protanomaly/deuteranomaly/tritanomaly/achromatopsia/blueConeMonochromacy, default "deuteranomaly") |
duotone |
Duotone | shadow (hex), highlight (hex), intensity (0-100) |
pixelate |
Pixelate | blockSize (2-128), region ({left, top, width, height} for partial pixelation) |
vignette |
Vignette | strength (0.1-1), color (hex), radius, softness, roundness, centerX, centerY |
AI Tools
All AI tools run on your hardware: CPU by default, or NVIDIA CUDA when a supported NVIDIA GPU is available. Intel/AMD iGPU acceleration through VA-API, Quick Sync, or OpenCL is not supported for AI inference today. No internet required.
| Tool ID | Name | AI Model | Key settings |
|---|---|---|---|
remove-background |
Remove Background | rembg (BiRefNet / U2-Net) | model, backgroundType (transparent/color/gradient/blur/image), backgroundColor, gradientColor1, gradientColor2, gradientAngle, blurEnabled, blurIntensity, shadowEnabled, shadowOpacity |
upscale |
Image Upscaling | RealESRGAN | scale (2/4), model, faceEnhance, denoise, format, quality |
erase-object |
Object Eraser | LaMa (ONNX) | Mask sent as second file part (fieldname mask), format, quality |
ocr |
OCR / Text Extraction | Tesseract (fast); RapidOCR + PP-OCR ONNX (balanced/best) | quality (fast/balanced/best), language, enhance |
blur-faces |
Face / PII Blur | MediaPipe | blurRadius, sensitivity |
smart-crop |
Smart Crop | MediaPipe + Sharp | mode (subject/face/trim), strategy (attention/entropy), width, height, padding, facePreset (closeup/head-shoulders/upper-body/half-body), sensitivity, threshold, padToSquare, padColor, targetSize, quality |
image-enhancement |
Image Enhancement | Analysis-based | mode (auto/exposure/contrast/color/sharpness), strength |
enhance-faces |
Face Enhancement | GFPGAN / CodeFormer | model (gfpgan/codeformer), strength, sensitivity, centerFace |
colorize |
AI Colorization | DDColor | intensity, model |
noise-removal |
Noise Removal | Tiered denoising | tier (quick/balanced/quality/maximum), strength, detailPreservation, colorNoise, format, quality |
red-eye-removal |
Red Eye Removal | Face landmark + color analysis | sensitivity, strength |
restore-photo |
Photo Restoration | Multi-step pipeline | mode (auto/light/heavy), scratchRemoval, faceEnhancement, fidelity, denoise, denoiseStrength, colorize |
passport-photo |
Passport Photo | MediaPipe landmarks | Two-phase flow. Analyze uses multipart file; generate uses JSON with countryCode, bgColor, printLayout (none/4x6/a4), landmarks, image dimensions |
content-aware-resize |
Content-Aware Resize | Seam carving (caire) | width, height, protectFaces, blurRadius, sobelThreshold, square |
transparency-fixer |
PNG Transparency Fixer | BiRefNet HR-matting | defringe (0-100), outputFormat (png/webp) |
background-replace |
Background Replace | rembg (BiRefNet) | backgroundType (color/gradient), color (hex), gradientColor1, gradientColor2, gradientAngle, feather (0-20), format (png/webp) |
blur-background |
Blur Background | rembg (BiRefNet) | intensity (1-100), feather (0-20), format (png/webp) |
ai-canvas-expand |
AI Canvas Expand | LaMa (outpainting) | extendTop, extendRight, extendBottom, extendLeft (px), tier (fast/balanced/high), format, quality |
Watermark & Overlay
| Tool ID | Name | Key settings |
|---|---|---|
watermark-text |
Text Watermark | text, font, fontSize, color, opacity, position, rotation, tile |
watermark-image |
Image Watermark | opacity, position, scale - second file is the watermark |
text-overlay |
Text Overlay | text, font, fontSize, color, x, y, background, padding, borderRadius |
compose |
Image Composition | x, y, opacity, blend - second file is layered on top |
meme-generator |
Meme Generator | templateId, textLayout (top-bottom/top-only/bottom-only/center/side-by-side), textBoxes ([{id, text}]), fontFamily (anton/arial-black/comic-sans/montserrat/bebas-neue/permanent-marker/roboto), fontSize, textColor, strokeColor, textAlign, allCaps. Supports template mode (JSON body with templateId) or custom image mode (multipart with file). |
Utilities
| Tool ID | Name | Key settings |
|---|---|---|
info |
Image Info | - (returns width, height, format, size, channels, hasAlpha, DPI, EXIF) |
compare |
Image Compare | mode (side-by-side/overlay/diff), diffThreshold - second file is the comparison target |
find-duplicates |
Find Duplicates | threshold (perceptual hash distance, default 8) - multi-file |
color-palette |
Color Palette | count (dominant color count), format (hex/rgb) |
qr-generate |
QR Code Generator | data, size, margin, colorDark, colorLight, errorCorrectionLevel, dotStyle, cornerStyle, logo (optional file) |
barcode-read |
Barcode Reader | - (auto-detects QR, EAN, Code128, DataMatrix, etc.) |
image-to-base64 |
Image to Base64 | format (data-uri/plain), mimeType |
html-to-image |
HTML to Image | url, format (png/jpg/webp), quality, fullPage, devicePreset (desktop/tablet/mobile/custom), viewportWidth, viewportHeight |
histogram |
Histogram | scale (linear/log) - returns RGB histogram chart + per-channel stats |
lqip-placeholder |
LQIP Placeholder | width (4-64), blur, strategy (blur/pixelate/solid), format (webp/png/jpeg), quality |
barcode-generate |
Barcode Generator | text, type (code128/ean13/upca/code39/itf14/datamatrix), scale (1-8), includeText (bool). JSON body, no file upload. |
Layout & Composition
| Tool ID | Name | Key settings |
|---|---|---|
collage |
Collage / Grid | template (25+ layouts), gap, backgroundColor, borderRadius - multi-file |
stitch |
Stitch / Combine | direction (horizontal/vertical/grid), gap, backgroundColor, alignment - multi-file |
split |
Image Splitting | mode (grid/rows/cols), rows, cols, tileWidth, tileHeight |
border |
Border & Frame | width, color, style (solid/gradient/pattern), borderRadius, padding, shadow |
beautify |
Beautify Screenshot | backgroundType (solid/linear-gradient/radial-gradient/image/transparent), gradientStops, padding, borderRadius, shadowPreset, frame (none/macos-light/macos-dark/windows-light/windows-dark/browser-light/browser-dark/iphone/macbook/ipad/...), socialPreset (none/twitter/linkedin/instagram-square/instagram-story/facebook/producthunt), watermarkText, outputFormat |
circle-crop |
Circle Crop | zoom (1-5), offsetX, offsetY, borderWidth, borderColor, background (transparent/hex), outputSize |
image-pad |
Image Pad | target (16:9/9:16/1:1/4:3/3:4/custom), ratioW, ratioH, background (color/transparent/blur), color (hex), padding (0-50%) |
sprite-sheet |
Sprite Sheet | columns (1-16), padding, background (hex), format (png/webp/jpeg), quality - multi-file (2-64 images) |
Format & Conversion
| Tool ID | Name | Key settings |
|---|---|---|
svg-to-raster |
SVG to Raster | format (png/jpeg/webp/avif/tiff/gif/heif), width, height, scale, dpi, background |
vectorize |
Image to SVG | colorMode (bw/color), threshold, colorPrecision, filterSpeckle, pathMode (none/polygon/spline) |
gif-tools |
GIF Tools | action (resize/optimize/reverse/speed/extract-frames/rotate/add-text), action-specific params |
gif-webp |
GIF/WebP Converter | quality (1-100), lossless (bool), resizePercent (10-100) |
Video Tools
| Tool ID | Name | Key settings |
|---|---|---|
convert-video |
Convert Video | format (mp4/mov/webm/avi/mkv), quality (high/balanced/small) |
compress-video |
Compress Video | quality (light/balanced/strong), resolution (original/1080p/720p/480p) |
trim-video |
Trim Video | startS, endS, precise (bool, frame-accurate cut) |
mute-video |
Mute Video | - |
video-to-gif |
Video to GIF | fps (1-30), width, startS, durationS (max 60s) |
resize-video |
Resize Video | width, height, preset (custom/2160p/1440p/1080p/720p/480p/360p) |
crop-video |
Crop Video | width, height, x, y |
rotate-video |
Rotate Video | transform (cw90/ccw90/180/hflip/vflip) |
change-fps |
Change FPS | fps (1-120) |
video-color |
Video Color | brightness, contrast, saturation, gamma |
video-speed |
Video Speed | factor (0.25-4), keepPitch (bool) |
reverse-video |
Reverse Video | - (max 5 minutes) |
video-loudnorm |
Normalize Audio | - (EBU R128) |
aspect-pad |
Aspect Pad | target (16:9/9:16/1:1/4:3/3:4), color (hex) |
blur-pad |
Blur Pad | target (16:9/9:16/1:1/4:3/3:4), blur (2-50) |
watermark-video |
Watermark Video | text, position, fontSize, opacity, color |
stabilize-video |
Stabilize Video | smoothing (5-60, in frames) |
gif-to-video |
GIF to Video | format (mp4/webm/mov) |
video-to-webp |
Video to WebP | fps, width, quality, loop (bool) |
video-to-frames |
Video to Frames | mode (all/nth/timestamps), n, timestamps, format (png/jpg) |
merge-videos |
Merge Videos | - (multi-file, normalized to first video's resolution) |
replace-audio |
Replace Audio | - (video + audio file, two files) |
burn-subtitles |
Burn Subtitles | fontSize (8-72) - video + subtitle file |
embed-subtitles |
Embed Subtitles | language (ISO 639-2/B code) - video + subtitle file |
extract-subtitles |
Extract Subtitles | - (outputs SRT) |
images-to-video |
Images to Video | secondsPerImage (0.5-10), resolution (1080p/720p/square), fps - multi-file |
video-metadata |
Clean Video Metadata | - |
auto-subtitles |
Auto Subtitles (AI) | language (auto/en/de/fr/es/zh/ja/ko/id/th/vi), format (srt/vtt) |
extract-audio |
Extract Audio | format (mp3/wav/m4a/ogg) |
Audio Tools
| Tool ID | Name | Key settings |
|---|---|---|
convert-audio |
Convert Audio | format (mp3/wav/ogg/flac/m4a), bitrateKbps (32-320) |
trim-audio |
Trim Audio | startS, endS |
volume-adjust |
Volume Adjust | gainDb (-30 to 30) |
normalize-audio |
Normalize Audio | - (EBU R128, -16 LUFS) |
fade-audio |
Fade Audio | fadeInS (0-30), fadeOutS (0-30) |
reverse-audio |
Reverse Audio | - |
audio-speed |
Audio Speed | factor (0.25-4) |
pitch-shift |
Pitch Shift | semitones (-12 to 12) |
audio-channels |
Audio Channels | mode (stereo-to-mono/mono-to-stereo/swap) |
silence-removal |
Silence Removal | thresholdDb (-80 to -20), minSilenceS (0.1-5) |
noise-reduction |
Noise Reduction | strength (light/medium/strong) |
merge-audio |
Merge Audio | format (mp3/wav/flac/m4a) - multi-file |
split-audio |
Split Audio | mode (time/parts/silence), segmentS, parts, thresholdDb, minSilenceS |
ringtone-maker |
Ringtone Maker | startS, durationS (1-30) |
waveform-image |
Waveform Image | width, height, color (hex) |
audio-metadata |
Audio Metadata | strip (bool), title, artist, album |
transcribe-audio |
Transcribe Audio (AI) | language (auto/en/de/fr/es/zh/ja/ko/id/th/vi), outputFormat (txt/srt/vtt) |
Document Tools
| Tool ID | Name | Key settings |
|---|---|---|
merge-pdf |
Merge PDFs | - (multi-file, up to 20 PDFs) |
split-pdf |
Split PDF | mode (range/every), range, everyN (1-500) |
compress-pdf |
Compress PDF | mode (quality/targetSize), quality (1-100), targetSizeKb |
rotate-pdf |
Rotate PDF | angle (90/180/270), range (page range) |
extract-pages |
Extract Pages | range (qpdf syntax, e.g. "1-5,8,10-z") |
remove-pages |
Remove Pages | pages (qpdf range to remove) |
organize-pdf |
Organize PDF | order (qpdf page order, e.g. "3,1,2,5-z") |
protect-pdf |
Protect PDF | userPassword, ownerPassword (AES-256) |
unlock-pdf |
Unlock PDF | password |
repair-pdf |
Repair PDF | - |
linearize-pdf |
Web-Optimize PDF | - (linearize for fast web viewing) |
grayscale-pdf |
Grayscale PDF | - |
pdfa-convert |
PDF/A Convert | - (archival PDF/A-2) |
crop-pdf |
Crop PDF | margin (0-2000 points) |
nup-pdf |
N-up PDF | perSheet (2/3/4/8/9/12/16) |
booklet-pdf |
Booklet PDF | perSheet (2/4/6/8) |
watermark-pdf |
Watermark PDF | text, position, fontSize, opacity, rotation |
pdf-page-numbers |
PDF Page Numbers | position (bl/bc/br/tl/tc/tr), fontSize |
flatten-pdf |
Flatten PDF | - (bakes forms and annotations) |
redact-pdf |
Redact PDF | terms (string[]), caseSensitive (bool) |
sign-pdf |
Sign PDF | Custom multipart route with PDF file, signature files sig0, sig1, and placements JSON array |
pdf-to-text |
PDF to Text | - |
pdf-to-word |
PDF to Word | - |
pdf-metadata |
PDF Metadata | title, author, subject, keywords |
convert-document |
Convert Document | format (docx/odt/rtf/txt) |
convert-presentation |
Convert Presentation | format (pptx/odp) |
convert-spreadsheet |
Convert Spreadsheet | format (xlsx/ods/csv) |
excel-to-pdf |
Excel to PDF | - |
word-to-pdf |
Word to PDF | - |
powerpoint-to-pdf |
PowerPoint to PDF | - |
html-to-pdf |
HTML to PDF | - (remote resources disabled) |
markdown-to-docx |
Markdown to Word | - |
markdown-to-html |
Markdown to HTML | - |
markdown-to-pdf |
Markdown to PDF | - (remote resources disabled) |
epub-convert |
Convert EPUB | format (pdf/docx/html/md) |
to-epub |
Convert to EPUB | - (accepts .docx, .md, .html, .txt) |
ocr-pdf |
PDF OCR (AI) | quality (fast/balanced/best), language (auto/en/de/fr/es/zh/ja/ko), pages |
pdf-to-image |
PDF to Image | pages (all/range), format, dpi, quality |
pdf-to-jpg |
PDF to JPG | pages, dpi, quality, colorMode |
pdf-to-png |
PDF to PNG | pages, dpi, quality, colorMode |
pdf-to-tiff |
PDF to TIFF | pages, dpi, quality, colorMode |
File Tools
| Tool ID | Name | Key settings |
|---|---|---|
chart-maker |
Chart Maker | kind (bar/line/pie), title, width, height |
csv-excel |
CSV to Excel | sheet (worksheet number for XLSX input) - bidirectional |
csv-json |
CSV to JSON | pretty (bool) - bidirectional |
json-xml |
JSON to XML | pretty (bool) - bidirectional |
split-csv |
Split CSV | rowsPerFile (1-1000000), keepHeader (bool) |
merge-csvs |
Merge CSVs | - (multi-file, matching columns) |
yaml-json |
YAML / JSON | - (bidirectional) |
xml-to-csv |
XML to CSV | - (auto-finds repeating elements) |
excel-to-csv |
Excel to CSV | dedicated conversion preset backed by convert-spreadsheet |
create-zip |
Create ZIP | - (multi-file, 2-50 files) |
extract-zip |
Extract ZIP | - (bomb-protected) |
HTML to Image
Capture a webpage as an image. Unlike other tools, this endpoint accepts application/json instead of multipart form data (no file upload needed).
Endpoint: POST /api/v1/tools/image/html-to-image
Content-Type: application/json
| Parameter | Type | Default | Description |
|---|---|---|---|
url |
string | (required) | URL to capture (http/https only) |
format |
string | "png" |
Output format: jpg, png, webp |
quality |
number | 90 |
Quality 1-100 (JPG/WebP only) |
fullPage |
boolean | false |
Capture full scrollable page |
devicePreset |
string | "desktop" |
desktop, tablet, mobile, custom |
viewportWidth |
number | 1280 |
Custom viewport width 320-3840 |
viewportHeight |
number | 720 |
Custom viewport height 320-2160 |
Example:
curl -X POST http://localhost:1349/api/v1/tools/image/html-to-image \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "https://snapotter.com", "format": "png", "devicePreset": "desktop"}'
Response:
{
"jobId": "uuid",
"downloadUrl": "/api/v1/download/{jobId}/screenshot.png",
"originalSize": 0,
"processedSize": 54321
}
Tool Sub-Routes
Some tools expose additional endpoints beyond the standard POST /api/v1/tools/<section>/<toolId>:
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/tools/popular |
Return popular tool IDs, falling back to a curated default list when usage data is sparse |
POST |
/api/v1/tools/image/remove-background/effects |
Apply background effects (color/gradient/blur/shadow) without re-running AI. Uses cached mask from initial removal. |
POST |
/api/v1/tools/image/edit-metadata/inspect |
Read existing EXIF/IPTC/XMP metadata from an image |
POST |
/api/v1/tools/image/strip-metadata/inspect |
Inspect metadata fields before stripping |
POST |
/api/v1/tools/image/passport-photo/analyze |
Phase 1: AI face detection + background removal. Returns face landmarks and cached data. |
POST |
/api/v1/tools/image/passport-photo/generate |
Phase 2: Crop, resize, and tile using cached analysis. No AI re-run. |
POST |
/api/v1/tools/image/gif-tools/info |
Get GIF metadata (frame count, dimensions, duration) |
POST |
/api/v1/tools/pdf/pdf-to-image/info |
Get PDF metadata (page count, dimensions) |
POST |
/api/v1/tools/pdf/pdf-to-image/preview |
Generate a preview of a specific PDF page |
POST |
/api/v1/tools/pdf/pdf-to-jpg/info |
Get PDF metadata for the dedicated JPG preset |
POST |
/api/v1/tools/pdf/pdf-to-jpg/preview |
Generate a JPG preset PDF page preview |
POST |
/api/v1/tools/pdf/pdf-to-png/info |
Get PDF metadata for the dedicated PNG preset |
POST |
/api/v1/tools/pdf/pdf-to-png/preview |
Generate a PNG preset PDF page preview |
POST |
/api/v1/tools/pdf/pdf-to-tiff/info |
Get PDF metadata for the dedicated TIFF preset |
POST |
/api/v1/tools/pdf/pdf-to-tiff/preview |
Generate a TIFF preset PDF page preview |
POST |
/api/v1/tools/image/svg-to-raster/batch |
Batch convert multiple SVGs to raster |
POST |
/api/v1/tools/image/image-enhancement/analyze |
Analyze image quality and return enhancement recommendations |
POST |
/api/v1/tools/image/optimize-for-web/preview |
Lightweight preview for live parameter tuning. Returns optimized image with size headers. |
Batch Processing
Apply a generic batch-enabled tool to multiple files at once. Returns a ZIP archive. Custom multi-file or multi-step routes, such as PDF signing and PDF-to-image preset routes, use their own endpoint contract instead of the generic /batch route.
The ocr-pdf tool supports this generic /batch route.
curl -X POST http://localhost:1349/api/v1/tools/image/compress/batch \
-H "Authorization: Bearer <token>" \
-F "files=@a.jpg" \
-F "files=@b.jpg" \
-F "files=@c.jpg" \
-F 'settings={"quality":80}'
Concurrency is controlled by CONCURRENT_JOBS (default: auto-detected from CPU cores). MAX_BATCH_SIZE limits the number of files per batch (default: 100; set 0 for unlimited).
Pipelines
Execute a pipeline
# Single file
curl -X POST http://localhost:1349/api/v1/pipeline/execute \
-H "Authorization: Bearer <token>" \
-F "file=@input.jpg" \
-F 'pipeline={"steps":[
{"toolId":"resize","settings":{"width":1200}},
{"toolId":"compress","settings":{"quality":80}},
{"toolId":"watermark-text","settings":{"text":"© 2025"}}
]}'
# Batch (multiple files → ZIP)
curl -X POST http://localhost:1349/api/v1/pipeline/batch \
-H "Authorization: Bearer <token>" \
-F "files=@a.jpg" \
-F "files=@b.jpg" \
-F 'pipeline={"steps":[{"toolId":"resize","settings":{"width":800}}]}'
Each step's output is the next step's input. Pipelines allow 20 steps by default, configurable via MAX_PIPELINE_STEPS. Set MAX_PIPELINE_STEPS=0 to remove the limit.
Save and manage pipelines
| Method | Path | Description |
|---|---|---|
POST |
/api/v1/pipeline/save |
Save a named pipeline (name, description, steps[]) |
GET |
/api/v1/pipeline/list |
List saved pipelines (admins see all; users see own) |
DELETE |
/api/v1/pipeline/:id |
Delete (owner or admin) |
GET |
/api/v1/pipeline/tools |
List tool IDs valid for pipeline steps |
Progress Tracking
Long-running jobs, queued tools, batch jobs, and pipelines emit real-time progress via Server-Sent Events. The progress stream is public and keyed by job ID, so clients do not need to send an Authorization header to read it.
# Connect to the SSE stream (jobId is in the JSON response body from the tool endpoint)
curl -N http://localhost:1349/api/v1/jobs/<jobId>/progress
Event format:
data: {"jobId":"...","type":"single","phase":"processing","stage":"Upscaling","percent":42}
data: {"jobId":"...","type":"single","phase":"complete","percent":100,"result":{"downloadUrl":"/api/v1/download/..."}}
data: {"jobId":"...","type":"batch","status":"processing","completedFiles":2,"totalFiles":5,"failedFiles":0,"errors":[]}
You can request cancellation for a queued or running job with POST /api/v1/jobs/:jobId/cancel. The response is {"canceled":true|false}.
File Library
Persistent file storage with version history.
| Method | Path | Description |
|---|---|---|
POST |
/api/v1/upload |
Upload files to workspace (temp processing) |
POST |
/api/v1/files/upload |
Upload files to the persistent file library |
POST |
/api/v1/files/save-result |
Save a tool processing result as a new file version |
GET |
/api/v1/files |
List saved files (paginated, with search) |
GET |
/api/v1/files/:id |
Get file metadata + version chain |
GET |
/api/v1/files/:id/download |
Download file |
GET |
/api/v1/files/:id/thumbnail |
Get 300px JPEG thumbnail |
DELETE |
/api/v1/files |
Bulk delete files and their version chains (body: { ids: [...] }) |
POST |
/api/v1/fetch-urls |
Fetch remote URLs into the workspace for URL-based imports |
POST |
/api/v1/preview |
Generate a browser-compatible WebP preview (for HEIC/HEIF/RAW formats) |
GET |
/api/v1/files/:id/preview |
Stream a cached or generated browser-compatible preview for a saved PDF, office document, video, or audio file |
POST |
/api/v1/preview/generate |
Generate an on-demand MP4 or MP3 preview for an uploaded media file without saving it first |
GET |
/api/v1/download/:jobId/:filename |
Download a processed file from a workspace |
To auto-save a tool result to the library, include fileId as a multipart form field referencing an existing library file. The processed result will be saved as a new version.
API Key Management
| Method | Path | Access | Description |
|---|---|---|---|
POST |
/api/v1/api-keys |
Auth | Generate new key - shown once |
GET |
/api/v1/api-keys |
Auth | List keys (name, id, lastUsedAt - not raw key) |
DELETE |
/api/v1/api-keys/:id |
Auth | Delete key |
Teams
| Method | Path | Access | Description |
|---|---|---|---|
GET |
/api/v1/teams |
Admin (teams:manage) |
List teams |
POST |
/api/v1/teams |
Admin (teams:manage) |
Create team |
PUT |
/api/v1/teams/:id |
Admin (teams:manage) |
Rename team |
DELETE |
/api/v1/teams/:id |
Admin (teams:manage) |
Delete team (cannot delete default team or teams with members) |
Settings
Runtime configuration uses a closed set of recognized keys. Reading requires settings:read and writing requires settings:write; security and compliance keys additionally require security:manage or compliance:manage. Secret settings require full-administrator authority, while credentials and state owned by dedicated endpoints are read-only here. Bulk updates are validated before any value is written.
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/settings |
Get all settings |
PUT |
/api/v1/settings |
Bulk update settings (JSON body with key-value pairs) |
GET |
/api/v1/settings/:key |
Get a specific setting by key |
Representative keys: disabledTools (JSON array of tool IDs), enableExperimentalTools (boolean), loginAttemptLimit (security policy), and auditRetentionDays (compliance policy). Unknown keys are rejected.
Preferences
Per-user preferences are separate from instance settings. Any authenticated user can read and update their own preference map.
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/preferences |
Get the current user's preferences as { "preferences": { ... } } |
PUT |
/api/v1/preferences |
Upsert one or more preference keys for the current user |
Roles
Custom role management with granular permissions.
Role creation and mutation are constrained by authority containment: the proposed or current role cannot outrank the actor, exceed the actor's effective permissions, or broaden the actor's tool scope. API-key scopes participate in this check. Deleting a custom role also requires authority to assign the built-in user fallback used for its members.
| Method | Path | Access | Description |
|---|---|---|---|
GET |
/api/v1/roles |
Admin (audit:read) |
List all roles with user counts |
POST |
/api/v1/roles |
Admin (security:manage) |
Create a custom role (name, description, permissions) |
PUT |
/api/v1/roles/:id |
Admin (security:manage) |
Update a custom role (cannot modify built-in roles) |
DELETE |
/api/v1/roles/:id |
Admin (security:manage) |
Delete a custom role (cannot delete built-in roles; affected users revert to user role) |
Available permissions (17): tools:use, files:own, files:all, apikeys:own, apikeys:all, pipelines:own, pipelines:all, settings:read, settings:write, users:manage, teams:manage, features:manage, system:health, audit:read, compliance:manage, webhooks:manage, security:manage.
Audit Log
Admin-only endpoint for reviewing security-relevant actions.
| Method | Path | Access | Description |
|---|---|---|---|
GET |
/api/v1/audit-log |
Admin (audit:read) |
Paginated audit log with optional filters |
Query parameters:
| Parameter | Description |
|---|---|
page |
Page number (default: 1) |
limit |
Entries per page (default: 50, max: 100) |
action |
Filter by action type (e.g. ROLE_CREATED, ROLE_DELETED) |
ip |
Filter by source IP address |
from |
Filter entries after this ISO 8601 date |
to |
Filter entries before this ISO 8601 date |
Analytics
| Method | Path | Access | Description |
|---|---|---|---|
GET |
/api/v1/config/analytics |
Public | Get the effective analytics configuration (PostHog key, Sentry DSN, sample rate). Keys, DSN, and instance ID are blank when analytics is off, either from the compile-time bake or the instance analyticsEnabled setting. |
POST |
/api/v1/feedback |
Auth | Submit explicit user feedback to the configured PostHog project as feedback_submitted. The route respects the analytics gate, rate-limits submissions, strips contact fields unless contactOk is true, and never accepts file contents, file names, upload paths, or raw private error text. When analytics is disabled, it returns { "ok": true, "accepted": false }. |
PUT |
/api/v1/settings |
Admin (settings:write) |
Set the instance-wide opt-out. Send a JSON body { "analyticsEnabled": "false" } to turn analytics off for everyone, or "true" to turn it back on. |
Features / AI Bundles
Manage AI feature bundles (install/uninstall AI model packages in the Docker environment). Prefer the tool-level install endpoint when enabling a tool from custom automation: some AI tools need more than one shared bundle, and this endpoint skips already-installed bundles while queuing only the missing ones.
OCR is an optional enhancement rather than a hard dependency. Its fast Tesseract tier works without a pack; POST /api/v1/admin/features/ocr/install installs the signed RapidOCR pack for balanced and best on Linux amd64 or arm64. The accurate OCR runtime uses CPU on CPU-only and NVIDIA hosts and requires at least 4 GiB of effective memory (the configured container cgroup limit, otherwise host memory). SnapOtter reports requiredMemoryBytes, effectiveMemoryBytes, and an insufficient-memory compatibility reason, and rejects an incompatible install before download. This memory requirement does not apply to fast. The pack is about 208-234 MiB to download and 409-488 MiB installed, depending on the target; the signed index binds the exact sizes enforced during installation.
| Method | Path | Access | Description |
|---|---|---|---|
GET |
/api/v1/features |
Auth | List all feature bundles and their install status |
POST |
/api/v1/admin/features/:bundleId/install |
Admin (features:manage) |
Install a feature bundle (async, returns jobId for progress tracking) |
POST |
/api/v1/admin/tools/:toolId/features/install |
Admin (features:manage) |
Install every bundle a tool requires; returns per-bundle queued/skipped status |
POST |
/api/v1/admin/features/:bundleId/uninstall |
Admin (features:manage) |
Uninstall a feature bundle and clean up model files |
GET |
/api/v1/admin/features/disk-usage |
Admin (features:manage) |
Get total disk usage of AI models |
POST |
/api/v1/admin/features/import |
Admin (features:manage) |
Import a legacy AI bundle (file) or a signed offline OCR release (index plus archive) |
An air-gapped OCR import must include the release's signed ocr-runtime-index.json and the matching platform archive. SnapOtter applies the same Ed25519 signature, artifact hash, compatibility, extraction, and smoke-test checks used by online installation:
curl -X POST http://localhost:1349/api/v1/admin/features/import \
-H "Authorization: Bearer <admin-token>" \
-F "index=@ocr-runtime-index.json" \
-F "archive=@ocr-linux-amd64-cpu-py312.tar.gz"
Use the linux-arm64-cpu-py311 archive on arm64. A signed artifact for another target is rejected rather than installed.
Admin Operations
Operational endpoints for observability, support, usage reporting, and backup status.
| Method | Path | Access | Description |
|---|---|---|---|
GET |
/api/v1/admin/log-level |
Admin (settings:write) |
Read the current runtime log level |
POST |
/api/v1/admin/log-level |
Admin (settings:write) |
Change the runtime log level (fatal, error, warn, info, debug, trace, or silent) |
GET |
/api/v1/metrics |
Admin (system:health) |
Prometheus metrics in text format |
GET |
/api/v1/admin/support-bundle |
Admin (system:health) |
Download a redacted diagnostic support bundle ZIP |
GET |
/api/v1/admin/usage |
Admin (audit:read) |
Usage dashboard data, with optional days query parameter |
GET |
/api/v1/admin/backup-status |
Admin (system:health) |
Read last backup metadata and freshness status |
POST |
/api/v1/admin/backup-status |
Admin (system:health) |
Record a completed backup (type, optional sizeBytes, optional notes) |
Enterprise APIs
These routes are license-gated by their related enterprise feature. They still require the listed SnapOtter permission.
Full built-in admin means the authenticated actor has the admin role and the complete effective admin permission set. An API-key scope that omits any admin permission does not qualify.
| Method | Path | Access | Description |
|---|---|---|---|
GET |
/api/v1/enterprise/audit/export |
Admin (audit:read) |
Export audit entries as JSON or CSV with filters |
GET |
/api/v1/enterprise/config/export |
Full built-in admin | Export redacted instance config, custom roles, and teams |
POST |
/api/v1/enterprise/config/import |
Full built-in admin | Import config, with optional dry run |
GET |
/api/v1/enterprise/ip-allowlist |
Admin (security:manage) |
Read configured CIDR allowlist |
PUT |
/api/v1/enterprise/ip-allowlist |
Admin (security:manage) |
Update CIDR allowlist with self-lockout prevention |
GET |
/api/v1/enterprise/legal-hold |
Admin (compliance:manage) |
List user and team legal holds |
PUT |
/api/v1/enterprise/legal-hold |
Admin (compliance:manage) |
Apply or release a legal hold on a user or team |
POST |
/api/v1/enterprise/scim/token |
Full built-in admin | Generate a SCIM bearer token, returned once |
DELETE |
/api/v1/enterprise/scim/token |
Full built-in admin | Revoke the current SCIM bearer token |
GET |
/api/v1/enterprise/siem/config |
Admin (webhooks:manage) |
Read SIEM forwarding config |
PUT |
/api/v1/enterprise/siem/config |
Admin (webhooks:manage) |
Update SIEM forwarding config |
GET |
/api/v1/enterprise/webhooks |
Admin (webhooks:manage) |
List webhook destinations |
POST |
/api/v1/enterprise/webhooks |
Admin (webhooks:manage) |
Create a webhook destination |
PUT |
/api/v1/enterprise/webhooks/:index |
Admin (webhooks:manage) |
Update a webhook destination |
DELETE |
/api/v1/enterprise/webhooks/:index |
Admin (webhooks:manage) |
Delete a webhook destination |
POST |
/api/v1/enterprise/webhooks/:index/test |
Admin (webhooks:manage) |
Send a test webhook payload |
POST |
/api/v1/enterprise/users/:id/export |
Admin (compliance:manage) |
Start a GDPR user export job |
GET |
/api/v1/enterprise/users/:id/export/:jobId |
Admin (compliance:manage) |
Read GDPR export status and download URL |
DELETE |
/api/v1/enterprise/users/:id/purge |
Admin (compliance:manage; target authority) |
Permanently purge a user's data after confirmation |
DELETE |
/api/v1/enterprise/teams/:id/purge |
Admin (compliance:manage; all-member authority) |
Permanently purge a team's data after confirmation |
GET |
/api/v1/admin/version |
Admin (system:health) |
Read app, build, Node, and schema version metadata |
GET |
/api/v1/admin/migrations/pending |
Admin (system:health) |
Compare packaged migrations with applied migrations |
GET |
/api/v1/admin/upgrade-check |
Admin (system:health) |
Run upgrade readiness checks |
SCIM 2.0
SCIM discovery endpoints are public. User and group endpoints require the SCIM bearer token generated above. Legacy unversioned tokens are invalid and must be reissued as so_scim_v2_... tokens by a full built-in admin.
| Method | Path | Access | Description |
|---|---|---|---|
GET |
/api/v1/scim/v2/ServiceProviderConfig |
Public | SCIM server capabilities |
GET |
/api/v1/scim/v2/Schemas |
Public | SCIM schema discovery |
GET |
/api/v1/scim/v2/ResourceTypes |
Public | SCIM resource type discovery |
GET |
/api/v1/scim/v2/Users |
SCIM token | List users, with optional SCIM filter |
POST |
/api/v1/scim/v2/Users |
SCIM token | Create a user |
GET |
/api/v1/scim/v2/Users/:id |
SCIM token | Get a user |
PUT |
/api/v1/scim/v2/Users/:id |
SCIM token | Replace a user |
DELETE |
/api/v1/scim/v2/Users/:id |
SCIM token | Soft deactivate a user |
GET |
/api/v1/scim/v2/Groups |
SCIM token | List teams as SCIM groups |
POST |
/api/v1/scim/v2/Groups |
SCIM token | Create a team |
GET |
/api/v1/scim/v2/Groups/:id |
SCIM token | Get a team |
PUT |
/api/v1/scim/v2/Groups/:id |
SCIM token | Replace a team and group membership |
DELETE |
/api/v1/scim/v2/Groups/:id |
SCIM token | Delete a team |
Meme Templates
Supporting API for the meme generator tool.
| Method | Path | Access | Description |
|---|---|---|---|
GET |
/api/v1/meme-templates |
Auth | List all available meme templates with text box positions |
GET |
/api/v1/meme-templates/full/:filename |
Auth | Serve full-size template image |
GET |
/api/v1/meme-templates/thumbs/:filename |
Auth | Serve template thumbnail |
GET |
/api/v1/meme-templates/fonts/:filename |
Auth | Serve font file used for meme text rendering |
Error Responses
All errors return JSON:
{
"error": "Human-readable message",
"code": "MACHINE_READABLE_CODE"
}
| Status | Meaning |
|---|---|
| 400 | Invalid request / validation failed |
| 401 | Not authenticated |
| 403 | Insufficient permissions |
| 404 | Resource not found |
| 413 | File too large (see MAX_UPLOAD_SIZE_MB) |
| 422 | Processing failed after validation |
| 429 | Rate limited (see RATE_LIMIT_PER_MIN) |
| 501 | Required AI feature bundle is not installed (FEATURE_NOT_INSTALLED) |
| 500 | Internal server error |