kalapa-consent-web-sdk
v1.0.3-0.1
Published
Kalapa CMP SDK
Readme
Kalapa Consent Web SDK
Package: kalapa-consent-web-sdk
Version: 1.0.30.1
A consent management library for web apps. It provides consent UI, server sync, and local persistence so tracked features (ads, analytics, personalization) only run after valid user consent.
Contents
- Requirements
- Installation
- Quick Start
- Core Concepts
- API Reference
- Common Patterns
- Error Handling
- Troubleshooting
- Migration from 1.0.29.x
- Changelog
Requirements
| Item | Minimum | | --- | --- | | Browser | Modern browsers with ES module support (or use UMD build) | | Network | Internet access to CMP backend | | Optional | TypeScript for type definitions |
Installation
Use package manager (recommended), CDN, or Kalapa-hosted script.
Package manager (npm / Yarn / pnpm)
npm install kalapa-consent-web-sdkyarn add kalapa-consent-web-sdkpnpm add kalapa-consent-web-sdkCDN (jsDelivr)
Replace 1.0.30.1 with your target version.
ESM:
https://cdn.jsdelivr.net/npm/[email protected]/dist/kalapa-consent-web-sdk.jsUMD:
https://cdn.jsdelivr.net/npm/[email protected]/dist/kalapa-consent-web-sdk.umd.jsQuick Start
Version 1.0.30.1 uses a two-phase initialization model. Creating the SDK instance and loading project / user data are separate steps:
- Construct — provide immutable connection credentials (and optional UI options).
- Setup — provide project code and optional user ID; makes network calls to load config and existing consent.
Recommended: Builder pattern
import KalapaCMPSDK from "kalapa-consent-web-sdk";
const sdk = await KalapaCMPSDK.builder({
baseUrl: "https://cmp-api-dev.kalapa.vn",
orgApiKey: "your-org-api-key",
})
.withLanguage("vi") // optional
.withForceConsent(true) // optional
.create({
projectCode: "my-project",
userId: "logged-in-user-id", // optional
});
// sdk is fully ready — setup() has already been calledAlternative: Constructor + explicit setup()
import KalapaCMPSDK from "kalapa-consent-web-sdk";
const sdk = new KalapaCMPSDK({
baseUrl: "https://cmp-api-dev.kalapa.vn",
orgApiKey: "your-org-api-key",
});
await sdk.setup({
projectCode: "my-project",
userId: "logged-in-user-id", // optional
});UMD (CDN) usage
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/kalapa-consent-web-sdk.umd.js"></script>
<script>
var sdk = new window.KalapaCMPSDK({
baseUrl: "https://cmp-api-dev.kalapa.vn",
orgApiKey: "your-org-api-key",
});
// Pass autoShow: false so setup() does not show the modal on its own.
// ensureConsent() will show it exactly once and resolve when the user grants consent.
sdk.setup({ projectCode: "my-project", autoShow: false }).then(function () {
return sdk.ensureConsent();
}).then(function (granted) {
if (granted) {
loadAds(); // replace with your own tracked features
loadAnalytics();
}
});
</script>Parameter reference
| Parameter | Type | Where | Description |
| --- | --- | --- | --- |
| baseUrl | string | SDKCoreParams | Dev: https://cmp-api-dev.kalapa.vn · Prod: https://api.cmp.kalapa.vn |
| orgApiKey | string | SDKCoreParams | Organisation API key provided by Kalapa |
| projectCode | string | SDKSetupParams | Project identifier configured in the Kalapa CMP console |
| userId | string? | SDKSetupParams | Logged-in user ID. If omitted, SDK uses an anonymous device identity |
| autoShow | boolean? | SDKSetupParams | Controls the auto-show during setup. Defaults to true (show if consent is missing or need_reconsent). Pass false to suppress the "no consent yet" show while still auto-showing when the server signals need_reconsent |
Core Concepts
Two-phase initialization
The SDK separates concerns cleanly:
- Constructor / builder — validates credentials, pre-loads UI assets. No network calls.
setup()— loads project config and the subject's existing consent from the backend; initialises the consent UI; and auto-shows the consent modal if consent is missing or re-consent is required. Must resolve before calling any other public method.
Subject identity
userIdinsetup()(orsetUser()) identifies the consent subject.- If omitted, the SDK generates an anonymous device-scoped identity.
- Consent is scoped per subject. To switch users at runtime, call
setUser()— do not create a new SDK instance.
Re-consent
If the backend returns need_reconsent: true (e.g. the project's consent policy changed), the SDK automatically presents the modal again.
Local snapshot vs. server state
| Method | Network call | Typical use |
| --- | --- | --- |
| isConsentGranted() | No | Fast gate check before loading a tracked feature |
| lastGrantedConsent() | No | Read local category-level consent |
| checkUserConsent() | Yes | Sync latest valid consent from server |
| ensureConsent() | Yes | Full consent flow (shows UI when required) |
| withdrawConsent() | Yes | Withdraw consent and reset local / UI state |
GUI options & remote defaults
SDKOptions (passed via builder or constructor) are merged with remote options fetched from the backend during setup(). Remote settings act as the base; anything you pass locally overrides the corresponding field.
API Reference
KalapaCMPSDK.builder(coreParams)
Returns a fluent KalapaCMPSDKBuilder for constructing the SDK.
KalapaCMPSDK.builder(coreParams: SDKCoreParams): KalapaCMPSDKBuilderBuilder methods
| Method | Type | Description |
| --- | --- | --- |
| .withLanguage(lang) | "vi" \| "en" | UI language |
| .withDarkMode(enabled) | boolean | Enable dark mode |
| .withForceConsent(enabled) | boolean | Prevent the page from being used without consent |
| .withGuiOptions(options) | SDKGuiOptions | Layout, position, and color overrides (see below) |
| .build() | KalapaCMPSDK | Returns SDK instance; setup() not yet called |
| .create(setupParams) | Promise<KalapaCMPSDK> | Returns ready SDK instance; calls setup() internally |
new KalapaCMPSDK(coreParams, options?)
Direct constructor. Prefer the builder for readability.
new KalapaCMPSDK(
coreParams: { baseUrl: string; orgApiKey: string },
options?: SDKOptions
)sdk.setup(params): Promise<void>
Initialises (or re-initialises) the SDK for a project and user. Fetches project config and existing consent from the backend, then auto-shows the consent modal if needed. Must be called before any other public method. Safe to call again to switch project or user — tears down previous state first.
sdk.setup({
projectCode: string;
userId?: string;
autoShow?: boolean; // default: true
}): Promise<void>When autoShow: false, setup still fetches remote consent (to detect policy changes), but suppresses the modal unless the server signals need_reconsent (e.g. consent was revoked or the policy changed). In that case the modal is always shown regardless of this flag. This removes the double-show flicker when ensureConsent() is called immediately after.
| Error code | Cause |
| --- | --- |
| 1202 SETUP_IN_PROGRESS | Called while a previous setup() is still running |
| 1203 INVALID_AUTO_SHOW | autoShow is not a boolean |
sdk.setUser(userId?): Promise<void>
Switches the active user without changing the project. Equivalent to calling setup({ projectCode: <current>, userId }). Throws if setup() has never been called.
await sdk.setUser("new-user-id");
// or clear to anonymous:
await sdk.setUser();sdk.ensureConsent(callback?): Promise<boolean>
Ensures a valid consent exists for the current subject. Shows the modal when needed, and resolves true once the user grants consent.
await sdk.ensureConsent((granted, consent) => {
if (granted) {
console.log("Granted:", consent);
}
});sdk.isConsentGranted(): boolean
Local-only quick check. No network call.
sdk.lastGrantedConsent(): ConsentSnapshot | null
Returns the local consent snapshot. No network call.
type ConsentSnapshot = {
version?: string;
consents: Record<string, boolean>;
};sdk.checkUserConsent(): Promise<Consent | null>
Fetches the latest valid consent from the server. Returns null if consent is missing or re-consent is required.
const consent = await sdk.checkUserConsent();
console.log(consent); // e.g. { analytics: true, ads: false }sdk.withdrawConsent(): Promise<boolean>
Withdraws consent on the server, clears local snapshot and UI state, and shows the consent modal again.
await sdk.withdrawConsent();sdk.getSubjectKey(): string
Returns the effective subject key currently used for consent sync (userId-derived or anonymous device ID).
SDKOptions
interface SDKOptions {
language?: "vi" | "en";
forceConsent?: boolean;
darkMode?: boolean;
guiOptions?: SDKGuiOptions;
}SDKGuiOptions
interface SDKGuiOptions {
consentModal?: {
layout?: "box" | "box wide" | "box inline" | "cloud" | "cloud inline" | "bar" | "bar inline";
position?: "top" | "bottom" | "middle" | "top left" | "top center" | "top right"
| "middle left" | "middle center" | "middle right"
| "bottom left" | "bottom center" | "bottom right";
};
preferencesModal?: {
layout?: "box" | "bar" | "bar wide";
position?: "left" | "right";
};
ui?: {
themeMode?: "light" | "dark" | "system";
color?: {
light?: SDKUiPalette;
dark?: SDKUiPalette;
};
radius?: {
sm?: number | string;
md?: number | string;
lg?: number | string;
};
};
}
interface SDKUiPalette {
primary?: string;
on_primary?: string;
secondary?: string;
on_secondary?: string;
text?: string;
background?: string;
border?: string;
overlay?: string;
section?: string;
}Common Patterns
Gate tracked features with consent
Pass autoShow: false to setup() so the modal is only shown once by ensureConsent().
await sdk.setup({ projectCode: "my-project", autoShow: false });
await sdk.ensureConsent();
if (sdk.isConsentGranted()) {
loadAds();
loadAnalytics();
}Full builder setup with UI customization
const sdk = await KalapaCMPSDK.builder({
baseUrl: "https://cmp-api-dev.kalapa.vn",
orgApiKey: "your-org-api-key",
})
.withLanguage("vi")
.withDarkMode(false)
.withForceConsent(true)
.withGuiOptions({
consentModal: { layout: "box", position: "bottom right" },
preferencesModal: { layout: "box", position: "right" },
ui: {
themeMode: "system",
color: {
light: { primary: "#0d5c63", on_primary: "#ffffff" },
dark: { primary: "#0d5c63", on_primary: "#ffffff" },
},
},
})
.create({ projectCode: "my-project", userId: "user-id" });
await sdk.ensureConsent();Switch user after login
await sdk.setUser("logged-in-user-id");
await sdk.ensureConsent();Clear user after logout (back to anonymous)
await sdk.setUser(); // no userId → anonymous
await sdk.ensureConsent();Switch project
await sdk.setup({ projectCode: "another-project", userId: currentUserId });
await sdk.ensureConsent();Withdraw consent
const ok = await sdk.withdrawConsent();
if (ok) {
stopAllTrackedFeatures();
}Error Handling
- Do not assume consent is granted if a network call fails.
- Treat
isConsentGranted() === falseas "not safe to track yet". - For
checkUserConsent(), handlenulland keep tracking disabled until consent is confirmed.
const consent = await sdk.checkUserConsent();
if (consent == null && !sdk.isConsentGranted()) {
disableTracking();
}SDK error codes
| Code | Key | Cause |
| --- | --- | --- |
| 1002 | INVALID_URL | baseUrl is missing or not a valid http(s):// URL |
| 1003 | INVALID_API_KEY | orgApiKey is missing or fewer than 10 characters |
| 1101 | INVALID_OPTIONS | guiOptions is present but not an object |
| 1102 | INVALID_CONSENT_MODAL_LAYOUT | consentModal.layout is not a recognised value |
| 1103 | INVALID_CONSENT_MODAL_POSITION | consentModal.position is not a recognised value |
| 1104 | INVALID_PREFERENCES_MODAL_LAYOUT | preferencesModal.layout is not a recognised value |
| 1105 | INVALID_PREFERENCES_MODAL_POSITION | preferencesModal.position is not a recognised value |
| 1106 | INVALID_LANGUAGE | language is not a string |
| 1107 | INVALID_LANGUAGE | language is not "vi" or "en" |
| 1108 | INVALID_FORCE_CONSENT | forceConsent is not a boolean |
| 1109 | INVALID_DARK_MODE_OPTION | darkMode is not a boolean |
| 1110 | INVALID_THEME_MODE | ui.themeMode is not "light", "dark", or "system" |
| 1111 | INVALID_UI_COLOR | A color palette value is not a string |
| 1112 | INVALID_UI_RADIUS | A radius value is not a number or string |
| 1200 | INVALID_PROJECT_CODE | projectCode is empty |
| 1201 | SETUP_NOT_CALLED | Public method called before setup() |
| 1202 | SETUP_IN_PROGRESS | setup() called while a previous call is still running |
| 1203 | INVALID_AUTO_SHOW | autoShow is not a boolean |
Troubleshooting
Consent modal does not appear
- Verify
baseUrlandorgApiKey. - Confirm
setup()has resolved before callingensureConsent(). - Check network access / CORS for the CMP backend.
- Check the browser console and Network tab.
isConsentGranted() stays false after accepting
- Ensure you are checking with the same SDK subject identity (same
projectCode+userId). - If the user changed, call
setUser()thenensureConsent(). - Verify the
/consentsendpoint returns success and a tracking ID.
User switched but old consent is still used
- Call
setUser(newUserId)whenever the subject changes at runtime — do not construct a new SDK instance. - Call
ensureConsent()again aftersetUser()resolves.
Build / bundler issues
- Ensure the package entry for
kalapa-consent-web-sdkresolves todist/kalapa-consent-web-sdk.js. - If using a local package link, rebuild the SDK so
dist/is up to date.
Migration from 1.0.29.x
| Old (1.0.29.x) | New (1.0.30.x) |
| --- | --- |
| new KalapaCMPSDK({ credentials: { url, key, userId? } }) | new KalapaCMPSDK({ baseUrl, orgApiKey }) + sdk.setup({ projectCode, userId? }) |
| credentials.url | baseUrl |
| credentials.key | orgApiKey |
| credentials.userId | setup({ userId }) or setUser(userId) |
| No projectCode concept | Required in setup() |
| updateCredentials() | setUser(userId?) |
| Recreate SDK instance to switch user | sdk.setUser(userId?) |
| N/A | KalapaCMPSDK.builder() fluent API |
| N/A | SDKOptions: language, forceConsent, darkMode, guiOptions |
| ConsentSnapshot.version: number \| null \| undefined | ConsentSnapshot.version?: string |
| CDN: *.min.js / *.umd.min.js | CDN: *.js / *.umd.js |
Changelog
1.0.30
- Breaking: Two-phase initialization — constructor sets credentials;
setup({ projectCode, userId? })loads config and consent. - Breaking: Credential fields renamed:
credentials.url→baseUrl,credentials.key→orgApiKey. - Breaking:
userIdmoved from constructor credentials tosetup()/setUser(). - New fluent builder:
KalapaCMPSDK.builder(coreParams).withXxx().create(setupParams). - New
setUser(userId?)for switching subjects at runtime without recreating the SDK instance. - New
getSubjectKey()returns the effective subject key. - New
SDKOptionsfor local overrides:language,forceConsent,darkMode,guiOptions(layout, position, colors, border-radius, theme mode). - Remote GUI options fetched from backend and merged with local overrides.
- Structured
SDKErrorwith error codes for validation failures and setup guard violations.
1.0.29
- Bug fixes.
- Public API
ensureConsentwith callback.
1.0.28
- UI/UX improvements.
- Bug fixes.
1.0.27
- Subject-scoped consent key and improved identity switching behaviour.
- Public API:
ensureConsent(),withdrawConsent().
Earlier
- Initial Web SDK release with consent UI, server sync, and local snapshot.
For environment URLs, API keys, and backend integration details, contact Kalapa support.
