{
  "openapi": "3.0.3",
  "info": {
    "title": "PodPDF API",
    "version": "1.0.0",
    "description": "Generate PDFs from HTML, Markdown, URLs, images and saved templates, and merge, split or\nedit existing documents.\n\nThis spec covers the endpoints an **API key** can call. The dashboard-only routes\n(account, billing, key and webhook management, template CRUD) need a Cognito ID token and\nare left out deliberately: an API key is rejected on them at the gateway.\n\nEach successful PDF costs one credit ($0.01), taken from the monthly allowance first and\nthen from credit packs. An account with no paid plan gets `402 UPGRADE_REQUIRED`.\n",
    "contact": {
      "name": "PodPDF support",
      "email": "podpdfapp@gmail.com",
      "url": "https://podpdf.com"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://podpdf.com/terms/"
    }
  },
  "servers": [
    {
      "url": "https://api.podpdf.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "tags": [
    {
      "name": "Convert",
      "description": "Turn content into a PDF."
    },
    {
      "name": "Jobs",
      "description": "Check jobs and fetch their output."
    },
    {
      "name": "Templates",
      "description": "Render a saved template."
    },
    {
      "name": "PDF tools",
      "description": "Merge, split and inspect existing PDFs."
    },
    {
      "name": "PDF editor",
      "description": "Stamp, number, redact and rewrite an uploaded document."
    },
    {
      "name": "Account",
      "description": "Who the key belongs to, and the public plan list."
    }
  ],
  "paths": {
    "/me": {
      "get": {
        "operationId": "getMe",
        "tags": [
          "Account"
        ],
        "summary": "Get the authenticated account",
        "description": "Returns the account behind the key. Useful as a connection test.",
        "responses": {
          "200": {
            "description": "The account",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "example": "01KHD59K7TY1EN0JP9BMY7XGHG"
                    },
                    "email": {
                      "type": "string",
                      "format": "email",
                      "example": "someone@example.com"
                    },
                    "name": {
                      "type": "string",
                      "nullable": true,
                      "example": "Jane Doe"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/accounts/me/subscription/plans": {
      "get": {
        "operationId": "listPublicPlans",
        "tags": [
          "Account"
        ],
        "summary": "List the active plans",
        "description": "Public: no credentials needed.",
        "security": [],
        "responses": {
          "200": {
            "description": "The active plans",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "plans": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "price_id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "price": {
                            "type": "number"
                          },
                          "monthly_pdfs": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/quickjob": {
      "post": {
        "operationId": "createQuickJob",
        "tags": [
          "Convert"
        ],
        "summary": "Convert to PDF, synchronously",
        "description": "Up to 25 pages, returned within 30 seconds. Send `store: true` for JSON with a signed\ndownload link (valid 1 hour); omit it to get the PDF bytes in the response body.\n\nLarger or slower documents time out with `408 QUICKJOB_TIMEOUT` — use `/longjob`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuickJobRequest"
              },
              "examples": {
                "html": {
                  "summary": "HTML",
                  "value": {
                    "input_type": "html",
                    "html": "<h1>Invoice</h1><p>Thank you.</p>",
                    "options": {
                      "format": "A4",
                      "printBackground": true
                    },
                    "store": true
                  }
                },
                "markdown": {
                  "summary": "Markdown",
                  "value": {
                    "input_type": "markdown",
                    "markdown": "# Report\n\n**Customer:** Acme Ltd",
                    "store": true
                  }
                },
                "url": {
                  "summary": "Public HTTPS URL",
                  "value": {
                    "input_type": "url",
                    "url": "https://example.com/",
                    "store": true
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "input_type",
                  "images"
                ],
                "properties": {
                  "input_type": {
                    "type": "string",
                    "enum": [
                      "image"
                    ]
                  },
                  "images": {
                    "type": "array",
                    "description": "PNG or JPEG files; each becomes one page. Up to 25, 5 MB each.",
                    "items": {
                      "type": "string",
                      "format": "binary"
                    }
                  },
                  "options": {
                    "type": "string",
                    "description": "JSON string of options, e.g. `{\"format\":\"A4\",\"fit\":\"contain\"}`."
                  },
                  "store": {
                    "type": "string",
                    "enum": [
                      "true",
                      "false"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The PDF, or JSON when `store` is true",
            "headers": {
              "X-Job-Id": {
                "$ref": "#/components/headers/XJobId"
              },
              "X-PDF-Pages": {
                "$ref": "#/components/headers/XPdfPages"
              }
            },
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StoredPdf"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/UpgradeRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "408": {
            "description": "Rendering took longer than 30 seconds; use `/longjob`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/longjob": {
      "post": {
        "operationId": "createLongJob",
        "tags": [
          "Convert"
        ],
        "summary": "Queue a large document",
        "description": "Asynchronous rendering for HTML or Markdown up to 100 pages. The document is rendered\nonce at submission to count its pages, so this call can take nearly 30 seconds and\nreturns `400 PAGE_LIMIT_EXCEEDED` immediately when the document is too long.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "input_type"
                ],
                "properties": {
                  "input_type": {
                    "type": "string",
                    "enum": [
                      "html",
                      "markdown"
                    ]
                  },
                  "html": {
                    "type": "string"
                  },
                  "markdown": {
                    "type": "string"
                  },
                  "options": {
                    "$ref": "#/components/schemas/PdfOptions"
                  },
                  "webhook_url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Validated but ignored; register webhooks through the dashboard."
                  }
                }
              },
              "example": {
                "input_type": "markdown",
                "markdown": "# Large report\n\nOne section per page…",
                "options": {
                  "format": "A4"
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "job_id": {
                      "$ref": "#/components/schemas/JobId"
                    },
                    "status": {
                      "type": "string",
                      "example": "queued"
                    },
                    "message": {
                      "type": "string"
                    },
                    "estimated_completion": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Always three minutes after submission; a placeholder, not an estimate."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/UpgradeRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/bulkjob": {
      "post": {
        "operationId": "createBulkJob",
        "tags": [
          "Convert"
        ],
        "summary": "Convert a bundle of files",
        "description": "Converts every file in a ZIP or file list that matches `bundle_type`; the rest are\ntreated as assets for the pages. Send exactly one of `s3_key`, `zip_base64` or\n`files`, or use multipart. Requires `bulk_enabled` on the plan.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "bundle_type": {
                    "type": "string",
                    "enum": [
                      "html",
                      "markdown"
                    ],
                    "default": "html"
                  },
                  "s3_key": {
                    "type": "string",
                    "description": "Key from `/bulkjob/upload-url`."
                  },
                  "zip_base64": {
                    "type": "string",
                    "description": "Base64 ZIP, up to 4 MB decoded."
                  },
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "path": {
                          "type": "string",
                          "example": "invoice-1.html"
                        },
                        "content_base64": {
                          "type": "string"
                        }
                      }
                    }
                  },
                  "options": {
                    "$ref": "#/components/schemas/PdfOptions"
                  }
                }
              },
              "example": {
                "bundle_type": "html",
                "files": [
                  {
                    "path": "invoice-1.html",
                    "content_base64": "PGgxPkludm9pY2UgMTwvaDE+"
                  }
                ]
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "bundle_type": {
                    "type": "string",
                    "enum": [
                      "html",
                      "markdown"
                    ]
                  },
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "binary"
                    }
                  },
                  "paths": {
                    "type": "string",
                    "description": "JSON array of relative paths, in the same order as the files."
                  },
                  "options": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "job_id": {
                      "$ref": "#/components/schemas/JobId"
                    },
                    "status": {
                      "type": "string",
                      "example": "queued"
                    },
                    "job_type": {
                      "type": "string",
                      "example": "bulk"
                    },
                    "bundle_type": {
                      "type": "string",
                      "example": "html"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/UpgradeRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/bulkjob/upload-url": {
      "post": {
        "operationId": "createBulkUploadUrl",
        "tags": [
          "Convert"
        ],
        "summary": "Get a signed URL for a large ZIP",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "content_length_bytes"
                ],
                "properties": {
                  "content_length_bytes": {
                    "type": "integer",
                    "example": 15728640
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Where to PUT the ZIP",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "upload_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "s3_key": {
                      "type": "string"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "max_bytes": {
                      "type": "integer"
                    },
                    "required_headers": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/jobs/{job_id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/JobIdPath"
        }
      ],
      "get": {
        "operationId": "getJob",
        "tags": [
          "Jobs"
        ],
        "summary": "Get a job",
        "responses": {
          "200": {
            "description": "The job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/jobs/{job_id}/download": {
      "parameters": [
        {
          "$ref": "#/components/parameters/JobIdPath"
        }
      ],
      "get": {
        "operationId": "getJobDownloadLink",
        "tags": [
          "Jobs"
        ],
        "summary": "Get a fresh download link",
        "description": "Works for any job with stored output: long, bulk, merge and split jobs, and quick or\ntemplate jobs sent with `store: true`. Links last one hour; files are kept 30 days.\n",
        "responses": {
          "200": {
            "description": "A signed link",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "job_id": {
                      "$ref": "#/components/schemas/JobId"
                    },
                    "download_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "expires_in_seconds": {
                      "type": "integer",
                      "example": 3600
                    },
                    "content_type": {
                      "type": "string",
                      "example": "application/pdf"
                    },
                    "size_bytes": {
                      "type": "integer",
                      "example": 1843200
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The job has not finished yet",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          }
        }
      }
    },
    "/jobs/{job_id}/files": {
      "parameters": [
        {
          "$ref": "#/components/parameters/JobIdPath"
        }
      ],
      "get": {
        "operationId": "listJobFiles",
        "tags": [
          "Jobs"
        ],
        "summary": "List the files a bulk job produced",
        "responses": {
          "200": {
            "description": "One entry per file in the bundle",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "job_id": {
                      "$ref": "#/components/schemas/JobId"
                    },
                    "files": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "index": {
                            "type": "integer"
                          },
                          "path": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "success",
                              "failed",
                              "skipped"
                            ]
                          },
                          "pages": {
                            "type": "integer",
                            "nullable": true
                          },
                          "error_message": {
                            "type": "string",
                            "nullable": true
                          },
                          "blocked_requests": {
                            "type": "integer"
                          },
                          "missing_assets": {
                            "type": "integer"
                          },
                          "render_timeout": {
                            "type": "boolean"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/jobs/{job_id}/files/{file_index}/download": {
      "parameters": [
        {
          "$ref": "#/components/parameters/JobIdPath"
        },
        {
          "name": "file_index",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          },
          "description": "Index from `GET /jobs/{job_id}/files`."
        }
      ],
      "get": {
        "operationId": "getJobFileDownloadLink",
        "tags": [
          "Jobs"
        ],
        "summary": "Get a download link for one file of a bulk job",
        "responses": {
          "200": {
            "description": "A signed link",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "download_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          }
        }
      }
    },
    "/templates/{template_id}/render": {
      "parameters": [
        {
          "name": "template_id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "From the dashboard, under Templates → Use via API."
        }
      ],
      "post": {
        "operationId": "renderTemplate",
        "tags": [
          "Templates"
        ],
        "summary": "Render a saved template",
        "description": "Send the field values only; layout, totals, currency formatting and page numbering come\nfrom the template. Nested fields are nested objects, and list fields are arrays of\nobjects. Up to 1 MB of data.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "additionalProperties": true
                  },
                  "filename": {
                    "type": "string"
                  },
                  "store": {
                    "type": "boolean",
                    "default": false
                  }
                }
              },
              "example": {
                "data": {
                  "invoice_number": "INV-1042",
                  "customer": {
                    "name": "Acme Ltd",
                    "address": "1 Main St"
                  },
                  "items": [
                    {
                      "description": "Design work",
                      "quantity": 10,
                      "unit_price": 85
                    }
                  ],
                  "tax_rate": 20
                },
                "store": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The PDF, or JSON when `store` is true",
            "headers": {
              "X-Job-Id": {
                "$ref": "#/components/headers/XJobId"
              },
              "X-PDF-Pages": {
                "$ref": "#/components/headers/XPdfPages"
              }
            },
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/StoredPdf"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "template_id": {
                          "type": "string"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/UpgradeRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          }
        }
      }
    },
    "/pdf/merge": {
      "post": {
        "operationId": "mergePdfs",
        "tags": [
          "PDF tools"
        ],
        "summary": "Merge PDFs",
        "description": "One `files` part per PDF, in merge order. Up to 4 MB in total; larger sets go through `/pdf/upload-url` and `/pdf/jobs`.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "files"
                ],
                "properties": {
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "binary"
                    }
                  },
                  "page_ranges": {
                    "type": "string",
                    "description": "JSON array with one entry per file — a page range, or null for every page."
                  },
                  "options": {
                    "type": "string",
                    "description": "JSON object; `output_filename`, `title`."
                  },
                  "store": {
                    "type": "string",
                    "enum": [
                      "true",
                      "false"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The merged PDF, or JSON when `store` is true",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StoredPdf"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          }
        }
      }
    },
    "/pdf/split": {
      "post": {
        "operationId": "splitPdf",
        "tags": [
          "PDF tools"
        ],
        "summary": "Split a PDF",
        "description": "Modes are `extract`, `ranges`, `every_n` and `each_page`. A multi-file result comes back as a ZIP.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file",
                  "split"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "split": {
                    "type": "string",
                    "description": "JSON object, e.g. `{\"mode\":\"extract\",\"pages\":\"1-3\"}`."
                  },
                  "options": {
                    "type": "string",
                    "description": "JSON object; `filename_pattern`."
                  },
                  "store": {
                    "type": "string",
                    "enum": [
                      "true",
                      "false"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The PDF or ZIP, or JSON when `store` is true",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StoredPdf"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          }
        }
      }
    },
    "/pdf/inspect": {
      "post": {
        "operationId": "inspectPdf",
        "tags": [
          "PDF tools"
        ],
        "summary": "Inspect a PDF",
        "description": "Page count, page sizes, and the page ids an editor recipe refers to. Free — no credit is spent.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "s3_key": {
                    "type": "string"
                  },
                  "filename": {
                    "type": "string"
                  },
                  "for": {
                    "type": "string",
                    "enum": [
                      "edit"
                    ],
                    "description": "Include the page ids used by the editor."
                  }
                }
              },
              "example": {
                "s3_key": "pdf-uploads/01HZ…/01J8…pdf",
                "for": "edit"
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "What the file contains",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "filename": {
                      "type": "string"
                    },
                    "size_bytes": {
                      "type": "integer"
                    },
                    "page_count": {
                      "type": "integer"
                    },
                    "encrypted": {
                      "type": "boolean"
                    },
                    "has_forms": {
                      "type": "boolean"
                    },
                    "has_signatures": {
                      "type": "boolean"
                    },
                    "pages": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "example": "p0"
                          },
                          "width": {
                            "type": "number"
                          },
                          "height": {
                            "type": "number"
                          },
                          "rotation": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/pdf/upload-url": {
      "post": {
        "operationId": "createPdfUploadUrl",
        "tags": [
          "PDF tools"
        ],
        "summary": "Get a signed URL for a large PDF",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "content_length_bytes"
                ],
                "properties": {
                  "content_length_bytes": {
                    "type": "integer",
                    "example": 482913
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Where to PUT the file",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "upload_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "s3_key": {
                      "type": "string"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/pdf/jobs": {
      "post": {
        "operationId": "createPdfToolJob",
        "tags": [
          "PDF tools"
        ],
        "summary": "Merge or split uploaded PDFs as a job",
        "description": "For sets over 4 MB, uploaded with `/pdf/upload-url` first.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "operation",
                  "inputs"
                ],
                "properties": {
                  "operation": {
                    "type": "string",
                    "enum": [
                      "merge",
                      "split"
                    ]
                  },
                  "inputs": {
                    "type": "array",
                    "description": "2 to 100 entries for merge, exactly 1 for split.",
                    "items": {
                      "type": "object",
                      "properties": {
                        "s3_key": {
                          "type": "string"
                        },
                        "filename": {
                          "type": "string"
                        },
                        "pages": {
                          "type": "string",
                          "description": "Page range; merge only."
                        }
                      }
                    }
                  },
                  "split": {
                    "type": "object",
                    "additionalProperties": true
                  },
                  "options": {
                    "type": "object",
                    "additionalProperties": true
                  }
                }
              },
              "example": {
                "operation": "split",
                "inputs": [
                  {
                    "s3_key": "pdf-uploads/01HZ…/01J8…pdf",
                    "filename": "payslips.pdf"
                  }
                ],
                "split": {
                  "mode": "each_page"
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "job_id": {
                      "$ref": "#/components/schemas/JobId"
                    },
                    "status": {
                      "type": "string",
                      "example": "queued"
                    },
                    "operation": {
                      "type": "string",
                      "example": "split"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/pdf/edit/preflight": {
      "post": {
        "operationId": "preflightPdfEdit",
        "tags": [
          "PDF editor"
        ],
        "summary": "See what a recipe would do",
        "description": "Reports the effect of the recipe without applying it. Free.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EditRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "What would happen",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          }
        }
      }
    },
    "/pdf/edit": {
      "post": {
        "operationId": "editPdf",
        "tags": [
          "PDF editor"
        ],
        "summary": "Apply a recipe to a document",
        "description": "Applies the operations in order — stamps, page numbers, watermarks, rotation, metadata\nand active content — and returns a link to the new document. Saving rewrites the whole\nfile, so an existing digital signature stops validating.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EditRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The edited document",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "example": "completed"
                    },
                    "download_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "output_pages": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/UpgradeRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          }
        }
      }
    },
    "/pdf/extract": {
      "post": {
        "operationId": "extractPdfText",
        "tags": [
          "PDF editor"
        ],
        "summary": "Read the text inside a box",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "source_key"
                ],
                "properties": {
                  "source_key": {
                    "type": "string"
                  },
                  "page": {
                    "type": "string",
                    "example": "p0"
                  },
                  "box": {
                    "type": "object",
                    "properties": {
                      "x": {
                        "type": "number"
                      },
                      "y": {
                        "type": "number"
                      },
                      "width": {
                        "type": "number"
                      },
                      "height": {
                        "type": "number"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The text found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "text": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Create one at https://app.podpdf.com under Settings → API Keys."
      }
    },
    "headers": {
      "XJobId": {
        "description": "The job this PDF came from.",
        "schema": {
          "type": "string"
        }
      },
      "XPdfPages": {
        "description": "How many pages were generated.",
        "schema": {
          "type": "integer"
        }
      }
    },
    "parameters": {
      "JobIdPath": {
        "name": "job_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "JobId": {
        "type": "string",
        "example": "9f0a4b78-2c0c-4d14-9b8b-123456789abc"
      },
      "PdfOptions": {
        "type": "object",
        "description": "Rendering options. `width`/`height` need `format: null`, which otherwise wins.",
        "properties": {
          "format": {
            "type": "string",
            "nullable": true,
            "enum": [
              "A3",
              "A4",
              "A5",
              "Legal",
              "Letter",
              "Tabloid",
              null
            ],
            "default": "A4"
          },
          "margin": {
            "type": "object",
            "properties": {
              "top": {
                "type": "string",
                "example": "20mm"
              },
              "right": {
                "type": "string",
                "example": "20mm"
              },
              "bottom": {
                "type": "string",
                "example": "20mm"
              },
              "left": {
                "type": "string",
                "example": "20mm"
              }
            }
          },
          "printBackground": {
            "type": "boolean",
            "default": true
          },
          "scale": {
            "type": "number",
            "default": 1,
            "minimum": 0.1,
            "maximum": 2
          },
          "landscape": {
            "type": "boolean",
            "default": false
          },
          "preferCSSPageSize": {
            "type": "boolean",
            "default": false
          },
          "width": {
            "type": "string",
            "example": "100mm"
          },
          "height": {
            "type": "string",
            "example": "150mm"
          },
          "pageRanges": {
            "type": "string",
            "example": "1-3, 5"
          },
          "displayHeaderFooter": {
            "type": "boolean",
            "default": false
          },
          "headerTemplate": {
            "type": "string"
          },
          "footerTemplate": {
            "type": "string"
          }
        }
      },
      "QuickJobRequest": {
        "type": "object",
        "required": [
          "input_type"
        ],
        "properties": {
          "input_type": {
            "type": "string",
            "enum": [
              "html",
              "markdown",
              "url"
            ]
          },
          "html": {
            "type": "string",
            "description": "Required when `input_type` is `html`."
          },
          "markdown": {
            "type": "string",
            "description": "Required when `input_type` is `markdown`."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Required when `input_type` is `url`. HTTPS only, public hosts only, 10 second\ntimeout, 5 MB limit. Relative assets on the page are not resolved.\n"
          },
          "options": {
            "$ref": "#/components/schemas/PdfOptions"
          },
          "store": {
            "type": "boolean",
            "default": false,
            "description": "Return JSON with a signed link instead of the PDF bytes."
          }
        }
      },
      "StoredPdf": {
        "type": "object",
        "properties": {
          "job_id": {
            "$ref": "#/components/schemas/JobId"
          },
          "pages": {
            "type": "integer",
            "example": 3
          },
          "truncated": {
            "type": "boolean",
            "example": false
          },
          "download_url": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "Signed link, valid one hour. Null when the upload failed."
          },
          "download_url_expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "EditRequest": {
        "type": "object",
        "required": [
          "source_key",
          "recipe"
        ],
        "properties": {
          "source_key": {
            "type": "string",
            "description": "Key from `/pdf/upload-url`."
          },
          "recipe": {
            "type": "array",
            "description": "Operations, applied in order.",
            "items": {
              "type": "object",
              "required": [
                "op"
              ],
              "properties": {
                "op": {
                  "type": "string",
                  "example": "text.add",
                  "description": "e.g. `text.add`, `bates.stamp`, `watermark.text`, `metadata.strip`."
                },
                "pages": {
                  "description": "Page ids from `/pdf/inspect`, or a page-number range.",
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    {
                      "type": "string"
                    }
                  ]
                }
              },
              "additionalProperties": true
            }
          },
          "confirm_signature_break": {
            "type": "boolean",
            "description": "Required when the document carries a digital signature."
          }
        }
      },
      "Job": {
        "type": "object",
        "properties": {
          "job_id": {
            "$ref": "#/components/schemas/JobId"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "processing",
              "completed",
              "partial_failed",
              "failed",
              "timeout"
            ]
          },
          "job_type": {
            "type": "string",
            "enum": [
              "quick",
              "long",
              "bulk"
            ]
          },
          "mode": {
            "type": "string",
            "example": "html"
          },
          "pages": {
            "type": "integer",
            "nullable": true
          },
          "truncated": {
            "type": "boolean"
          },
          "timeout_occurred": {
            "type": "boolean"
          },
          "bundle_type": {
            "type": "string",
            "nullable": true
          },
          "file_count": {
            "type": "integer",
            "nullable": true
          },
          "processed_count": {
            "type": "integer",
            "nullable": true
          },
          "success_count": {
            "type": "integer",
            "nullable": true
          },
          "failure_count": {
            "type": "integer",
            "nullable": true
          },
          "pages_total": {
            "type": "integer",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "s3_url": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "Present only for jobs with stored output."
          },
          "s3_url_expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "webhook_delivered": {
            "type": "boolean"
          },
          "webhook_delivered_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "webhook_retry_count": {
            "type": "integer"
          },
          "error_code": {
            "type": "string",
            "nullable": true
          },
          "error_message": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "example": "INSUFFICIENT_CREDITS"
              },
              "message": {
                "type": "string"
              },
              "details": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request was invalid",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid API key",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "UpgradeRequired": {
        "description": "`UPGRADE_REQUIRED` — the account has no paid plan yet",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "`INSUFFICIENT_CREDITS`, `CONVERSION_TYPE_NOT_ENABLED`, `PDF_TOOLS_NOT_ENABLED`,\n`RATE_LIMIT_EXCEEDED` or `ACCOUNT_NOT_FOUND`. Note that a plan's rate limit is a 403,\nnot a 429.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "No such resource for this account",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Gone": {
        "description": "`OUTPUT_EXPIRED` — the file has been deleted",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PayloadTooLarge": {
        "description": "The request body is over the limit",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "UnprocessableEntity": {
        "description": "The values did not validate; `error.details` says which",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Platform throttling, shared across accounts. Back off and retry with jitter.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ServerError": {
        "description": "Rendering or the service failed",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  }
}
