EveryPage Variants API

The Variants API from EveryPage — 2 operation(s) for variants.

GitHub OpenAPI

Operations 4

GET /api/v1/files/{uuid}/variants List a file's link variants #
POST /api/v1/files/{uuid}/variants Create a link variant #
PUT /api/v1/files/{uuid}/variants/{variantUuid} Update a link variant #
DELETE /api/v1/files/{uuid}/variants/{variantUuid} Delete or redact a link variant #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/everypage-co:everypage-co-variants-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

everypage-co-variants-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: EveryPage Variants API
  description: 'Upload documents and get trackable share links programmatically. Authenticate with a personal API key (create one at https://everypage.co/account under API keys) or an OAuth 2.0 access token, sent as a bearer token. Every error under /api/v1 is an RFC 9457 problem document (application/problem+json) - branch on its `code`.


    Versioning: /api/v1 is the contract - changes inside a version are additive only, and anything scheduled to go away carries Deprecation (RFC 9745) and Sunset (RFC 8594) headers with at least six months'' notice (https://everypage.co/docs/developers/versioning; no active deprecations). Rate limits: 120 requests per minute per bearer token, advertised on every response in RateLimit and RateLimit-Policy (IETF structured fields) plus X-RateLimit-*, with Retry-After on 429. OAuth 2.0 server metadata: https://everypage.co/.well-known/oauth-authorization-server.'
  version: 1.1.0
  termsOfService: https://everypage.co/terms
  x-deprecation-policy:
    url: https://everypage.co/docs/developers/versioning
    minimumNoticeDays: 180
    signals:
    - Deprecation
    - Sunset
    - Link rel=deprecation
    activeDeprecations: []
  contact:
    email: support@everypage.co
servers:
- url: https://everypage.co
security:
- bearerAuth: []
tags:
- name: Variants
paths:
  /api/v1/files/{uuid}/variants:
    parameters:
    - $ref: '#/components/parameters/fileId'
    get:
      tags:
      - Variants
      summary: List a file's link variants
      operationId: listVariants
      description: Every variant of the file, newest first - revoked and redacted ones included, so they can be managed here. Pro plan required; scope `files:read`.
      security:
      - bearerAuth: []
      - oauth2:
        - files:read
      responses:
        '200':
          description: The file's variants
          content:
            application/json:
              schema:
                type: object
                properties:
                  variants:
                    type: array
                    items:
                      $ref: '#/components/schemas/Variant'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
    post:
      tags:
      - Variants
      summary: Create a link variant
      operationId: createVariant
      description: 'Mint a per-recipient child link for a file you own (Pro, `files:write`). The variant has its own 12-char short id and URL, an optional `label` (recipient name, GDPR-redactable) and `overrides` (`allowDownload`, `pageRange`). Views through it are attributed in readership and in `file.opened`/`file.viewed`/`file.downloaded` webhook payloads (`variantUuid`/`variantLabel`). Up to 200 variants per file.


        Passwords and viewing gates are deliberately per-FILE, not per-variant: a viewer who passed the file''s gate through one link has passed it for all of the file''s links.'
      security:
      - bearerAuth: []
      - oauth2:
        - files:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VariantCreate'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Variant'
        '400':
          description: Invalid body, label too long, or variant limit (200) reached.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/TooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/v1/files/{uuid}/variants/{variantUuid}:
    parameters:
    - $ref: '#/components/parameters/fileId'
    - name: variantUuid
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: The variant's UUID from the create/list response (never the variant's short id).
    put:
      tags:
      - Variants
      summary: Update a link variant
      operationId: updateVariant
      description: 'Update the label and/or overrides, or revoke/un-revoke the variant. Omitted fields keep their current values; `label: ""` clears the label. An `overrides` object replaces the stored override set as a whole (see VariantUpdate) - include every override you want to keep. A revoked variant''s link answers 404 to viewers (without revealing that a canonical link exists) until un-revoked. A redacted variant''s label can never be set again (409).'
      security:
      - bearerAuth: []
      - oauth2:
        - files:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VariantUpdate'
      responses:
        '200':
          description: The updated variant
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Variant'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/TooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
    delete:
      tags:
      - Variants
      summary: Delete or redact a link variant
      operationId: deleteVariant
      description: 'Without parameters, permanently deletes the variant (its sessions survive, unattributed). With `?redact=1`, performs a GDPR redaction instead: the recipient label - the only personal field - is erased from the variant, all analytics surfaces, and every stored or pending webhook payload (anything already delivered to your own endpoints is yours to erase), and the variant keeps working, so analytics stay consistent. Redaction is the compliance path for erasure requests (for example Shopify''s `customers/redact`). Idempotent.'
      security:
      - bearerAuth: []
      - oauth2:
        - files:write
      parameters:
      - name: redact
        in: query
        required: false
        schema:
          type: string
          enum:
          - '1'
        description: Redact in place instead of deleting.
      responses:
        '204':
          description: Deleted (or redacted).
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  schemas:
    VariantOverrides:
      type: object
      description: Per-variant setting overrides applied to viewers arriving through this variant. Omitted keys inherit the file's settings. On update the object is stored exactly as sent (whole-object replace, never a per-key merge).
      properties:
        allowDownload:
          type: boolean
          description: Override the file's explicit-download permission for this variant's viewers.
        pageRange:
          allOf:
          - $ref: '#/components/schemas/PageRange'
          description: 'Override the file-level page range for this variant''s viewers: a valid pair (1 <= from <= to; file at most 100MB) replaces the file''s range outright, and the explicit `{"from":0,"to":0}` serves the FULL document through this variant even when the file has a range. Omitted, the file-level range (if any) applies. The variant-effective window is what the variant''s public metadata advertises and what its viewers'' bytes are trimmed to; like the file-level range it keeps enforcing after a Pro downgrade.

            '
    VariantUpdate:
      type: object
      description: 'All fields optional; omitted fields keep their current value. One exception in granularity: `overrides`, when present, REPLACES the stored override set as a whole object - it is never merged key by key. Send every override you want to keep: `{"overrides": {"allowDownload": true}}` also removes a stored `pageRange` override, `{"overrides": {}}` removes all overrides, and only omitting `overrides` entirely leaves the stored set untouched.

        '
      properties:
        label:
          type: string
          maxLength: 200
          description: New label; empty string clears it. Rejected (409) on a redacted variant.
        overrides:
          $ref: '#/components/schemas/VariantOverrides'
        revoked:
          type: boolean
          description: true revokes (viewers get 404), false un-revokes.
    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).

        '
      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).
    Variant:
      type: object
      description: A per-recipient child link of a file.
      properties:
        uuid:
          type: string
          format: uuid
        shortId:
          type: string
          example: aB3xY9kQ2mZ7
          description: The variant's own share-URL alias.
        url:
          type: string
          example: https://everypage.co/aB3xY9kQ2mZ7
          description: The full shareable link (the owner's custom domain when one is verified).
        label:
          type: string
          example: Jane at Acme
          description: Recipient tag. Absent when never set - and, permanently, after redaction.
        overrides:
          $ref: '#/components/schemas/VariantOverrides'
        revoked:
          type: boolean
          description: A revoked variant's link answers 404 to viewers until un-revoked.
        redactedAt:
          type: string
          format: date-time
          description: Present after GDPR redaction (the label is gone for good).
        createdAt:
          type: string
          format: date-time
    Problem:
      type: object
      description: 'RFC 9457 Problem Details, returned with `Content-Type: application/problem+json` for every error under /api/v1 and for unknown /api paths. Branch on `code` (or `status`), never on `detail`. `type` links to the matching section of https://everypage.co/docs/developers/authentication#error-responses.'
      required:
      - type
      - title
      - status
      - detail
      - code
      properties:
        type:
          type: string
          format: uri
          example: https://everypage.co/docs/developers/authentication#insufficient-scope
        title:
          type: string
          description: The HTTP reason phrase.
          example: Forbidden
        status:
          type: integer
          minimum: 400
          maximum: 599
          example: 403
        detail:
          type: string
          description: The human-readable message for this occurrence.
          example: 'insufficient_scope: requires files:write'
        instance:
          type: string
          format: uri
          description: Absolute URL of the request that failed.
          example: https://everypage.co/api/v1/files
        code:
          type: string
          description: Stable machine code. Values not listed here are the HTTP reason phrase in lower snake case and may be added without notice.
          enum:
          - missing_token
          - invalid_token
          - insufficient_scope
          - plan_required
          - validation_error
          - forbidden
          - not_found
          - method_not_allowed
          - conflict
          - gone
          - payload_too_large
          - unsupported_media_type
          - unprocessable
          - rate_limited
          - internal_error
          - service_unavailable
        hint:
          type: string
          description: What to do next.
          example: Reconnect with a token that grants files:write. API keys (ep_live_) hold every scope.
    VariantCreate:
      type: object
      properties:
        label:
          type: string
          maxLength: 200
          description: Optional recipient tag (shown in readership and webhook payloads; erasable via redaction).
        overrides:
          $ref: '#/components/schemas/VariantOverrides'
  responses:
    BadRequest:
      description: Malformed body or multipart, or an invalid value (code `validation_error`).
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    Forbidden:
      description: Not the owner (code `forbidden`), an OAuth token missing the required scope (`insufficient_scope`, with a `WWW-Authenticate` challenge naming it), a feature above your plan (`plan_required`), a plan limit reached such as file slots or the per-file asset cap (`forbidden`), or - on the two manage-token operations - a token acting at the free tier on a plan-gated field.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    Unauthorized:
      description: No or invalid API key, OAuth token, manage token or claim token (code `missing_token` or `invalid_token`).
      headers:
        WWW-Authenticate:
          description: RFC 6750 challenge, e.g. `Bearer realm="EveryPage API", error="invalid_token"`.
          schema:
            type: string
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    Conflict:
      description: Slug already in use, redacted label re-set, file claimed by another account, or a concurrent replace (code `conflict`).
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    TooLarge:
      description: The upload (or the PDF a Word/PowerPoint file converted to) is above your plan's size cap - 20MB Free, 200MB Basic, 2GB Pro - or a JSON body is over its fixed cap (1MB for settings and hotspots, 64KB for variants and claims) (code `payload_too_large`).
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    RateLimited:
      description: Over the limit - 120 requests per minute per Authorization value on /api/v1 (webhook test 10/min; the Canva lane 30/min per IP). Code `rate_limited`; wait for `Retry-After`.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
        X-RateLimit-Limit:
          schema:
            type: integer
        X-RateLimit-Remaining:
          schema:
            type: integer
        X-RateLimit-Reset:
          schema:
            type: integer
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    NotFound:
      description: Unknown id, or a file you do not own on routes where 404 replaces 403 (code `not_found`).
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
  parameters:
    fileId:
      name: uuid
      in: path
      required: true
      schema:
        type: string
        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|(?=.*[A-Z])[A-Za-z0-9]{12})$
      description: 'The file''s UUID or its 12-character short id (base62 with at least one uppercase letter - the last path segment of the share link, e.g. `aB3xY9kQ2mZ7`). Case-sensitive. Vanity slugs and link-variant short ids are NOT accepted here: a variant short id names a child link, not the file, and answers 404.'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: 'An API key (ep_live_...) or OAuth access token (ep_at_...) sent as `Authorization: Bearer <token>`. API keys hold every scope; OAuth tokens are limited to the scopes granted at consent.'
    oauth2:
      type: oauth2
      description: Authorization Code + PKCE (S256 only, no plain). Client authentication on the token and revoke endpoints via HTTP Basic or `client_id`/`client_secret` body params; public clients send `client_id` alone. Refresh tokens rotate on every refresh (the previous one is retired; reusing it revokes the token family) and may be down-scoped with `scope`. Each operation lists the scope it requires.
      flows:
        authorizationCode:
          authorizationUrl: https://everypage.co/oauth/authorize
          tokenUrl: https://everypage.co/oauth/token
          refreshUrl: https://everypage.co/oauth/token
          scopes:
            files:read: See your files and share links
            files:write: Upload, configure, and delete files
            readership:read: See who read your documents
            webhooks:manage: Create, list, test, and delete webhook subscriptions
            profile: See your account email and plan
    manageToken:
      type: http
      scheme: bearer
      bearerFormat: EveryPage manage token
      description: 'The `manageToken` returned by an anonymous upload or a Canva import, sent as `Authorization: Bearer <manageToken>`. Bound to one file; valid for 30 days and only while that file remains unclaimed - once the file is claimed into an account, token calls answer 404. Token callers act at the free tier: plan-gated settings answer 403 and content replacement (Pro) is always refused. Accepted ONLY by PUT /api/v1/files/{uuid}/settings and POST /api/v1/files/{uuid}/content; every other operation answers 401 to it. Not the `claimToken` (a different credential that attaches the file to an account).'
    canvaJwt:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: A Canva Apps SDK user token (`auth.getCanvaUserToken()`), verified against Canva JWKS. Accepted only by the /api/v1/canva/* operations, which are called by the EveryPage Canva app; it is not an EveryPage credential and no other operation accepts it.
externalDocs:
  url: https://everypage.co/developers
  description: Developer guide