# Kilden documentation (full)
> Complete developer documentation for Kilden, concatenated for LLM consumption. Each page starts with an H1 and a `URL:` line pointing at its canonical location on https://docs.kilden.io
---
# Quickstart
URL: https://docs.kilden.io/quickstart/
> From snippet to your first event in the live tail, in about two minutes.
By the end of this page you'll have Kilden on your site and you'll watch your first event arrive live.
## 1. Create a project
Sign in at [app.kilden.io](https://app.kilden.io) and create a project. You land on the onboarding page, which shows your install snippet with the write key already filled in — and a live indicator waiting for your first event.
## 2. Paste the snippet
Copy the snippet from onboarding (or use this one, replacing `YOUR_WRITE_KEY`) and paste it before ``:
```html
```
The snippet loads the SDK asynchronously and queues any calls made before it finishes — nothing is lost, and it never blocks your page. Prefer a package? See [installing with npm](/sdk/#install-with-npm).
## 3. Watch the first event arrive
Open a page with the snippet installed. Loading the page emits a `$pageview` automatically — and the onboarding screen flips to **"First event received"**, then takes you to the **live tail**: a real-time stream of every event your project receives. Keep it open while you instrument; it's the fastest feedback loop you have.
## 4. Track something of your own
```js
kilden.track('signup_completed', { plan: 'pro' });
```
It appears in the live tail within a couple of seconds, alongside the autocaptured clicks and pageviews.
## Next steps
- [Identify your users](/guides/identifying-users/) so pre- and post-signup activity join up.
- [Verify identity](/guides/identity-verification/) so nobody can send events as someone else.
- [Autocapture & privacy](/guides/autocapture/) — tune what gets captured automatically.
- [Feature flags](/guides/feature-flags/) and [campaigns](/guides/campaigns/) run on the same events you just started sending.
---
# SDK overview & installation
URL: https://docs.kilden.io/sdk/
> The Kilden web SDK — installation via snippet or npm, the client surface, and the guarantees it makes.
`@kilden/sdk` is the Kilden web SDK: event capture, identity, autocapture, feature flags and session replay in one client. The core is dependency-free and small (under 15 KB gzipped; autocapture and replay load as separate lazy chunks only when used).
## Install with the snippet
Paste this before ``. It creates `window.kilden` with a stub queue **before** the bundle loads, so no call is ever lost — the SDK replays queued calls in their original order once it loads:
```html
```
The project onboarding page in the panel gives you this exact snippet with your write key and API host already filled in.
## Install with npm
```sh
npm install @kilden/sdk
```
```ts
import kilden from '@kilden/sdk';
kilden.init('YOUR_WRITE_KEY');
kilden.track('signup_completed', { plan: 'pro' });
```
The default export is a singleton — call `init` once and import it anywhere. If you need isolated clients (multiple projects on one page, tests), use `createClient`:
```ts
import { createClient } from '@kilden/sdk';
const analytics = createClient('YOUR_WRITE_KEY', { debug: true });
analytics.track('signup_completed');
```
## The client surface
| Method | What it does |
| --- | --- |
| `init(writeKey, options?)` | Initialize. See [Configuration](/sdk/configuration/) |
| `track(event, properties?, options?)` | Queue an event. See [Tracking events](/sdk/tracking/) |
| `identify(userId, traits?, options?)` | Tie the anonymous visitor to your user id. See [Identity methods](/sdk/identity/) |
| `setPersonProperties(set, setOnce?)` | Update person traits |
| `reset()` | Log out: rotate anonymous id, clear token and super properties |
| `register(properties)` / `unregister(key)` | Persisted super properties merged into every event |
| `getDistinctId()` / `getSessionId()` | Current distinct id / session id |
| `optOut()` / `optIn()` / `hasOptedOut()` | Persisted capture kill switch |
| `setIdentityToken(token)` | Set or clear the identity verification JWT |
| `flush()` | Force-send the queue. The only method that returns a `Promise` |
| `use(plugin)` / `removePlugin(name)` | Event pipeline plugins. See [Plugins](/sdk/plugins/) |
| `isFeatureEnabled(key)` / `getFeatureFlag(key)` / `onFeatureFlags(cb)` | Feature flags. See [Feature flags](/sdk/feature-flags/) |
| `startSessionRecording()` / `stopSessionRecording()` / `getReplayId()` | Session replay. See [Session replay](/sdk/session-replay/) |
| `group(groupType, groupKey, properties?)` | **Reserved.** Group analytics is not yet available; calling it is a safe no-op that logs a warning in debug mode |
## Guarantees
These behaviors are contractual — the SDK's public surface is frozen and versioned:
- **The public API never throws.** Invalid input is discarded, with a warning only when `debug: true`.
- **Your data is never mutated** — no trimming, lowercasing or normalizing.
- **Calls made before `init` are queued** and processed at init, in order. Nothing is lost.
- Events are sent in batches and retried with exponential backoff. Each event carries a client-generated UUID v7, so retries are idempotent — the pipeline deduplicates by UUID.
- On page unload the queue is flushed with `fetch(keepalive)` (or `sendBeacon` as a fallback) so tail events survive navigation.
- `optOut()` persists and stops all capture and network traffic.
---
# Configuration
URL: https://docs.kilden.io/sdk/configuration/
> Every InitOptions field of the Kilden web SDK, with defaults and behavior notes.
`init(writeKey, options?)` accepts an `InitOptions` object. All fields are optional.
```ts
import kilden from '@kilden/sdk';
kilden.init('YOUR_WRITE_KEY', {
flushAt: 20,
flushIntervalMs: 5000,
autocapture: true,
debug: false,
});
```
## InitOptions
| Option | Type | Default | Notes |
| --- | --- | --- | --- |
| `apiHost` | `string` | `https://ingest.kilden.io` | Base URL for ingestion. The default is Kilden Cloud — only set it if you self-host. One host serves `/capture`, `/decide` and `/replay` — there is no separate host for feature flags or replay |
| `flushAt` | `number` | `20` | Queue size that triggers a flush |
| `flushIntervalMs` | `number` | `5000` | Max time an event waits in the queue |
| `persistence` | `'localStorage' \| 'cookie' \| 'memory'` | `'localStorage'` | Where the anonymous id, super properties and opt-out state live. Falls back to `memory` automatically when the chosen storage is unavailable |
| `autocapture` | `boolean \| AutocaptureOptions` | `true` | Automatic click/submit/pageview capture. With `false` the autocapture code is never even downloaded. See [Autocapture & privacy](/guides/autocapture/) |
| `debug` | `boolean` | `false` | Log warnings (invalid input, `$`-prefixed custom events, no-op calls) to the console. The SDK never throws either way |
| `beforeSend` | `(event: EventPayload) => EventPayload \| null` | — | Transform or drop (return `null`) every event before it is queued — **including** `$autocapture` and other automatic events |
| `identityToken` | `string` | — | Identity verification JWT, sent as `Authorization: Bearer` with each batch. See [Identity verification](/guides/identity-verification/) |
| `getIdentityToken` | `() => Promise` | — | Token refresh callback: called before the current token expires and after a `401` |
| `sessionRecording` | `boolean \| SessionRecordingOptions` | `false` | Session replay. Off by default — recording is expensive and privacy-sensitive. See [Session replay](/sdk/session-replay/) |
## AutocaptureOptions
Pass an object instead of `true` to tune autocapture:
| Option | Type | Default | Notes |
| --- | --- | --- | --- |
| `captureClicks` | `boolean` | `true` | Emit `$autocapture` on clicks |
| `captureSubmits` | `boolean` | `true` | Emit `$autocapture` on form submits |
| `captureChanges` | `boolean` | `false` | Emit `$autocapture` on input changes. Only the fact that something changed is recorded — never the value |
| `ignoreSelectors` | `string[]` | — | CSS selectors to exclude from capture |
| `maskAllText` | `boolean` | `false` | Replace all element text in `$elements` with `"*"` |
| `allowUrls` | `(string \| RegExp)[]` | — | Restrict autocapture to matching URLs |
## SessionRecordingOptions
Pass an object instead of `true` to tune replay:
| Option | Type | Default | Notes |
| --- | --- | --- | --- |
| `sampleRate` | `number` | `0.1` | Fraction of sessions to record, `0..1`. Decided once per session and sticky across reloads |
| `minimumDurationMs` | `number` | `2000` | Recordings shorter than this are discarded |
| `maskAllInputs` | `boolean` | `true` | Mask everything the user types. Deliberately the opposite default of autocapture: a replay shows keystrokes, so masking is opt-out |
| `maskAllText` | `boolean` | `false` | Mask all page text |
| `blockSelectors` | `string[]` | — | CSS selectors to exclude from the recording entirely |
| `recordCanvas` | `boolean` | `false` | Record `