EveryPage Variants API
The Variants API from EveryPage — 2 operation(s) for variants.
The Variants API from EveryPage — 2 operation(s) for variants.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
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.curl "https://apis.io/api/v1/apis/everypage-co:everypage-co-variants-api"
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.
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: 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