Skip to main content

kaleidr.js, the loader

kaleidr.js is the one script every Kaleidr embed starts from. It defines the global window.Kaleidr and the <kaleidr-map> custom element. It is small: it contains no map library and no product code. When you mount a product, it loads that product's bundle from the same CDN and hands it your options.

<script src="https://cdn.kaleidr.com/embed/v1/kaleidr.js"></script>

There is no npm package. The script tag is the only way to load the SDK. For React, Next.js, Vue and TypeScript, see Framework integration.

Loading the script​

QuestionAnswer
Where does the tag go?Anywhere. <kaleidr-map> elements mount whether they appear before or after the tag.
When can I call Kaleidr.mount?After the script has run. With a plain <script src> tag, any later script on the page can call it.
Can I use async or defer?Yes for elements. For Kaleidr.mount, wait for the tag's load event, because window.Kaleidr does not exist until then.
How often?Once per page.
Can I add integrity?No. The file is updated in place, so a pinned hash stops matching at the next release. See CDN versioning.
Does it run on the server?No. It needs window and document. In a server-rendered app, load it in the browser only.

Loading the script with async and mounting when it arrives:

<div id="map" style="height:480px"></div>
<script>
const s = document.createElement('script');
s.src = 'https://cdn.kaleidr.com/embed/v1/kaleidr.js';
s.async = true;
s.onload = () => {
Kaleidr.mount('#map', { product: 'viewer', shareId: 'YOUR_MAP_ID' });
};
document.head.appendChild(s);
</script>

window.Kaleidr​

MemberDescription
Kaleidr.mount(target, options)Mounts a product into an element and returns a handle.
Kaleidr.init()Upgrades <kaleidr-map> elements already in the document. Rarely needed.
Kaleidr.enable(on)Switches every later mount on or off.
Kaleidr.versionThe loader's version string.

Kaleidr.mount(target, options)​

const handle = Kaleidr.mount('#chat', {
product: 'chat',
publishableKey: 'kld_pk_live_…',
map: myMap,
contained: true,
});
ParameterTypeDescription
targetCSS selector or HTMLElementThe element to mount into. A selector is resolved once, with document.querySelector.
optionsobjectRequired. See Options.

Returns a handle.

How it behaves:

  • It returns immediately. The session exchange and the product bundle load in the background. Handle calls you make meanwhile are kept and applied, in order, when the product is ready. For viewer and tile that is the moment the map inside the frame reports ready, so a setCamera written on the line after mount takes effect.
  • It does not throw for a failed mount. A missing target, a bundle that cannot load, a refused session or an invalid id is written to the console as an error that contains [kaleidr:<product>]. You still get a handle, and its methods do nothing. The one call that throws is a mount with no options object: Kaleidr.mount('#map') raises a TypeError.
  • It reports nothing back. There is no promise to await and no callback. See Knowing whether a mount worked.
  • One mount per target. Mounting into a target that already holds a mount is not supported. Call handle.destroy() on the first one before you mount again. Destroying and then mounting again into the same element is safe at any moment, including while the first mount is still loading: the destroyed mount adds nothing to the element and does not disturb the new one.

Options​

OptionTypeDefaultProductsDescription
product'chat' | 'viewer' | 'editor' | 'tile'none, requiredallWhich product to mount.
publishableKeystringnonechat, editor, tileYour publishable key, kld_pk_…. The SDK exchanges it for a short-lived session and gives only the session to the product. Do not pass it to a viewer. A server key (kld_sk_…) is refused before any request is made.
shareIdstringnoneviewer, chatViewer: the id of the published map. Chat with no key: the published map whose owner provides the chat.
mapIdstringnoneviewerAlias of shareId, used when shareId is absent.
styleIdstringthe organization's default basemaptile, editorA basemap style id such as kaleidr-morning, or a full style URL.
mapa live map instancenonechatThe map the assistant should drive: a Mapbox GL JS, MapLibre GL JS, Google Maps or Leaflet map object. See Supported maps. Omit it for a panel with no map.
mapTargetCSS selectornonechatA <kaleidr-map> viewer or tile element for the assistant to drive, in place of map.
containedbooleanfalsechatDocks the panel inside the mount target, which must be positioned and sized. By default the panel is a fixed window at the top-left of the viewport.
langstringthe page's <html lang> when that is a well-formed language tag, otherwise EnglishchatThe language the panel answers in, as a BCP 47 tag such as en, ko or pt-BR. See Chat language.
titlestringthe panel's own titlechatThe title in the panel's header. On the element the attribute is chat-title.
placeholderstringthe panel's own placeholderchatThe placeholder text of the panel's input.
center[longitude, latitude]product defaultviewer, tile, editorOpening center.
zoomnumberproduct defaultviewer, tile, editorOpening zoom. On viewer and tile it is used only together with center.
pitchnumberproduct defaultviewer, tile, editorOpening tilt, in degrees.
bearingnumberproduct defaultviewer, tile, editorOpening rotation, in degrees.
themeobject, or a JSON object stringnonechat, editorChat: CSS custom properties, for example { '--highlight-color': '#0b5fff' }. Editor: the 17 theme keys. A string that is not JSON is ignored with a warning.
enabledbooleannoneallPer-mount arm of the enable gate. Read at mount only.
baseURLhttps://cdn.kaleidr.comallWhere product bundles load from. Only the origin is used: bundles are always requested from /embed/v1/ on that origin. Honored only for the origins listed under The base override; any other value is ignored with a console warning.
viewerBaseURLhttps://map-embed.kaleidr.comviewer, tileThe origin the iframe loads from. Only the origin is used.
apiBaseURLhttps://api.kaleidr.com/inference-apichat, editor, tileWhere the session exchange and the product's requests are sent. Must include the /inference-api path. A bare origin returns 404.
tileBaseURLhttps://tile.kaleidr.comeditorThe host that style ids are resolved against.

Notes:

  • apiKey is a deprecated alias of publishableKey.
  • The four …Base options exist for testing against another deployment. A production page does not need them. base moves only the bundles: the map page, the API and the basemap host stay on Kaleidr's hosts unless you move each one.
  • lang, title and placeholder are read at mount. To change one, destroy the mount and mount again.
  • An option a product does not use is ignored without a message.

The base override​

base (the option and the element attribute) and the page global window.__KALEIDR_EMBED_BASE__ name the origin product bundles load from. An override is honored only when its origin is one of these:

Accepted originExample
The Kaleidr CDNhttps://cdn.kaleidr.com
The origin kaleidr.js itself was loaded fromYou serve kaleidr.js from https://static.example.com, and base is https://static.example.com
The page's own originThe page is https://www.example.com/…, and base is https://www.example.com

Anything else is ignored. The mount still happens, the bundles load from the default CDN, and the console shows one warning for that value:

[kaleidr] base "https://other.example.net" is IGNORED — bundles load from https://cdn.kaleidr.com. A base must be the Kaleidr CDN, the origin kaleidr.js was loaded from, or this page's own origin.

The page global must be a string. Any other value is skipped without a message. Self-hosting works within this rule when the loader and the bundles are served from the same origin.

Product defaults for the camera:

ProductDefault camera
viewerThe view the map's author saved.
tileThe style's default view, otherwise the whole world.
editorCenter [0, 0], zoom 2, pitch 0, bearing 0.
chatNot applicable. The chat does not create a map.

Each product's page has the details: Chat, Viewer, Tile, Editor.

The handle​

Kaleidr.mount returns the same shape for every product.

interface KaleidrHandle {
readonly product: 'chat' | 'viewer' | 'editor' | 'tile';
setCamera(camera: {
center?: [number, number]; // [longitude, latitude]
zoom?: number;
pitch?: number;
bearing?: number;
duration?: number; // milliseconds
}): void;
setTheme(theme: string | Record<string, string>): void;
setStyle(style: string): void;
destroy(): void;
}

Every method exists on every handle. Whether it does anything depends on the product:

Methodchatviewereditortile
setCamera(camera)warnsyesyesyes
setTheme(theme)yeswarnswarnswarns
setStyle(style)warnswarnswarnsyes
destroy()yesyesyesyes

"warns" means the call does nothing and the console shows

[kaleidr] setCamera() is not supported on product "chat" — the call is a no-op. See the handle matrix at https://docs.kaleidr.com/sdk/kaleidr-map-element#handle-matrix.

This is part of the /embed/v1 contract: an unsupported method never throws.

Method notes:

  • setCamera: fields you omit keep their value. duration animates the move; omitted or 0, the map jumps. On chat, setCamera is not supported because the chat does not own a map. The assistant still moves your map when it answers. To move it yourself, use your map library's own API.
  • setTheme: chat only. Takes an object of CSS custom properties or a JSON string of one.
  • setStyle: tile only. Takes a style id or a style URL.
  • destroy: removes what the mount added, stops the session renewals, and drops queued calls. Calling it twice is harmless. A destroyed handle cannot be reused: mount again.

You can call the handle straight after mount. For viewer and tile, a setCamera (and on tile, a setStyle) made before the map inside the frame is ready is kept and applied, in order, when the map reports ready: with its kaleidr-embed:ready message, or with the first camera position it reports to the SDK. The latest 256 such calls are kept. If the map has still not reported ready after a bounded wait, normally 10 seconds from the moment the frame loads, the kept calls are sent anyway, and the console shows one warning:

[kaleidr] the map frame has not reported ready — calls held for it are sent now, and may reach no map. The frame may be showing an error (an unpublished map, a blocked request).

The wait starts when the frame loads, so a newly mounted frame below the fold, which has not started loading, keeps its calls until it does. You do not have to wait for the kaleidr-embed:ready message before calling the handle. That message, and kaleidr-embed:error, remain the way your code learns that the map loaded or failed. See Viewer messages.

The handle has no ready promise, no on() method, and no map or iframe member.

Kaleidr.init()​

Upgrades every <kaleidr-map> element that is in the document at the moment you call it. The loader calls it for you when it runs, and elements added later mount themselves when they are inserted, so you rarely need it. Calling it more than once is harmless.

The enable gate​

Embeds are on by default. The enable gate is the off switch: while it is off, Kaleidr.mount loads nothing, sends nothing, and returns a handle whose methods do nothing. Use it to hold embeds until a visitor consents, or to switch embeds off on a page without removing the markup.

The three levers​

LeverScopeSet it with
Page flagEvery mount on the pagewindow.__KALEIDR_EMBED_ENABLED__ = false, assigned before the mounts you want to hold
Kaleidr.enable()Every mount after the callKaleidr.enable(false)
Per mountOne mountThe enabled: false option, or flag="false" on the element

How a mount decides​

Every time something mounts, the SDK reads all three levers:

  1. If any lever is explicitly false, the mount is held.
  2. Otherwise, if any lever is explicitly true, the mount proceeds.
  3. Otherwise the default applies, which is on.

Three consequences follow, and they are the part that is easy to get wrong:

  • A false can only be released by the lever that set it. Kaleidr.enable(true) does not lift enabled: false on a mount, and does not lift a page flag of false. No lever overrides another.
  • The gate is checked at mount time only. Releasing a lever does not mount anything that was held, and switching a lever off does not remove anything that is already mounted. After you release, mount again: call Kaleidr.mount again, or take the element out of the document and put it back.
  • The page flag is read at every mount, not only when the script loads. You can assign it later and it applies to the next mount.
To release a hold set byDo this
window.__KALEIDR_EMBED_ENABLED__ = falseAssign true to it (or delete it).
Kaleidr.enable(false)Call Kaleidr.enable(true), or Kaleidr.enable(null) to return to the default.
enabled: false or flag="false"Mount again without it. Changing the flag attribute on a mounted element does nothing.

When a mount is held, the console shows one warning that names the lever:

[kaleidr] embed is disabled — window.__KALEIDR_EMBED_ENABLED__ is false. Reassigning it releases that lever — Kaleidr.enable() cannot.

Accepted values​

The levers are strict about what counts as a boolean, so that a stray string cannot switch embeds on by accident.

LeverCounts as trueCounts as falseIgnored, with a console warning
window.__KALEIDR_EMBED_ENABLED__truefalseAnything that is not a real boolean, including the strings "true" and "false"
Kaleidr.enable(x)no argument, true, "true", "1", "yes", "on"false, 0, "", "false", "0", "no", "off"Any other string, undefined, objects, functions
enabled optiontrue, "true", "1", "yes", "on"false, 0, "", "false", "0", "no", "off"Any other string, objects, functions
flag attributeflag with no value, "true", "1", "yes", "on""false", "0", "no", "off"Any other value

Words are matched without regard to case or surrounding spaces. Two details matter when the value comes from storage:

  • Kaleidr.enable(null) resets the lever to the default. It does not mean "no opinion". localStorage.getItem(key) returns null when nothing is stored, which is exactly the state before a visitor has consented, so never pass a stored value straight through. Compare it first: Kaleidr.enable(stored === 'true').
  • An ignored value leaves the lever as it was. It neither enables nor disables.

This page holds every embed with the page flag, which is the lever that works for elements, because elements mount as soon as the loader runs. On consent it releases the same lever and re-inserts the elements so they mount.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Consent-gated map</title>
<script>
// A real boolean, set before the loader. A stored string is compared, never passed through.
window.__KALEIDR_EMBED_ENABLED__ =
localStorage.getItem('embeds-consent') === 'true';
</script>
<script src="https://cdn.kaleidr.com/embed/v1/kaleidr.js"></script>
</head>
<body>
<div id="banner">
This page shows an interactive map from Kaleidr.
<button id="accept">Show the map</button>
</div>

<kaleidr-map product="viewer" share-id="YOUR_MAP_ID"
style="display:block; height:480px"></kaleidr-map>

<script>
const banner = document.getElementById('banner');
if (window.__KALEIDR_EMBED_ENABLED__) banner.hidden = true;

document.getElementById('accept').addEventListener('click', () => {
localStorage.setItem('embeds-consent', 'true');
banner.hidden = true;

// 1. Release the lever that held the embeds.
window.__KALEIDR_EMBED_ENABLED__ = true;

// 2. A held element does not mount by itself. Re-insert each one.
document.querySelectorAll('kaleidr-map').forEach((el) => {
const parent = el.parentNode;
const next = el.nextSibling;
el.remove();
parent.insertBefore(el, next);
});
});
</script>
</body>
</html>

Before consent, the Network tab shows kaleidr.js and nothing else from Kaleidr: no product bundle, no session request, no iframe.

If you mount from JavaScript, you do not need the gate at all: do not call Kaleidr.mount until you have consent. The gate earns its place when embeds are written as elements, or when a tag manager or another team's code calls mount and you need one switch for the whole page:

Kaleidr.enable(false); // hold every later mount
// …
Kaleidr.enable(true); // release it
const handle = Kaleidr.mount('#map', { product: 'viewer', shareId: 'YOUR_MAP_ID' });

Page globals​

GlobalTypeReadPurpose
window.__KALEIDR_EMBED_ENABLED__booleanAt every mountThe page flag of the enable gate.
window.__KALEIDR_EMBED_BASE__URL stringAt every mountThe origin product bundles load from, for a self-hosted copy. Only the origin is used, and it must be one the base override accepts. See CDN versioning.

Knowing whether a mount worked​

The SDK gives your code no readiness or failure signal. What you can use:

ProductLoadedFailed
viewerThe iframe posts kaleidr-embed:ready.The iframe posts kaleidr-embed:error, or the console shows a [kaleidr:viewer] error.
tileThe iframe posts kaleidr-embed:ready.A refused session appears only in the console. A drawing problem posts kaleidr-embed:error.
chatNo signal. The panel appears in the page.Console only.
editorNo signal. The editor appears in the page.Console only.

The two messages are documented in Viewer messages.

What happens when a mount fails​

CauseWhat you seeRequest sent?
The target selector matches nothing[kaleidr:<product>] mount target not found: <selector>No
The element has no valid product[kaleidr] <kaleidr-map> requires product="chat|viewer|editor|tile"No
A server key was passed[kaleidr:<product>] Kaleidr: a server key (kld_sk_…) must never be used in the browser — it is a standing secret. Use your publishable key (kld_pk_…) here and keep the server key server-side.No
The key is unknown or revoked, or the page's origin is not allowedA browser CORS error for the sessions request, then a [kaleidr:<product>] fetch error. Nothing renders.Yes
The key lacks the product's scope, the key is limited to other products, or the plan does not admit it[kaleidr:<product>] kaleidr session exchange failed (403). Nothing renders.Yes
The organization's map loads are used up (tile), or too many session requests[kaleidr:<product>] kaleidr session exchange failed (429). Nothing renders.Yes
The product bundle cannot load[kaleidr:<product>] [kaleidr] failed to load https://cdn.kaleidr.com/embed/v1/<product>.jsn/a
Viewer with no id or a malformed id[kaleidr:viewer] Kaleidr map: invalid id "" — must match ^[A-Za-z0-9._~-]{1,100}$.No
Editor with no key[kaleidr:editor] [editor-embed] attach() requires an { apiKey }No
Chat with neither a key nor a shareIdThe panel renders, and every question fails.Not at mount
The enable gate is off[kaleidr] embed is disabled — …No

The SDK never shows the server's reason. For a refused session it logs only the HTTP status. The reason is in the response body. A 403 or 404 carries its code in the detail field, for example {"detail":"insufficient_scope"}. A 429 carries its code in the error field, with a sentence in detail; the exception is map_load_quota_exceeded, which arrives in detail. Open the Network tab, select the sessions request and read the response. When the browser reports a CORS error it may hide the body. In that case repeat the request from a terminal:

curl -i https://api.kaleidr.com/inference-api/b2b/v1/sdk/sessions \
-H "x-api-key: kld_pk_live_…" \
-H "Origin: https://www.example.com" \
-H "content-type: application/json" \
-d '{"product":"chat"}'
HTTP/2 403
content-type: application/json

{"detail":"origin_not_allowed"}

Use the origin of the page that fails. Use "chat", "editor" or "viewer" as the product when you test: a successful "tile" exchange counts a map load. Every code is explained in Errors.

A failed first exchange is final for that mount. The SDK does not retry it. Fix the cause and mount again.

The session exchange, in brief​

For chat, editor and tile, the SDK never uses your publishable key to call Kaleidr's runtime API. It trades the key for a session first.

  1. At mount, the SDK sends POST https://api.kaleidr.com/inference-api/b2b/v1/sdk/sessions with the header x-api-key: <your publishable key> and the body {"product":"<product>"}. The browser adds your page's Origin.
  2. Kaleidr checks, in order: that the key is a publishable key, that the request has an origin, that the origin is in the key's allowed origins (when the key has any), that the key and the plan admit the product, and that the key carries the product's scope: ai for chat, design for editor, maps for tile, none for viewer.
  3. The response is {"session_token": "…", "expires_in": 900, "product": "…", "basemap_style_id": …}. basemap_style_id is your organization's default basemap, or null when none is set.
  4. The product uses the session token for its requests. The token is bound to your origin and to that product.
  5. The SDK renews the session 90 seconds before it expires, and again when a background tab becomes visible with a session that is about to expire. If a renewal fails, it tries again every 30 seconds while the mount exists.
  6. handle.destroy() stops the renewals.

A viewer needs no session. A chat on a published map, mounted with a shareId and no key, gets its session from the map instead of from a key. That flow is described in Chat on a published map.

The request and response are specified in Endpoints. Key types, sessions and what an allowed-origins list does and does not protect are explained in the Security model.

Other console messages​

MessageMeaning
[kaleidr] <method>() is not supported on product "<product>" — the call is a no-op. …You called a handle method that product does not have.
[kaleidr] <method>() IS supported on product "<product>", but this mount did not expose it, so the call did nothing. …The product bundle in the browser's cache is older than the loader. Reload without the cache.
[kaleidr] the map frame has not reported ready — calls held for it are sent now, and may reach no map. …The map in a Viewer or Tile frame did not report ready within the wait described under the handle. The setCamera and setStyle calls kept for it were sent anyway. The frame may be showing an error: listen for kaleidr-embed:error.
[kaleidr] theme must be an object of CSS tokens, not an array; ignored.theme was an array.
[kaleidr] theme string must be JSON …; named string themes are not supported. Ignored.theme was a string that does not start with {.
[kaleidr] theme JSON parse failed (…); ignored.theme was malformed JSON.
[kaleidr] <attribute>="<value>" is not a recognized boolean — the attribute is IGNORED. …The flag or contained attribute had a value outside the accepted words. The message names the attribute: flag="…" or contained="…".
[kaleidr] base "<value>" is IGNORED — bundles load from <origin>. …A base option or attribute, or window.__KALEIDR_EMBED_BASE__, named an origin that the base override does not accept. Bundles load from the default CDN.
[kaleidr] window.__KALEIDR_EMBED_ENABLED__ must be a boolean; got <type> (…). …The page flag was not a real boolean and was ignored.
[kaleidr] Kaleidr.enable(): "<value>" is not a recognized boolean — the value is IGNORED. …Kaleidr.enable received an unusable value.
[kaleidr] enabled: "<value>" is not a recognized boolean — the value is IGNORED. …The enabled mount option received an unusable value.
[kaleidr] Kaleidr.enable(undefined) is IGNORED and leaves the gate exactly as it was. …Kaleidr.enable was called with undefined.
[kaleidr] Kaleidr.enable(null) RESET the gate to the build default, releasing the <value> set earlier. …Kaleidr.enable(null) released an earlier setting.

Not available​

  • An npm package, ES module build or type definitions. A declaration you can copy is in Framework integration.
  • A native SDK for iOS, Android, React Native or Flutter.
  • A ready promise, events, or callbacks on the handle.
  • A JavaScript client for calling the API without mounting a product. Call the server API from your backend instead.