Authorization
header, either with ApiKey in front or bare, exactly as the CMS showed it:
In the SDKs
The SDKs take the key exactly as the site showed it, from the environment, and add the header (and each signature) themselves:.env
What a key may do
Each key holds only the scopes its app needs. A family’s…:all scope holds every scope in it, and
admin:all holds them all.
Media
images:read · videos:read · photo-feeds:readFamily: media:allBlog
content:blog:read · :write (drafts, imports too) · :publish (going live) · :delete ·
:generate-image (AI images, which cost money). An import’s new images and videos need the media
content: scopes.Family: content:blog:allForms
forms:read: the submissions visitors send.Family: forms:allAds & conversions
conversions:read · conversions:write · attribution:read · ads-metrics:readFamily: ads:allRequest log
request-log:read · request-log:params:readContent
content:<type>:read · :write · :publish · :delete per content family: pages covers every
page type, images / files / videos their tags and folders too. Images and files go live on
every save, so their :write publishes too and they have no :publish. The page templates are not
pages of the site: the Pages list leaves them out, and content:pages:read-templates reads them
through Page Templates. Required scopes lists each operation’s.Per family: content:<type>:all. Family: content:allSite settings & design
global:read · global:write · global:publish · global:css (Custom CSS)Family: global:allLanguages
languages:read · languages:write (add, rename and remove languages: a save is live at once)Family: languages:allEverything
admin:allWhat a key reaches
Every content type
List, read, create, change, publish and delete through each type’s own routes, with that type’s
scopes. Required scopes lists every operation’s.
The feature routes
Media, the blog import, form submissions, conversions and attribution, ads metrics and the
request log: each with its scope above.
A write scope works on drafts. On a type with drafts, a write in published mode (the default) also
takes the document live, so it needs the type’s publish scope too; send
aposMode=draft to change only
the draft. A type without drafts (redirects, photo feeds, the media folders) needs only its write scope.
A key with only a read scope sees published documents only.Each key’s security policy
Chosen when the key is created, and shown on its Security tab.Signing mode
Signing mode
Token only, signed writes, or every request signed. A signed request carries
X-Fetchski-Timestamp and X-Fetchski-Signature, made with the key’s signing secret (fss_…).An unsigned request that had to be signed gets 401, and the message says so.Each signature works once, so a replayed request is refused. The SDKs sign for you: give them the
secret along with the key. Signing requests shows how to sign by hand, uploads included.
Network allowlist
Network allowlist
Optional: the addresses the key may be used from. A call from anywhere else gets
403.Rate limits
Rate limits
Reads and writes per minute, set per key: 600 reads and 120 writes by default. Over the limit the
site answers
429 with Retry-After, the seconds to wait. The SDKs wait and retry for you.How a signed request is checked
An upload signs the SHA-256 of its body in the body’s place, declared inX-Fetchski-Content-SHA256.
Signing requests has the details and code in cURL, Node, Laravel and Python.
Trying calls in the playground
Set your site
In any endpoint’s playground, set host to your site’s host name (
acme.example).Paste your key
In Authorization, paste the key exactly as the CMS showed it (
fsk_…). Nothing goes in front.Send
The playground can’t sign, so use a key whose signing mode is token only, holding the scope
the endpoint names under Access. A Conversion Data API write must always be signed, so try it
from code instead (Signing requests).