Security model
This page is for the person who has to decide whether Kaleidr is safe to put on a site, and how to configure it. It states what each credential can do, what stops misuse, which controls exist today, and which do not.
The model in one picture
Your web page Kaleidr API
───────────── ───────────
publishable key ──── POST /sdk/sessions ────▶ checks key, origin, product, plan
(in page source) │
◀──── session token ───────────────────┘
(minutes, one origin, one product)
session token ──── chat, lookups ─── ──────▶ checks token, origin, scope, quota, rate
Your backend Kaleidr API
──────────── ───────────
server key ──── chat, lookups ─────────▶ checks key, no Origin header, IP list,
(secret) scope, products, quota, rate
Two ideas carry the whole design:
- Nothing secret is ever in the browser. The key in your page is public by design. What the page calls the API with is a token that expires in minutes and works from one origin for one product.
- The secret stays on your server. A server key is refused whenever a request looks like it came from a browser.
The three credentials
| Publishable key | Session token | Server key | |
|---|---|---|---|
| Prefix | kld_pk_… | kld_sess_… | kld_sk_… |
| Lives in | Page source. Public. | Browser memory | Your backend's secrets |
| Lifetime | Until revoked or expired | 15 minutes; 5 minutes for a published map's chat | Until revoked or expired |
| Can call | Only the session exchange | Runtime endpoints for its one product | Every runtime endpoint its scopes allow |
| Bound to | Allowed origins, if you set them | One origin, one product, the key's scopes | Optional IP allowlist |
| Plan | Free, Pro, Enterprise | n/a | Pro, Enterprise |
A session token is sent in a request header and is never put in a URL. The frames that Viewer and Tile load carry a map or style id and a camera, and no credential: a Tile mount exchanges the key only so that Kaleidr can check it and count the map load. The Editor's basemap requests carry the publishable key, which is public.
None of the three can create, edit or revoke keys, change your plan, read your account or upload data. Those actions need a signed-in account that is an owner or admin of the organization.
If a publishable key is copied
It will be. It is in your page source, and that is expected.
| Someone who has it can | What limits them |
|---|---|
| Use it on a website you have allowed | Nothing: that is your site working. |
| Use it on their own website | The allowed-origins list. With a list set, browsers on other sites are refused. With an empty list, any site may use the key. |
Call from outside a browser, sending your origin in the Origin header | The allowed-origins list does not stop this. What bounds the cost is the key's rate limit, its monthly cap if you set one, and your organization's quota. |
| Receive whatever the key's scopes allow | The key's scopes. A key without vendor cannot draw on your own data. |
So the damage a copied publishable key can do is spend: AI credits and map loads, up to the limits you set. It cannot reach anything the key was not scoped for.
If a session token is copied
A session token is short-lived and bound to an origin, but treat it as usable by whoever holds it until it expires. It stops working when it expires, when its key is revoked, and when its origin or product is removed from the key. It carries no more access than the key it came from.
A session token cannot read your organization's plan or usage. A visitor's browser holds the token, so the
responses it gets are written for a visitor. When the AI-credit pool runs out, the 429 body and the quota
event at the end of a stream say that the limit was reached and when it resets. They leave out the plan, the
amount used and the limit. This is true for a session exchanged from a publishable key and for a published
map's visitor session. A server key, which only your backend holds, still receives the full numbers. The two
shapes are in Quota and rate limits.
If a server key is copied
This is the serious case. Whoever has it can make every call the key's scopes allow, from anywhere, until you
revoke it. They can spend your quota and, if the key has vendor, receive answers that draw on your own data.
What limits it: an IP allowlist on the key (calls from other addresses get 403 ip_not_allowed), a
product limit (calls to another product's endpoints get 403 product_not_allowed), a monthly cap, and
an expiry date. What ends it: revoking the key.
Kaleidr refuses a server key that arrives with an Origin header, and the SDK refuses one at mount. Those
checks stop accidents. They do not un-leak a key that was published in a page: revoke it and create a new one. See
If a key leaks.
What the allowed-origins list is, and is not
It is a rule browsers enforce for you. A browser always tells the API which site a request came from, and a page cannot change that. With a list on your key, a copy of the key on someone else's site does not work for their visitors.
It is not authentication. The Origin header is only trustworthy when a browser sent it. A script or a
server can send any value. Kaleidr checks the header on every exchange, but it cannot know whether a browser
set it.
That leads to one working rule: anything a publishable key can reach, treat as reachable by anyone who can read your page. Decide what to put behind a publishable key with that in mind. Enforcement details are in CORS and allowed origins.
The vendor scope needs a decision
Most scopes only let a key call endpoints, and the cost of misuse is quota. vendor is different: it lets
chat answers draw on your organization's own published data. A key with vendor can therefore be used to
ask questions about that data.
Only an Enterprise organization can create a key with vendor. On Free and Pro the request is refused with
403 tier_capability_not_allowed, so this decision arises on Enterprise only. Uploading the data is Enterprise
only as well.
On a server key, this is contained: only your backend asks.
On a publishable key, every visitor of the page can ask, and so can anyone who copies the key. That is the right choice when the data is public anyway: opening hours, locations, published room rates. It is the wrong choice for anything you would not print on the page.
Kaleidr makes you say so on purpose. A publishable key with vendor can only be created through the key
management API with vendor_acknowledged: true; the API Keys page cannot create one, and the scope is never a
default. Before you send that field:
- Review what is in your published data. Remove cost prices, internal notes, unreleased rates and anything personal.
- Give
vendorto one key, used on one site. Do not reuse it on a demo page or a staging site. - Remember that an answer can quote your data. There is no setting that restricts which parts of it an answer may use.
If the organization later moves to a lower plan, vendor stops applying without an error: keys that carry it
keep working, their answers no longer draw on your data, and browser sessions that were open are ended. A key
rotated on the lower plan comes back without the scope.
Your data is isolated to your organization and is never used in Kaleidr's own consumer map. The path from upload to answer is in Your data: overview.
What you can control today
| Control | Where you set it | What it does |
|---|---|---|
| Which sites may use a key | API Keys page, or allowed_origins | Browsers on other origins are refused at the session exchange. |
| Which products a key unlocks | Key management API, allowed_products | Applies to every key type. A publishable key limited to tile cannot start chat: the session exchange is refused. A server key limited to tile cannot call the chat endpoints: each request is refused with 403 product_not_allowed. |
| Which scopes a key carries | Key management API, at creation | A key without ai cannot call chat endpoints. Scopes cannot be changed later. |
| Which addresses may use a server key | Key management API, allowed_ips | Calls from other addresses are refused. |
| How much one key may spend | Key management API, monthly_cap_tokens | The key is refused with quota_exceeded once it reaches the cap in a billing period. A value of 1 or more is a cap. 0, null or no value means no cap. |
| When a key stops working | Key management API, expires_at; or revoke on the API Keys page | Revoking takes effect on the next request, including for sessions already issued. |
| Replacing a key without downtime | Rotate, on the API Keys page or through the API | The old key keeps working for a grace period you choose. |
| Whether embeds load before consent | The page flag window.__KALEIDR_EMBED_ENABLED__ = false, set before the loader script | Holds every mount, including <kaleidr-map> elements already in the HTML, until you release it. Kaleidr.enable(false) holds only mounts made after the call, so it cannot hold those elements: they mount as the loader runs. See kaleidr.js. |
| Which sites may embed a published map | Allowed domains, in Studio | Other sites see a "not allowed" message in place of the map. |
| Whether a published map offers chat | Visitor AI Chat switch, in Studio | Chat works only while the switch is on, and stops within five minutes of being switched off. |
| How much a published map's chat may spend | Monthly cap on the map, in Studio | The map's chat stops for the period when the cap is reached. |
| Who can manage keys | Organization roles | Only owners and admins. |
| Whether your uploaded data is in service | Unpublish in Studio's Releases list, or the upload API | Answers stop drawing on the release at once. The organization has no published release until it publishes again. |
| Whether a map is published | Go Live and Unpublish on Studio's Publish & embed page | A published map shows the map as it is currently saved, so saved edits are public without a re-publish. Unpublishing stops every embed of the map; publishing again restores the same embed URLs. Share links already handed out keep working. |
| Whether a published map shows a dataset | Delete the dataset in Studio's Data panel, or unpublish the map | Deleting removes the dataset for good, from the published map and from every share link that drew it. Unpublishing stops the map's embeds and leaves its share links working. Studio has no control to delete or pause a share link: a link keeps working until you delete its map or ask Kaleidr to remove it. |
| Whether an uploaded release still exists | Delete in Studio's Releases list, or the upload API | Removes a release that is not published, with everything made from it. Available on any plan. Releases that are no longer current are also removed automatically after 30 days. |
A key's management history (who created it, edited it and rotated it) is available from the key management API. See Auth and scopes.
Not available today
These are common requirements. Kaleidr does not offer them now, so do not design around them:
- Instructions or a persona for the assistant. There is no system prompt setting, and
systemmessages sent to the API are ignored. - Topic restrictions. You cannot limit what visitors may ask about.
- Restricting answers to your own data. Answers draw on your data alongside Kaleidr's general knowledge.
- Per-user identity or permissions. Kaleidr does not know who your end user is. Every visitor of a page has the same access: the key's.
- An allowlist of map actions. You cannot switch off camera moves, pins or routes for the chat panel.
- Transcripts or audit logs of conversations. Kaleidr does not provide a log of what was asked or answered.
- Events to your page. The chat panel has no callbacks and sends no analytics events to the host page.
- Editing a key's rate limit. The requests-per-minute value is not a setting.
If you need per-user rules, logging or content filtering, call the API from your own backend with a server key. Your backend then sees every question and every answer, and can apply its own rules before and after. See the Server quickstart.
What leaves the page
Chat panel. With the session token, the chat panel sends to api.kaleidr.com:
| When | What is sent |
|---|---|
| A question | The conversation's messages, a random id for that conversation, the map's zoom level and a language tag |
| Opening a place card | The place's name, category, address and coordinates |
| Address search in Directions | The text typed, the travel mode and the coordinates of the place the card is about |
| Directions | The travel mode, and the start and end points as coordinates |
It does not send your map's layers, sources or markers, or the area the map is showing: of the camera, only the zoom level is sent. Place photos are loaded by the browser from third-party image hosts, and the icons of cited sites from Google's favicon service, which is sent the cited site's address. Those hosts see the visitor's IP address as any image host does. See What the page loads from elsewhere.
In the browser. The API uses no cookies. The chat panel stores a few preferences, such as units and
layout, in the page's localStorage. The session token is kept in memory.
Server API. Kaleidr receives exactly what your backend puts in the request body. You decide whether a user's identity or precise location is in it. Nothing in the API requires either.
An embedded map takes instructions only from the page that embeds it. A published map in a Viewer iframe acts on a control message, such as a camera move, only when the message comes from the page that directly contains the iframe. A message from any other window, such as a window that opened your page or another frame on it, is ignored.
How long Kaleidr keeps request data, and how it is processed, is set by your agreement with Kaleidr, not by this page.
Your uploaded business data is stored per organization. It is uploaded by an owner or admin of an
Enterprise organization, in Kaleidr Studio or with the upload API, and it is looked up when a question is
answered. Through the API and the SDK it is read only for chat calls
made with a vendor key of that organization, which only an Enterprise organization can hold. Members of your
organization who are signed in to Kaleidr Studio can also ask Studio's assistant about the published data, for
as long as the organization is on Enterprise. It is never read for another organization or on Kaleidr's consumer
map.
Datasets you add to a map in Kaleidr Studio, on any plan, are a separate feature. They are not read through an API key or the SDK. See Datasets on a map.
A published map makes its datasets public. The datasets and image overlays a map draws are served to every visitor of the published map and of its share links, each dataset whole and with all of its columns. Allowed domains decide where the map renders; they do not keep its data confidential. A dataset is served only as part of a published map or a share link that includes it: nothing lists a map's datasets, and a dataset's id opens nothing by itself. A published map shows the map as it is currently saved, so a dataset added to a map that is already published is public as soon as the map is saved. Deleting the dataset in Studio's Data panel stops it being served everywhere, on the published map and on share links. Unpublishing the map stops its embeds; share links already handed out keep working with the data they had, and Studio has no control to delete or pause one. A share link keeps working until you delete its map or ask Kaleidr to remove the link; see Share links. In every case a browser that already loaded the data keeps it, and a download link already issued can stay readable for up to one hour. See Uploaded data on a published map.
You can take uploaded data back out. An owner or admin can unpublish the published release, which takes it out of service at once, and delete any release that is not published, on any plan and after a downgrade. A published release must be unpublished before it can be deleted, and a deletion completes shortly after it is requested. Releases that are no longer current are removed automatically after 30 days. The calls are in Upload your business data.
What deletion does not reach
A deleted release is used in no answer made after the deletion completes. Deletion does not reach:
- cached answers, which can be served again for a limited time;
- saved chat transcripts, such as a Kaleidr Studio conversation that already quoted the data;
- backups taken before the deletion;
- text from your data that was already sent to a model provider when it was processed.
Platform support
The Kaleidr SDK is for web pages. There is no native SDK for iOS, Android, React Native or Flutter.
A mobile app has two supported options:
| Option | How | Credential |
|---|---|---|
| Web view | Load an HTTPS page you host, on an origin in the key's allowed origins, inside the app's web view. The page uses kaleidr.js as on any website. | Publishable key, in that page |
| Your backend | The app calls your server; your server calls the Kaleidr API and returns or relays the result. The app draws the places on its native map. | Server key, on your server |
Do not put a server key in a mobile app. An app binary can be unpacked, and a key inside it is a leaked key.
Before you go live
- Every production publishable key has an allowed-origins list.
- Each website has its own key.
- No server key appears in page source, a mobile app, a public repository or client-side configuration.
- Server keys have an IP allowlist where your servers have fixed addresses.
- Keys that could be abused for spend have a monthly cap.
-
vendoris on a publishable key only where the data is public on that site. - Your page's Content Security Policy allows the Kaleidr hosts it needs. See Content Security Policy.
- You know who the owners and admins of the organization are.
If a key leaks
- Revoke it on the API Keys page. Revoking takes effect on the next request and ends the key's sessions.
Rotation on that page keeps the old key working for 24 hours, which suits a planned change and not a leak.
Through the API, rotate with
grace_hours: 0to replace and cut off in one step. - Create a replacement with the same restrictions, and deploy it.
- Check usage on kaleidr.com/billing for spend you do not recognize.
- Find how it leaked. A server key in a browser bundle is the usual cause; the build step that put it there will do it again.
A leaked publishable key needs action only if it has no allowed-origins list, carries vendor, or is being
used to run up spend. Otherwise it is working as designed.
Related
- Auth and scopes: credentials, the exchange and the key management API.
- CORS and allowed origins: how origins are enforced.
- Quota and rate limits: what bounds spend.
- Production checklist: everything else before launch.