You get two keys per storefront, both requested from Unipage. Together they are one storefront, with a name, its registered origins and optionally an order webhook. The public key’s origins are where your shoppers may browse from and return to after paying. The restaurant is resolved from the key: there is no restaurant id in the URL.
| Public key | Secret key | |
|---|---|---|
| Looks like | upg_sf_pk_... |
upg_sf_sk_... |
| Where it lives | in your browser bundle, safe to expose | only on your server, never in a browser |
| Sent as | X-Unipage-Key: <key> header |
Authorization: Bearer <key> header |
| Grants | the read and quote routes | every route, including orders |
| Bound to | the origins you registered (https://shop.example.com) |
nothing: server-to-server only |
const res = await fetch("https://api.unipage.eu/storefront/v1/menu?orderType=TAKEOUT", {
headers: { "X-Unipage-Key": import.meta.env.VITE_UNIPAGE_PUBLIC_KEY },
});curl https://api.unipage.eu/storefront/v1/orders/q4m8z \
-H "Authorization: Bearer $UNIPAGE_SECRET_KEY"A Core API key (upg_capi_..., the restaurant’s own back-office key) is also
accepted where the secret key is, but give a storefront its own keys.
Requesting keys
When you request keys, give every origin your shop is served from
(scheme://host[:port], no path, no trailing slash).
Local development needs no registration: every public key also accepts
localhost, 127.0.0.1 and [::1] on any port, over http or https. A
storefront on your machine that uses production keys places real orders, so
test against a test restaurant.
Origins can be changed later without a new key: the public key stays the same, so your storefront needs no new build.
Rotating a key
If a key leaks, ask Unipage to rotate it. Each key is rotated on its own: the old one stops working at once and the new one is shown to you once.
- Secret key: only your server needs the new key.
- Public key: it keeps its origins and webhook, but it is in your browser bundle, so rebuild and deploy your storefront with the new one.
Authentication errors
- A missing, unknown or revoked key answers
401 UNAUTHENTICATED. - A public key on a secret route, or used from an origin that is not
registered for it (and is not local), answers
403 FORBIDDEN. - A request without an
Originheader (curl, a server) is accepted with a public key: the origin list restricts browsers, the key identifies you.
CORS
A browser preflight (OPTIONS) is answered 204 for a registered or local origin, with:
Access-Control-Allow-Headers: X-Unipage-Key, Content-Type
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Max-Age: 600Every response (errors and 429 included) carries Vary: Origin and, for a
registered or local origin:
Access-Control-Allow-Origin: <origin>
Access-Control-Expose-Headers: Retry-AfterThe secret routes send no CORS headers for a browser: call them from your server.