Authentication

Cascade publishes two APIs, and they authenticate differently.

APICalled fromCredentialBase URL
Admin APIYour serverA secret key, as a bearer tokenhttps://api.cascade.dev
Widget APIA shopper's browserA publishable key, plus a signature for anything personalhttps://api.cascade.dev

Both live under the same host. The admin API is everything the dashboard can do; the widget API is the narrow surface behind the storefront widgets, documented separately in the Widget API reference.

Your keys

Create keys in the dashboard under Settings → API keys.

KeyLooks likeWhere it belongs
Secret keysk_...Your server only. It can read and change everything it has permission for.
Publishable keypk_...Safe in page source. Reaches the widget endpoints and nothing else.

A secret key's token is shown once, when you create it and again when you rotate it. Cascade stores only a hash, so a key you did not write down cannot be recovered: create a replacement instead. Afterwards the dashboard shows the last few characters only, which is enough to tell two keys apart in a list.

There is no separate test mode. To work against data you can throw away, create a second organization and give it its own keys.

While you are signed in to the dashboard, every reference page offers to fill its examples with one of your keys in place of the placeholder. Publishable keys are filled in whole; secret keys are filled in masked, so swap in the token you saved before running a request.

Calling the admin API

Send the secret key as a bearer token:

curl "https://api.cascade.dev/customers?limit=10" \
  -H "Authorization: Bearer sk_YOUR_SECRET_KEY"

Requests from your own browser session in the dashboard are authenticated by that session instead, which is why nothing in the dashboard needs a key.

Permissions

Every secret key carries a set of permissions, written as <group>:<read|write>. Each endpoint in this reference states the one it requires, in the header beside the method and path. write covers creating, updating and deleting, and does not imply read: a key that should both list and edit reviews needs reviews:read and reviews:write.

GroupCovers
analyticsReports, saved charts and dashboards.
customersCustomer records and customer tags.
exportsCSV export jobs and their download links.
importsCSV import jobs and their progress.
loyaltyLoyalty programs, rules, tiers, memberships, points, rewards, stamp cards and referrals.
ordersOrders and order tags.
productsProducts, variants and product tags.
reviewsReviews, review responses and moderation.
settingsOrganization settings, sites, email templates, brand assets, integrations, saved filters, file uploads and the widget signing secret.
surveysSurvey flows and the survey responses they collect.
webhooksWebhook endpoints and their delivery history.
wishlistsWishlists, their items and their share links.

When you create a key you can take every permission, take read on everything, or pick from the list. A secret key must hold at least one. Publishable keys hold none: they reach the widget endpoints and are not scoped further.

Changing a key's permissions takes effect on its next request. Requests already in flight are unaffected.

Two things no key can do, whatever it holds:

  • Manage API keys. Creating, re-scoping and rotating keys is available to a signed-in user only, so a leaked key can never mint another. Those endpoints answer 403 with "This endpoint is only available to a signed-in user, not to an API key."
  • Reach another organization. A key belongs to the organization that created it, and every record it can see belongs to that organization.

Expiry and rotation

A secret key can be issued to last 30, 60 or 90 days, a year, or forever. Publishable keys do not expire; they sit in your storefront, so they are long lived by nature.

An expired key stops authenticating but is not deleted, so you can still rotate it. The response says so plainly:

{
  "error": {
    "message": "This API key expired on 2026-03-01. Rotate it to get a new token.",
    "code": "UNAUTHORIZED"
  }
}

Rotating mints a new token on the same key, keeping its name and permissions, and gives it a fresh expiry window. There is no overlap: the old token stops working the moment rotation completes, so deploy the new one immediately. This is the remedy for a leaked key, since nothing else about the key has to change. Publishable keys cannot be rotated. Delete one you no longer trust and create another.

Both creation and rotation are recorded in your audit trail, along with permission and allowlist changes. The trail never records anything that could authenticate as the key.

Restricting a key to your servers

A secret key can carry an allowlist of IP addresses and CIDR blocks. A request from anywhere else is refused before it reaches your data, and before it spends any of your rate limit, so a leaked key cannot be used to deny you service from an address you never allowed.

Leave the list empty for a key that works from anywhere. Both IPv4 and IPv6 are accepted, and an IPv4-mapped IPv6 address matches an IPv4 entry.

If Cascade cannot work out where a request came from, an allowlisted key is refused rather than waved through. Behind a proxy this depends on how many forwarded hops the deployment is configured to trust, so test the allowlist from the address you intend to use before you rely on it.

The order of checks

Worth knowing when you are debugging a response you did not expect:

  1. Is the key expired?
  2. Is the key valid?
  3. Is the caller's address on the key's allowlist?
  4. Does the organization have rate limit budget left? See Rate limits.
  5. Does the key hold the endpoint's permission?

The rate limit is checked before permissions on purpose: a loop hammering an endpoint its key cannot reach is exactly what needs bounding.

Authenticating the widget API

Widget endpoints are called from a shopper's browser, so they take a publishable key as the apiPublishableKey query parameter rather than a bearer token.

Anything belonging to one shopper (their wishlists, their loyalty balance) also needs proof of who is signed in, as three more parameters: customerEmail, timestamp, and a signature that is the HMAC-SHA256 of the email and timestamp keyed with your widget signing secret. The signature is made on your server and is good for 24 hours.

The signing secret is not a publishable credential. Anyone holding it can sign as any of your customers, so keep it out of theme files and JavaScript bundles. The full recipe, with examples in several languages, is in Identifying the customer.

OAuth

For an application acting on behalf of many merchants, Cascade is an OAuth 2.1 authorization server. The merchant approves your app, and you get an access token instead of holding a key of theirs.

The permission half of an OAuth scope is exactly the vocabulary above, so reviews:read means the same thing on a token as it does on a key, plus the standard openid, profile, email and offline_access scopes. Unlike a key, a grant is always explicit: there is no legacy token with unrestricted access.

An access token is locked to one organization, chosen by the merchant on the consent screen, and can never reach another. Discovery is at https://api.cascade.dev/.well-known/oauth-authorization-server.

This is the same mechanism the MCP server uses when you connect Cascade to a chat client.