EveryPage Webhooks API
The Webhooks API from EveryPage — 3 operation(s) for webhooks.
The Webhooks API from EveryPage — 3 operation(s) for webhooks.
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-webhooks-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 Webhooks 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: Webhooks
paths:
/api/v1/webhooks:
get:
tags:
- Webhooks
summary: List your webhooks
operationId: listWebhooks
description: Webhook endpoints for the caller's account. Never includes signing secrets (shown once at creation). Scope `webhooks:manage` (API keys hold every scope).
security:
- bearerAuth: []
- oauth2:
- webhooks:manage
responses:
'200':
description: The caller's webhooks
content:
application/json:
schema:
type: object
properties:
webhooks:
type: array
items:
$ref: '#/components/schemas/Webhook'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
$ref: '#/components/responses/RateLimited'
post:
tags:
- Webhooks
summary: Create a webhook
operationId: createWebhook
description: 'Register an HTTPS endpoint to receive signed event deliveries (available on every plan; up to 25 webhooks per account). Set `fileUuid` to scope delivery to one file; omit it for account-wide delivery. The signing secret (`whsec_...`) is returned exactly once, in this response, and is never retrievable again. Deliveries carry an `X-Everypage-Signature` header: `t=,v1=` where v1 is HMAC-SHA256 over `.` keyed with the FULL secret string (the `whsec_` prefix is part of the key). Endpoints failing 20 consecutive deliveries are disabled automatically.
Payload note: when the viewer arrived through a link variant, the `file.opened`, `file.viewed` and `file.downloaded` data objects additionally carry `variantUuid` and `variantLabel` (the label is omitted for variants whose label was GDPR-redacted). Both keys are additive - canonical-link events carry neither. `file.opened` fires the moment a non-owner reading session starts, before any page is read, and shares its `sessionId` with the `file.viewed` event that follows at session end.
Plan note: `gate.completed` deliveries require the Pro plan at event time; other kinds deliver on every plan.'
security:
- bearerAuth: []
- oauth2:
- webhooks:manage
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookCreate'
responses:
'201':
description: Created; `secret` is shown only here.
content:
application/json:
schema:
type: object
properties:
webhook:
$ref: '#/components/schemas/Webhook'
secret:
type: string
example: whsec_9f2c...
'400':
description: Invalid body; `url` not a plain HTTPS URL (or pointing at a private/local address, or over 2048 chars); empty or unknown `events`; bad `format`; or the 25-webhooks-per-account limit reached (code `validation_error`).
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
description: '`fileUuid` does not name a file you own.'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
'413':
$ref: '#/components/responses/TooLarge'
'429':
$ref: '#/components/responses/RateLimited'
/api/v1/webhooks/{uuid}:
parameters:
- name: uuid
in: path
required: true
schema:
type: string
format: uuid
description: The webhook's UUID from the create/list response.
delete:
tags:
- Webhooks
summary: Delete a webhook
operationId: deleteWebhook
description: Remove one of your webhooks by UUID; queued deliveries for it are discarded. Scope `webhooks:manage`. A webhook you do not own answers 404.
security:
- bearerAuth: []
- oauth2:
- webhooks:manage
responses:
'204':
description: Deleted (queued deliveries are discarded).
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/RateLimited'
/api/v1/webhooks/{uuid}/test:
parameters:
- name: uuid
in: path
required: true
schema:
type: string
format: uuid
description: The webhook's UUID from the create/list response.
post:
tags:
- Webhooks
summary: Send a test delivery
operationId: testWebhook
description: Synchronously sends a sample `webhook.test` event through the real delivery path (same body format and signature) and reports the outcome. Fires regardless of any per-file scope - it is an endpoint-reachability check with a placeholder file. Rate-limited more tightly than the rest of the API (10 requests/minute per token).
security:
- bearerAuth: []
- oauth2:
- webhooks:manage
responses:
'200':
description: Test outcome (delivered may be false)
content:
application/json:
schema:
type: object
properties:
delivered:
type: boolean
status:
type: integer
description: HTTP status your endpoint returned (0 if unreachable).
latencyMs:
type: integer
error:
type: string
description: Present when not delivered (coarse reason only).
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'429':
description: Test rate limit exceeded (10 per minute per token) - or the general 120/min limit (code `rate_limited`).
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
components:
schemas:
WebhookCreate:
type: object
required:
- url
- events
properties:
url:
type: string
description: Plain HTTPS URL (no credentials, no private/local addresses).
events:
type: array
items:
type: string
enum:
- file.opened
- file.viewed
- file.downloaded
- gate.completed
- note.created
- receipt.confirmed
- file.burned
- content.replaced
- invite.viewed
- proofing.updated
description: 'Event kinds to deliver (at least one). `gate.completed` deliveries require the Pro plan at event time; other kinds deliver on every plan.
'
format:
type: string
enum:
- json
- slack
default: json
fileUuid:
type: string
description: 'Optional: deliver only this file''s events (UUID or short id; must be a file you own).'
Webhook:
type: object
properties:
uuid:
type: string
format: uuid
url:
type: string
example: https://example.com/hooks/everypage
events:
type: array
items:
type: string
example:
- file.opened
- file.viewed
- gate.completed
format:
type: string
enum:
- json
- slack
active:
type: boolean
description: False only after automatic disable for repeated delivery failures.
consecutiveFailures:
type: integer
createdAt:
type: string
format: date-time
fileUuid:
type: string
format: uuid
description: Present when delivery is scoped to one file; absent for account-wide webhooks.
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.
responses:
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'
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'
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