{
 "openapi": "3.1.0",
 "info": {
  "title": "MarqPDF API",
  "version": "1.0.0",
  "description": "Edit, convert, compress, OCR and organise PDFs from code. Upload a file, run operations on file ids, download the outputs. Docs: https://marqpdf.com/developers (Markdown: https://marqpdf.com/developers.md). SDKs: npm @nucleushq/marqpdf and the Python package nucleushq-marqpdf. AI agents: the same operations as an MCP server at https://marqpdf.com/v1/mcp (Streamable HTTP, the same bearer key), or locally with `npx -y @nucleushq/marqpdf-mcp`. A guide for language models: https://marqpdf.com/llms.txt",
  "contact": {
   "email": "support@marqpdf.com",
   "url": "https://marqpdf.com/developers"
  },
  "termsOfService": "https://marqpdf.com/?page=terms"
 },
 "externalDocs": {
  "description": "MarqPDF API reference: operations, jobs, webhooks, workflows, the agent and the MCP server",
  "url": "https://marqpdf.com/developers"
 },
 "servers": [
  {
   "url": "https://marqpdf.com/v1"
  }
 ],
 "security": [
  {
   "bearer": []
  }
 ],
 "paths": {
  "/files": {
   "post": {
    "operationId": "uploadFile",
    "summary": "Upload a file",
    "description": "multipart/form-data with a `file` field (and optional `name`); the raw bytes as the body with `?name=`; or JSON `{\"url\": …}` to have MarqPDF fetch it (http/https, public addresses only). Up to 100 MB. Files are deleted after 24 hours.",
    "requestBody": {
     "required": true,
     "content": {
      "multipart/form-data": {
       "schema": {
        "type": "object",
        "properties": {
         "file": {
          "type": "string",
          "format": "binary"
         },
         "name": {
          "type": "string"
         }
        },
        "required": [
         "file"
        ]
       }
      },
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "url": {
          "type": "string",
          "format": "uri"
         },
         "name": {
          "type": "string"
         }
        },
        "required": [
         "url"
        ]
       }
      },
      "application/octet-stream": {
       "schema": {
        "type": "string",
        "format": "binary",
        "description": "The raw file; name it with ?name=contract.pdf"
       }
      }
     }
    },
    "parameters": [
     {
      "name": "name",
      "in": "query",
      "schema": {
       "type": "string"
      },
      "description": "File name, for a raw-body upload."
     }
    ],
    "responses": {
     "201": {
      "description": "The stored file",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/File"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "413": {
      "description": "Too large",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "415": {
      "description": "Unsupported type",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   },
   "get": {
    "operationId": "listFiles",
    "summary": "List recent files",
    "parameters": [
     {
      "name": "limit",
      "in": "query",
      "schema": {
       "type": "integer",
       "minimum": 1,
       "maximum": 200,
       "default": 50
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Files, newest first",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "object": {
           "type": "string",
           "const": "list"
          },
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/File"
           }
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/files/{id}": {
   "parameters": [
    {
     "name": "id",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     }
    }
   ],
   "get": {
    "operationId": "getFile",
    "summary": "Get a file's details (and a fresh download_url)",
    "responses": {
     "200": {
      "description": "The file",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/File"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   },
   "delete": {
    "operationId": "deleteFile",
    "summary": "Delete a file now",
    "responses": {
     "200": {
      "description": "Deleted",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "object": {
           "type": "string"
          },
          "id": {
           "type": "string"
          },
          "deleted": {
           "type": "boolean"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/files/{id}/content": {
   "parameters": [
    {
     "name": "id",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     }
    }
   ],
   "get": {
    "operationId": "downloadFile",
    "summary": "Download a file",
    "description": "With the API key, or with no key through a file's signed `download_url` (valid for an hour).",
    "responses": {
     "200": {
      "description": "The file's bytes",
      "content": {
       "application/octet-stream": {
        "schema": {
         "type": "string",
         "format": "binary"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/jobs": {
   "get": {
    "operationId": "listJobs",
    "summary": "List recent jobs",
    "parameters": [
     {
      "name": "limit",
      "in": "query",
      "schema": {
       "type": "integer",
       "minimum": 1,
       "maximum": 100,
       "default": 20
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Jobs, newest first",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "object": {
           "type": "string",
           "const": "list"
          },
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Job"
           }
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/jobs/{id}": {
   "parameters": [
    {
     "name": "id",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     }
    }
   ],
   "get": {
    "operationId": "getJob",
    "summary": "Get a job",
    "responses": {
     "200": {
      "description": "The job",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/usage": {
   "get": {
    "operationId": "getUsage",
    "summary": "This month's operations",
    "responses": {
     "200": {
      "description": "Usage",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Usage"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/workflows": {
   "post": {
    "operationId": "createWorkflow",
    "summary": "Save a workflow",
    "description": "A named chain of 1–10 operations. Each step works on the previous step's output files (a step that only makes data passes its inputs on). Steps are checked when saved.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "name": {
          "type": "string"
         },
         "steps": {
          "type": "array",
          "items": {
           "$ref": "#/components/schemas/Step"
          }
         }
        },
        "required": [
         "name",
         "steps"
        ],
        "example": {
         "name": "Scan to small searchable PDF",
         "steps": [
          {
           "operation": "ocr"
          },
          {
           "operation": "compress",
           "options": {
            "target_mb": 1
           }
          }
         ]
        }
       }
      }
     }
    },
    "responses": {
     "201": {
      "description": "The workflow",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Workflow"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   },
   "get": {
    "operationId": "listWorkflows",
    "summary": "List workflows",
    "responses": {
     "200": {
      "description": "Workflows",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "object": {
           "type": "string",
           "const": "list"
          },
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Workflow"
           }
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/workflows/{id}": {
   "parameters": [
    {
     "name": "id",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     }
    }
   ],
   "get": {
    "operationId": "getWorkflow",
    "summary": "Get a workflow",
    "responses": {
     "200": {
      "description": "The workflow",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Workflow"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   },
   "delete": {
    "operationId": "deleteWorkflow",
    "summary": "Delete a workflow",
    "responses": {
     "200": {
      "description": "Deleted",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/workflows/{id}/run": {
   "parameters": [
    {
     "name": "id",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     }
    }
   ],
   "post": {
    "operationId": "runWorkflow",
    "summary": "Run a saved workflow",
    "description": "One job; output holds the last step's results and output.steps what each step did. Each step that succeeds counts as one operation.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "file": {
          "type": "string"
         },
         "files": {
          "type": "array",
          "items": {
           "type": "string"
          }
         },
         "wait": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300,
          "default": 60
         },
         "webhook_url": {
          "type": "string",
          "format": "uri"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "202": {
      "description": "Still running",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "Free plan's monthly operations used",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/workflows/run": {
   "post": {
    "operationId": "runSteps",
    "summary": "Run steps without saving them",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "steps": {
          "type": "array",
          "items": {
           "$ref": "#/components/schemas/Step"
          }
         },
         "file": {
          "type": "string"
         },
         "files": {
          "type": "array",
          "items": {
           "type": "string"
          }
         },
         "wait": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300,
          "default": 60
         },
         "webhook_url": {
          "type": "string",
          "format": "uri"
         }
        },
        "required": [
         "steps"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "202": {
      "description": "Still running",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "Free plan's monthly operations used",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/models": {
   "get": {
    "operationId": "listModels",
    "summary": "Models this account can use",
    "description": "marqpdf, plus a provider entry for each model key added under Account → Developers → Models, and which model each pipeline uses. Keys are never returned.",
    "responses": {
     "200": {
      "description": "Models",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/ModelList"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/compress": {
   "post": {
    "operationId": "compress",
    "summary": "Compress a PDF",
    "description": "Smallest file that fits under `target_mb`, or a fixed quality `level`. Counts as one operation when it succeeds.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "file": {
          "type": "string",
          "description": "A file id from POST /files.",
          "example": "file_…"
         },
         "target_mb": {
          "type": "number",
          "minimum": 0.05,
          "maximum": 500,
          "example": 1,
          "description": "Fit under this size, at the best quality that does."
         },
         "level": {
          "type": "string",
          "enum": [
           "screen",
           "ebook",
           "printer",
           "prepress"
          ],
          "default": "ebook"
         },
         "wait": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300,
          "default": 60,
          "description": "Seconds to wait for the result before answering 202 with a job to poll. 0 = answer at once."
         },
         "webhook_url": {
          "type": "string",
          "format": "uri",
          "description": "POSTed the finished job, signed with your webhook secret (MarqPDF-Signature)."
         }
        },
        "required": [
         "file"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished: the job with its output",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "202": {
      "description": "Still running: poll GET /jobs/{id} (also in Location)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "Free plan's monthly operations used",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "The operation failed; the body also carries the job",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/merge": {
   "post": {
    "operationId": "merge",
    "summary": "Merge PDFs",
    "description": "Join 2–50 PDFs, in the order given. Counts as one operation when it succeeds.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "files": {
          "type": "array",
          "items": {
           "type": "string"
          },
          "description": "File ids, in order."
         },
         "wait": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300,
          "default": 60,
          "description": "Seconds to wait for the result before answering 202 with a job to poll. 0 = answer at once."
         },
         "webhook_url": {
          "type": "string",
          "format": "uri",
          "description": "POSTed the finished job, signed with your webhook secret (MarqPDF-Signature)."
         }
        },
        "required": [
         "files"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished: the job with its output",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "202": {
      "description": "Still running: poll GET /jobs/{id} (also in Location)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "Free plan's monthly operations used",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "The operation failed; the body also carries the job",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/split": {
   "post": {
    "operationId": "split",
    "summary": "Split a PDF",
    "description": "A ZIP of PDFs: every N pages, or the page ranges you give. Counts as one operation when it succeeds.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "file": {
          "type": "string",
          "description": "A file id from POST /files.",
          "example": "file_…"
         },
         "every": {
          "type": "integer",
          "minimum": 1,
          "default": 1
         },
         "ranges": {
          "type": "array",
          "items": {
           "type": "array",
           "items": {
            "type": "integer",
            "minimum": 1
           },
           "minItems": 2,
           "maxItems": 2
          },
          "example": [
           [
            1,
            3
           ],
           [
            4,
            9
           ]
          ]
         },
         "wait": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300,
          "default": 60,
          "description": "Seconds to wait for the result before answering 202 with a job to poll. 0 = answer at once."
         },
         "webhook_url": {
          "type": "string",
          "format": "uri",
          "description": "POSTed the finished job, signed with your webhook secret (MarqPDF-Signature)."
         }
        },
        "required": [
         "file"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished: the job with its output",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "202": {
      "description": "Still running: poll GET /jobs/{id} (also in Location)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "Free plan's monthly operations used",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "The operation failed; the body also carries the job",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/rotate": {
   "post": {
    "operationId": "rotate",
    "summary": "Rotate pages",
    "description": "Counts as one operation when it succeeds.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "file": {
          "type": "string",
          "description": "A file id from POST /files.",
          "example": "file_…"
         },
         "angle": {
          "type": "integer",
          "enum": [
           90,
           180,
           270
          ]
         },
         "pages": {
          "type": "array",
          "items": {
           "type": "integer",
           "minimum": 1
          },
          "description": "Page numbers, counting from 1."
         },
         "wait": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300,
          "default": 60,
          "description": "Seconds to wait for the result before answering 202 with a job to poll. 0 = answer at once."
         },
         "webhook_url": {
          "type": "string",
          "format": "uri",
          "description": "POSTed the finished job, signed with your webhook secret (MarqPDF-Signature)."
         }
        },
        "required": [
         "file",
         "angle"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished: the job with its output",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "202": {
      "description": "Still running: poll GET /jobs/{id} (also in Location)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "Free plan's monthly operations used",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "The operation failed; the body also carries the job",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/organize": {
   "post": {
    "operationId": "organize",
    "summary": "Reorder or drop pages",
    "description": "Keep `pages`, in that order. Counts as one operation when it succeeds.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "file": {
          "type": "string",
          "description": "A file id from POST /files.",
          "example": "file_…"
         },
         "pages": {
          "type": "array",
          "items": {
           "type": "integer",
           "minimum": 1
          },
          "description": "Page numbers, counting from 1."
         },
         "wait": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300,
          "default": 60,
          "description": "Seconds to wait for the result before answering 202 with a job to poll. 0 = answer at once."
         },
         "webhook_url": {
          "type": "string",
          "format": "uri",
          "description": "POSTed the finished job, signed with your webhook secret (MarqPDF-Signature)."
         }
        },
        "required": [
         "file",
         "pages"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished: the job with its output",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "202": {
      "description": "Still running: poll GET /jobs/{id} (also in Location)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "Free plan's monthly operations used",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "The operation failed; the body also carries the job",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/remove-pages": {
   "post": {
    "operationId": "remove_pages",
    "summary": "Remove pages",
    "description": "Counts as one operation when it succeeds.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "file": {
          "type": "string",
          "description": "A file id from POST /files.",
          "example": "file_…"
         },
         "pages": {
          "type": "array",
          "items": {
           "type": "integer",
           "minimum": 1
          },
          "description": "Page numbers, counting from 1."
         },
         "wait": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300,
          "default": 60,
          "description": "Seconds to wait for the result before answering 202 with a job to poll. 0 = answer at once."
         },
         "webhook_url": {
          "type": "string",
          "format": "uri",
          "description": "POSTed the finished job, signed with your webhook secret (MarqPDF-Signature)."
         }
        },
        "required": [
         "file",
         "pages"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished: the job with its output",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "202": {
      "description": "Still running: poll GET /jobs/{id} (also in Location)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "Free plan's monthly operations used",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "The operation failed; the body also carries the job",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/page-numbers": {
   "post": {
    "operationId": "page_numbers",
    "summary": "Add page numbers",
    "description": "Counts as one operation when it succeeds.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "file": {
          "type": "string",
          "description": "A file id from POST /files.",
          "example": "file_…"
         },
         "position": {
          "type": "string",
          "enum": [
           "bottom-center",
           "bottom-right",
           "bottom-left",
           "top-center",
           "top-right",
           "top-left"
          ],
          "default": "bottom-center"
         },
         "wait": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300,
          "default": 60,
          "description": "Seconds to wait for the result before answering 202 with a job to poll. 0 = answer at once."
         },
         "webhook_url": {
          "type": "string",
          "format": "uri",
          "description": "POSTed the finished job, signed with your webhook secret (MarqPDF-Signature)."
         }
        },
        "required": [
         "file"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished: the job with its output",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "202": {
      "description": "Still running: poll GET /jobs/{id} (also in Location)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "Free plan's monthly operations used",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "The operation failed; the body also carries the job",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/watermark": {
   "post": {
    "operationId": "watermark",
    "summary": "Add a text watermark",
    "description": "Counts as one operation when it succeeds.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "file": {
          "type": "string",
          "description": "A file id from POST /files.",
          "example": "file_…"
         },
         "text": {
          "type": "string",
          "maxLength": 120
         },
         "wait": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300,
          "default": 60,
          "description": "Seconds to wait for the result before answering 202 with a job to poll. 0 = answer at once."
         },
         "webhook_url": {
          "type": "string",
          "format": "uri",
          "description": "POSTed the finished job, signed with your webhook secret (MarqPDF-Signature)."
         }
        },
        "required": [
         "file",
         "text"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished: the job with its output",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "202": {
      "description": "Still running: poll GET /jobs/{id} (also in Location)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "Free plan's monthly operations used",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "The operation failed; the body also carries the job",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/protect": {
   "post": {
    "operationId": "protect",
    "summary": "Password-protect a PDF",
    "description": "Counts as one operation when it succeeds.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "file": {
          "type": "string",
          "description": "A file id from POST /files.",
          "example": "file_…"
         },
         "password": {
          "type": "string"
         },
         "wait": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300,
          "default": 60,
          "description": "Seconds to wait for the result before answering 202 with a job to poll. 0 = answer at once."
         },
         "webhook_url": {
          "type": "string",
          "format": "uri",
          "description": "POSTed the finished job, signed with your webhook secret (MarqPDF-Signature)."
         }
        },
        "required": [
         "file",
         "password"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished: the job with its output",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "202": {
      "description": "Still running: poll GET /jobs/{id} (also in Location)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "Free plan's monthly operations used",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "The operation failed; the body also carries the job",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/unlock": {
   "post": {
    "operationId": "unlock",
    "summary": "Remove a PDF's password",
    "description": "Needs the password unless the PDF only restricts printing or copying. Counts as one operation when it succeeds.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "file": {
          "type": "string",
          "description": "A file id from POST /files.",
          "example": "file_…"
         },
         "password": {
          "type": "string"
         },
         "wait": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300,
          "default": 60,
          "description": "Seconds to wait for the result before answering 202 with a job to poll. 0 = answer at once."
         },
         "webhook_url": {
          "type": "string",
          "format": "uri",
          "description": "POSTed the finished job, signed with your webhook secret (MarqPDF-Signature)."
         }
        },
        "required": [
         "file"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished: the job with its output",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "202": {
      "description": "Still running: poll GET /jobs/{id} (also in Location)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "Free plan's monthly operations used",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "The operation failed; the body also carries the job",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/ocr": {
   "post": {
    "operationId": "ocr",
    "summary": "Make a scanned PDF searchable",
    "description": "Adds a text layer (English and Arabic) under the page images. Counts as one operation when it succeeds.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "file": {
          "type": "string",
          "description": "A file id from POST /files.",
          "example": "file_…"
         },
         "wait": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300,
          "default": 60,
          "description": "Seconds to wait for the result before answering 202 with a job to poll. 0 = answer at once."
         },
         "webhook_url": {
          "type": "string",
          "format": "uri",
          "description": "POSTed the finished job, signed with your webhook secret (MarqPDF-Signature)."
         }
        },
        "required": [
         "file"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished: the job with its output",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "202": {
      "description": "Still running: poll GET /jobs/{id} (also in Location)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "Free plan's monthly operations used",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "The operation failed; the body also carries the job",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/convert": {
   "post": {
    "operationId": "convert",
    "summary": "Convert a file",
    "description": "PDF → docx, xlsx, pptx or jpg (a ZIP of page images); images, Office files, HTML, Markdown or text → pdf. PDF → docx OCRs scans first. Counts as one operation when it succeeds.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "file": {
          "type": "string",
          "description": "A file id from POST /files.",
          "example": "file_…"
         },
         "to": {
          "type": "string",
          "enum": [
           "pdf",
           "docx",
           "xlsx",
           "pptx",
           "jpg"
          ]
         },
         "wait": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300,
          "default": 60,
          "description": "Seconds to wait for the result before answering 202 with a job to poll. 0 = answer at once."
         },
         "webhook_url": {
          "type": "string",
          "format": "uri",
          "description": "POSTed the finished job, signed with your webhook secret (MarqPDF-Signature)."
         }
        },
        "required": [
         "file",
         "to"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished: the job with its output",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "202": {
      "description": "Still running: poll GET /jobs/{id} (also in Location)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "Free plan's monthly operations used",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "The operation failed; the body also carries the job",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/images-to-pdf": {
   "post": {
    "operationId": "images_to_pdf",
    "summary": "Images to one PDF",
    "description": "One page per image, in the order given. Counts as one operation when it succeeds.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "files": {
          "type": "array",
          "items": {
           "type": "string"
          },
          "description": "File ids, in order."
         },
         "wait": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300,
          "default": 60,
          "description": "Seconds to wait for the result before answering 202 with a job to poll. 0 = answer at once."
         },
         "webhook_url": {
          "type": "string",
          "format": "uri",
          "description": "POSTed the finished job, signed with your webhook secret (MarqPDF-Signature)."
         }
        },
        "required": [
         "files"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished: the job with its output",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "202": {
      "description": "Still running: poll GET /jobs/{id} (also in Location)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "Free plan's monthly operations used",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "The operation failed; the body also carries the job",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/compare": {
   "post": {
    "operationId": "compare",
    "summary": "Compare two PDFs",
    "description": "A PDF marking what changed between `files[0]` and `files[1]`. Counts as one operation when it succeeds.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "files": {
          "type": "array",
          "items": {
           "type": "string"
          },
          "description": "File ids, in order."
         },
         "wait": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300,
          "default": 60,
          "description": "Seconds to wait for the result before answering 202 with a job to poll. 0 = answer at once."
         },
         "webhook_url": {
          "type": "string",
          "format": "uri",
          "description": "POSTed the finished job, signed with your webhook secret (MarqPDF-Signature)."
         }
        },
        "required": [
         "files"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished: the job with its output",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "202": {
      "description": "Still running: poll GET /jobs/{id} (also in Location)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "Free plan's monthly operations used",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "The operation failed; the body also carries the job",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/extract-text": {
   "post": {
    "operationId": "extract_text",
    "summary": "Extract text",
    "description": "The PDF's text (also as a .txt output file). `ocr: auto` reads scans with OCR when there's no text layer. Counts as one operation when it succeeds.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "file": {
          "type": "string",
          "description": "A file id from POST /files.",
          "example": "file_…"
         },
         "ocr": {
          "type": "string",
          "enum": [
           "auto",
           "always",
           "never"
          ],
          "default": "auto"
         },
         "wait": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300,
          "default": 60,
          "description": "Seconds to wait for the result before answering 202 with a job to poll. 0 = answer at once."
         },
         "webhook_url": {
          "type": "string",
          "format": "uri",
          "description": "POSTed the finished job, signed with your webhook secret (MarqPDF-Signature)."
         }
        },
        "required": [
         "file"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished: the job with its output",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "202": {
      "description": "Still running: poll GET /jobs/{id} (also in Location)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "Free plan's monthly operations used",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "The operation failed; the body also carries the job",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/extract": {
   "post": {
    "operationId": "extract",
    "summary": "Extract data with AI",
    "description": "Read a PDF or an image (invoice, ID, statement, form) and return the fields you ask for as JSON in output.data (also saved as a .json output file). Name the fields, or pass a JSON Schema. A field the document doesn't contain comes back null. Runs on MarqPDF's model or one of your own (model). Counts as one operation when it succeeds.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "file": {
          "type": "string",
          "description": "A file id from POST /files.",
          "example": "file_…"
         },
         "fields": {
          "type": "array",
          "items": {
           "anyOf": [
            {
             "type": "string"
            },
            {
             "type": "object",
             "properties": {
              "name": {
               "type": "string"
              },
              "type": {
               "type": "string",
               "enum": [
                "string",
                "number",
                "integer",
                "boolean",
                "date",
                "list"
               ]
              },
              "description": {
               "type": "string"
              }
             },
             "required": [
              "name"
             ]
            }
           ]
          },
          "description": "What to extract: names, or {name, type, description}.",
          "example": [
           "invoice_number",
           {
            "name": "total",
            "type": "number"
           },
           {
            "name": "due_date",
            "type": "date"
           }
          ]
         },
         "schema": {
          "type": "object",
          "description": "Instead of fields: a JSON Schema for the result (an object)."
         },
         "instructions": {
          "type": "string",
          "description": "Extra guidance, e.g. 'amounts in AED'."
         },
         "model": {
          "type": "string",
          "description": "\"marqpdf\" (the default) or \"provider/model\" on a key you've added, e.g. \"anthropic/claude-opus-5-5\", \"openai/gpt-5.1\", \"gemini/gemini-2.5-pro\", \"openrouter/…\", \"custom/…\".",
          "example": "anthropic/claude-opus-5-5"
         },
         "wait": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300,
          "default": 60,
          "description": "Seconds to wait for the result before answering 202 with a job to poll. 0 = answer at once."
         },
         "webhook_url": {
          "type": "string",
          "format": "uri",
          "description": "POSTed the finished job, signed with your webhook secret (MarqPDF-Signature)."
         }
        },
        "required": [
         "file"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished: the job with its output",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "202": {
      "description": "Still running: poll GET /jobs/{id} (also in Location)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "Free plan's monthly operations used",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "The operation failed; the body also carries the job",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/ask": {
   "post": {
    "operationId": "ask",
    "summary": "Ask a document",
    "description": "Answer a question about a PDF or an image, from the document only; the answer is in output.text. Runs on MarqPDF's model or one of your own (model). Counts as one operation when it succeeds.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "file": {
          "type": "string",
          "description": "A file id from POST /files.",
          "example": "file_…"
         },
         "question": {
          "type": "string"
         },
         "instructions": {
          "type": "string",
          "description": "Extra guidance, e.g. 'answer in one sentence'."
         },
         "model": {
          "type": "string",
          "description": "\"marqpdf\" (the default) or \"provider/model\" on a key you've added, e.g. \"anthropic/claude-opus-5-5\", \"openai/gpt-5.1\", \"gemini/gemini-2.5-pro\", \"openrouter/…\", \"custom/…\".",
          "example": "anthropic/claude-opus-5-5"
         },
         "wait": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300,
          "default": 60,
          "description": "Seconds to wait for the result before answering 202 with a job to poll. 0 = answer at once."
         },
         "webhook_url": {
          "type": "string",
          "format": "uri",
          "description": "POSTed the finished job, signed with your webhook secret (MarqPDF-Signature)."
         }
        },
        "required": [
         "file",
         "question"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished: the job with its output",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "202": {
      "description": "Still running: poll GET /jobs/{id} (also in Location)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "Free plan's monthly operations used",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "The operation failed; the body also carries the job",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/agent": {
   "post": {
    "operationId": "agent",
    "summary": "Run the agent",
    "description": "Give a goal in plain words; the agent plans and runs MarqPDF operations on your files with the model you chose (pipeline \"agent\"), reading each result before the next step. output.text is its summary, output.files the files it hands back, output.steps every call it made. Each operation it runs counts as one. Counts as one operation when it succeeds.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "files": {
          "type": "array",
          "items": {
           "type": "string"
          },
          "description": "File ids, in order."
         },
         "goal": {
          "type": "string",
          "description": "What to do, e.g. 'make these scans searchable, merge them in date order and get the result under 2 MB'."
         },
         "max_steps": {
          "type": "integer",
          "minimum": 1,
          "maximum": 15,
          "default": 8,
          "description": "Model turns before it stops."
         },
         "model": {
          "type": "string",
          "description": "\"marqpdf\" (the default) or \"provider/model\" on a key you've added, e.g. \"anthropic/claude-opus-5-5\", \"openai/gpt-5.1\", \"gemini/gemini-2.5-pro\", \"openrouter/…\", \"custom/…\".",
          "example": "anthropic/claude-opus-5-5"
         },
         "wait": {
          "type": "integer",
          "minimum": 0,
          "maximum": 300,
          "default": 60,
          "description": "Seconds to wait for the result before answering 202 with a job to poll. 0 = answer at once."
         },
         "webhook_url": {
          "type": "string",
          "format": "uri",
          "description": "POSTed the finished job, signed with your webhook secret (MarqPDF-Signature)."
         }
        },
        "required": [
         "files",
         "goal"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Finished: the job with its output",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "202": {
      "description": "Still running: poll GET /jobs/{id} (also in Location)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Job"
        }
       }
      }
     },
     "400": {
      "description": "Invalid request",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid API key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "402": {
      "description": "Free plan's monthly operations used",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "404": {
      "description": "Unknown file or operation",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "410": {
      "description": "File expired (kept 24 hours)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "429": {
      "description": "Rate limit or job queue full (see Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "502": {
      "description": "The operation failed; the body also carries the job",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  }
 },
 "components": {
  "securitySchemes": {
   "bearer": {
    "type": "http",
    "scheme": "bearer",
    "description": "An API key, mp_live_…, from Account → Developers on marqpdf.com."
   }
  },
  "schemas": {
   "Error": {
    "type": "object",
    "properties": {
     "error": {
      "type": "object",
      "properties": {
       "code": {
        "type": "string",
        "example": "invalid_request"
       },
       "message": {
        "type": "string"
       }
      },
      "required": [
       "code",
       "message"
      ]
     },
     "job": {
      "$ref": "#/components/schemas/Job"
     }
    },
    "required": [
     "error"
    ]
   },
   "File": {
    "type": "object",
    "properties": {
     "object": {
      "type": "string",
      "const": "file"
     },
     "id": {
      "type": "string",
      "example": "file_8rX2…"
     },
     "name": {
      "type": "string"
     },
     "size": {
      "type": "integer"
     },
     "content_type": {
      "type": "string"
     },
     "pages": {
      "type": "integer"
     },
     "created_at": {
      "type": "string",
      "format": "date-time"
     },
     "expires_at": {
      "type": "string",
      "format": "date-time"
     },
     "source": {
      "type": "string",
      "enum": [
       "upload",
       "url",
       "job"
      ]
     },
     "job": {
      "type": "string"
     },
     "download_url": {
      "type": "string",
      "format": "uri",
      "description": "Works without a key for an hour."
     }
    },
    "required": [
     "object",
     "id",
     "name",
     "size",
     "content_type",
     "created_at",
     "expires_at",
     "source",
     "download_url"
    ]
   },
   "Job": {
    "type": "object",
    "properties": {
     "object": {
      "type": "string",
      "const": "job"
     },
     "id": {
      "type": "string",
      "example": "job_3Fq…"
     },
     "operation": {
      "type": "string"
     },
     "status": {
      "type": "string",
      "enum": [
       "queued",
       "running",
       "succeeded",
       "failed"
      ]
     },
     "created_at": {
      "type": "string",
      "format": "date-time"
     },
     "started_at": {
      "type": [
       "string",
       "null"
      ]
     },
     "finished_at": {
      "type": [
       "string",
       "null"
      ]
     },
     "input": {
      "type": "object",
      "properties": {
       "files": {
        "type": "array",
        "items": {
         "type": "string"
        }
       },
       "options": {
        "type": "object"
       }
      }
     },
     "output": {
      "type": [
       "object",
       "null"
      ],
      "properties": {
       "files": {
        "type": "array",
        "items": {
         "$ref": "#/components/schemas/File"
        }
       },
       "text": {
        "type": "string"
       },
       "text_truncated": {
        "type": "boolean"
       },
       "note": {
        "type": "string"
       },
       "data": {
        "type": "object",
        "description": "extract: the data"
       },
       "model": {
        "type": "string",
        "description": "AI operations: the model that answered"
       },
       "steps": {
        "type": "array",
        "items": {
         "type": "object"
        },
        "description": "Workflows and the agent: each step"
       }
      }
     },
     "error": {
      "type": [
       "object",
       "null"
      ],
      "properties": {
       "code": {
        "type": "string"
       },
       "message": {
        "type": "string"
       }
      }
     },
     "webhook_url": {
      "type": [
       "string",
       "null"
      ]
     },
     "webhook": {
      "type": [
       "object",
       "null"
      ]
     }
    },
    "required": [
     "object",
     "id",
     "operation",
     "status",
     "created_at"
    ]
   },
   "ModelList": {
    "type": "object",
    "properties": {
     "object": {
      "type": "string",
      "const": "list"
     },
     "data": {
      "type": "array",
      "items": {
       "type": "object"
      }
     },
     "pipelines": {
      "type": "object"
     }
    }
   },
   "Step": {
    "type": "object",
    "properties": {
     "operation": {
      "type": "string",
      "example": "compress"
     },
     "options": {
      "type": "object",
      "example": {
       "target_mb": 1
      }
     }
    },
    "required": [
     "operation"
    ]
   },
   "Workflow": {
    "type": "object",
    "properties": {
     "object": {
      "type": "string",
      "const": "workflow"
     },
     "id": {
      "type": "string",
      "example": "wf_…"
     },
     "name": {
      "type": "string"
     },
     "steps": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Step"
      }
     },
     "created_at": {
      "type": "string",
      "format": "date-time"
     }
    }
   },
   "Usage": {
    "type": "object",
    "properties": {
     "object": {
      "type": "string",
      "const": "usage"
     },
     "month": {
      "type": "string",
      "example": "2026-10"
     },
     "operations": {
      "type": "integer"
     },
     "limit": {
      "type": [
       "integer",
       "null"
      ],
      "description": "null = unlimited"
     },
     "plan": {
      "type": "string",
      "enum": [
       "free",
       "week",
       "pro",
       "team"
      ]
     }
    }
   }
  }
 }
}