--- title: Authentication overview description: The two ways an integration authenticates to Luigi's Box AI — publishable keys from the browser, OAuth client credentials from your backend — and what each one is allowed to do. slug: authentication/overview docKind: concept hub: luigisbox-ai --- Every call to Luigi's Box AI carries a short-lived access token in an `Authorization` header. You never send a long-lived secret to an API endpoint; you exchange a credential for a token first, and the token is narrow — it is valid for one service and one set of resources, and it expires in minutes. Which credential you use depends on where the code runs. | Where your code runs | Credential | What it can do | |---|---|---| | A shopper's browser | A **publishable key** (`pub_…`) | Read discovery and write analytics events, on one channel — and nothing else | | Your server | An **OAuth client ID and secret** (`lbk_…` / `lbe_…`) | Whatever your integration is granted — indexing, configuration, events, discovery | Both paths end in the same kind of token, and endpoints do not care which path produced it. They only check what the token is allowed to do. ## The two flows ### From the browser A publishable key is visible by design. It ships in your page source, it is bound to the origins you register, and it is bound to exactly one channel. To turn it into a token, the browser solves a proof-of-work challenge that takes a few hundred milliseconds of CPU. The challenge is one layer of [bot control](/api-basics/bot-control/), not an authorization mechanism. **A browser token can do exactly two things.** | It can | It cannot | |---|---| | Read discovery surfaces — search, collections, recommendations, facets, object lookup, variants | Write anything to the catalog | | Send analytics events | Read the catalog configuration | | | Touch organizations, catalogs, channels, surfaces or campaigns | | | Reach any other channel than the one its key is bound to | | | Be used from an origin you have not registered | Those permissions are **derived from the key** — the channel it is bound to plus the audience requested — not from your account's grants. Asking for any other audience produces a token with no permissions. See [Browser tokens](/authentication/browser-tokens/). ### From your server An OAuth client ID and secret exchange for a token with the standard `client_credentials` grant. The secret never leaves your infrastructure. This is the path for indexing catalog content, configuring feeds, reporting events, and for storefronts that render search server-side. Use it for **all** automation. A server-side script attempting the browser flow is refused by [bot control](/api-basics/bot-control/#browser-code-solves-the-challenge-server-code-does-not). See [Server-to-server tokens](/authentication/server-to-server/). ## Hosts and regions Luigi's Box AI runs in independent regions, and a credential belongs to exactly one of them. There are two hosts per region: | Host | Purpose | |---|---| | `https://auth..luigisbox.ai` | Getting a token | | `https://api..luigisbox.ai` | Everything else | `` is the zone your account was provisioned in, for example `eu1` or `us1`. Your credentials carry the region in their own value (`pub_live_eu1_…`), and a credential from one region cannot authenticate in another — if you operate in two regions, you hold two sets of credentials. Examples in these docs use `eu1`. Substitute your own region. ## Audience: which service a token is for The API is one host in front of several services, each answering one path prefix. When you request a token you name an **audience** — the service the token is for. A token minted for indexing cannot be used against discovery. | Service | Audience | |---|---| | Discovery — search, collections, recommendations | `https://api.eu1.luigisbox.ai/discovery` | | Content indexing | `https://api.eu1.luigisbox.ai/index` | | Catalog configuration — feeds, mappings, metadata | `https://api.eu1.luigisbox.ai/catalog` | | Object change history | `https://api.eu1.luigisbox.ai/ledger` | | Analytics events ingestion | `https://api.eu1.luigisbox.ai/events` | | Account structure and merchandising | `https://api.eu1.luigisbox.ai/platform` | Browser tokens accept an **array** of audiences, so a storefront that both searches and reports behaviour uses one token for both. Server-to-server tokens name a single audience, so a backend that indexes *and* reports events holds one token per service. A `401` on a token that has not expired usually means the token was minted for a different audience. ## Permissions Beyond its audience, a token carries the permissions your integration was granted. A permission is always a pair: **an action, and the resource it applies to** — a specific catalog, a specific channel, a specific surface. "Serve discovery" is not a permission; "serve discovery for channel `lbn_4hj9tv`" is. An integration granted indexing on one catalog cannot write to another: the grant is not there. In practice: - **Grants are per resource, not per account.** Two catalogs in the same organization are two separate grants. Being entitled to one says nothing about the other. - **You do not assemble them per request.** You ask for a token; the permissions your integration holds are stamped into it. There is no scope parameter to widen. - **You cannot change them yourself.** Grants are configured by Luigi's Box when the integration is set up, and there is no API for an integration to grant itself anything. If a call is refused that should succeed, contact Luigi's Box. Ask for the narrowest set that does the job. ### When a call is refused | Status | Meaning | |---|---| | `403` | The token holds a grant on this resource, but not the one this call needs | | `404` | The token holds no grant on this resource at all | The API does not confirm that a resource exists to a caller who cannot see it, so a `404` on a single object can mean either. Check the permission before assuming a typo. ## Token lifetime Access tokens are short-lived — about five minutes. - **Cache the token, not the request.** Reuse one token until shortly before it expires, then fetch another. Do not mint one per API call. - **Retry once on `401`.** A `401` on a previously working call almost always means the token expired between checks. Get a new one and retry. - **Never persist a token.** It expires before it would be read back. Browser tokens work the same way, and re-minting one does not disturb the shopper's session: the session identifier your integration sends lives independently of the token. ## See also - [Browser tokens](/authentication/browser-tokens/) — publishable keys and the proof-of-work flow - [Server-to-server tokens](/authentication/server-to-server/) — the client credentials flow - [Browser quickstart](/start-here/browser-quickstart/) — a working search box - [Backend quickstart](/start-here/backend-quickstart/) — a working indexing call