Skip to main content
Every kit call is authorised by a publishable key. It is scope-limited, tied to one shop, grants no merchant data, and is designed to be shipped to a browser. That’s why a NEXT_PUBLIC_ (or VITE_, or a data- attribute) is the right place for it.

Format

The segment after qb_pk_ is the shop’s storage prefix, for example 101928Y. It’s the folder the shop’s images live under on the CDN, and the client exposes it as shopkit.shopPrefix.
The storage prefix is not the shop’s numeric id. The hosted checkout URL needs the numeric id, which comes on every cart as cart.storeId. checkout.start() reads it for you. If you build a checkout URL from shopPrefix, the checkout answers “We couldn’t find 101928Y”. Since the key is tied to one shop, the kit never asks for a separate shop id option.

Where to get one

From the Quickbutik admin (normal path)

Open Custom storefront in the Quickbutik admin (https://admin.quickbutik.com) and copy the shop’s publishable key. It carries every scope the kit needs: the five storefront scopes plus storefront:read.
One unauthenticated request creates a real, unclaimed shop and returns its keys:
The response includes a full-scope publishable_key for the storefront, a server-side personal_access_token for catalog writes, and a claim_token. The shop expires after 72 hours unless it’s claimed by email through POST /v2/shops/{id}/claim. The publishable key keeps working after the claim. Rate limits are 3 per hour and 10 per day per IP. A shop created this way runs its checkout in demo mode until payments are activated. The whole flow is in Create a shop without an account.
A key from the API keys page’s Create public key button carries storefront:read only. The first catalog call is refused with a 403 naming required_scopes. Use the Custom storefront key instead.

Scopes

DEFAULT_SHOPKIT_SCOPES is the first five, which is the set a storefront needs to browse, build a cart and hand off. Two mappings surprise people, and both are deliberate:
  • Categories sit behind products:read. They’re catalog structure, not a separate surface.
  • shop.get() needs checkout:read. It’s served by the checkout’s shop endpoint, the only shop surface a publishable key can reach.
A publishable key can never carry more than this set. The API refuses to issue one with a merchant scope (orders, customers, product writes).

The local scope check

The kit checks scopes locally, before the request leaves, so a missing scope surfaces as a named ShopkitScopeError instead of an opaque 403. The local check only knows the scopes you declare (the default five), not the key’s real ones. The platform still enforces either way.

Never ship a personal access token

A qb_pat_… token is a merchant credential. Depending on its scopes it can do anything the merchant can do. It belongs on a server or in a gitignored .env, never in a client bundle, a public repo or a log.
The kit refuses a PAT outright, so it can’t reach a browser through the kit. Nothing stops you from shipping it some other way, though, so keep the two apart. Both keys belong to the /v2 API. The older /v1 API on api.quickbutik.com uses its own API key and answers 401 Unauthenticated to either of them.

Key rotation

On a 401 the handler is called, the returned key is adopted, and the request is retried once. Concurrent 401s share a single refresh. Return null to let the 401 surface.

Redacting a key in logs

The shop prefix is kept. It isn’t a secret, and it’s the part that makes a log line actionable.

What a storefront key can and cannot do

Can

List and read visible products and variants, search and filter the catalog, read the category tree and shop branding, create and edit carts, create checkout sessions, and read session snapshots and confirmations.

Cannot

Read orders or customers, log shoppers in, apply discount codes outside the hosted checkout, take payment, or write anything on the merchant side.