{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/everypage-co/json-schema/everypage-co-file-schema.json",
  "title": "File",
  "description": "The owner's view of a file (`FileResponse`). Optional fields are omitted when empty rather than sent as null. `revisions` is populated only by the single-file GET, never in the list.",
  "x-generated": "2026-11-07",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/everypage-openapi.json#/components/schemas/File",
  "type": "object",
  "properties": {
    "uuid": {
      "type": "string",
      "format": "uuid"
    },
    "shortId": {
      "type": "string",
      "description": "Short share-URL alias; absent on files created before short IDs."
    },
    "slug": {
      "type": "string",
      "description": "Vanity link name (Pro); absent unless set. Resolves only on the owner's custom domain."
    },
    "originalName": {
      "type": "string"
    },
    "size": {
      "type": "integer",
      "format": "int64",
      "description": "Bytes"
    },
    "mimeType": {
      "type": "string"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "updatedAt": {
      "type": "string",
      "format": "date-time"
    },
    "deleteAt": {
      "type": "string",
      "format": "date-time",
      "description": "Absent when the file never expires."
    },
    "viewCount": {
      "type": "integer"
    },
    "totalPages": {
      "type": "integer"
    },
    "viewerMode": {
      "type": "string",
      "description": "How readers see the document. `scroll` is a continuous vertical column of every page.",
      "enum": [
        "standard",
        "scroll",
        "flipbook",
        "swipe",
        "magazine"
      ]
    },
    "protected": {
      "type": "boolean",
      "description": "True if a password is set."
    },
    "allowDownload": {
      "type": "boolean"
    },
    "requireEmail": {
      "type": "boolean"
    },
    "notifyOnView": {
      "type": "boolean"
    },
    "commentsEnabled": {
      "type": "boolean",
      "description": "Reader feedback notes are on (Pro to enable)."
    },
    "commentsAllowPublic": {
      "type": "boolean",
      "description": "Commenters may opt into public display of their note."
    },
    "notifyOnComment": {
      "type": "boolean",
      "description": "The owner is emailed (throttled) when a note arrives."
    },
    "askReceipt": {
      "type": "boolean",
      "description": "Viewers are asked to confirm receipt (Basic+ to enable)."
    },
    "showExpiry": {
      "type": "boolean",
      "description": "Readers see a live \"Expires in\" countdown next to Download (default true, every plan). When false the public share-link metadata omits the expiry instant entirely."
    },
    "marksEnabled": {
      "type": "boolean",
      "description": "Proofing page marks are on (Pro to enable)."
    },
    "annotationsEnabled": {
      "type": "boolean",
      "description": "Proofing annotations are on (Pro to enable)."
    },
    "viewLimit": {
      "type": "integer",
      "description": "Self-destruct cap (Basic+). Absent when unlimited."
    },
    "viewsConsumed": {
      "type": "integer",
      "description": "Views consumed against `viewLimit` (always present, 0 when no limit)."
    },
    "burnedAt": {
      "type": "string",
      "format": "date-time",
      "description": "Present once the view-limit sweep destroyed the blob; the row and its readership survive, and content replacement un-burns it."
    },
    "underReview": {
      "type": "boolean",
      "description": "Present (true) while the document is paused for abuse review. The owner can still read it (this DTO, the content, readership); every write to it - settings, content, hotspots, assets, link variants, delete - answers 403 with detail \"This document is under review\" (code `forbidden`) until a person has reviewed it. Email support@everypage.co with the link to ask."
    },
    "pausedAt": {
      "type": "string",
      "format": "date-time",
      "description": "When the pause began. Present with `underReview`."
    },
    "reviewReasons": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "reports",
          "unsafe_link",
          "automatic_check"
        ]
      },
      "description": "Why the document is paused, as distinct kinds in this order - `reports` (readers reported it), `unsafe_link` (Google Web Risk lists a link in it), `automatic_check` (the hosted-page upload scan). Absent when the pause carries no open flag (a manual pause) and when not under review."
    },
    "gateDomains": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Email-gate domain allowlist (Pro). Absent when any domain is accepted."
    },
    "gateFields": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/GateField"
      },
      "description": "The stored lead-capture form definition (Pro). Absent when the gate is the plain email prompt."
    },
    "gateHeading": {
      "type": "string",
      "description": "The owner's heading for the gate card readers see before the document opens (Pro). Absent when the default wording applies."
    },
    "gateMessage": {
      "type": "string",
      "description": "The owner's explanatory line on the gate card (Pro). Absent when the default wording applies."
    },
    "watermark": {
      "type": "boolean",
      "description": "Viewer pages are stamped with the reader identity (Pro)."
    },
    "viewerSettings": {
      "type": "object",
      "additionalProperties": true,
      "description": "The stored appearance/behaviour blob exactly as saved (see the FileSettings.viewerSettings description); `{}` when never customised. Always present for the owner regardless of current plan."
    },
    "shareDomain": {
      "type": "string",
      "description": "The owner's verified Pro custom domain that share links and embeds are built on. Absent when links use everypage.co."
    },
    "folderId": {
      "type": "string",
      "format": "uuid",
      "description": "UUID of the folder the file is filed in. Absent when unfiled."
    },
    "anonShareable": {
      "type": "boolean",
      "description": "True for owner-less files that are public-by-link (browser anonymous uploads, Canva imports). Always false once claimed."
    },
    "revisions": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/Revision"
      },
      "description": "Replacement history - superseded content versions, newest first. Populated only by GET /api/v1/files/{uuid}; absent in the list and when the content was never replaced."
    },
    "pageRange": {
      "allOf": [
        {
          "$ref": "#/$defs/PageRange"
        }
      ],
      "description": "Present when a page range limits non-owner viewers (absent = the full document serves). Enforced regardless of your current plan (see the setting's description); owners always get the full document.\n"
    },
    "contentVersion": {
      "type": "integer",
      "description": "Counts in-place content replacements (1 = the original upload)."
    },
    "palette": {
      "$ref": "#/$defs/Palette"
    },
    "canvaDesignId": {
      "type": "string",
      "description": "The Canva design this file was published from. Absent on files with no design binding (including after a detach)."
    },
    "canvaUpdatesEnabled": {
      "type": "boolean",
      "description": "Whether a Canva-side republish of the bound design may replace this file's bytes (see the setting). Present only alongside canvaDesignId."
    }
  },
  "$defs": {
    "GateField": {
      "type": "object",
      "description": "One field of a lead-capture gate form. A form holds 1-10 fields, exactly one of type email (always first and required).",
      "required": [
        "key",
        "label",
        "type"
      ],
      "properties": {
        "key": {
          "type": "string",
          "pattern": "^[a-z][a-z0-9_]{0,39}$",
          "description": "Unique snake_case key; submitted values and captured responses are keyed by it."
        },
        "label": {
          "type": "string",
          "minLength": 1,
          "maxLength": 300
        },
        "type": {
          "type": "string",
          "enum": [
            "text",
            "email",
            "phone",
            "company",
            "select"
          ],
          "description": "`select` is a drop-down: the viewer picks exactly one of `options`. Text, phone and company values are capped at 500 characters."
        },
        "required": {
          "type": "boolean"
        },
        "options": {
          "type": "array",
          "minItems": 2,
          "maxItems": 20,
          "items": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "description": "The drop-down choices (type `select` only; unique). Omitted for every other type."
        }
      }
    },
    "PageRange": {
      "type": "object",
      "description": "A non-owner page window, 1-based and inclusive. `{\"from\":0,\"to\":0}` is the explicit \"no range\" value (clears the file-level range; as a variant override it serves the full document through that variant).\n",
      "properties": {
        "from": {
          "type": "integer",
          "minimum": 0,
          "description": "First shared page (1-based)."
        },
        "to": {
          "type": "integer",
          "minimum": 0,
          "maximum": 20000,
          "description": "Last shared page (inclusive; must be >= from)."
        }
      }
    },
    "Palette": {
      "type": "array",
      "description": "Colour palette extracted from the document at import time: hex swatches (\"#rrggbb\"), dominant colour first, at most 6. ADVISORY ONLY - extraction is best-effort (first-page embedded raster images, short time budget), so the field is simply absent for vector-only documents, for files uploaded through non-import paths, for files created before the feature, and whenever extraction fails or times out. Absence is normal, never an error; use the palette for theming (e.g. styling a share card), never for logic.\n",
      "items": {
        "type": "string"
      }
    },
    "Revision": {
      "type": "object",
      "description": "One superseded content version of a file (see `File.revisions`).",
      "properties": {
        "version": {
          "type": "integer",
          "description": "The content version this entry was (1 = the original upload)."
        },
        "originalName": {
          "type": "string"
        },
        "size": {
          "type": "integer",
          "format": "int64",
          "description": "Bytes"
        },
        "totalPages": {
          "type": "integer"
        },
        "createdAt": {
          "type": "string",
          "format": "date-time",
          "description": "When this version was uploaded."
        }
      }
    }
  }
}