{
  "openapi": "3.1.0",
  "info": {
    "title": "Papermark Public API",
    "version": "1.0.0",
    "description": "Papermark's public REST API. All endpoints require a bearer token created in Settings → API Tokens, or issued via OAuth 2.1.",
    "contact": {
      "name": "Papermark",
      "url": "https://www.papermark.com"
    },
    "license": {
      "name": "AGPL-3.0",
      "url": "https://www.papermark.com/license"
    }
  },
  "servers": [
    {
      "url": "https://api.papermark.com",
      "description": "Production"
    },
    {
      "url": "http://localhost:3000/api",
      "description": "Local development (no subdomain rewrite)"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Documents",
      "description": "Upload and manage documents."
    },
    {
      "name": "Links",
      "description": "Create and manage share links."
    },
    {
      "name": "Datarooms",
      "description": "Create and manage virtual datarooms."
    },
    {
      "name": "Visitors",
      "description": "Persistent visitors and their view history."
    },
    {
      "name": "Analytics",
      "description": "Aggregate analytics backed by Tinybird. Subject to a tighter per-minute rate limit."
    }
  ],
  "paths": {
    "/v1/documents": {
      "get": {
        "summary": "List documents",
        "description": "Returns a paginated list of documents for the authenticated team.",
        "tags": [
          "Documents"
        ],
        "security": [
          {
            "bearerAuth": [
              "documents.read"
            ]
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "folderId",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of documents",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentList"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a document",
        "description": "Create a document from a previously-uploaded file (via POST /v1/documents/upload-url using `upload_id`) or an external URL.",
        "tags": [
          "Documents"
        ],
        "security": [
          {
            "bearerAuth": [
              "documents.write"
            ]
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDocumentRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Document created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Document"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/documents/{id}": {
      "get": {
        "summary": "Get a document",
        "tags": [
          "Documents"
        ],
        "security": [
          {
            "bearerAuth": [
              "documents.read"
            ]
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Document",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Document"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a document",
        "tags": [
          "Documents"
        ],
        "security": [
          {
            "bearerAuth": [
              "documents.write"
            ]
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "Document deleted"
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/documents/search": {
      "get": {
        "summary": "Search documents",
        "description": "Case-insensitive substring match against document name. Returns up to `limit` most-recent matches.",
        "tags": [
          "Documents"
        ],
        "security": [
          {
            "bearerAuth": [
              "documents.read"
            ]
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "q",
            "schema": {
              "type": "string",
              "minLength": 1,
              "example": "pitch deck"
            },
            "required": true
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching documents",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentList"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/documents/upload-url": {
      "post": {
        "summary": "Generate a presigned upload URL",
        "description": "Step 1 of the document upload flow. Returns a presigned S3 PUT URL and an opaque one-time `upload_id`. Upload the file bytes directly to `upload_url`, then call POST /v1/documents with that `upload_id`.",
        "tags": [
          "Documents"
        ],
        "security": [
          {
            "bearerAuth": [
              "documents.write"
            ]
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UploadUrlRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Presigned URL",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadUrlResponse"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/links": {
      "get": {
        "summary": "List share links",
        "tags": [
          "Links"
        ],
        "security": [
          {
            "bearerAuth": [
              "links.read"
            ]
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "document_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "dataroom_id",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of links",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkList"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a share link",
        "tags": [
          "Links"
        ],
        "security": [
          {
            "bearerAuth": [
              "links.write"
            ]
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateLinkRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Link created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Link"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/links/{id}": {
      "get": {
        "summary": "Get a share link",
        "tags": [
          "Links"
        ],
        "security": [
          {
            "bearerAuth": [
              "links.read"
            ]
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Link",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Link"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update a share link",
        "tags": [
          "Links"
        ],
        "security": [
          {
            "bearerAuth": [
              "links.write"
            ]
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateLinkRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated link",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Link"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Revoke a share link (soft delete)",
        "tags": [
          "Links"
        ],
        "security": [
          {
            "bearerAuth": [
              "links.write"
            ]
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "responses": {
          "204": {
            "description": "Link revoked"
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/links/{id}/views": {
      "get": {
        "summary": "List views for a share link",
        "description": "Returns cursor-paginated view records for a link in reverse-chronological order.",
        "tags": [
          "Analytics"
        ],
        "security": [
          {
            "bearerAuth": [
              "analytics.read"
            ]
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "schema": {
              "type": "string"
            },
            "required": true
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of views",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ViewList"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/datarooms": {
      "get": {
        "summary": "List datarooms",
        "description": "Paginated list of datarooms for the authenticated team.",
        "tags": [
          "Datarooms"
        ],
        "security": [
          {
            "bearerAuth": [
              "datarooms.read"
            ]
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of datarooms",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DataroomList"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a dataroom",
        "description": "Create a new dataroom. Documents are added separately.",
        "tags": [
          "Datarooms"
        ],
        "security": [
          {
            "bearerAuth": [
              "datarooms.write"
            ]
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDataroomRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Dataroom created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Dataroom"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/datarooms/{id}": {
      "get": {
        "summary": "Get a dataroom",
        "tags": [
          "Datarooms"
        ],
        "security": [
          {
            "bearerAuth": [
              "datarooms.read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Dataroom",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Dataroom"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update a dataroom",
        "tags": [
          "Datarooms"
        ],
        "security": [
          {
            "bearerAuth": [
              "datarooms.write"
            ]
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateDataroomRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated dataroom",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Dataroom"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a dataroom",
        "tags": [
          "Datarooms"
        ],
        "security": [
          {
            "bearerAuth": [
              "datarooms.write"
            ]
          }
        ],
        "responses": {
          "204": {
            "description": "Dataroom deleted"
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/datarooms/{id}/documents": {
      "get": {
        "summary": "List documents in a dataroom",
        "description": "Returns DataroomDocument rows (the join between a dataroom and an underlying Document), ordered by folder then orderIndex.",
        "tags": [
          "Datarooms"
        ],
        "security": [
          {
            "bearerAuth": [
              "datarooms.read"
            ]
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "folder_id",
            "description": "Filter to documents inside a specific folder.",
            "schema": {
              "type": "string",
              "description": "Filter to documents inside a specific folder."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of dataroom documents",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DataroomItemList"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/visitors": {
      "get": {
        "summary": "List visitors",
        "description": "Paginated list of persistent visitors for the team (rows in the Viewer table). Anonymous views that were never tied to a Viewer record are available via GET /v1/links/{id}/views instead.",
        "tags": [
          "Visitors"
        ],
        "security": [
          {
            "bearerAuth": [
              "visitors.read"
            ]
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "email",
            "description": "Exact email match (case-insensitive) — e.g. ?email=jane@acme.com",
            "schema": {
              "type": "string",
              "description": "Exact email match (case-insensitive) — e.g. ?email=jane@acme.com"
            }
          },
          {
            "in": "query",
            "name": "dataroom_id",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of visitors",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VisitorList"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/visitors/{id}": {
      "get": {
        "summary": "Get a visitor",
        "tags": [
          "Visitors"
        ],
        "security": [
          {
            "bearerAuth": [
              "visitors.read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Visitor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Visitor"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/visitors/{id}/views": {
      "get": {
        "summary": "List a visitor's views",
        "description": "All view events tied to this visitor, newest first.",
        "tags": [
          "Visitors"
        ],
        "security": [
          {
            "bearerAuth": [
              "visitors.read"
            ]
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of views",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VisitorViewList"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/analytics/documents/{id}": {
      "get": {
        "summary": "Document analytics",
        "description": "Aggregate analytics for a single document: total views, unique viewers, total read time, per-page average duration. Backed by Tinybird — subject to a tighter per-minute rate limit than other endpoints.",
        "tags": [
          "Analytics"
        ],
        "security": [
          {
            "bearerAuth": [
              "analytics.read"
            ]
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "since",
            "description": "Unix milliseconds — start of window (default: 30 days ago)",
            "schema": {
              "type": "integer",
              "description": "Unix milliseconds — start of window (default: 30 days ago)",
              "example": 1734567890000
            }
          },
          {
            "in": "query",
            "name": "until",
            "description": "Unix milliseconds — end of window (default: now)",
            "schema": {
              "type": "integer",
              "description": "Unix milliseconds — end of window (default: now)"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Document analytics",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentAnalytics"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/analytics/links/{id}": {
      "get": {
        "summary": "Link analytics",
        "description": "Aggregate analytics for a single share link: total views, total read time, unique viewers. Backed by Tinybird — tighter rate limit.",
        "tags": [
          "Analytics"
        ],
        "security": [
          {
            "bearerAuth": [
              "analytics.read"
            ]
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "since",
            "description": "Unix milliseconds — start of window (default: 30 days ago)",
            "schema": {
              "type": "integer",
              "description": "Unix milliseconds — start of window (default: 30 days ago)",
              "example": 1734567890000
            }
          },
          {
            "in": "query",
            "name": "until",
            "description": "Unix milliseconds — end of window (default: now)",
            "schema": {
              "type": "integer",
              "description": "Unix milliseconds — end of window (default: now)"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Link analytics",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkAnalytics"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/analytics/datarooms/{id}": {
      "get": {
        "summary": "Dataroom analytics",
        "description": "Aggregate analytics across all documents in a dataroom: total views, unique viewers, total read time. Backed by Tinybird — tighter rate limit.",
        "tags": [
          "Analytics"
        ],
        "security": [
          {
            "bearerAuth": [
              "analytics.read"
            ]
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "since",
            "description": "Unix milliseconds — start of window (default: 30 days ago)",
            "schema": {
              "type": "integer",
              "description": "Unix milliseconds — start of window (default: 30 days ago)",
              "example": 1734567890000
            }
          },
          {
            "in": "query",
            "name": "until",
            "description": "Unix milliseconds — end of window (default: now)",
            "schema": {
              "type": "integer",
              "description": "Unix milliseconds — end of window (default: now)"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Dataroom analytics",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DataroomAnalytics"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/analytics/views/{id}": {
      "get": {
        "summary": "Per-view breakdown",
        "description": "Page-by-page duration, viewer location, and client info for a single view event. Backed by Tinybird — tighter rate limit.",
        "tags": [
          "Analytics"
        ],
        "security": [
          {
            "bearerAuth": [
              "analytics.read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "View analytics",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ViewAnalytics"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Token lacks required scope or team access",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Input validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "papermark-token",
        "description": "Dashboard-issued token (`pm_live_...` / `pm_test_...`) or OAuth-issued access token."
      }
    },
    "schemas": {
      "DocumentList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Document"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "data",
          "next_cursor"
        ]
      },
      "Document": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "clxy9abc1234567890"
          },
          "name": {
            "type": "string",
            "example": "pitch-deck-q4.pdf"
          },
          "type": {
            "type": [
              "string",
              "null"
            ],
            "example": "pdf"
          },
          "content_type": {
            "type": [
              "string",
              "null"
            ],
            "example": "application/pdf"
          },
          "num_pages": {
            "type": [
              "integer",
              "null"
            ],
            "example": 12
          },
          "folder_id": {
            "type": [
              "string",
              "null"
            ],
            "example": null
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "example": "2026-04-17T10:00:00.000Z"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "example": "2026-04-17T10:00:00.000Z"
          }
        },
        "required": [
          "id",
          "name",
          "type",
          "content_type",
          "num_pages",
          "folder_id",
          "created_at",
          "updated_at"
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "bad_request",
                  "unauthorized",
                  "forbidden",
                  "not_found",
                  "conflict",
                  "unprocessable_entity",
                  "rate_limit_exceeded",
                  "internal_server_error"
                ],
                "example": "unauthorized"
              },
              "message": {
                "type": "string",
                "example": "Missing bearer token."
              },
              "doc_url": {
                "type": "string",
                "format": "uri",
                "example": "https://papermark.com/docs/api/errors#unauthorized"
              },
              "details": {}
            },
            "required": [
              "code",
              "message",
              "doc_url"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "CreateDocumentRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "example": "pitch-deck-q4.pdf"
          },
          "upload_id": {
            "type": "string",
            "minLength": 1,
            "description": "Opaque upload handle returned from POST /v1/documents/upload-url.",
            "example": "upload_3xA5v7r8K9mN2pQ4sT6uVwXy"
          },
          "source_url": {
            "type": "string",
            "format": "uri",
            "description": "Alternatively, a publicly-accessible URL."
          },
          "content_type": {
            "type": "string",
            "minLength": 1,
            "example": "application/pdf"
          },
          "num_pages": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "example": 12
          },
          "file_size": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "example": 2048576
          },
          "folder_path": {
            "type": "string",
            "description": "Folder path; folder is created if it does not exist.",
            "example": "/Pitches/Q4"
          },
          "create_link": {
            "type": "boolean",
            "default": false,
            "description": "If true, also create a default share link for this document."
          }
        },
        "required": [
          "name",
          "content_type"
        ]
      },
      "UploadUrlRequest": {
        "type": "object",
        "properties": {
          "fileName": {
            "type": "string",
            "minLength": 1,
            "example": "pitch-deck.pdf"
          },
          "contentType": {
            "type": "string",
            "minLength": 1,
            "example": "application/pdf"
          },
          "contentLength": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "example": 2048576
          }
        },
        "required": [
          "fileName",
          "contentType"
        ]
      },
      "UploadUrlResponse": {
        "type": "object",
        "properties": {
          "upload_url": {
            "type": "string",
            "format": "uri",
            "description": "Presigned S3 URL. PUT the file bytes to this URL.",
            "example": "https://s3.eu-central-1.amazonaws.com/..."
          },
          "upload_id": {
            "type": "string",
            "description": "Opaque one-time upload handle to pass back in POST /v1/documents `upload_id`.",
            "example": "upload_3xA5v7r8K9mN2pQ4sT6uVwXy"
          },
          "required_headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "HTTP headers the client MUST include in the PUT request — these are part of the signed request and an omission produces a 403.",
            "example": {
              "Content-Type": "application/pdf",
              "Content-Disposition": "attachment; filename=\"pitch.pdf\"; filename*=UTF-8''pitch.pdf"
            }
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "upload_url",
          "upload_id",
          "required_headers",
          "expires_at"
        ]
      },
      "LinkList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Link"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "data",
          "next_cursor"
        ]
      },
      "Link": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "clxabc1234567890"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "example": "Q4 pitch — Acme Ventures"
          },
          "link_type": {
            "type": "string",
            "enum": [
              "DOCUMENT_LINK",
              "DATAROOM_LINK"
            ],
            "example": "DOCUMENT_LINK"
          },
          "document_id": {
            "type": [
              "string",
              "null"
            ],
            "example": "clxdoc0987654321"
          },
          "dataroom_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": "string",
            "description": "The public view URL for this link.",
            "example": "https://www.papermark.com/view/clxabc1234567890"
          },
          "slug": {
            "type": [
              "string",
              "null"
            ],
            "example": null
          },
          "domain_slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "is_password_protected": {
            "type": "boolean",
            "description": "True if a password is set. The password itself is never returned."
          },
          "email_protected": {
            "type": "boolean"
          },
          "email_authenticated": {
            "type": "boolean"
          },
          "allow_download": {
            "type": "boolean"
          },
          "allow_list": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Email or domain allow list (e.g., ['@acme.com', 'bob@example.com'])."
          },
          "deny_list": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "enable_watermark": {
            "type": "boolean"
          },
          "enable_feedback": {
            "type": "boolean"
          },
          "enable_screenshot_protection": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "link_type",
          "document_id",
          "dataroom_id",
          "url",
          "slug",
          "domain_slug",
          "expires_at",
          "is_password_protected",
          "email_protected",
          "email_authenticated",
          "allow_download",
          "allow_list",
          "deny_list",
          "enable_watermark",
          "enable_feedback",
          "enable_screenshot_protection",
          "created_at",
          "updated_at"
        ]
      },
      "CreateLinkRequest": {
        "type": "object",
        "properties": {
          "document_id": {
            "type": "string"
          },
          "dataroom_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "password": {
            "type": "string",
            "minLength": 1
          },
          "email_protected": {
            "type": "boolean",
            "default": true
          },
          "email_authenticated": {
            "type": "boolean",
            "default": false
          },
          "allow_download": {
            "type": "boolean",
            "default": false
          },
          "allow_list": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "default": []
          },
          "deny_list": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "default": []
          },
          "enable_watermark": {
            "type": "boolean",
            "default": false
          },
          "enable_feedback": {
            "type": "boolean",
            "default": false
          },
          "enable_screenshot_protection": {
            "type": "boolean",
            "default": false
          }
        }
      },
      "UpdateLinkRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "password": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "description": "Pass null to remove the password."
          },
          "email_protected": {
            "type": "boolean"
          },
          "email_authenticated": {
            "type": "boolean"
          },
          "allow_download": {
            "type": "boolean"
          },
          "allow_list": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "deny_list": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "enable_watermark": {
            "type": "boolean"
          },
          "enable_feedback": {
            "type": "boolean"
          },
          "enable_screenshot_protection": {
            "type": "boolean"
          }
        }
      },
      "ViewList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/View"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "data",
          "next_cursor"
        ]
      },
      "View": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "clxview1234567890"
          },
          "link_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "document_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "dataroom_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "viewer_email": {
            "type": [
              "string",
              "null"
            ],
            "example": "jane@acme.com"
          },
          "view_type": {
            "type": [
              "string",
              "null"
            ],
            "example": "DOCUMENT_VIEW"
          },
          "viewed_at": {
            "type": "string",
            "format": "date-time"
          },
          "downloaded_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "download_type": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "link_id",
          "document_id",
          "dataroom_id",
          "viewer_email",
          "view_type",
          "viewed_at",
          "downloaded_at",
          "download_type"
        ]
      },
      "DataroomList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Dataroom"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "data",
          "next_cursor"
        ]
      },
      "Dataroom": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "clxy9abc1234567890"
          },
          "pid": {
            "type": "string",
            "example": "dr_abc123def456",
            "description": "Stripe-style public ID, suitable for URLs and user references"
          },
          "name": {
            "type": "string",
            "example": "Acme Series A"
          },
          "internal_name": {
            "type": [
              "string",
              "null"
            ],
            "example": "acme-series-a",
            "description": "Private alias visible only to the team"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "example": "Due-diligence materials"
          },
          "document_count": {
            "type": "integer",
            "example": 12
          },
          "folder_count": {
            "type": "integer",
            "example": 3
          },
          "conversations_enabled": {
            "type": "boolean",
            "example": false
          },
          "agents_enabled": {
            "type": "boolean",
            "example": false
          },
          "allow_bulk_download": {
            "type": "boolean",
            "example": true
          },
          "is_frozen": {
            "type": "boolean",
            "example": false
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "pid",
          "name",
          "internal_name",
          "description",
          "document_count",
          "folder_count",
          "conversations_enabled",
          "agents_enabled",
          "allow_bulk_download",
          "is_frozen",
          "created_at",
          "updated_at"
        ]
      },
      "CreateDataroomRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "example": "Acme Series A"
          },
          "internal_name": {
            "type": "string",
            "example": "acme-series-a"
          },
          "description": {
            "type": "string"
          }
        },
        "required": [
          "name"
        ]
      },
      "UpdateDataroomRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "internal_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "conversations_enabled": {
            "type": "boolean"
          },
          "agents_enabled": {
            "type": "boolean"
          },
          "allow_bulk_download": {
            "type": "boolean"
          }
        }
      },
      "DataroomItemList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DataroomItem"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "data",
          "next_cursor"
        ]
      },
      "DataroomItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "clxy9abc1234567890",
            "description": "DataroomDocument id (the link between a document and the dataroom)"
          },
          "document_id": {
            "type": "string",
            "example": "clxyabc1234567890"
          },
          "document_name": {
            "type": "string",
            "example": "pitch-deck-q4.pdf"
          },
          "content_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "num_pages": {
            "type": [
              "integer",
              "null"
            ]
          },
          "folder_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "folder_path": {
            "type": [
              "string",
              "null"
            ],
            "example": "/Legal/Contracts"
          },
          "order_index": {
            "type": [
              "integer",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "document_id",
          "document_name",
          "content_type",
          "num_pages",
          "folder_id",
          "folder_path",
          "order_index",
          "created_at"
        ]
      },
      "VisitorList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Visitor"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "data",
          "next_cursor"
        ]
      },
      "Visitor": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "clxyvwr1234567890"
          },
          "email": {
            "type": "string",
            "example": "jane@acme.com"
          },
          "verified": {
            "type": "boolean",
            "example": true
          },
          "dataroom_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "If the visitor is tied to a specific dataroom (invitation or group member), this is its id"
          },
          "invited_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "total_views": {
            "type": "integer",
            "example": 14
          },
          "last_viewed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "email",
          "verified",
          "dataroom_id",
          "invited_at",
          "total_views",
          "last_viewed_at",
          "created_at",
          "updated_at"
        ]
      },
      "VisitorViewList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VisitorView"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "data",
          "next_cursor"
        ]
      },
      "VisitorView": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "link_id": {
            "type": "string"
          },
          "document_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "dataroom_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "view_type": {
            "type": "string"
          },
          "viewed_at": {
            "type": "string",
            "format": "date-time"
          },
          "downloaded_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "is_archived": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "link_id",
          "document_id",
          "dataroom_id",
          "view_type",
          "viewed_at",
          "downloaded_at",
          "is_archived"
        ]
      },
      "DocumentAnalytics": {
        "type": "object",
        "properties": {
          "document_id": {
            "type": "string"
          },
          "total_views": {
            "type": "integer"
          },
          "unique_viewers": {
            "type": "integer"
          },
          "total_duration_seconds": {
            "type": "number"
          },
          "avg_page_duration_seconds": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "page_number": {
                  "type": "integer"
                },
                "avg_duration_seconds": {
                  "type": "number"
                }
              },
              "required": [
                "page_number",
                "avg_duration_seconds"
              ]
            }
          },
          "since": {
            "type": "string",
            "format": "date-time"
          },
          "until": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "document_id",
          "total_views",
          "unique_viewers",
          "total_duration_seconds",
          "avg_page_duration_seconds",
          "since",
          "until"
        ]
      },
      "LinkAnalytics": {
        "type": "object",
        "properties": {
          "link_id": {
            "type": "string"
          },
          "total_views": {
            "type": "integer"
          },
          "total_duration_seconds": {
            "type": "number"
          },
          "unique_viewers": {
            "type": "integer"
          },
          "since": {
            "type": "string",
            "format": "date-time"
          },
          "until": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "link_id",
          "total_views",
          "total_duration_seconds",
          "unique_viewers",
          "since",
          "until"
        ]
      },
      "DataroomAnalytics": {
        "type": "object",
        "properties": {
          "dataroom_id": {
            "type": "string"
          },
          "total_views": {
            "type": "integer"
          },
          "unique_viewers": {
            "type": "integer"
          },
          "total_duration_seconds": {
            "type": "number"
          },
          "since": {
            "type": "string",
            "format": "date-time"
          },
          "until": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "dataroom_id",
          "total_views",
          "unique_viewers",
          "total_duration_seconds",
          "since",
          "until"
        ]
      },
      "ViewAnalytics": {
        "type": "object",
        "properties": {
          "view_id": {
            "type": "string"
          },
          "document_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "viewer_email": {
            "type": [
              "string",
              "null"
            ]
          },
          "viewed_at": {
            "type": "string",
            "format": "date-time"
          },
          "page_durations": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "page_number": {
                  "type": "integer"
                },
                "duration_seconds": {
                  "type": "number"
                }
              },
              "required": [
                "page_number",
                "duration_seconds"
              ]
            }
          },
          "total_duration_seconds": {
            "type": "number"
          },
          "location": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "country": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "city": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "country",
              "city"
            ]
          },
          "client": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "browser": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "os": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "device": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "browser",
              "os",
              "device"
            ]
          }
        },
        "required": [
          "view_id",
          "document_id",
          "viewer_email",
          "viewed_at",
          "page_durations",
          "total_duration_seconds",
          "location",
          "client"
        ]
      }
    }
  }
}
