Skip to content

Authentication

Every request authenticates with a per-site API key sent as an HTTP Bearer token.

Authorization: Bearer ck_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Class Prefix Can write Where it belongs
Secret ck_ yes Server-side only. Never in a browser.
Public read pk_ no In the HTML of a page that embeds the widget.

A pk_ key is read-only, bound to one site, works only from the origins its owner registered, and may travel as ?key=pk_… so a browser can skip the CORS preflight. It reaches only the two public read routes. A ck_ key is a secret: the read routes answer it but never return CORS headers for it, so a browser cannot use one even if you try, and passing it as ?key= is a deliberate 400.

Full comparison, the origin allowlist rules and the per-key restrictions: Keys, scopes & restrictions.

A key is a class prefix (ck_ or pk_) followed by 32 base62 characters (≈ 190 bits of entropy — the same entropy in both classes; “public” means visible, not guessable). Keys are opaque — do not parse or split them. Canverly stores only a SHA-256 hash of the secret and reserves the right to change the encoding.

A single key is bound to exactly one site. The site (site_id) is derived from the key on every request, so a key can only ever read or write its own site — there is no way to address another site by putting an id in the URL or body.

Generate a key in the admin under Configurações → Integrações API:

  1. Open the site, go to Configurações → Integrações API.
  2. Click Gerar chave, give it a recognizable name (e.g. zapier-blog, ci-publisher) and pick the scopes it needs.
  3. The full ck_… secret is shown once in a modal with a Copy button. Store it in your secret manager immediately — it is never retrievable again.

After closing the modal only the key’s name, scopes and last_used_at are visible. If you lose a secret, revoke it and create a new one.

A key carries one or more scopes, and scopes are opt-in: a key gets exactly what was asked for at creation and nothing by default. Request only what the integration needs.

Scope Grants
posts:write Create and update posts
posts:read Read the published archive (listing + detail)
post_types:read Discover valid post_type slugs
sites:read Resolve the site’s id, slug, domain and default language
site:read / site:write Read / update the site’s settings
seo:read / seo:write Read / update post-level and site-level SEO
media:read / media:write List and read / upload and delete media assets
analytics:read Site views and visitor numbers
leads:read Form submissions — personal data, audited, own rate limit
export Bulk export (also needs leads:read / analytics:read for those resources)
import Bulk import (also needs posts:write)

sites:read and site:read are different scopes and the singular/plural is not a typo — see the full table with the routes each one unlocks.

A request to an endpoint whose scope the key lacks returns 403:

{ "error": { "code": "forbidden", "message": "scope `posts:write` not granted" } }

Independently of scopes, the owner can pin a key to an IP allowlist, a country allowlist (GeoIP, fail-closed), a time window in a chosen zone, and — for pk_ keys — a list of allowed origins. They are checked on every request, before the rate-limit bucket is charged, and a violation is a 403 naming the dimension without ever revealing the list:

{ "error": { "code": "forbidden",
"message": "request blocked by this key's region restriction" } }

See Keys, scopes & restrictions.

The API returns a compact JSON error envelope (not RFC-7807):

{ "error": { "code": "unauthorized", "message": "missing Authorization header" } }
Situation Status error.code
No / non-Bearer / malformed Authorization 401 unauthorized
Valid scheme, unknown or revoked key 401 unauthorized
A pk_ key on a route that accepts only ck_ 401 unauthorized
Key lacks the required scope 403 forbidden
Request blocked by the key’s IP / region / time restriction 403 forbidden
A pk_ key used from an unregistered origin 403 forbidden
Over the key’s rate limit 429 rate_limited

See Errors & rate limits for the full table.

Revoke a key from Configurações → Integrações API (trash icon). Revocation is immediate — the next request with that key returns 401. There is no soft-delete and no “show again”.

To rotate without downtime:

  1. Generate a new key with the same scopes.
  2. Switch the integration to the new key.
  3. Once you confirm it works, revoke the old key.
  • Never commit a key to a repo or ship it in client-side code (a key is a server-side secret). If one leaks, revoke it immediately.
  • No API key of any class can mint, list or revoke another key — key management is a site-owner session in the admin. An integration cannot escalate its own permissions.
  • Treat ck_… like a password: store it in a secret manager / environment variable.
  • Prefer one key per consumer so blast radius on a leak is a single integration.