Skip to documentation
Desktop 1.0.81Public documentation · No account needed to read

Your workflows.Powered by MetaGhost.

Connect scripts and n8n to the MetaGhost application running on your computer. Submit images or videos, follow each job, and retrieve its output.

For the released Desktop 1.0.81

This guide and every download target Desktop 1.0.81. Use the /api routes shown here. The health response identifies the application with version: "1.0.81". The documentation will be updated when the next Desktop release ships.

From connection to first result

  1. Open MetaGhost Desktop, sign in, and go to Settings → API.
  2. Enable the API server, keep remote access disabled, generate your key if needed, then click Apply changes. Leave Desktop running.
  3. Check the version from a terminal on the same computer. This request does not need a key.
PowerShell / Windows
curl.exe "http://127.0.0.1:3847/api/health"
JSON
{
  "status": "ok",
  "version": "1.0.81",
  "activeJobs": 0
}

Download either complete example. Both submit one local job, wait for completion, reject partial results and download the first output. Python requires 3.10+; JavaScript requires Node.js 22+. No additional packages.

PowerShell
$env:METAGHOST_API_KEY = Read-Host "MetaGhost API key"
# Replace the paths with your own files and folders.
python .\quickstart.py "C:\Media\photo.jpg" "C:\Media\output" ".\result.jpg"
# Or, with Node.js:
node .\quickstart.mjs "C:\Media\photo.jpg" "C:\Media\output" ".\result-2.jpg"

Use a small test image you own and a new output directory. Change the input and download extensions to use a video. The API key is read from your environment; examples never include your key. The download filename must not already exist. The waiting limit is 10 minutes; stopping the script does not cancel the Desktop job.

Connection & access

The API runs inside your installed Desktop at http://127.0.0.1:3847/api by default. Keep MetaGhost open and use the port displayed in Settings → API. This public guide does not require a login; processing uses your Desktop application and its configured presets.

HTTP
GET http://127.0.0.1:3847/api/presets
Authorization: Bearer YOUR_LOCAL_API_KEY

Every route except GET /api/health requires the Bearer key from Desktop Settings → API. A missing or invalid key returns 401. Use the API key, not your account password. After generating a new key and applying the settings, update your scripts and n8n credential. Keep the key out of URLs and exported workflows.

Files & presets

Provide an absolute filePath for one input, or files for several inputs. Paths refer to the computer running Desktop and the files must already exist. Use an absolute outputFolder; Desktop creates it if missing. A path on another machine or inside a container is not automatically accessible.

Supported images: JPG, JPEG, PNG, WebP, HEIC, BMP, TIFF. Supported videos: MP4, MOV, AVI, MKV, WebM, M4V. Other extensions are rejected.

Upload raw bytes when needed

POST /api/upload accepts raw file bytes and requires X-Filename. It returns filePath, originalName and size; pass that filePath to POST /api/jobs. Maximum: 500 MiB per upload. Use raw bytes, not multipart/form-data. An oversized upload can close the connection before an error response arrives.

PowerShell · raw bytes
curl.exe "http://127.0.0.1:3847/api/upload" `
  -H "Authorization: Bearer $env:METAGHOST_API_KEY" `
  -H "Content-Type: application/octet-stream" `
  -H "X-Filename: photo.jpg" `
  --data-binary "@C:\Media\photo.jpg"

Use a saved preset

GET /api/presets returns image and video arrays of saved preset names. Set preset to the exact name saved in Desktop. A mixed image/video batch needs that name in both preset lists. Omit preset for basic defaults. Configure processing options in the Desktop preset; the API request selects its name.

JSON
{
  "filePath": "C:\\Media\\photo.jpg",
  "outputFolder": "C:\\Media\\output",
  "batchCount": 1,
  "preset": "My saved image preset"
}

Google Drive

Connect Google Drive in Desktop Settings, then check GET /api/drive/status. Both clientConfigured and connected must be true. Use driveInputFolder instead of filePath/files, and/or driveOutputFolder instead of outputFolder. These are folder IDs accessible to the connected account, not sharing URLs. A missing connection returns 412.

JSON
{
  "driveInputFolder": "YOUR_INPUT_FOLDER_ID",
  "driveOutputFolder": "YOUR_OUTPUT_FOLDER_ID",
  "batchCount": 1
}

Drive transfers use the network. For Drive output, processing may reach completed before the upload finishes. Keep polling until drive.uploadResult or drive.uploadError is present, then inspect uploadResult.errors / uploadResult.cancelled. Retrieve the delivered files from Drive: temporary local outputs may already be removed and /api/download can return 404.

Submit, follow, retrieve

POST /api/jobs202 + jobIdGET /api/jobs/{jobId}GET /api/download/{jobId}
JSON
{
  "filePath": "C:\\Media\\photo.jpg",
  "outputFolder": "C:\\Media\\output",
  "batchCount": 1
}

POST returns 202 with jobId, status, inputFiles, batchCount, totalOperations, skipped, position and drive. 202 means accepted. Inspect skipped immediately. With Drive input, inputFiles and totalOperations are initially null because files are discovered later.

Poll GET /api/jobs/{jobId} every two seconds. queued and processing are in progress; completed, failed and cancelled are terminal processing states. Single jobs expose result.outputPath or error. Batches expose results and errors. A completed batch can still contain failures, so check errors and the number of successful results. There is no outcome field in 1.0.81.

Download local output with GET /api/download/{jobId}?index=0. For a batch, index selects an entry in results, starting at zero; it does not refer to the original input index. Downloading before completion returns 400. A missing job, index or file returns 404.

Do not submit the same request automatically

Desktop 1.0.81 does not support Idempotency-Key: every POST /api/jobs can create another job. After a timeout or lost response, inspect GET /api/jobs before submitting again. The examples submit once and retain the accepted jobId. Stopping their polling does not cancel the Desktop job.

Cancellation & history

DELETE /api/jobs/{jobId} requests cancellation of a queued or processing job. Inspect its status again; an in-progress batch file may finish and already-created files can remain. GET /api/jobs lists up to 50 recent jobs. Finished jobs become eligible for cleanup after one hour, checked every ten minutes. Restarting Desktop loses the in-memory history. DELETE /api/jobs clears terminal history entries.

What the 1.0.81 API supports

This release exposes preset-driven image and video spoofing, uploads, local downloads, job management and the Drive connection state. Other tools visible in Desktop do not automatically have an API endpoint. Use only the routes and fields in the reference below.

  • One job processes at a time, with up to 10 waiting jobs; a full queue returns 429.
  • Keep JSON requests below 64 KiB and uploaded files at or below 500 MiB.
  • batchCount repeats each valid file. Start with a positive integer of 1 and small batches, then increase gradually according to your computer’s capacity.
  • There is no API route for credit balance or cost estimation in this release. Choose and review the processing preset in Desktop before automation.

A ready-to-configure n8n workflow

Start with n8n running directly on the same computer as Desktop. The workflow checks Desktop 1.0.81, submits one standard local job, polls with a 10-minute deadline, handles failure/partial results, and retrieves the first output as binary data. It stays inactive until you run it manually.

Download n8n workflow
  1. Import the JSON file into n8n. Open Configure and replace filePath and outputFolder with paths on the Desktop computer.
  2. Create one Header Auth credential: Name = Authorization, Value = Bearer followed by a space and your Desktop API key. Select it in Submit job, Read job and Download output. The exported workflow contains no key.
  3. Run the manual trigger. Download output exposes the binary field data; connect your own storage node if you want to save or forward it. Start with one small test file.

Docker or n8n Cloud?

127.0.0.1 points to the machine or container running n8n, not necessarily your PC. A Docker container needs a reachable host address and a deliberately configured private connection. n8n Cloud cannot access your PC’s localhost. Use a self-hosted runner on the Desktop computer for this starter workflow. Never expose port 3847 directly to the public internet; remote setups need a private authenticated network and appropriate host restrictions.

n8n credential setup · HTTP Request

API reference

OpenAPI is a downloadable description of the API: routes, authentication, parameters and responses. Developer tools can import it to prepare requests or generate integration code. Reading the guide is enough to get started; this download is optional. 3.1 is the OpenAPI format version, this guide describes the API shipped with Desktop 1.0.81.

All paths below are relative to http://127.0.0.1:3847/api. Expand an endpoint for its request and response contract. Schemas preserve the API’s English field names. This page contains documentation only and never calls your Desktop from the browser.

GET/health

Check Desktop 1.0.81 and active jobs

No authentication

JSON
{
  "responses": {
    "200": {
      "description": "Successful response",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Health"
          }
        }
      }
    }
  }
}
Resolve schema references
GET/presets

List saved image and video preset names

Authorization: Bearer YOUR_LOCAL_API_KEY

JSON
{
  "responses": {
    "200": {
      "description": "Successful response",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Presets"
          }
        }
      }
    },
    "401": {
      "description": "Invalid or missing API key",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}
Resolve schema references
GET/drive/status

Check the Desktop Google Drive connection

Authorization: Bearer YOUR_LOCAL_API_KEY

JSON
{
  "responses": {
    "200": {
      "description": "Successful response",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/DriveStatus"
          }
        }
      }
    },
    "401": {
      "description": "Invalid or missing API key",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}
Resolve schema references
POST/upload

Upload raw image or video bytes

Authorization: Bearer YOUR_LOCAL_API_KEY

Raw bytes, not multipart. Maximum 500 MiB (524288000 bytes). Oversized uploads and write errors may close the connection before a JSON error arrives. There is no upload-retention guarantee in 1.0.81.

JSON
{
  "parameters": [
    {
      "name": "X-Filename",
      "in": "header",
      "required": true,
      "schema": {
        "type": "string",
        "description": "Name ending in .jpg, .jpeg, .png, .webp, .heic, .bmp, .tiff, .mp4, .mov, .avi, .mkv, .webm or .m4v."
      }
    }
  ]
}
JSON
{
  "requestBody": {
    "required": true,
    "content": {
      "application/octet-stream": {
        "schema": {
          "type": "string",
          "format": "binary"
        }
      }
    }
  }
}
JSON
{
  "responses": {
    "200": {
      "description": "Successful response",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Upload"
          }
        }
      }
    },
    "400": {
      "description": "Invalid request or incompatible job state",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "401": {
      "description": "Invalid or missing API key",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "500": {
      "description": "Internal server or upload write error",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}
Resolve schema references
POST/jobs

Create an image/video spoofing job

Authorization: Bearer YOUR_LOCAL_API_KEY

Each POST creates a new job. No automatic retries or Idempotency-Key support. JSON body ceiling: 64 KiB; exceeding it may close the connection. Local inputs are checked before acceptance; Drive inputs are discovered afterwards.

JSON
{
  "requestBody": {
    "required": true,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/SpoofJobRequest"
        },
        "example": {
          "filePath": "C:\\Media\\photo.jpg",
          "outputFolder": "C:\\Media\\output",
          "batchCount": 1
        }
      }
    }
  }
}
JSON
{
  "responses": {
    "202": {
      "description": "Successful response",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/AcceptedJob"
          }
        }
      }
    },
    "400": {
      "description": "Invalid request or incompatible job state",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "401": {
      "description": "Invalid or missing API key",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "412": {
      "description": "Google Drive is not configured or connected",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "429": {
      "description": "Ten jobs already waiting in the queue",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "500": {
      "description": "Internal server or upload write error",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}
Resolve schema references
GET/jobs

List the 50 most recent jobs and queue state

Authorization: Bearer YOUR_LOCAL_API_KEY

In-memory history. Completed, failed and cancelled jobs become eligible for cleanup after one hour from completedAt, or createdAt when completedAt is absent. Cleanup runs every ten minutes. Restarting Desktop loses this history.

JSON
{
  "responses": {
    "200": {
      "description": "Successful response",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/JobList"
          }
        }
      }
    },
    "401": {
      "description": "Invalid or missing API key",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}
Resolve schema references
DELETE/jobs

Clear finished jobs from in-memory history

Authorization: Bearer YOUR_LOCAL_API_KEY

Clears terminal history. This is not a read operation.

JSON
{
  "responses": {
    "200": {
      "description": "Successful response",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "cleared": {
                "type": "integer"
              }
            },
            "required": [
              "cleared"
            ]
          }
        }
      }
    },
    "401": {
      "description": "Invalid or missing API key",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}
Resolve schema references
GET/jobs/{jobId}

Read one job's progress and results

Authorization: Bearer YOUR_LOCAL_API_KEY

JSON
{
  "parameters": [
    {
      "name": "jobId",
      "in": "path",
      "required": true,
      "schema": {
        "type": "string",
        "pattern": "^[a-zA-Z0-9_-]+$"
      }
    }
  ]
}
JSON
{
  "responses": {
    "200": {
      "description": "Successful response",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Job"
          }
        }
      }
    },
    "401": {
      "description": "Invalid or missing API key",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "404": {
      "description": "Unknown job, missing output or unsupported route",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}
Resolve schema references
DELETE/jobs/{jobId}

Request cancellation of a queued or processing job

Authorization: Bearer YOUR_LOCAL_API_KEY

Inspect status again after cancellation. Already-produced files may remain. The currently processing batch file may finish before the queue stops.

Cancels the selected job.

JSON
{
  "parameters": [
    {
      "name": "jobId",
      "in": "path",
      "required": true,
      "schema": {
        "type": "string",
        "pattern": "^[a-zA-Z0-9_-]+$"
      }
    }
  ]
}
JSON
{
  "responses": {
    "200": {
      "description": "Successful response",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "jobId": {
                "type": "string"
              },
              "status": {
                "const": "cancelled"
              }
            },
            "required": [
              "jobId",
              "status"
            ]
          }
        }
      }
    },
    "400": {
      "description": "Invalid request or incompatible job state",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "401": {
      "description": "Invalid or missing API key",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "404": {
      "description": "Unknown job, missing output or unsupported route",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}
Resolve schema references
GET/download/{jobId}

Download an existing local output of a completed job

Authorization: Bearer YOUR_LOCAL_API_KEY

For batches, index addresses the successful results array, not the original input array. A Drive-delivered temporary output may already have been cleaned up and return 404. An unfinished job returns 400.

JSON
{
  "parameters": [
    {
      "name": "jobId",
      "in": "path",
      "required": true,
      "schema": {
        "type": "string",
        "pattern": "^[a-zA-Z0-9_-]+$"
      }
    },
    {
      "name": "index",
      "in": "query",
      "schema": {
        "type": "integer",
        "minimum": 0,
        "default": 0
      }
    }
  ]
}
JSON
{
  "responses": {
    "200": {
      "description": "Output bytes; Content-Type follows the file extension and Content-Disposition contains the filename.",
      "content": {
        "application/octet-stream": {
          "schema": {
            "type": "string",
            "format": "binary"
          }
        }
      }
    },
    "400": {
      "description": "Job is not completed",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "401": {
      "description": "Invalid API key",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "404": {
      "description": "Unknown job, index or output file",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}
Resolve schema references
All schemas and reusable parameters
#/components/securitySchemes
LocalApiKey
JSON
{
  "type": "http",
  "scheme": "bearer",
  "description": "Key generated in Desktop Settings > API."
}
#/components/schemas
Error
JSON
{
  "type": "object",
  "properties": {
    "error": {
      "type": "string"
    },
    "skipped": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/FileError"
      }
    }
  },
  "required": [
    "error"
  ]
}
FileError
JSON
{
  "type": "object",
  "properties": {
    "file": {
      "type": "string"
    },
    "error": {
      "type": "string"
    }
  },
  "required": [
    "file",
    "error"
  ]
}
Health
JSON
{
  "type": "object",
  "properties": {
    "status": {
      "const": "ok"
    },
    "version": {
      "const": "1.0.81"
    },
    "activeJobs": {
      "type": "integer",
      "minimum": 0
    }
  },
  "required": [
    "status",
    "version",
    "activeJobs"
  ]
}
Presets
JSON
{
  "type": "object",
  "properties": {
    "image": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "video": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": [
    "image",
    "video"
  ]
}
DriveStatus
JSON
{
  "type": "object",
  "properties": {
    "clientConfigured": {
      "type": "boolean"
    },
    "connected": {
      "type": "boolean"
    },
    "email": {
      "type": [
        "string",
        "null"
      ]
    },
    "scope": {
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "clientConfigured",
    "connected",
    "email",
    "scope"
  ]
}
DriveUploadResult
JSON
{
  "type": "object",
  "properties": {
    "uploaded": {
      "type": "integer",
      "minimum": 0
    },
    "errors": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "error": {
            "type": "string"
          }
        },
        "required": [
          "name",
          "error"
        ]
      }
    },
    "cancelled": {
      "type": "boolean"
    }
  },
  "required": [
    "uploaded",
    "errors"
  ]
}
Upload
JSON
{
  "type": "object",
  "properties": {
    "filePath": {
      "type": "string",
      "description": "Absolute path on the Desktop computer."
    },
    "originalName": {
      "type": "string"
    },
    "size": {
      "type": "integer",
      "minimum": 1
    }
  },
  "required": [
    "filePath",
    "originalName",
    "size"
  ]
}
SpoofJobRequest
JSON
{
  "type": "object",
  "properties": {
    "filePath": {
      "type": "string",
      "description": "Absolute path to one existing image or video."
    },
    "files": {
      "type": "array",
      "items": {
        "type": "string",
        "description": "Absolute path to an existing image or video."
      }
    },
    "driveInputFolder": {
      "type": "string",
      "description": "Folder ID accessible to the connected Desktop Drive account."
    },
    "outputFolder": {
      "type": "string",
      "description": "Absolute local output directory; created when missing."
    },
    "driveOutputFolder": {
      "type": "string",
      "description": "Destination folder ID accessible to the Desktop Drive account."
    },
    "preset": {
      "type": "string",
      "description": "Exact saved preset name for each input media type. Omit for basic defaults."
    },
    "batchCount": {
      "type": "integer",
      "minimum": 1,
      "default": 1,
      "description": "Variations per valid input. Send a positive integer and keep batches small; increase gradually according to your computer's capacity."
    }
  },
  "description": "Preset-driven image/video spoofing only. Choose filePath, files OR driveInputFolder, and outputFolder OR driveOutputFolder. No operation selector or idempotency support. Unknown extra fields are ignored by 1.0.81, not a way to enable another tool.",
  "allOf": [
    {
      "oneOf": [
        {
          "anyOf": [
            {
              "required": [
                "filePath"
              ]
            },
            {
              "required": [
                "files"
              ]
            }
          ]
        },
        {
          "required": [
            "driveInputFolder"
          ]
        }
      ]
    },
    {
      "oneOf": [
        {
          "required": [
            "outputFolder"
          ]
        },
        {
          "required": [
            "driveOutputFolder"
          ]
        }
      ]
    }
  ]
}
AcceptedJob
JSON
{
  "type": "object",
  "properties": {
    "jobId": {
      "type": "string"
    },
    "status": {
      "const": "queued"
    },
    "inputFiles": {
      "type": [
        "integer",
        "null"
      ]
    },
    "batchCount": {
      "type": "integer"
    },
    "totalOperations": {
      "type": [
        "integer",
        "null"
      ]
    },
    "skipped": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/FileError"
      }
    },
    "position": {
      "type": "integer",
      "description": "May be zero when processing has already started."
    },
    "drive": {
      "anyOf": [
        {
          "type": "null"
        },
        {
          "type": "object",
          "properties": {
            "input": {
              "type": "boolean"
            },
            "output": {
              "type": "boolean"
            }
          }
        }
      ]
    }
  },
  "required": [
    "jobId",
    "status",
    "inputFiles",
    "batchCount",
    "totalOperations",
    "skipped",
    "position",
    "drive"
  ]
}
Output
JSON
{
  "type": "object",
  "properties": {
    "outputPath": {
      "type": "string"
    }
  },
  "required": [
    "outputPath"
  ]
}
BatchOutput
JSON
{
  "type": "object",
  "properties": {
    "file": {
      "type": "string"
    },
    "outputPath": {
      "type": "string"
    }
  },
  "required": [
    "file",
    "outputPath"
  ]
}
Progress
JSON
{
  "type": "object",
  "properties": {
    "phase": {
      "type": "string"
    },
    "phaseProgress": {
      "type": "number"
    },
    "totalProgress": {
      "type": "number"
    },
    "currentFile": {
      "type": "integer"
    },
    "totalFiles": {
      "type": [
        "integer",
        "null"
      ]
    },
    "completedFiles": {
      "type": "integer"
    },
    "fileName": {
      "type": "string"
    }
  }
}
Job
JSON
{
  "type": "object",
  "properties": {
    "jobId": {
      "type": "string"
    },
    "status": {
      "type": "string",
      "enum": [
        "queued",
        "processing",
        "completed",
        "failed",
        "cancelled"
      ]
    },
    "type": {
      "type": "string",
      "enum": [
        "single",
        "batch"
      ]
    },
    "preset": {
      "type": "string"
    },
    "progress": {
      "$ref": "#/components/schemas/Progress"
    },
    "createdAt": {
      "type": "string"
    },
    "startedAt": {
      "type": [
        "string",
        "null"
      ]
    },
    "completedAt": {
      "type": [
        "string",
        "null"
      ]
    },
    "fileType": {
      "type": "string"
    },
    "fileName": {
      "type": "string"
    },
    "totalFiles": {
      "type": "integer"
    },
    "result": {
      "$ref": "#/components/schemas/Output"
    },
    "results": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/BatchOutput"
      }
    },
    "error": {
      "type": "string",
      "description": "Single-job failure."
    },
    "errors": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/FileError"
      }
    },
    "drive": {
      "type": "object",
      "properties": {
        "inputFolderId": {
          "type": [
            "string",
            "null"
          ]
        },
        "outputFolderId": {
          "type": [
            "string",
            "null"
          ]
        },
        "uploadResult": {
          "anyOf": [
            {
              "type": "null"
            },
            {
              "$ref": "#/components/schemas/DriveUploadResult"
            }
          ]
        },
        "uploadError": {
          "type": [
            "string",
            "null"
          ]
        }
      }
    }
  },
  "required": [
    "jobId",
    "status",
    "type",
    "preset",
    "progress",
    "createdAt",
    "startedAt",
    "completedAt"
  ],
  "description": "A completed batch may include errors or have no successful results. Check errors and results, plus skipped from POST. For Drive output, processing can reach completed before upload finishes; keep polling until uploadResult or uploadError is present, and inspect uploadResult.errors and uploadResult.cancelled. No outcome field exists in this release."
}
JobList
JSON
{
  "type": "object",
  "properties": {
    "jobs": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/Job"
      }
    },
    "queueLength": {
      "type": "integer"
    },
    "processing": {
      "type": "boolean"
    }
  },
  "required": [
    "jobs",
    "queueLength",
    "processing"
  ]
}

When something does not work

Connection refused

Open Desktop, enable the API, apply settings and verify the port. Keep Desktop running.

401

Check the Bearer API key and update your scripts after regenerating it.

400

Read error and skipped. Check absolute paths, preset names, media extensions and the current job state.

404

Check the /api route, jobId and results index. History is temporary and output files may no longer exist locally.

412

Connect Google Drive in Desktop Settings and check GET /api/drive/status.

429

The queue already holds 10 waiting jobs. Check GET /api/jobs and wait before another submission.

Connection closed / 500

An oversized body/upload or disk error can close the connection. Keep within the size limits and verify output-folder permissions. Inspect job history before retrying a lost submission.

Need a hand with your workflow?

The MetaGhost support team can help.

Support

Contract reviewed September 10, 2026 · Desktop 1.0.81