> ## 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.

# Showing your first ad

> Create a session, feed messages, mount InlineAd, and observe events.

This is the 5-minute integration walkthrough. Before you start, make sure you've completed [Installation](/sdk/kotlin/installation).

## 1. Create a session

The entry point is `KontextAds.createSession(context, options)`, which returns a `Session` you'll use for the rest of the conversation lifecycle.

```kotlin theme={null}
import java.net.URI
import so.kontext.ads.KontextAds
import so.kontext.ads.model.Character
import so.kontext.ads.model.SessionOptions

val session = KontextAds.createSession(
    context = applicationContext,
    options = SessionOptions(
        publisherToken = "<your-publisher-token>",
        userId = "user-1234",
        conversationId = "conv-5678",
        character = Character(
            id = "character-1234",
            name = "John Doe",
            avatarUrl = URI.create("https://example.com/avatar.png"),
            greeting = "Hello, how can I help you today?",
        ),
        onEvent = { event ->
            // Handle ad lifecycle events
            android.util.Log.d("kontext", "${event.name} $event")
        },
    ),
)
```

The session keeps `publisherToken`, `userId`, and `conversationId` fixed for its lifetime. Recreate the session when any of those change (e.g. when the user starts a new chat).

<Note>
  Pass `context.applicationContext` rather than an Activity context — the Session outlives any single Activity and the SDK manages WebView resources internally.
</Note>

## 2. Feed conversation messages

Add every message to the session as it appears. User messages trigger a debounced preload in the background; assistant messages let the SDK link the matched ad to the corresponding placement.

```kotlin theme={null}
import so.kontext.ads.model.Message
import so.kontext.ads.model.Role

session.addMessage(
    Message(id = "msg-1", role = Role.USER, content = "Hello, how are you?"),
)

session.addMessage(
    Message(id = "msg-2", role = Role.ASSISTANT, content = "I am good, thank you!"),
)
```

`addMessage(...)` returns synchronously — it is not a `suspend` function. The preload result is delivered later via the `onEvent` callback (`Filled`, `NoFill`, `Error`, …) — not via a return value.

## 3. Render the ad

The recommended way is the `InlineAd` composable. It takes the assistant message's `messageId` and the `Session`, and handles the rest:

```kotlin theme={null}
import androidx.compose.runtime.Composable
import so.kontext.ads.ui.InlineAd

@Composable
fun ChatRow(message: ChatMessage, session: Session) {
    MessageBubble(message)

    if (message.role == Role.ASSISTANT) {
        InlineAd(messageId = message.id, session = session)
    }
}
```

`InlineAd` is idempotent across recompositions — under the hood it calls `session.createAd(messageId)`, which returns the same `Ad` if one already exists for that `(messageId, code, theme)`. Scrolling the ad off-screen and back does **not** rebuild the iframe.

If your app is not on Compose, use `InlineAdView` instead — see [Guides → View interop](/sdk/kotlin/guides#view-interop-non-compose).

## 4. Tear down

Call `session.destroy()` when the conversation ends or the screen is closed. It is idempotent and required to cancel pending network requests and release WebView resources.

In Compose:

```kotlin theme={null}
DisposableEffect(session) {
    onDispose { session.close() } // alias for session.destroy()
}
```

## Observing events

`AdEvent` is delivered both via the `onEvent` callback you pass to `SessionOptions` and via the `Session.events` Flow — pick whichever fits your codebase. Both deliver the same events on the main thread.

### `onEvent` callback

```kotlin theme={null}
val session = KontextAds.createSession(
    context = applicationContext,
    options = SessionOptions(
        publisherToken = "...",
        userId = "...",
        conversationId = "...",
        onEvent = { event ->
            when (event) {
                is AdEvent.Filled -> println("ad filled: ${event.bidId} revenue=${event.revenue}")
                is AdEvent.Clicked -> println("clicked: ${event.url}")
                else -> Unit
            }
        },
    ),
)
```

### Flow

```kotlin theme={null}
import kotlinx.coroutines.flow.collect

lifecycleScope.launch {
    session.events.collect { event ->
        println("event: ${event.name}")
    }
}
```

The complete event list lives in the [API reference](/sdk/kotlin/api).
