> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kontext.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Guides

> Patterns and recipes for common JavaScript SDK integration tasks.

## Handling no-fill

Subscribe to `ad.no-fill` to know when no ad was returned (geo-restriction, frequency cap, server-side skip). The `skipCode` payload tells you why.

```ts theme={null}
onEvent: ({ name, payload }) => {
  if (name === 'ad.no-fill') {
    console.log('No ad available:', payload.skipCode)
  }
}
```

Every preload produces exactly one of `ad.filled` / `ad.no-fill` / `ad.error` per response, so you can rely on hearing back on every user message.

## Pass every message, suppress ads with `trackOnly`

Always feed every message into the session, even when you don't want an ad to appear (e.g. user is on a free trial, in a no-ads region, or you've decided to show ads only every Nth message). Skipping `addMessage` calls breaks the conversation context the server relies on for targeting. See [Pacing](/concepts/pacing) for the full pattern.

Use `{ trackOnly: true }` to send the preload for analytics without generating an ad:

```ts theme={null}
const shouldShowAd = userMessageCount % 5 === 0

session.addMessage(
  { id: msg.id, role: 'user', content: msg.content, createdAt: new Date() },
  { trackOnly: !shouldShowAd }
)
```

When `trackOnly: true`, the preload still fires (server keeps full analytics) but no `ad.filled` event will arrive for that message and `session.createAd(...)` won't resolve to an ad.

## Live-updating consent mid-session

When the user updates their consent in your CMP, pass the new TCF string to `updateOptions`:

```ts theme={null}
session.updateOptions({
  regulatory: { gdpr: 1, gdprConsent: '<new-TCF-string>' },
})
```

The next preload picks up the new value — no session recreation needed.

## Switching character

The active `character` cannot be live-updated — the accumulated message history belongs to the original persona, so swapping mid-session would leave messages targeted at the wrong character.

To switch character, destroy the current session and create a new one:

```ts theme={null}
session.destroy()

session = createSession({
  publisherToken: '<your-publisher-token>',
  userId: 'user-1234',
  conversationId: 'conv-new',
  character: newCharacter,
})
```

The same applies to `publisherToken`, `userId`, `conversationId`, and `enabledPlacementCodes` — recreate the session whenever any of those change.

## Loading older messages (conversation restore)

When restoring a conversation from your backend, call `addMessage(...)` once per historical message in order. Preloads are debounced (\~10 ms), so rapid sequential calls coalesce into a single preload for the most recent user message — you won't fire one preload per restored message.

```ts theme={null}
for (const m of loadedFromBackend) {
  session.addMessage({
    id: m.id,
    role: m.role,
    content: m.content,
    createdAt: m.timestamp,
  })
}
```
