{
  "openapi": "3.1.0",
  "info": {
    "title": "MetaGhost Desktop 1.0.81 Local API",
    "version": "1.0.81",
    "description": "Released Desktop 1.0.81 API. Preset-driven image/video spoofing with local files or Google Drive. OpenAPI 3.1 describes this file format; the release uses unversioned /api routes."
  },
  "servers": [
    {
      "url": "http://127.0.0.1:3847/api",
      "description": "Default local Desktop server. Replace the port if changed in Settings > API."
    }
  ],
  "security": [
    {
      "LocalApiKey": []
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "summary": "Check Desktop 1.0.81 and active jobs",
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          }
        },
        "security": []
      }
    },
    "/presets": {
      "get": {
        "summary": "List saved image and video preset names",
        "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"
                }
              }
            }
          }
        }
      }
    },
    "/drive/status": {
      "get": {
        "summary": "Check the Desktop Google Drive connection",
        "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"
                }
              }
            }
          }
        }
      }
    },
    "/upload": {
      "post": {
        "summary": "Upload raw image or video bytes",
        "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"
                }
              }
            }
          }
        },
        "description": "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.",
        "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."
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        }
      }
    },
    "/jobs": {
      "post": {
        "summary": "Create an image/video spoofing job",
        "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"
                }
              }
            }
          }
        },
        "description": "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.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SpoofJobRequest"
              },
              "example": {
                "filePath": "C:\\Media\\photo.jpg",
                "outputFolder": "C:\\Media\\output",
                "batchCount": 1
              }
            }
          }
        }
      },
      "get": {
        "summary": "List the 50 most recent jobs and queue state",
        "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"
                }
              }
            }
          }
        },
        "description": "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."
      },
      "delete": {
        "summary": "Clear finished jobs from in-memory history",
        "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"
                }
              }
            }
          }
        }
      }
    },
    "/jobs/{jobId}": {
      "parameters": [
        {
          "name": "jobId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[a-zA-Z0-9_-]+$"
          }
        }
      ],
      "get": {
        "summary": "Read one job's progress and results",
        "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"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Request cancellation of a queued or processing job",
        "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"
                }
              }
            }
          }
        },
        "description": "Inspect status again after cancellation. Already-produced files may remain. The currently processing batch file may finish before the queue stops."
      }
    },
    "/download/{jobId}": {
      "parameters": [
        {
          "name": "jobId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[a-zA-Z0-9_-]+$"
          }
        }
      ],
      "get": {
        "summary": "Download an existing local output of a completed job",
        "description": "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.",
        "parameters": [
          {
            "name": "index",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "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"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "LocalApiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Key generated in Desktop Settings > API."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "skipped": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FileError"
            }
          }
        },
        "required": [
          "error"
        ]
      },
      "FileError": {
        "type": "object",
        "properties": {
          "file": {
            "type": "string"
          },
          "error": {
            "type": "string"
          }
        },
        "required": [
          "file",
          "error"
        ]
      },
      "Health": {
        "type": "object",
        "properties": {
          "status": {
            "const": "ok"
          },
          "version": {
            "const": "1.0.81"
          },
          "activeJobs": {
            "type": "integer",
            "minimum": 0
          }
        },
        "required": [
          "status",
          "version",
          "activeJobs"
        ]
      },
      "Presets": {
        "type": "object",
        "properties": {
          "image": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "video": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "image",
          "video"
        ]
      },
      "DriveStatus": {
        "type": "object",
        "properties": {
          "clientConfigured": {
            "type": "boolean"
          },
          "connected": {
            "type": "boolean"
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "scope": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "clientConfigured",
          "connected",
          "email",
          "scope"
        ]
      },
      "DriveUploadResult": {
        "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": {
        "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": {
        "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": {
        "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": {
        "type": "object",
        "properties": {
          "outputPath": {
            "type": "string"
          }
        },
        "required": [
          "outputPath"
        ]
      },
      "BatchOutput": {
        "type": "object",
        "properties": {
          "file": {
            "type": "string"
          },
          "outputPath": {
            "type": "string"
          }
        },
        "required": [
          "file",
          "outputPath"
        ]
      },
      "Progress": {
        "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": {
        "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": {
        "type": "object",
        "properties": {
          "jobs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Job"
            }
          },
          "queueLength": {
            "type": "integer"
          },
          "processing": {
            "type": "boolean"
          }
        },
        "required": [
          "jobs",
          "queueLength",
          "processing"
        ]
      }
    }
  }
}
