Files
Ewald 990071069f Remove automatic GitHub release/update check
The frontend called checkMajoorVersion() unconditionally on startup,
and the backend /mjr/am/releases endpoint made outbound requests to
api.github.com to power it. This violates the no-internet-requests
policy for core ComfyUI code, even for update checks framed as
diagnostic or opt-out. Removes the endpoint, the frontend check, the
now-dead version-update badge dot in HeaderSection.vue, and associated
tests/docs.
2026-09-30 16:55:50 +02:00

25 KiB

Majoor API Reference

Base Path: /mjr/am Version: 2.4.9 Last Updated: April 7, 2026


Compatibility

Versioning

  • Current API: /mjr/am/* (unversioned, stable)
  • Version Alias: /mjr/am/v1/* → redirects to /mjr/am/* (308 Permanent Redirect)
  • Legacy Alias: /majoor/version → redirects to /mjr/am/version

Authentication

Most endpoints require authentication when ComfyUI auth is enabled:

  • Read operations: May work without auth (depends on configuration)
  • Write operations: Require authentication or API token
  • CSRF Protection: All state-changing endpoints require X-Requested-With: XMLHttpRequest or X-CSRF-Token

API Token

For remote access or when MAJOOR_API_TOKEN is configured:

X-MJR-Token: <your-token>
# OR
Authorization: Bearer <your-token>

Table of Contents


Health & Diagnostics

Health Summary

GET /mjr/am/health

Response: Extension health summary including version, status, and basic metrics.

{
  "ok": true,
  "data": {
    "version": "2.4.9",
    "status": "healthy",
    "indexed_count": 1234,
    "scopes_available": ["output", "input", "custom", "collections"]
  }
}

Health Counters

GET /mjr/am/health/counters

Response: Indexed counters for all scopes.

{
  "ok": true,
  "data": {
    "output": { "count": 1000, "last_scan": "2026-02-28T10:00:00Z" },
    "input": { "count": 200, "last_scan": "2026-02-28T10:00:00Z" },
    "custom": { "count": 34, "last_scan": "2026-02-28T10:00:00Z" },
    "collections": { "count": 5 }
  }
}

Database Health

GET /mjr/am/health/db

Response: Database diagnostics including schema version, integrity status, and statistics.

{
  "ok": true,
  "data": {
    "schema_version": 7,
    "integrity_check": "ok",
    "page_count": 1024,
    "freelist_count": 128,
    "cache_size": 2000
  }
}

Runtime Configuration

GET /mjr/am/config

Response: Current runtime configuration snapshot.

{
  "ok": true,
  "data": {
    "output_directory": "/path/to/output",
    "index_directory": "/path/to/_mjr_index",
    "media_probe_backend": "auto",
    "db_timeout": 30.0,
    "max_connections": 8,
    "safe_mode": true,
    "allow_symlinks": false
  }
}

Version Information

GET /mjr/am/version

Response: Extension version and build information.

{
  "ok": true,
  "data": {
    "version": "2.4.9",
    "branch": "main",
    "build_date": "2026-03-29",
    "python_version": "3.11.0",
    "comfyui_version": "0.13.0"
  }
}

Tool Status

GET /mjr/am/tools/status

Response: Availability status of external tools (ExifTool, FFprobe).

{
  "ok": true,
  "data": {
    "exiftool": { "available": true, "version": "12.40" },
    "ffprobe": { "available": true, "version": "6.0" },
    "backend": "both"
  }
}

Settings & Configuration

Probe Backend Settings

GET  /mjr/am/settings/probe-backend
POST /mjr/am/settings/probe-backend

GET: Read current probe mode. POST: Set probe mode.

Request Body (POST):

{
  "mode": "auto"  // "auto", "exiftool", "ffprobe", "both"
}

Response:

{
  "ok": true,
  "data": {
    "mode": "auto",
    "available": ["exiftool", "ffprobe"]
  }
}

Output Directory Override

GET  /mjr/am/settings/output-directory
POST /mjr/am/settings/output-directory

GET: Read output directory override. POST: Set output directory override.

Request Body (POST):

{
  "path": "/path/to/custom/output"
}

Response:

{
  "ok": true,
  "data": {
    "path": "/path/to/custom/output",
    "exists": true,
    "writable": true
  }
}

Index Directory Override

GET  /mjr/am/settings/index-directory
POST /mjr/am/settings/index-directory

GET: Read the current index directory path.

Response:

{
  "ok": true,
  "data": {
    "index_directory": "/path/to/_mjr_index"
  }
}

POST: Set a new index directory path.

Requires write access (loopback or valid X-MJR-Token).

Request Body (POST):

{
  "index_directory": "/path/to/local/mjr_index"
}

Send an empty string to clear the override and revert to the default (<output>/_mjr_index/):

{
  "index_directory": ""
}

Response (POST):

{
  "ok": true,
  "data": {
    "index_directory": "/path/to/local/mjr_index",
    "requires_restart": true
  }
}

The new path is persisted to the .mjr_index_directory_override sidecar file and the in-process environment variables. The change takes effect after ComfyUI is restarted.

Error codes:

Code Meaning
INVALID_INPUT Path exists but is a file, not a directory
INVALID_INPUT Path was given as a non-empty string but the parent directory does not exist
DB_ERROR Sidecar file could not be written

Metadata Fallback Toggles

GET  /mjr/am/settings/metadata-fallback
POST /mjr/am/settings/metadata-fallback

GET: Read fallback toggles for metadata extraction. POST: Set fallback toggles.

Request Body (POST):

{
  "image": true,   // Use internal parser if ExifTool fails
  "media": true    // Use hachoir if ffprobe unavailable
}

Response:

{
  "ok": true,
  "data": {
    "image": true,
    "media": true
  }
}

Search & Discovery

Search with Filters

GET /mjr/am/search

Query Parameters:

Parameter Type Description
q string Search query (full-text)
scope string Scope: output, input, custom, collections
root_id string Custom root ID (for custom scope)
collection_id string Collection ID (for collections scope)
kind string File kind: image, video, audio, model3d, all
min_rating integer Minimum rating (0-5)
workflow boolean Filter by workflow presence
date_from string Start date (ISO 8601)
date_to string End date (ISO 8601)
size_from integer Minimum file size (bytes)
size_to integer Maximum file size (bytes)
width_from integer Minimum image width
width_to integer Maximum image width
height_from integer Minimum image height
height_to integer Maximum image height
sort string Sort field: relevance, name, date, size, rating
order string Sort order: asc, desc
page integer Page number (1-based)
page_size integer Items per page (default: 50)
hide_png_siblings boolean Hide PNGs when video exists

Example:

GET /mjr/am/search?q=fantasy&scope=output&kind=image&min_rating=4&sort=date&order=desc&page=1&page_size=50

Response:

{
  "ok": true,
  "data": {
    "total": 150,
    "page": 1,
    "page_size": 50,
    "total_pages": 3,
    "items": [
      {
        "id": "asset_123",
        "filename": "fantasy_character.png",
        "path": "/path/to/output/fantasy_character.png",
        "type": "image",
        "size": 2048576,
        "width": 1024,
        "height": 1536,
        "rating": 5,
        "tags": ["fantasy", "character"],
        "created_at": "2026-02-28T10:00:00Z",
        "metadata": {
          "prompt": "A fantasy character...",
          "model": "SDXL",
          "steps": 30
        }
      }
    ]
  }
}

List Assets (Paginated)

GET /mjr/am/list

Query Parameters: Same as search, but without q parameter.

Purpose: List assets without full-text search (faster for browsing).


Asset Details

GET /mjr/am/asset/{asset_id}

Response: Full asset details including metadata.

{
  "ok": true,
  "data": {
    "id": "asset_123",
    "filename": "fantasy_character.png",
    "path": "/path/to/output/fantasy_character.png",
    "type": "image",
    "extension": "png",
    "size": 2048576,
    "width": 1024,
    "height": 1536,
    "rating": 5,
    "tags": ["fantasy", "character"],
    "created_at": "2026-02-28T10:00:00Z",
    "modified_at": "2026-02-28T10:00:00Z",
    "metadata": {
      "prompt": "A fantasy character...",
      "negative_prompt": "ugly, blurry",
      "model": "SDXL",
      "sampler": "DPM++ 2M Karras",
      "steps": 30,
      "cfg_scale": 7.0,
      "seed": 123456789,
      "workflow": { ... }
    },
    "thumbnails": {
      "small": "/mjr/am/thumbnail/asset_123?size=small",
      "medium": "/mjr/am/thumbnail/asset_123?size=medium",
      "large": "/mjr/am/thumbnail/asset_123?size=large"
    }
  }
}

Metadata Lookup

GET /mjr/am/metadata

Query Parameters:

Parameter Type Description
type string Scope type: output, input, custom
filename string Filename
subfolder string Subfolder path (optional)
root_id string Custom root ID (for custom scope)

Example:

GET /mjr/am/metadata?type=output&filename=image.png&subfolder=2026-02-28

Response:

{
  "ok": true,
  "data": {
    "filename": "image.png",
    "path": "/path/to/output/2026-02-28/image.png",
    "metadata": { ... }
  }
}

Asset Operations

Update Rating

POST /mjr/am/asset/rating

Request Body:

{
  "asset_id": "asset_123",
  "rating": 5  // 0-5
}

Response:

{
  "ok": true,
  "data": {
    "asset_id": "asset_123",
    "rating": 5,
    "synced_to_file": true
  }
}

Update Tags

POST /mjr/am/asset/tags

Request Body:

{
  "asset_id": "asset_123",
  "tags": ["fantasy", "character", "sdxl"]
}

Response:

{
  "ok": true,
  "data": {
    "asset_id": "asset_123",
    "tags": ["fantasy", "character", "sdxl"],
    "synced_to_file": true
  }
}

Rename Asset

POST /mjr/am/asset/rename

Request Body:

{
  "asset_id": "asset_123",
  "new_filename": "new_name.png"
}

Response:

{
  "ok": true,
  "data": {
    "asset_id": "asset_123",
    "old_path": "/path/to/old_name.png",
    "new_path": "/path/to/new_name.png"
  }
}

Note: Requires MAJOOR_ALLOW_RENAME=1 if Safe Mode is enabled.


Delete Single Asset

POST /mjr/am/asset/delete

Request Body:

{
  "asset_id": "asset_123"
}

Response:

{
  "ok": true,
  "data": {
    "asset_id": "asset_123",
    "deleted": true,
    "path": "/path/to/deleted_file.png"
  }
}

Note: Requires MAJOOR_ALLOW_DELETE=1 if Safe Mode is enabled.


Bulk Delete Assets

POST /mjr/am/assets/delete

Request Body:

{
  "asset_ids": ["asset_123", "asset_124", "asset_125"]
}

Response:

{
  "ok": true,
  "data": {
    "deleted_count": 3,
    "deleted_ids": ["asset_123", "asset_124", "asset_125"],
    "failed": []
  }
}

Note: Requires MAJOOR_ALLOW_DELETE=1 if Safe Mode is enabled.


Open in Folder

POST /mjr/am/open-in-folder

Request Body:

{
  "asset_id": "asset_123"
}

or:

{
  "filepath": "/absolute/path/to/file.png"
}

Response:

{
  "ok": true,
  "data": {
    "asset_id": "asset_123",
    "path": "/path/to/output",
    "opened": true
  }
}

Note: Requires MAJOOR_ALLOW_OPEN_IN_FOLDER=1 if Safe Mode is enabled. This route only asks the OS file manager to reveal an existing local file; it does not read, write, delete, or download file contents. Because of that, filepath targets are not restricted to indexed or configured asset roots for this action.


Collect Files

POST /mjr/am/collect-files

Bundles an asset, its embedded workflow, the traced prompt text, and all media inputs referenced by the workflow into a ZIP written next to the asset. See COLLECT_FILES.md for the full guide.

Request Body:

{
  "filepath": "/absolute/path/to/file.png"
}

Response:

{
  "ok": true,
  "data": {
    "zip_path": "/path/to/output/render_collected.zip",
    "zip_name": "render_collected.zip",
    "directory": "/path/to/output",
    "fallback_used": false,
    "copied": 3,
    "missing": [],
    "inputs": [{ "name": "photo.png", "path": "/path/to/input/photo.png", "status": "ok" }],
    "models": [{ "name": "sdxl.safetensors", "path": "/path/to/checkpoints/sdxl.safetensors", "status": "located" }],
    "has_workflow": true,
    "has_prompt": true,
    "has_prompt_text": true
  }
}

Notes:

  • filepath must resolve inside the ComfyUI output/input directories or a registered custom root.
  • When the asset folder is not writable, the ZIP falls back to output/_mjr_collected/ and fallback_used is true.
  • Model files are listed in the manifest with resolved paths but never copied into the ZIP.
  • Rate limited (10 requests/minute) and requires write access.

Stage to Input

POST /mjr/am/stage-to-input

Request Body:

{
  "asset_id": "asset_123"
}

Response:

{
  "ok": true,
  "data": {
    "asset_id": "asset_123",
    "staged_path": "/path/to/input/staged_file.png",
    "staged": true
  }
}

Purpose: Copy/link asset to ComfyUI input directory for workflow use.


Indexing & Scanning

Scan Scope/Root

POST /mjr/am/scan

Request Body:

{
  "scope": "output",      // "output", "input", "custom"
  "root_id": null,        // Custom root ID (for custom scope)
  "incremental": true,    // Incremental scan (default)
  "force": false          // Force full rescan
}

Response:

{
  "ok": true,
  "data": {
    "scan_id": "scan_123",
    "scope": "output",
    "status": "started",
    "estimated_duration_ms": 5000
  }
}

Note: Scan runs in background. Status available via health endpoints.


Index Explicit Files

POST /mjr/am/index-files

Request Body:

{
  "files": [
    {
      "filename": "image.png",
      "subfolder": "2026-02-28",
      "type": "output"
    }
  ],
  "origin": "generation"  // "generation", "manual", "import"
}

Response:

{
  "ok": true,
  "data": {
    "indexed_count": 1,
    "failed": []
  }
}

Purpose: Index specific files (used for real-time generation tracking).


Reset Index

POST /mjr/am/index/reset

Request Body:

{
  "scope": "output",      // Scope to reset
  "clear_assets": true,   // Clear asset records
  "clear_metadata": true, // Clear metadata cache
  "clear_fts": true,      // Clear full-text search index
  "clear_journal": true   // Clear scan journal
}

Response:

{
  "ok": true,
  "data": {
    "scope": "output",
    "reset": true,
    "rescan_triggered": true
  }
}

Collections & Custom Roots

List Collections

GET /mjr/am/collections

Response:

{
  "ok": true,
  "data": {
    "collections": [
      {
        "id": "collection_123",
        "name": "Best Characters",
        "item_count": 25,
        "created_at": "2026-02-28T10:00:00Z",
        "modified_at": "2026-02-28T12:00:00Z"
      }
    ]
  }
}

Create/Update Collection

POST /mjr/am/collections/create
POST /mjr/am/collections/update

Request Body (Create):

{
  "name": "Best Characters",
  "items": [
    {
      "asset_id": "asset_123",
      "scope": "output"
    }
  ]
}

Request Body (Update):

{
  "collection_id": "collection_123",
  "name": "Updated Name",
  "items": [...],
  "add_items": [...],    // Items to add
  "remove_items": [...]  // Items to remove
}

Response:

{
  "ok": true,
  "data": {
    "collection_id": "collection_123",
    "name": "Updated Name",
    "item_count": 30
  }
}

Delete Collection

POST /mjr/am/collections/delete

Request Body:

{
  "collection_id": "collection_123"
}

Response:

{
  "ok": true,
  "data": {
    "collection_id": "collection_123",
    "deleted": true
  }
}

Note: Does not delete assets, only the collection definition.


List Custom Roots

GET /mjr/am/custom-roots

Response:

{
  "ok": true,
  "data": {
    "roots": [
      {
        "id": "root_123",
        "path": "/path/to/custom/dir",
        "name": "Custom Directory",
        "added_at": "2026-02-28T10:00:00Z"
      }
    ]
  }
}

Add Custom Root

POST /mjr/am/custom-roots/add

Request Body:

{
  "path": "/path/to/custom/dir",
  "name": "Custom Directory"
}

Response:

{
  "ok": true,
  "data": {
    "root_id": "root_123",
    "path": "/path/to/custom/dir",
    "added": true
  }
}

Remove Custom Root

POST /mjr/am/custom-roots/remove

Request Body:

{
  "root_id": "root_123"
}

Response:

{
  "ok": true,
  "data": {
    "root_id": "root_123",
    "removed": true
  }
}

Viewer & Metadata

Viewer Info

GET /mjr/am/viewer/info

Query Parameters:

Parameter Type Description
asset_id string Asset ID

Example:

GET /mjr/am/viewer/info?asset_id=asset_123

Response:

{
  "ok": true,
  "data": {
    "asset_id": "asset_123",
    "filename": "fantasy_character.png",
    "type": "image",
    "width": 1024,
    "height": 1536,
    "metadata": {
      "prompt": "A fantasy character...",
      "model": "SDXL",
      "workflow_minimap": { ... }
    }
  }
}

Download & Export

Download Asset

GET /mjr/am/download

Query Parameters:

Parameter Type Description
asset_id string Asset ID
filename string Filename (for direct download)
type string Scope type
subfolder string Subfolder path

Example:

GET /mjr/am/download?asset_id=asset_123

Response: File stream with appropriate Content-Type and Content-Disposition headers.


Create Batch ZIP

POST /mjr/am/batch-zip

Request Body:

{
  "asset_ids": ["asset_123", "asset_124", "asset_125"]
}

Response:

{
  "ok": true,
  "data": {
    "token": "zip_token_abc123",
    "expires_at": "2026-02-28T11:00:00Z",
    "download_url": "/mjr/am/batch-zip/zip_token_abc123"
  }
}

Download Batch ZIP

GET /mjr/am/batch-zip/{token}

Response: ZIP file stream containing all requested assets.


Database Maintenance

Optimize Database

POST /mjr/am/db/optimize

Purpose: Run PRAGMA optimize and ANALYZE for performance.

Response:

{
  "ok": true,
  "data": {
    "optimized": true,
    "duration_ms": 150
  }
}

Cleanup Case Duplicates

POST /mjr/am/db/cleanup-case-duplicates

Purpose: Remove historical path-case duplicates (Windows-specific).

Response:

{
  "ok": true,
  "data": {
    "duplicates_removed": 5,
    "kept_canonical": 100
  }
}

Force-Delete Database

POST /mjr/am/db/force-delete

Purpose: Emergency recovery for corrupted databases.

Response:

{
  "ok": true,
  "data": {
    "deleted": true,
    "reinitialized": true,
    "rescan_triggered": true
  }
}

Note: This endpoint bypasses normal DB-dependent security checks for emergency recovery.


Utilities

Date Histogram

GET /mjr/am/date-histogram

Query Parameters:

Parameter Type Description
month string Month in YYYY-MM format
scope string Scope type

Example:

GET /mjr/am/date-histogram?month=2026-02&scope=output

Response:

{
  "ok": true,
  "data": {
    "month": "2026-02",
    "days": [
      { "date": "2026-02-01", "count": 50 },
      { "date": "2026-02-02", "count": 75 },
      ...
    ]
  }
}

Purpose: Calendar histogram for date-based filtering UI.


Duplicate Alerts

GET /mjr/am/duplicates/alerts

Response:

{
  "ok": true,
  "data": {
    "duplicate_groups": [
      {
        "filename": "image.png",
        "paths": [
          "/path/to/output/image.png",
          "/path/to/output/subfolder/image.png"
        ],
        "count": 2
      }
    ],
    "total_groups": 5,
    "total_duplicates": 10
  }
}

Workflow Library

The Workflow tab uses the standard list endpoint with scope=workflow plus dedicated workflow routes for JSON content, organization, metadata, and thumbnails.

GET /mjr/am/list?scope=workflow&q=*&limit=100&offset=0&sort=workflow_default

Purpose: List saved workflow JSON files as grid cards.

Response fields include:

  • assets: workflow cards
  • total: total matched workflows
  • roots: scanned workflow roots
  • scope: workflow
GET /mjr/am/workflows/content?filepath=C%3A%5Cpath%5Cworkflow.json

Purpose: Read workflow JSON and prompt metadata for loading or Graph Map inspection.

POST /mjr/am/workflows/save
POST /mjr/am/workflows/duplicate
POST /mjr/am/workflows/move
POST /mjr/am/workflows/delete
POST /mjr/am/workflows/mark-loaded
POST /mjr/am/workflows/favorite
POST /mjr/am/workflows/tags
POST /mjr/am/workflows/info

Purpose: Manage workflow files and workflow library metadata. State-changing workflow routes require the normal Majoor write guards, CSRF/auth checks, audit logging, and workflow write rate limiting.

GET /mjr/am/workflows/thumbnail-candidates?filepath=...&limit=12
POST /mjr/am/workflows/thumbnail/set
GET /mjr/am/workflows/thumbnail?filepath=...
GET /mjr/am/workflows/graph-map-thumbnail?filepath=...
GET /mjr/am/workflows/model-families
POST /mjr/am/workflows/open-root

Purpose: Support workflow thumbnails, generated Graph Map previews, workflow model-family filtering, and opening the saved workflow root in the OS file manager.

GET /mjr/am/settings/workflow-roots
POST /mjr/am/settings/workflow-roots

Purpose: Read and save the Workflow tab root folders. The POST body accepts workflow_roots as a string or list of strings.

See WORKFLOWS.md for the user-facing Workflow tab behavior.



Security Notes

CSRF Protection

All state-changing endpoints (POST, PUT, PATCH, DELETE) require:

  • X-Requested-With: XMLHttpRequest header, OR
  • X-CSRF-Token header with valid token

Authentication

When ComfyUI auth is enabled:

  • Write operations require authenticated user
  • API token can be used for remote access
  • Token sent via X-MJR-Token or Authorization: Bearer

Safe Mode

When Safe Mode is enabled (default):

  • Delete operations require MAJOOR_ALLOW_DELETE=1
  • Rename operations require MAJOOR_ALLOW_RENAME=1
  • Open in folder requires MAJOOR_ALLOW_OPEN_IN_FOLDER=1; it may reveal any existing local path supplied to the route, but it does not grant file read/write access

Rate Limiting

Expensive endpoints are rate-limited:

  • Search: 10 requests/minute per client
  • Scan: 5 requests/minute per client
  • Batch ZIP: 3 requests/minute per client
  • Metadata: 20 requests/minute per client

Client identity based on IP address (or X-Forwarded-For from trusted proxies).


Error Responses

Standard Error Format

{
  "ok": false,
  "error": {
    "code": "?_CODE",
    "message": "Human-readable error message",
    "details": { ... }  // Optional additional context
  }
}

Common Error Codes

Code HTTP Status Description
AUTH_REQUIRED 401 Authentication required
FORBIDDEN 403 Operation not allowed (Safe Mode)
NOT_FOUND 404 Resource not found
INVALID_REQUEST 400 Invalid request body/parameters
RATE_LIMITED 429 Too many requests
DB_ERROR 500 Database error
FILE_SYSTEM_ERROR 500 File system error
TOOL_UNAVAILABLE 503 External tool not available

WebSocket Events

Real-Time Updates

The extension emits ComfyUI API events for real-time updates:

  • mjr-asset-added: New asset indexed
  • mjr-asset-updated: Asset metadata updated
  • mjr-scan-complete: Scan operation completed
  • mjr-enrichment-status: Metadata enrichment status
  • mjr-db-restore-status: Database restore status

Example (Frontend):

api.addEventListener("mjr-asset-added", (event) => {
  const asset = event.detail;
  // Update UI with new asset
});

API Reference Version: 2.0 Last Updated: April 5, 2026 Compatible with Majoor Assets Manager v2.4.4+