{
  "openapi": "3.1.0",
  "info": {
    "title": "rusticdoc",
    "description": "Fills Office and PDF templates from JSON, converts documents with LibreOffice, and runs PDF operations. One multipart request, or a JSON one for a stored template, does the whole job; webhooks or background jobs deliver results asynchronously.",
    "license": {
      "name": "Apache-2.0",
      "identifier": "Apache-2.0"
    },
    "version": "0.1.0"
  },
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "service"
        ],
        "summary": "Liveness. With credentials configured, a caller without them sees the status\nand nothing else.",
        "operationId": "health",
        "responses": {
          "200": {
            "description": "Service status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          }
        }
      }
    },
    "/metrics": {
      "get": {
        "tags": [
          "service"
        ],
        "summary": "Prometheus text format. With credentials configured it needs them too, unless\n`RUSTICDOC_METRICS_PUBLIC` is set.",
        "operationId": "metrics",
        "responses": {
          "200": {
            "description": "Prometheus text format",
            "content": {
              "text/plain": {}
            }
          }
        }
      }
    },
    "/ready": {
      "get": {
        "tags": [
          "service"
        ],
        "summary": "Readiness: liveness, and with a store, this replica's copy of the stored fonts\nbeing complete. A load balancer should send no documents here before that,\nbecause they would quietly come out in a fallback font.",
        "operationId": "ready",
        "responses": {
          "200": {
            "description": "Service status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          }
        }
      }
    },
    "/v1/aliases": {
      "get": {
        "tags": [
          "templates"
        ],
        "summary": "Lists this caller's aliases by name, each with the template it names now.",
        "operationId": "listAliases",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many to return, up to 200. Default 50.",
            "required": false,
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "`nextCursor` from the page before.",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of aliases",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AliasList"
                }
              }
            }
          },
          "501": {
            "description": "RUSTICDOC_STORE is not set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "The store cannot be reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/aliases/{name}": {
      "get": {
        "tags": [
          "templates"
        ],
        "summary": "An alias, the template it names now, and its history: every version since\nit was last deleted, oldest first.",
        "operationId": "getAlias",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "description": "The alias",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The alias and its versions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AliasHistorySchema"
                }
              }
            }
          },
          "404": {
            "description": "This caller has no such alias",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "501": {
            "description": "RUSTICDOC_STORE is not set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "The store cannot be reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      },
      "put": {
        "tags": [
          "templates"
        ],
        "summary": "Points an alias at a template. A new alias starts at version 1; pointing an\nexisting one elsewhere adds a version, and the earlier ones stay. Every\nreplica sees the change at once.",
        "operationId": "setAlias",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "description": "The alias, such as `invoice`",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AliasSet"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/AliasSet"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "A new version of an alias",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AliasSchema"
                }
              }
            }
          },
          "201": {
            "description": "A new alias",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AliasSchema"
                }
              }
            }
          },
          "400": {
            "description": "Not a usable name, or no template named",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "Too many aliases, or versions of this one",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "This caller has no such template",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "501": {
            "description": "RUSTICDOC_STORE is not set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "The store cannot be reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      },
      "delete": {
        "tags": [
          "templates"
        ],
        "summary": "Deletes an alias and its history. The templates it named stay. Setting it\nagain starts at version 1.",
        "operationId": "deleteAlias",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "description": "The alias",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "This caller has no such alias",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "501": {
            "description": "RUSTICDOC_STORE is not set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "The store cannot be reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/convert": {
      "post": {
        "tags": [
          "convert"
        ],
        "summary": "Converts uploads and runs any post-processing fields. HTML goes to Fulgur\nunless `htmlEngine` says `libreoffice`; everything else to LibreOffice.\nSeveral files come back as a ZIP, or as one PDF with `merge=true`.",
        "operationId": "convert",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/ConvertForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The converted file, or a ZIP of them",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "204": {
            "description": "Accepted; the result goes to the webhook"
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "An engine is unavailable or the request timed out",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/fonts": {
      "get": {
        "tags": [
          "fonts"
        ],
        "summary": "Lists the fonts LibreOffice and the PDF stage can use.",
        "operationId": "list",
        "responses": {
          "200": {
            "description": "The loaded fonts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/FontSchema"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      },
      "post": {
        "tags": [
          "fonts"
        ],
        "summary": "Stores fonts for every caller and every replica: a ZIP of TTF, OTF or TTC files,\nor one such file. Takes an admin. They are in use on this replica when this\nreturns, and on the others within `syncIntervalSeconds`.",
        "operationId": "uploadFonts",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/FontUploadForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Stored",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FontsStored"
                }
              }
            }
          },
          "400": {
            "description": "Not fonts, or too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "Needs an admin, or the store is full",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "501": {
            "description": "RUSTICDOC_STORE is not set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "The store cannot be reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/fonts/reload": {
      "post": {
        "tags": [
          "fonts"
        ],
        "summary": "Rescans the fonts directory and restarts idle LibreOffice workers, so a\nnew font needs no container restart. Busy workers restart after their\ncurrent job.",
        "operationId": "reload",
        "responses": {
          "200": {
            "description": "What is loaded now",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Reloaded"
                }
              }
            }
          },
          "429": {
            "description": "Reloaded moments ago",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "A worker could not restart",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/fonts/{id}": {
      "delete": {
        "tags": [
          "fonts"
        ],
        "summary": "Deletes a stored font. Takes an admin. It leaves this replica when this\nreturns, and the others within the sync interval. Documents that name it fall\nback to another font.",
        "operationId": "deleteFont",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The font's id, from `GET /v1/fonts`",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "403": {
            "description": "Needs an admin",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "No such stored font",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "501": {
            "description": "RUSTICDOC_STORE is not set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "The store cannot be reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/jobs/{id}": {
      "get": {
        "tags": [
          "jobs"
        ],
        "summary": "Where a background job is. Ask again until `status` is `done` or `failed`;\n`Retry-After` says how soon. A job and its result are kept for\n`RUSTICDOC_JOB_TTL` after it ends, then removed.",
        "operationId": "getJob",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The job's id, from the 202's body or Location",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobSchema"
                }
              }
            }
          },
          "404": {
            "description": "This caller has no such job, or it expired",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "501": {
            "description": "RUSTICDOC_STORE is not set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "The store cannot be reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/jobs/{id}/result": {
      "get": {
        "tags": [
          "jobs"
        ],
        "summary": "Downloads what a finished job made, as the request would have answered it.",
        "operationId": "getJobResult",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The job's id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "404": {
            "description": "This caller has no such job, or it expired",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "409": {
            "description": "The job has not finished, or it failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "501": {
            "description": "RUSTICDOC_STORE is not set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "The store cannot be reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/pdf/bookmarks/read": {
      "post": {
        "tags": [
          "pdf"
        ],
        "summary": "Reads the outline of each PDF sent, keyed by file name.",
        "operationId": "bookmarks_read",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PdfForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Per file name",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/pdf/bookmarks/write": {
      "post": {
        "tags": [
          "pdf"
        ],
        "summary": "Replaces the outline.",
        "operationId": "bookmarks_write",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/BookmarksWriteForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The PDF with its outline replaced",
            "content": {
              "application/pdf": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/pdf/convert": {
      "post": {
        "tags": [
          "pdf"
        ],
        "summary": "PDFs to PDF/A or PDF/UA through LibreOffice, with the same\npost-processing fields as `/v1/convert`.",
        "operationId": "convert_pdf",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/ConvertForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The converted PDF, or a ZIP of them",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "204": {
            "description": "Accepted; the result goes to the webhook"
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "An engine is unavailable or the request timed out",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/pdf/embed": {
      "post": {
        "tags": [
          "pdf"
        ],
        "summary": "Attaches files to a PDF.",
        "operationId": "embed",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/EmbedForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The PDF with attachments",
            "content": {
              "application/pdf": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/pdf/encrypt": {
      "post": {
        "tags": [
          "pdf"
        ],
        "summary": "Encrypts a PDF with AES-256.",
        "operationId": "encrypt",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/EncryptForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The encrypted PDF",
            "content": {
              "application/pdf": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "204": {
            "description": "Accepted; the result goes to the webhook"
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "An engine is unavailable or the request timed out",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/pdf/factur-x": {
      "post": {
        "tags": [
          "pdf"
        ],
        "summary": "Embeds Factur-X invoice XML and marks the PDF for it.",
        "operationId": "facturx",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/FacturxForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The Factur-X PDF",
            "content": {
              "application/pdf": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/pdf/flatten": {
      "post": {
        "tags": [
          "pdf"
        ],
        "summary": "Flattens forms and annotations into the page content.",
        "operationId": "flatten",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PdfForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The flattened PDF",
            "content": {
              "application/pdf": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/pdf/forms/fields": {
      "post": {
        "tags": [
          "pdf-forms"
        ],
        "summary": "Lists a PDF's form fields with their kinds, pages and values.",
        "operationId": "fields",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/OverlayForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The form fields",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/FieldSchema"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "501": {
            "description": "Not supported in this version",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/pdf/forms/fill": {
      "post": {
        "tags": [
          "pdf-forms"
        ],
        "summary": "Fills a PDF form from data, applying tags in annotations, optionally\nflattening the result.",
        "operationId": "fill",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/FillForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The filled PDF",
            "content": {
              "application/pdf": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "204": {
            "description": "Accepted; the result goes to the webhook"
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "The template failed to render",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "501": {
            "description": "Not supported in this version",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/pdf/merge": {
      "post": {
        "tags": [
          "pdf"
        ],
        "summary": "Merges PDFs in the order sent.",
        "operationId": "merge",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/MergeForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The merged PDF",
            "content": {
              "application/pdf": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "204": {
            "description": "Accepted; the result goes to the webhook"
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "An engine is unavailable or the request timed out",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/pdf/metadata/read": {
      "post": {
        "tags": [
          "pdf"
        ],
        "summary": "Reads the metadata of each PDF sent, keyed by file name.",
        "operationId": "metadata_read",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PdfForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Per file name",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/pdf/metadata/write": {
      "post": {
        "tags": [
          "pdf"
        ],
        "summary": "Writes metadata keys; existing keys not named are kept.",
        "operationId": "metadata_write",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/MetadataWriteForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The PDF with its metadata changed",
            "content": {
              "application/pdf": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/pdf/optimize": {
      "post": {
        "tags": [
          "pdf"
        ],
        "summary": "Recompresses streams and drops unused objects.",
        "operationId": "optimize",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PdfForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The smaller PDF",
            "content": {
              "application/pdf": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/pdf/overlay": {
      "post": {
        "tags": [
          "pdf-forms"
        ],
        "summary": "Lays the second PDF over the first, page by page.",
        "operationId": "overlay",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/OverlayForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The combined PDF",
            "content": {
              "application/pdf": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "204": {
            "description": "Accepted; the result goes to the webhook"
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "501": {
            "description": "Not supported in this version",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/pdf/rotate": {
      "post": {
        "tags": [
          "pdf"
        ],
        "summary": "Rotates pages by 90, 180 or 270 degrees.",
        "operationId": "rotate",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/RotateForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The rotated PDF",
            "content": {
              "application/pdf": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/pdf/sign": {
      "post": {
        "tags": [
          "pdf"
        ],
        "summary": "Signs a PDF. With `signature=certificate`, a cryptographic signature with\nthe key in `RUSTICDOC_SIGNING_KEY` and `RUSTICDOC_SIGNING_CERT`, or the\nnamed key `signingKey` chooses: an incremental update with a PAdES\n`/ETSI.CAdES.detached` signature at the `level` asked for (B-B to B-LTA),\napproving or, with `certify`, certifying. It is invisible, shown with the\none image sent, or, with a position and no image, shown as text naming\nthe signer, the date and the reason. Without a configured key that answers\n503; a timestamp authority or revocation source that fails answers 502. By\ndefault (`signature=image`) the images are drawn at exact coordinates,\nwhich is not a cryptographic signature.",
        "operationId": "sign",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/SignForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The signed PDF",
            "content": {
              "application/pdf": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "204": {
            "description": "Accepted; the result goes to the webhook"
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "The caller may not use the signing key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "502": {
            "description": "The timestamp authority or a revocation source failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "signature=certificate and no signing key is configured, the level needs a timestamp authority that is not configured, or the certificate has expired",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/pdf/split": {
      "post": {
        "tags": [
          "pdf"
        ],
        "summary": "Splits a PDF by page ranges or into fixed intervals; several outputs\ncome back as a ZIP.",
        "operationId": "split",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/SplitForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "A PDF, or a ZIP of PDFs",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/pdf/stamp": {
      "post": {
        "tags": [
          "pdf"
        ],
        "summary": "Puts text, an image or a PDF over the content of each page.",
        "operationId": "stamp",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/OverlayForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The stamped PDF",
            "content": {
              "application/pdf": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/pdf/watermark": {
      "post": {
        "tags": [
          "pdf"
        ],
        "summary": "Puts text, an image or a PDF under the content of each page.",
        "operationId": "watermark",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/OverlayForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The watermarked PDF",
            "content": {
              "application/pdf": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/render": {
      "post": {
        "tags": [
          "render"
        ],
        "summary": "Fills a template and runs the steps.",
        "description": "Send a multipart form, or JSON with a `templateId` (see `RenderJson`).\n\nAdd `Prefer: respond-async` to get 202 at once, with the job in the body and\nits address in `Location`, and fetch the result from `/v1/jobs/{id}/result`\nonce it is done. This needs `RUSTICDOC_STORE`.\n\nAdd `X-Rusticdoc-Webhook-Url` to get 204 at once and the result by webhook.\nWith `RUSTICDOC_WEBHOOK_SECRET` set, each caller's deliveries are signed with its\nown key: `X-Rusticdoc-Signature` is `v1=` and the hex HMAC-SHA256 of\n`<timestamp>.<event>.<request id>.<body>`, with the timestamp in\n`X-Rusticdoc-Timestamp`, the event in `X-Rusticdoc-Event` and the request id in\n`X-Request-Id`. The key is `HMAC-SHA256(secret, principal)`, or the secret itself\nwhen the service requires no credentials; `rusticdoc webhook-secret` prints it.",
        "operationId": "render",
        "parameters": [
          {
            "name": "X-Rusticdoc-Webhook-Url",
            "in": "header",
            "description": "Deliver the result here instead of in the response",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          {
            "name": "X-Rusticdoc-Webhook-Error-Url",
            "in": "header",
            "description": "Deliver failures here as JSON",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          {
            "name": "Prefer",
            "in": "header",
            "description": "`respond-async` to run in the background and answer 202",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RenderJson"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/RenderForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The rendered file",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "202": {
            "description": "Accepted as a background job (`Prefer: respond-async`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobSchema"
                }
              }
            }
          },
          "204": {
            "description": "Accepted; the result goes to the webhook"
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "401": {
            "description": "Credentials required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "The template failed to render",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "501": {
            "description": "Not supported in this version",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "An engine is unavailable or the request timed out",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/render/batch": {
      "post": {
        "tags": [
          "render"
        ],
        "summary": "Renders the template once per element of an array in the data and\nreturns the files in a ZIP.",
        "description": "`splitBy` names the array with a dotted path such as `items` or\n`order.lines`. Each render sees the whole data with that array replaced\nby the one element, plus `item` (the element) and `batch` (`index`,\n`count`). `batchName` is a template for each file's name, default\n`{{ batch.index }}`.",
        "operationId": "batch",
        "parameters": [
          {
            "name": "X-Rusticdoc-Webhook-Url",
            "in": "header",
            "description": "Deliver the ZIP here instead of in the response",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          {
            "name": "Prefer",
            "in": "header",
            "description": "`respond-async` to run in the background and answer 202",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RenderJson"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/RenderForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "A ZIP of the rendered files",
            "content": {
              "application/zip": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "202": {
            "description": "Accepted as a background job (`Prefer: respond-async`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobSchema"
                }
              }
            }
          },
          "204": {
            "description": "Accepted; the result goes to the webhook"
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "The template failed to render",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/templates": {
      "get": {
        "tags": [
          "templates"
        ],
        "summary": "Lists this caller's templates, oldest first.",
        "operationId": "listTemplates",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many to return, up to 200. Default 50.",
            "required": false,
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "minimum": 0
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "`nextCursor` from the page before.",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of templates",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateList"
                }
              }
            }
          },
          "501": {
            "description": "RUSTICDOC_STORE is not set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "The store cannot be reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      },
      "post": {
        "tags": [
          "templates"
        ],
        "summary": "Stores a template and returns its id. Syntax is checked now, without data, so a\nbroken template is refused here rather than at its first render. The content\nnever changes: to change a template, store the new one and use its id.",
        "operationId": "uploadTemplate",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/TemplateUploadForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Stored",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateSchema"
                }
              }
            }
          },
          "400": {
            "description": "Not a template format",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "This caller's quota is used up",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "413": {
            "description": "Too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "The template has syntax errors",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "501": {
            "description": "RUSTICDOC_STORE is not set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "The store cannot be reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/templates/validate": {
      "post": {
        "tags": [
          "render"
        ],
        "summary": "Checks a template against sample data without converting anything.",
        "operationId": "validate",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RenderJson"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/RenderForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The validation result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Validation"
                }
              }
            }
          },
          "400": {
            "description": "The request is wrong",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/templates/{id}": {
      "get": {
        "tags": [
          "templates"
        ],
        "summary": "One template's details.",
        "operationId": "getTemplate",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The template's id, or an alias as `name` or `name@version`",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The template",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateSchema"
                }
              }
            }
          },
          "404": {
            "description": "This caller has no such template",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "501": {
            "description": "RUSTICDOC_STORE is not set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "The store cannot be reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      },
      "delete": {
        "tags": [
          "templates"
        ],
        "summary": "Deletes a template. Every replica stops serving it at once.",
        "operationId": "deleteTemplate",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The template's id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "This caller has no such template",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "501": {
            "description": "RUSTICDOC_STORE is not set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "The store cannot be reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/templates/{id}/content": {
      "get": {
        "tags": [
          "templates"
        ],
        "summary": "Downloads a template as it was uploaded.",
        "operationId": "downloadTemplate",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The template's id, or an alias as `name` or `name@version`",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "404": {
            "description": "This caller has no such template",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "501": {
            "description": "RUSTICDOC_STORE is not set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "The store cannot be reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/templates/{id}/preview": {
      "post": {
        "tags": [
          "templates"
        ],
        "summary": "Renders a stored template with sample data and draws one page as a PNG,\nfor checking a template without opening the document. Office templates\nconvert with LibreOffice, HTML with its HTML engine, and PDF forms are filled;\npoppler's `pdftoppm` draws the page. The page count is in `X-Page-Count`.",
        "operationId": "previewTemplate",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The template's id, or an alias as `name` or `name@version`",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PreviewForm"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PreviewForm"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The page as a PNG",
            "headers": {
              "X-Page-Count": {
                "schema": {
                  "type": "integer",
                  "format": "int32",
                  "minimum": 0
                },
                "description": "How many pages the rendered document has"
              }
            },
            "content": {
              "image/png": {
                "schema": {
                  "$ref": "#/components/schemas/BinaryFile"
                }
              }
            }
          },
          "400": {
            "description": "The request is wrong, or the page does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "This caller has no such template",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "The template failed to render",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "501": {
            "description": "RUSTICDOC_STORE is not set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "pdftoppm or LibreOffice is unavailable, or the request timed out",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/templates/{id}/variables": {
      "get": {
        "tags": [
          "templates"
        ],
        "summary": "Lists the data a template reads: each path, which paths are arrays it loops\nover, and the fields it reads from their items, with a skeleton of data.\nRead from the template's tags, so a path built at render time, such as\n`row[column]`, is listed only as far as it is written.",
        "operationId": "templateVariables",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The template's id, or an alias as `name` or `name@version`",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "What the template reads",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TemplateVariables"
                }
              }
            }
          },
          "404": {
            "description": "This caller has no such template",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "The template has syntax errors",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "501": {
            "description": "RUSTICDOC_STORE is not set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "The store cannot be reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    },
    "/v1/version": {
      "get": {
        "tags": [
          "service"
        ],
        "summary": "Engine versions and paths.",
        "operationId": "version",
        "responses": {
          "200": {
            "description": "Version and engines",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Version"
                }
              }
            }
          }
        },
        "security": [
          {
            "api_key": []
          },
          {
            "bearer": []
          },
          {
            "basic": []
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "AliasHistorySchema": {
        "type": "object",
        "description": "An alias, the template it names now, and every version, oldest first.",
        "required": [
          "name",
          "templateId",
          "version",
          "setAt",
          "versions"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "setAt": {
            "type": "string"
          },
          "templateId": {
            "type": "string"
          },
          "version": {
            "type": "integer",
            "minimum": 0
          },
          "versions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AliasVersionSchema"
            }
          }
        }
      },
      "AliasList": {
        "type": "object",
        "description": "One page of aliases.",
        "required": [
          "aliases"
        ],
        "properties": {
          "aliases": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AliasSchema"
            }
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` for the next page. Absent on the last."
          }
        }
      },
      "AliasSchema": {
        "type": "object",
        "description": "An alias and the template it names now.",
        "required": [
          "name",
          "templateId",
          "version",
          "setAt"
        ],
        "properties": {
          "name": {
            "type": "string",
            "example": "invoice"
          },
          "setAt": {
            "type": "string",
            "description": "RFC 3339: when it was pointed at this template."
          },
          "templateId": {
            "type": "string"
          },
          "version": {
            "type": "integer",
            "description": "1 for the first template it named, counted since it was last deleted.\nRender an earlier one as `name@version`.",
            "minimum": 0
          }
        }
      },
      "AliasSet": {
        "type": "object",
        "description": "What `PUT /v1/aliases/{name}` takes, as JSON or a multipart form.",
        "required": [
          "templateId"
        ],
        "properties": {
          "templateId": {
            "type": "string",
            "description": "The template the alias names from now on: an id, or another alias.",
            "example": "01966f3e-7c1a-7d3c-9a44-1d2f5a6b7c8d"
          }
        }
      },
      "AliasVersionSchema": {
        "type": "object",
        "description": "One version of an alias.",
        "required": [
          "version",
          "templateId",
          "setAt"
        ],
        "properties": {
          "setAt": {
            "type": "string",
            "description": "RFC 3339."
          },
          "templateId": {
            "type": "string"
          },
          "version": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "BinaryFile": {
        "type": "string",
        "format": "binary",
        "description": "A file in a response, for the API description: generated clients return\nit as bytes."
      },
      "BookmarksWriteForm": {
        "type": "object",
        "required": [
          "files",
          "bookmarks"
        ],
        "properties": {
          "bookmarks": {
            "description": "JSON array of `{title, page, children}`."
          },
          "files": {
            "type": "string",
            "format": "binary"
          },
          "outputFilename": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "ConvertForm": {
        "type": "object",
        "description": "The multipart form `/v1/convert` and `/v1/pdf/convert` accept.\nLibreOffice options (`landscape`, `nativePageRanges`,\n`exportFormFields`, `singlePageSheets`, `password`, ...) and\npost-processing fields (`merge`, `splitMode`, `watermarkSource`,\n`stampSource`, `rotateAngle`, `flatten`, `metadata`, `bookmarks`,\n`embeds`, `facturxXml`, `userPassword`, ...) are accepted as well.",
        "required": [
          "files"
        ],
        "properties": {
          "assets": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "binary"
            },
            "description": "Images (PNG, JPEG, GIF, SVG), stylesheets and fonts an HTML page names\nby file name, as in `<img src=\"logo.png\">`. Every stylesheet applies to\nthe whole page. Nothing is fetched from anywhere else."
          },
          "files": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "binary"
            },
            "description": "One or more documents."
          },
          "htmlEngine": {
            "type": [
              "string",
              "null"
            ],
            "description": "What turns HTML into PDF: `fulgur` (default), a layout engine with\nno browser and no network, or `libreoffice`.",
            "example": "fulgur"
          },
          "landscape": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "merge": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Merge several inputs into one PDF instead of returning a ZIP."
          },
          "nativePageRanges": {
            "type": [
              "string",
              "null"
            ],
            "example": "1-3,5"
          },
          "outputFilename": {
            "type": [
              "string",
              "null"
            ]
          },
          "password": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opens a protected input."
          },
          "pdfa": {
            "type": [
              "string",
              "null"
            ],
            "description": "PDF/A level: `PDF/A-1b`, `PDF/A-2b`, `PDF/A-3b`, `PDF/A-4`."
          },
          "pdfua": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Export as PDF/UA."
          },
          "to": {
            "type": [
              "string",
              "null"
            ],
            "description": "Output format. Default `pdf`."
          }
        }
      },
      "EmbedForm": {
        "type": "object",
        "required": [
          "files"
        ],
        "properties": {
          "files": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "binary"
            },
            "description": "The PDF first, then the files to attach."
          },
          "outputFilename": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "EncryptForm": {
        "type": "object",
        "required": [
          "files",
          "ownerPassword"
        ],
        "properties": {
          "files": {
            "type": "string",
            "format": "binary",
            "description": "One PDF."
          },
          "outputFilename": {
            "type": [
              "string",
              "null"
            ]
          },
          "ownerPassword": {
            "type": "string"
          },
          "userPassword": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "EngineStatus": {
        "type": "object",
        "required": [
          "libreoffice",
          "qpdf"
        ],
        "properties": {
          "libreoffice": {
            "type": "boolean"
          },
          "qpdf": {
            "type": "boolean"
          }
        }
      },
      "ErrorBody": {
        "type": "object",
        "description": "The JSON body of every error response.",
        "required": [
          "error",
          "message"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "A stable, machine-readable kind.",
            "example": "invalid_request"
          },
          "message": {
            "type": "string",
            "description": "What went wrong, for a person.",
            "example": "unknown step 'teleport'"
          }
        }
      },
      "FaceSchema": {
        "type": "object",
        "required": [
          "family",
          "style",
          "weight"
        ],
        "properties": {
          "family": {
            "type": "string"
          },
          "style": {
            "type": "string"
          },
          "weight": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          }
        }
      },
      "FacturxForm": {
        "type": "object",
        "required": [
          "files",
          "profile"
        ],
        "properties": {
          "files": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "binary"
            },
            "description": "The PDF/A-3 invoice, then the Factur-X XML."
          },
          "outputFilename": {
            "type": [
              "string",
              "null"
            ]
          },
          "profile": {
            "type": "string",
            "description": "`MINIMUM`, `BASIC_WL`, `BASIC`, `EN16931` or `EXTENDED`.",
            "example": "EN16931"
          }
        }
      },
      "FieldSchema": {
        "type": "object",
        "description": "One form field, as `/v1/pdf/forms/fields` lists them.",
        "required": [
          "name",
          "kind",
          "page",
          "options"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "description": "`text`, `checkbox`, `radio`, `choice`, `signature` or `button`."
          },
          "name": {
            "type": "string",
            "description": "Fully qualified name, such as `customer.name`."
          },
          "options": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "page": {
            "type": "integer",
            "format": "int32",
            "description": "1-based page of the first widget.",
            "minimum": 0
          },
          "value": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "FillForm": {
        "type": "object",
        "required": [
          "files"
        ],
        "properties": {
          "ctx": {},
          "data": {
            "description": "JSON object, sent as text or as a file; a field named `customer.name` takes\nthat path."
          },
          "files": {
            "type": "string",
            "format": "binary",
            "description": "The PDF with form fields, and optionally tags in annotations."
          },
          "flatten": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Fix the values into the page content."
          },
          "outputFilename": {
            "type": [
              "string",
              "null"
            ]
          },
          "stripAnnotations": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Remove the annotations that carried tags. Default true."
          }
        }
      },
      "FontSchema": {
        "type": "object",
        "required": [
          "family",
          "style",
          "weight",
          "file",
          "mounted"
        ],
        "properties": {
          "family": {
            "type": "string"
          },
          "file": {
            "type": "string",
            "description": "The file's base name."
          },
          "id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The stored font this face belongs to, for deleting it. Absent for fonts\nin the image or the mounted directory."
          },
          "mounted": {
            "type": "boolean",
            "description": "True when it came from the mounted fonts directory."
          },
          "style": {
            "type": "string"
          },
          "weight": {
            "type": "integer",
            "format": "int32",
            "minimum": 0
          }
        }
      },
      "FontUploadForm": {
        "type": "object",
        "description": "The multipart form `POST /v1/fonts` accepts.",
        "required": [
          "file"
        ],
        "properties": {
          "file": {
            "type": "string",
            "format": "binary",
            "description": "A ZIP of TTF, OTF or TTC files, or one such file."
          }
        }
      },
      "FontsStored": {
        "type": "object",
        "description": "What an upload stored.",
        "required": [
          "fonts",
          "skipped",
          "syncIntervalSeconds"
        ],
        "properties": {
          "fonts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StoredFontSchema"
            }
          },
          "skipped": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SkippedSchema"
            },
            "description": "Files in the ZIP that were left out, and why."
          },
          "syncIntervalSeconds": {
            "type": "integer",
            "format": "int64",
            "description": "In use on this replica now, and on the others within this many seconds.",
            "minimum": 0
          }
        }
      },
      "Health": {
        "type": "object",
        "required": [
          "status",
          "uptime_seconds",
          "engines",
          "details"
        ],
        "properties": {
          "details": {
            "description": "Per-module status: `libreoffice`, and `store` when one is configured."
          },
          "engines": {
            "$ref": "#/components/schemas/EngineStatus"
          },
          "status": {
            "type": "string",
            "description": "`up` when the service answers.",
            "example": "up"
          },
          "uptime_seconds": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          }
        }
      },
      "JobResultSchema": {
        "type": "object",
        "description": "The file a finished job made.",
        "required": [
          "name",
          "contentType",
          "size",
          "sha256"
        ],
        "properties": {
          "contentType": {
            "type": "string",
            "example": "application/pdf"
          },
          "name": {
            "type": "string",
            "example": "invoice.pdf"
          },
          "sha256": {
            "type": "string",
            "description": "Hex SHA-256 of the content."
          },
          "size": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          }
        }
      },
      "JobSchema": {
        "type": "object",
        "description": "A background job.",
        "required": [
          "id",
          "status",
          "createdAt",
          "runsUntil",
          "requestId"
        ],
        "properties": {
          "createdAt": {
            "type": "string",
            "description": "RFC 3339, to the millisecond."
          },
          "error": {
            "type": [
              "object",
              "null"
            ],
            "description": "Present once it failed: what the request would have answered, as\n`error`, `message` and `status`."
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ],
            "description": "When the job and its result are removed."
          },
          "finishedAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "type": "string",
            "example": "01966f3e-7c1a-7d3c-9a44-1d2f5a6b7c8d"
          },
          "requestId": {
            "type": "string",
            "description": "The `X-Request-Id` of the request that started it."
          },
          "result": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/JobResultSchema",
                "description": "Present once it is done; download it from `/v1/jobs/{id}/result`."
              },
              {
                "type": "null"
              }
            ]
          },
          "runsUntil": {
            "type": "string",
            "description": "A job that has not ended by then is reported as failed with\n`job_lost`: the container running it stopped."
          },
          "startedAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "description": "`queued`, `running`, `done` or `failed`.",
            "example": "done"
          }
        }
      },
      "MergeForm": {
        "type": "object",
        "required": [
          "files"
        ],
        "properties": {
          "files": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "binary"
            },
            "description": "Two or more PDFs, merged in order."
          },
          "outputFilename": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "MetadataWriteForm": {
        "type": "object",
        "required": [
          "files",
          "metadata"
        ],
        "properties": {
          "files": {
            "type": "string",
            "format": "binary"
          },
          "metadata": {
            "description": "JSON object of keys such as `Title`, `Author`, `Subject`, `Keywords`."
          },
          "outputFilename": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "OverlayForm": {
        "type": "object",
        "required": [
          "files"
        ],
        "properties": {
          "files": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "binary"
            },
            "description": "Two PDFs: the base, then the overlay laid over it page by page."
          },
          "outputFilename": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "PdfForm": {
        "type": "object",
        "required": [
          "files"
        ],
        "properties": {
          "files": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "binary"
            },
            "description": "The PDF to work on. Several for merge."
          },
          "outputFilename": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "PreviewForm": {
        "type": "object",
        "description": "The fields `POST /v1/templates/{id}/preview` accepts, as JSON or as a\nmultipart form.",
        "properties": {
          "ctx": {
            "description": "Visible as `ctx`."
          },
          "data": {
            "description": "Sample data the template sees as its root."
          },
          "dpi": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "Draw at this resolution, from 18 to 300, as large as the page is. An\nimage may not pass 4000 pixels on a side.",
            "minimum": 0
          },
          "htmlEngine": {
            "type": [
              "string",
              "null"
            ],
            "description": "What turns HTML into PDF: `fulgur` (default) or `libreoffice`."
          },
          "page": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "The page to draw, from 1. Default 1.",
            "minimum": 0
          },
          "width": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "The image's width in pixels, from 16 to 2000; the height follows the\npage. Default 800. Send this or `dpi`.",
            "minimum": 0
          }
        }
      },
      "Reloaded": {
        "type": "object",
        "required": [
          "families",
          "faces",
          "changed"
        ],
        "properties": {
          "changed": {
            "type": "boolean",
            "description": "False when the directory has not changed, so no worker restarted."
          },
          "faces": {
            "type": "integer",
            "minimum": 0
          },
          "families": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "RenderForm": {
        "type": "object",
        "description": "The multipart form `/v1/render` accepts. Documented here; parsed by hand.",
        "required": [
          "template",
          "underlay",
          "files"
        ],
        "properties": {
          "assets": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "binary"
            },
            "description": "Images (PNG, JPEG, GIF, SVG), stylesheets and fonts an HTML page names\nby file name, as in `<img src=\"logo.png\">`. Every stylesheet applies to\nthe whole page. Nothing is fetched from anywhere else."
          },
          "certify": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "For `sign`: certify with DocMDP permission `1`, `2` or `3` instead of\napproving.",
            "minimum": 0
          },
          "contactInfo": {
            "type": [
              "string",
              "null"
            ],
            "description": "For `sign`: how to reach the signer."
          },
          "ctx": {
            "description": "JSON object visible as `ctx`, sent as text or as a file."
          },
          "currency": {
            "type": [
              "string",
              "null"
            ],
            "description": "The currency `currency` uses when given none, such as `EUR`. Wins over\nthe template's."
          },
          "data": {
            "description": "JSON object the template sees as its root, sent as text or as a file."
          },
          "files": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "binary"
            },
            "description": "Further files, used by `merge`."
          },
          "htmlEngine": {
            "type": [
              "string",
              "null"
            ],
            "description": "What turns HTML into PDF: `fulgur` (default), a layout engine with\nno browser and no network, or `libreoffice`.",
            "example": "fulgur"
          },
          "keep": {
            "type": [
              "string",
              "null"
            ],
            "description": "What an Office template's result may keep of what rendering takes\nout, comma-separated: `macros`, `objects` (OLE objects and ActiveX\ncontrols), `externalLinks`, `privateLinks` (hyperlinks to private\naddresses), or `all`. Default: none of them.",
            "example": "privateLinks"
          },
          "lang": {
            "type": [
              "string",
              "null"
            ],
            "description": "The language, such as `fr`, `fr-CA` or `en-us`, in any case. It wins\nover the template's `lang`: it picks translations for `t`, and when CLDR\nhas data for it, how `number`, `currency` and `date` write numbers,\nmoney and month names.",
            "example": "fr-CA"
          },
          "level": {
            "type": [
              "string",
              "null"
            ],
            "description": "For `sign`: `b-b`, `b-t`, `b-lt` or `b-lta`, as on `/v1/pdf/sign`."
          },
          "location": {
            "type": [
              "string",
              "null"
            ],
            "description": "For `sign`: where it was signed."
          },
          "metadata": {
            "description": "JSON object of metadata fields for `metadata`."
          },
          "outputFilename": {
            "type": [
              "string",
              "null"
            ],
            "description": "File name of the result, without extension."
          },
          "owner_password": {
            "type": [
              "string",
              "null"
            ]
          },
          "page": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "For a visible `sign`: the 1-based page. Default 1.",
            "minimum": 0
          },
          "pdfa": {
            "type": [
              "string",
              "null"
            ],
            "description": "PDF/A level for `pdfa`: `PDF/A-1b`, `PDF/A-2b`, `PDF/A-3b`."
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "For `sign`: why the document was signed."
          },
          "signatureImage": {
            "type": "string",
            "format": "binary",
            "description": "For `sign`: a PNG or JPEG to show the signature with, at `page`, `x`,\n`y` and `width`. With `x` and `y` and no image, the signature is shown\nas text: the signer, the date and the reason."
          },
          "signerName": {
            "type": [
              "string",
              "null"
            ],
            "description": "For `sign`: the signer's name as shown."
          },
          "signingKey": {
            "type": [
              "string",
              "null"
            ],
            "description": "For `sign`: a named key instead of the default one."
          },
          "steps": {
            "type": [
              "string",
              "null"
            ],
            "description": "Comma-separated steps in order: `convert`, `pdfa`, `metadata`,\n`encrypt`, `merge`, `underlay`, `sign`. `sign` signs cryptographically with the\nservice's key; it must be last and cannot be combined with `encrypt`.",
            "example": "convert,pdfa,encrypt"
          },
          "template": {
            "type": "string",
            "format": "binary",
            "description": "The template file. Send this or `templateId`."
          },
          "templateId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The id of a stored template, from `POST /v1/templates`, or an alias as\n`name` or `name@version`. Send this or `template`. Files in the form are\nthen only further inputs, for `merge`."
          },
          "timezone": {
            "type": [
              "string",
              "null"
            ],
            "description": "The time zone `date` shows moments in: an offset such as `+02:00`, or an\nIANA zone such as `Europe/Paris`. Wins over the template's.",
            "example": "Europe/Paris"
          },
          "to": {
            "type": [
              "string",
              "null"
            ],
            "description": "Output format for `convert`. Default `pdf`."
          },
          "translations": {
            "type": [
              "object",
              "null"
            ],
            "description": "Translations for `t`, by language, each a flat object of keys and texts.\nThey win over the service's dictionaries and the template's own. At most\n10,000 entries, keys of 256 bytes and texts of 16 KiB."
          },
          "undefinedAsEmpty": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "`true` prints a value missing from the data as nothing instead of\nfailing the render. Default `false`, so a misspelt name is an error."
          },
          "underlay": {
            "type": "string",
            "format": "binary",
            "description": "For `underlay`: the PDF the document is laid over, page by page, such\nas letterhead or a pre-printed form."
          },
          "user_password": {
            "type": [
              "string",
              "null"
            ]
          },
          "width": {
            "type": [
              "number",
              "null"
            ],
            "format": "float",
            "description": "For a visible `sign`: width in points. Default the image's natural\nsize, or 200 for text."
          },
          "x": {
            "type": [
              "number",
              "null"
            ],
            "format": "float",
            "description": "For a visible `sign`: points from the page's left edge."
          },
          "y": {
            "type": [
              "number",
              "null"
            ],
            "format": "float",
            "description": "For a visible `sign`: points from the page's bottom edge."
          }
        }
      },
      "RenderJson": {
        "type": "object",
        "description": "The JSON body `/v1/render`, `/v1/render/batch` and\n`/v1/templates/validate` accept with `Content-Type: application/json`, for a\nstored template: there are no files to send. Every text field of the\nmultipart form is accepted under the same name, with the same meaning and\nlimits; objects such as `data` and `metadata` are sent as JSON rather than as\ntext, and `steps` may be a list. Fields that carry files cannot be sent.",
        "required": [
          "templateId"
        ],
        "properties": {
          "batchName": {
            "type": [
              "string",
              "null"
            ],
            "description": "For `/v1/render/batch`: each file's name, as a template."
          },
          "ctx": {
            "description": "The object visible as `ctx`."
          },
          "currency": {
            "type": [
              "string",
              "null"
            ],
            "description": "The currency `currency` uses when given none, such as `EUR`. Wins over\nthe template's."
          },
          "data": {
            "description": "The object the template sees as its root."
          },
          "downloadFrom": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "object"
            },
            "description": "URLs to fetch as further inputs, as on the multipart form."
          },
          "htmlEngine": {
            "type": [
              "string",
              "null"
            ],
            "description": "`fulgur` (default) or `libreoffice`, for HTML."
          },
          "keep": {
            "type": [
              "string",
              "null"
            ],
            "description": "What an Office template's result may keep of what rendering takes\nout, comma-separated: `macros`, `objects` (OLE objects and ActiveX\ncontrols), `externalLinks`, `privateLinks` (hyperlinks to private\naddresses), or `all`. Default: none of them.",
            "example": "privateLinks"
          },
          "lang": {
            "type": [
              "string",
              "null"
            ],
            "description": "The language, such as `fr`, `fr-CA` or `en-us`, in any case. It wins\nover the template's `lang`: it picks translations for `t`, and when CLDR\nhas data for it, how `number`, `currency` and `date` write numbers,\nmoney and month names.",
            "example": "fr-CA"
          },
          "metadata": {
            "description": "Metadata fields for `metadata`."
          },
          "outputFilename": {
            "type": [
              "string",
              "null"
            ],
            "description": "File name of the result, without extension."
          },
          "ownerPassword": {
            "type": [
              "string",
              "null"
            ]
          },
          "pdfa": {
            "type": [
              "string",
              "null"
            ],
            "description": "PDF/A level for `pdfa`."
          },
          "splitBy": {
            "type": [
              "string",
              "null"
            ],
            "description": "For `/v1/render/batch`: the array to render once per element."
          },
          "steps": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "The steps in order, as a list or as comma-separated text.",
            "example": [
              "convert",
              "pdfa"
            ]
          },
          "templateId": {
            "type": "string",
            "description": "The stored template: its id, or an alias as `name` or `name@version`.",
            "example": "invoice"
          },
          "timezone": {
            "type": [
              "string",
              "null"
            ],
            "description": "The time zone `date` shows moments in: an offset such as `+02:00`, or an\nIANA zone such as `Europe/Paris`. Wins over the template's.",
            "example": "Europe/Paris"
          },
          "to": {
            "type": [
              "string",
              "null"
            ],
            "description": "Output format for `convert`. Default `pdf`."
          },
          "translations": {
            "type": [
              "object",
              "null"
            ],
            "description": "Translations for `t`, by language, each a flat object of keys and texts.\nThey win over the service's dictionaries and the template's own. At most\n10,000 entries, keys of 256 bytes and texts of 16 KiB."
          },
          "undefinedAsEmpty": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "`true` prints a value missing from the data as nothing."
          },
          "userPassword": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "RotateForm": {
        "type": "object",
        "required": [
          "files",
          "rotation"
        ],
        "properties": {
          "files": {
            "type": "string",
            "format": "binary"
          },
          "outputFilename": {
            "type": [
              "string",
              "null"
            ]
          },
          "pages": {
            "type": [
              "string",
              "null"
            ],
            "description": "Page selection such as `1-3,5`; all when empty."
          },
          "rotation": {
            "type": "string",
            "description": "90, 180 or 270.",
            "example": "90"
          }
        }
      },
      "SignForm": {
        "type": "object",
        "required": [
          "files"
        ],
        "properties": {
          "certify": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "With `signature=certificate`: certify rather than approve, with this\nDocMDP permission: `1` no changes, `2` form filling and signing, `3`\nalso annotations. Only the document's first signature can certify.",
            "minimum": 0
          },
          "contactInfo": {
            "type": [
              "string",
              "null"
            ],
            "description": "With `signature=certificate`: how to reach the signer."
          },
          "files": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "binary"
            },
            "description": "The PDF first, then one PNG or JPEG per placement. With\n`signature=certificate`, at most one image: the visible appearance."
          },
          "level": {
            "type": [
              "string",
              "null"
            ],
            "description": "With `signature=certificate`: the PAdES level, `b-b`, `b-t` (with an\nRFC 3161 timestamp), `b-lt` (and validation data in a DSS) or `b-lta`\n(and a document timestamp). Default the service's\n`RUSTICDOC_SIGNING_LEVEL`, `b-b` unless set.",
            "example": "b-lt"
          },
          "location": {
            "type": [
              "string",
              "null"
            ],
            "description": "With `signature=certificate`: where it was signed."
          },
          "outputFilename": {
            "type": [
              "string",
              "null"
            ]
          },
          "page": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "1-based page of each image; repeat the field for several.",
            "minimum": 0
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "With `signature=certificate`: why the document was signed."
          },
          "signature": {
            "type": [
              "string",
              "null"
            ],
            "description": "`image` (default) draws the images and is not a cryptographic\nsignature. `certificate` signs with the service's key (PAdES,\n`/ETSI.CAdES.detached`), invisibly unless an image or a position is\nsent.",
            "example": "certificate"
          },
          "signerName": {
            "type": [
              "string",
              "null"
            ],
            "description": "With `signature=certificate`: the signer's name as shown. Validators\nidentify the signer by the certificate, not by this."
          },
          "signingKey": {
            "type": [
              "string",
              "null"
            ],
            "description": "With `signature=certificate`: a named key from `RUSTICDOC_SIGNING_KEYS`\ninstead of the default one. A caller not on its allow list gets 403."
          },
          "width": {
            "type": [
              "number",
              "null"
            ],
            "format": "float",
            "description": "Width in points. Default the image's natural size, or 200 for a text\nappearance."
          },
          "x": {
            "type": [
              "number",
              "null"
            ],
            "format": "float",
            "description": "Points from the page's left edge to the image's left edge."
          },
          "y": {
            "type": [
              "number",
              "null"
            ],
            "format": "float",
            "description": "Points from the page's bottom edge to the image's bottom edge."
          }
        }
      },
      "SkippedSchema": {
        "type": "object",
        "required": [
          "file",
          "reason"
        ],
        "properties": {
          "file": {
            "type": "string"
          },
          "reason": {
            "type": "string"
          }
        }
      },
      "SplitForm": {
        "type": "object",
        "required": [
          "files",
          "splitMode",
          "splitSpan"
        ],
        "properties": {
          "files": {
            "type": "string",
            "format": "binary"
          },
          "outputFilename": {
            "type": [
              "string",
              "null"
            ]
          },
          "splitMode": {
            "type": "string",
            "description": "`pages` or `intervals`.",
            "example": "pages"
          },
          "splitSpan": {
            "type": "string",
            "description": "For `pages`: ranges such as `1-3,5`. For `intervals`: pages per file.",
            "example": "1-2"
          },
          "splitUnify": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "For `pages`: one file holding every range instead of one per range."
          }
        }
      },
      "StoredFontSchema": {
        "type": "object",
        "description": "A stored font.",
        "required": [
          "id",
          "file",
          "size",
          "faces",
          "uploadedAt"
        ],
        "properties": {
          "faces": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FaceSchema"
            }
          },
          "file": {
            "type": "string"
          },
          "id": {
            "type": "string",
            "description": "The hex SHA-256 of the file, so storing it twice changes nothing."
          },
          "size": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "uploadedAt": {
            "type": "string",
            "description": "RFC 3339."
          }
        }
      },
      "TemplateList": {
        "type": "object",
        "description": "One page of templates.",
        "required": [
          "templates"
        ],
        "properties": {
          "nextCursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` for the next page. Absent on the last."
          },
          "templates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TemplateSchema"
            }
          }
        }
      },
      "TemplateSchema": {
        "type": "object",
        "description": "A stored template.",
        "required": [
          "id",
          "name",
          "format",
          "size",
          "sha256",
          "createdAt"
        ],
        "properties": {
          "createdAt": {
            "type": "string",
            "description": "RFC 3339."
          },
          "format": {
            "type": "string",
            "example": "docx"
          },
          "id": {
            "type": "string",
            "description": "Pass as `templateId` to `/v1/render`, `/v1/render/batch` or `/v1/templates/validate`,\nor point an alias at it with `PUT /v1/aliases/{name}`.",
            "example": "01966f3e-7c1a-7d3c-9a44-1d2f5a6b7c8d"
          },
          "name": {
            "type": "string",
            "example": "invoice.docx"
          },
          "sha256": {
            "type": "string",
            "description": "Hex SHA-256 of the content."
          },
          "size": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          }
        }
      },
      "TemplateUploadForm": {
        "type": "object",
        "description": "The multipart form `POST /v1/templates` accepts.",
        "required": [
          "template"
        ],
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "A name for it, in place of the file's. The extension stays the file's."
          },
          "template": {
            "type": "string",
            "format": "binary",
            "description": "The template: DOCX, XLSX, PPTX, ODT, ODS, ODP, PDF or a text format."
          }
        }
      },
      "TemplateVariables": {
        "type": "object",
        "description": "What a stored template reads from its data.",
        "required": [
          "templateId",
          "format",
          "variables",
          "example"
        ],
        "properties": {
          "example": {
            "type": "object",
            "description": "Data of the right shape, with empty strings where values go."
          },
          "format": {
            "type": "string",
            "example": "docx"
          },
          "templateId": {
            "type": "string",
            "description": "The template's id, also when it was asked for by alias."
          },
          "variables": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Variable"
            },
            "description": "Every path the template reads, parents first: `customer.name`, `items`\n(an array), `items[].price`. For a PDF form, its fields' names and the\ntags in their values; tags in annotations are not listed."
          }
        }
      },
      "Validation": {
        "type": "object",
        "required": [
          "ok",
          "template_hash",
          "format",
          "engine",
          "diagnostics",
          "missing_fonts"
        ],
        "properties": {
          "diagnostics": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Every problem found: `severity`, `message`, and a `location` with\n`part`, `place` and `snippet` where known."
          },
          "engine": {
            "type": "string",
            "description": "The engine that would handle it: `office`, `text` or `none`."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "The first error, when `ok` is false."
          },
          "format": {
            "type": "string"
          },
          "missing_fonts": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Fonts the template names that the service has neither loaded nor\nmapped to a substitute with the same metrics. Text set in them renders\nin a fallback font, which moves line and page breaks. Each also appears\nin `diagnostics` as a warning."
          },
          "ok": {
            "type": "boolean",
            "description": "True when the template renders with the sample data."
          },
          "template_hash": {
            "type": "string",
            "description": "SHA-256 of the template, the cache key."
          }
        }
      },
      "Variable": {
        "type": "object",
        "description": "One path the template reads.",
        "required": [
          "path",
          "type"
        ],
        "properties": {
          "path": {
            "type": "string",
            "description": "Dotted, with `[]` for each item of an array: `items[].price`. `items[]`\nitself says what each item is.",
            "example": "items[].price"
          },
          "type": {
            "$ref": "#/components/schemas/VariableKind"
          }
        }
      },
      "VariableKind": {
        "type": "string",
        "description": "What a template does with a path.",
        "enum": [
          "value",
          "object",
          "array"
        ]
      },
      "Version": {
        "type": "object",
        "required": [
          "version",
          "engines"
        ],
        "properties": {
          "engines": {
            "description": "Paths of the engines in use, or null when absent."
          },
          "version": {
            "type": "string",
            "example": "0.1.0"
          }
        }
      }
    },
    "securitySchemes": {
      "api_key": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key"
      },
      "basic": {
        "type": "http",
        "scheme": "basic"
      },
      "bearer": {
        "type": "http",
        "scheme": "bearer"
      }
    }
  },
  "tags": [
    {
      "name": "render",
      "description": "Templates in, documents out"
    },
    {
      "name": "convert",
      "description": "Documents in, converted documents out"
    },
    {
      "name": "pdf",
      "description": "PDF operations"
    },
    {
      "name": "pdf-forms",
      "description": "PDF templates: form fields, tags in annotations, overlays"
    },
    {
      "name": "templates",
      "description": "Store a template, name it with an alias, and render it by id or alias from any replica"
    },
    {
      "name": "jobs",
      "description": "Requests run in the background with `Prefer: respond-async`, and their results"
    },
    {
      "name": "fonts",
      "description": "List, upload and delete fonts, and reload a mounted directory"
    },
    {
      "name": "service",
      "description": "Health, version, metrics"
    }
  ]
}
