Skip to content
Elara One docs

Authentication

The store key, the server secret, registered websites, bearer tokens for customers and merchant integrations, and how to rotate credentials.

Every call names its store with a public key. Calls from your servers also prove where they come from with the store's server secret, and calls from a browser are accepted only from websites you have registered. Customer and merchant routes add a bearer token on top.

The headers

HeaderWhenExample
X-Tenant-KeyEvery call. It is the only thing that identifies the store.pk_live_…
X-Tenant-SecretEvery call from a server. Never from a browser.your server secret
AuthorizationCustomer routes (a token from POST /v1/auth/login) and Merchant API routes (a merchant API key).Bearer …
X-Client-IPOptional on server calls: the shopper's IP address, so rate limits apply per shopper. Trusted only with a valid secret.203.0.113.7
X-Cart-TokenCart and checkout routes.from POST /v1/cart
X-Order-Access-TokenViewing a guest's order.from checkout
Idempotency-KeyWrites you might retry. See Idempotency keys.a UUID

All of these are in Settings → Storefront API in your store admin, apart from customer tokens (which the API issues) and merchant API keys.

Server calls and browser calls

The API tells the two apart by the Origin header, which browsers add and servers do not.

Server callBrowser call
Made byYour storefront server, a script, an integrationJavaScript on a web page
Must carryX-Tenant-Key and X-Tenant-SecretX-Tenant-Key only, from a registered website
Can useEvery route its credentials allow, and X-Client-IPThe public routes. Routes that need the secret, such as consent, refuse it.

We recommend calling the API only from your own server, as a backend-for-frontend. The starters all work this way, and refuse to start in production without the secret.

lib/elaraone.ts (server only)
const API = 'https://api.elaraone.app/api/v1'

export async function elaraone(path: string, init: RequestInit = {}, shopperIp?: string) {
  return fetch(`${API}${path}`, {
    ...init,
    headers: {
      'X-Tenant-Key': process.env.ELARAONE_TENANT_KEY!,
      'X-Tenant-Secret': process.env.ELARAONE_TENANT_SECRET!,
      ...(shopperIp ? { 'X-Client-IP': shopperIp } : {}),
      ...init.headers,
    },
  })
}

Never forward the browser's headers

Server-side fetch in Node.js sends no Origin. If you copy the shopper's request headers onto your API call, their Origin comes too and turns a server call into a browser call. Build the headers yourself, as above.

Keep the secret out of any environment variable your framework copies into browser code: NEXT_PUBLIC_* in Next.js, NUXT_PUBLIC_* and VITE_* in Nuxt, PUBLIC_* in Astro.

Registered websites

A browser call is accepted only from a website the store has registered in Settings → Storefront API → Your websites. Enter each origin with its scheme and host, such as https://shop.example.com, and no path. Only https:// websites can be registered; register www. and any separate checkout host as well.

If your storefront never calls the API from a browser, switch Browser calls off on the same page. The store then refuses every call without the secret.

CORS is set per store. A response to an accepted browser call echoes your website in Access-Control-Allow-Origin, never *, and credentials (cookies) are never allowed.

The website check is not a password

It stops other websites' JavaScript from using your public key. A program that is not a browser can send any Origin it likes, so browser calls only ever get the public tier. Anything private needs the secret, and so a server.

The API lock

A new store starts in report-only for 14 days: nothing is refused, but every call the lock would refuse is counted and shown in Settings → Storefront API → API lock, by website and by kind. After 14 days the lock turns on by itself. You can turn it on sooner from the same page.

Once the lock is on, these calls are refused with 403:

error.codeWhyFix
TENANT_SECRET_REQUIREDA server call without X-Tenant-Secret.Send the secret on every server call.
TENANT_SECRET_INVALIDA server call with the wrong secret, or a secret-only route called without it.Copy the current secret from Settings → Storefront API.
ORIGIN_NOT_ALLOWEDA browser call from a website the store has not registered.Register the website, or call from your server.
BROWSER_CALLS_DISABLEDA browser call while browser calls are switched off.Call from your server with the secret.

None of these is worth retrying: the same call gets the same answer until your code or the store's settings change.

Before the lock turns on, check that every website is registered, that every server, job and integration sends the secret, and that the report shows no calls without the secret for the last few days.

Customer tokens

POST /v1/auth/login (or POST /v1/auth/register) returns a bearer token for the customer. Send it as Authorization: Bearer …, together with your store key and secret, on account routes such as GET /v1/auth/me and GET /v1/orders. Keep the token on your server, for example in an httpOnly cookie on your own domain. See Customers and accounts.

Merchant API keys

The Merchant API (/v1/merchant/…) is for back-office integrations: CRM sync, order fulfilment, forms, flows and pages. A merchant API key is sent as a bearer token, alongside the store key and secret, and carries only the abilities it was given:

AbilityAllows
crm:read, crm:writeReading and writing contacts, companies, tasks, notes and associations
read_orders, manage_ordersListing orders; dispatching, marking delivered and recording offline refunds
manage_forms, manage_flows, manage_pagesForms, automation flows and CMS pages
manage_consentThe cookie consent banner and consent records
view_conversationsInbox conversations

A key without the ability a route needs gets 403 FORBIDDEN. A missing or revoked key gets 401 UNAUTHENTICATED.

Rotating credentials

The store key. Settings → Storefront API → Rotate store key. The new key keeps the old prefix. The old key keeps working for 7 days, and responses to calls made with it carry X-Tenant-Key-Deprecated: true: log that header, so a forgotten deployment shows up. End overlap now stops the old key at once, for a key that has leaked. After the overlap, the old key gets 404 TENANT_NOT_FOUND.

The server secret. Rotate it when it may have leaked: it was seen in a browser, committed to a repository, or someone who knew it has left. The change is immediate, with no overlap, so update every server's environment and restart. To keep a locked shop working throughout, rotate just before you deploy.

Rotating either asks for your admin password and authenticator code, and is recorded in the activity log.