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

# API reference

> Every type, method, callback and error of the Kotlin Video SDK.

Every call is main-thread, and every listener and callback is delivered on the main thread. Times are milliseconds; prices are CPM. The package is `so.kontext.video`.

## `KontextVideo`

The entry point, one object: `KontextVideo`.

**initialize(context, configuration, listener)** `method`

Starts the SDK: stores the configuration and calls the server in the background. Call it once, in `Application.onCreate`; later calls are ignored. `listener` is optional and is told the resulting `State` on the main thread once the server answered or failed.

```kotlin theme={null}
KontextVideo.initialize(this, Configuration("…")) { state -> … }
```

**awaitInitialization()** `suspend method`

Waits until the server answered or failed and returns the `State`; at once when it already has: `Enabled`, `Disabled(reason)` (the server switched the SDK off for this launch) or `Failed` (the server could not be reached; the next ads request tries again first).

```kotlin theme={null}
val state = KontextVideo.awaitInitialization()
```

**state** `State`

The current state; also `NotInitialized` before `initialize` and `Initializing` while the server is asked.

**user** `User?`

What you know about the viewer. Set it whenever you learn it; the next ads request carries it.

**regulatory** `Regulatory?`

Privacy and consent signals. The SDK reads the IAB values your consent platform writes to the default `SharedPreferences`; set here what it did not write (COPPA always comes from you).

**variantId** `String?`

Selects which type of ad to render, for A/B tests agreed with Kontext.

**VERSION** `String`

The SDK's version, as sent with every request. For your logs and crash reports.

## `Configuration`

`Configuration("…") { logLevel = … }` in Kotlin, `new Configuration.Builder("…").logLevel(…).build()` in Java.

**publisherToken** `String` *required*

Identifies the publisher. Not a secret. You get a `{publisher}-dev` token for testing and a production one.

**logLevel** `LogLevel`

How much the SDK prints to Logcat, tag `KontextVideo`: `NONE`, `ERROR`, `WARNING` (default), `INFO`, `DEBUG`.

**adServerUrl** `String`

The ad server, by default the production one. Change it only for testing against a server Kontext gives you.

## `Content`

The video the ads are for. Everything beyond `id` and `language` is contextual signal. `Content(id, language) { title = … }` in Kotlin, `new Content.Builder(id, language).title(…).build()` in Java.

**id** `String` *required*

Your own id of the video, any scheme.

**language** `String` *required*

BCP-47, for example `"en"` or `"pt-BR"`.

**mediaRating** `Rating?`

`SAFE` or `EXPLICIT`. Leave unset when you don't know; explicit content gets no ads that require safe content.

**url** `String?`

The episode's public page.

**title** `String?`

**categories** `List\<String\>?`

IAB Content Taxonomy 3.0 ids.

**description** `String?`

**durationMillis** `Long?`

Milliseconds.

**episodeNumber** `Int?`

**keywords** `List\<String\>?`

**genre** `String?`

For example `"romance"`.

**series** `Content.Series?`

`Content.Series(id) { title = …; episodeCount = … }`.

**Properties**

**id** `String` *required*

**title** `String?`

**description** `String?`

**episodeCount** `Int?`

Total number of episodes.

## `User`

`User { id = … }` in Kotlin, `new User.Builder().id(…).build()` in Java.

**id** `String?`

Your own unique identifier of the viewer.

**email** `String?`

Plain email address, when consent allows it. Sent as given; the server hashes it on arrival and never stores the plain address.

**emailHash** `String?`

SHA-256 of the normalized address (trimmed, lowercased, UTF-8), 64 lowercase hex characters. Sent as given.

**keywords** `List\<String\>?`

The viewer's interests, in your own words.

## `Regulatory`

`Regulatory { coppa = true }` in Kotlin, `new Regulatory.Builder().coppa(true).build()` in Java.

**gdpr** `Boolean?`

Whether the request is subject to GDPR.

**gdprConsent** `String?`

IAB TCF v2 consent string.

**coppa** `Boolean?`

Whether the request is subject to COPPA. Cannot be read from the device.

**gpp** `String?`

IAB Global Privacy Platform string.

**gppSid** `List\<Int\>?`

GPP section ids that apply.

**usPrivacy** `String?`

Legacy IAB US Privacy (CCPA) string.

## `AdFormat`

`OVERLAY`, `PAUSE`, `LINEAR`.

## `VideoAds`

The ads of one video. Create one per episode; creating it sends nothing. Any number can be alive at once. Call `destroy()` when the viewer leaves the video: dropping the reference does not end its ads.

**VideoAds(content, labels)** `constructor`

`labels` is optional: key-values that say where these ads play, such as the viewer's stage in the series or the player's layout. Every request of this `VideoAds` carries them, and your setup on our side decides what they change, such as which formats are offered. They are fixed for the object's life and describe the screen, never the viewer. Keys use `a-z`, `0-9` and `_`; values are not empty; both are at most 64 bytes, with at most 20 pairs. Anything else is dropped with a Logcat warning.

```kotlin theme={null}
val ads = VideoAds(Content("episode-1", "en"))
val firstEpisode = VideoAds(content, mapOf("stage" to "first_episode", "layout" to "fullscreen"))
```

**format(format)** `suspend method`

The ad for one format: the same `VideoAd` as last time while it is unshown and valid, a new one otherwise. Calls made while a request runs share it. A failed request returns the held ad when it is still valid.

Throws `NoFill`, `Request`, `Destroyed`, `Disabled`, `NotInitialized`, `InitializationFailed`. A cancelled coroutine stops waiting; the request itself continues.

```kotlin theme={null}
val ad = ads.format(AdFormat.PAUSE)
```

**format(format, callback)** `method`

The same for Java, or code without coroutines: `callback` is a `VideoAdsCallback<VideoAd>` and gets `onSuccess(ad)` or `onError(exception)` on the main thread.

**track(player)** `method`

The player this episode plays in: a Media3 `Player` (`import so.kontext.video.media3.track`, from `so.kontext:video-media3`), or any [`VideoAdsPlayer`](#videoadsplayer). The SDK reads its time for requests and events, and follows its playback for an overlay: an overlay shows over playing content only, and ends with `onAdEnded(INTERRUPTED)` when the content pauses, ends, or the player moves to another item. Held until `destroy()` or the next `track`; the Media3 adapter holds your player weakly. Call it when this episode starts playing in the player. Without a player, events carry 0 (right for a prefetch) and an overlay runs on its own time.

```kotlin theme={null}
ads.track(player)
```

**destroy()** `method`

Releases the video's ads. Every held ad is dropped and the advertiser told it was not used; the ad on screen ends with `onAdEnded(DESTROYED)`; a `format()` or `load()` in flight throws `Destroyed`. Idempotent.

**content** `Content`

What it was created with.

**isDestroyed** `Boolean`

`true` after `destroy()`.

**lastRequestId** `String?`

The id of the latest ads request, `null` before the first. Quote it to Kontext when an ad or a no-fill looks wrong.

## `VideoAdsPlayer`

What the SDK reads from a player that is not a Media3 `Player`. Implement it, in a small adapter around your player, and pass it to `track(player)`. The SDK reads it on the main thread.

**currentPositionMillis** `Long`

Where the content is, in milliseconds of the episode.

**isPlaying** `Boolean`

`false` when the viewer or your app paused the content, or it ended. Buffering and seeking count as playing.

**playbackSpeed** `Float?`

Optional. The playback speed, 1 being normal, when your player knows it. `null` (the default) allows up to about 4×.

**addListener(listener) / removeListener(listener)** `method`

Optional. Tell the `VideoAdsPlayer.Listener` what the player does, as it happens, so an overlay reacts at once: `onIsPlayingChanged(isPlaying)`, `onSeek()`, `onContentChanged()` (another item, or this one played to its end). Without a listener the SDK reads `isPlaying` four times a second while an overlay is on screen, and estimates seeks from the position.

An overlay's duration counts milliseconds of video. Without `onSeek()` and `playbackSpeed`, the SDK treats a jump faster than about 4× real time as a seek and counts nothing for it. The Media3 adapter reports all of it.

```kotlin theme={null}
class MyPlayerAdapter(private val player: MyPlayer) : VideoAdsPlayer {
    override val currentPositionMillis: Long get() = player.positionMillis
    override val isPlaying: Boolean get() = player.state == State.PLAYING || player.state == State.BUFFERING
}
```

## `VideoAd`

One ad, from the server's answer to the end of its presentation. Look at its facts, then `load()` or `reject()`, then `show()`, then exactly one `onAdEnded`. Shown at most once.

### Facts

**id** `String`

**format** `AdFormat`

**price** `VideoAd.Price`

**Properties**

**value** `Double`

CPM.

**currency** `String`

ISO 4217.

**precision** `Precision`

`EXACT` (a DSP's clearing price) or `ESTIMATED` (house demand, a forecast).

**durationMillis** `Long?`

Overlay: how long the SDK keeps it on screen, `null` when it stays until ended. Linear: the video's length. Always `null` for a pause ad.

### State

**isLoaded** `Boolean`

`load()` finished.

**isReady** `Boolean`

Loaded, not shown, not rejected, not expired: `show()` can be tried. A hint, not a guarantee; `show()` still measures the container.

**invalidationReason** `InvalidationReason?`

Why the ad ended before it was shown, once it did: `Expired`, `Rejected`, `Destroyed`, `CreativeFailed(code)`.

**wasShown** `Boolean`

`true` from a successful `show()` on.

**hasEnded** `Boolean`

`true` once the ad began to end. `onAdEnded` follows at once for an overlay or pause ad, and once its screen is gone for a linear ad.

**isCompleted** `Boolean`

Whether the creative ran to its end: a video reached complete, an overlay's duration elapsed. Separate from how it left the screen.

**listener** `VideoAdListener?`

Receives the ad's moments. Held until `onAdEnded` has run, then let go.

### Methods

**load()** `suspend method`

Renders the creative off screen and returns once it is ready, so `show()` is instant. Throws `CreativeFailed`, `Invalidated`, `Destroyed`, `Disabled`. A second call waits for the first. `load(callback)` is the same for Java: a `VideoAdsCallback<VideoAd>`.

**reject(reason)** `method`

You won't use the ad; the server tells the advertiser. `RejectReason.NotUsed` (default) or `RejectReason.Outbid(winningPrice, winner)`. Works until `show()`, loaded or not. Throws `AlreadyShown` after it, or `Invalidated` if the ad already ended.

```kotlin theme={null}
ad.reject(VideoAd.RejectReason.Outbid(winningPrice = 2.4, winner = "Other network"))
```

**show(container, insets)** `method`

Shows an overlay or pause ad inside `container`, the `ViewGroup` the content plays in. Returns once the ad is on screen. The ad follows the container when it resizes or rotates; a container that becomes too small for it ends the ad with `onAdEnded(INTERRUPTED)`. Throws, with nothing shown: `NotLoaded`, `AlreadyShown`, `AnotherAdShowing`, `Invalidated`, `Destroyed`, `Disabled`, `PresenterUnavailable` (the container is not attached to a window, not shown, or has no area), `ContainerTooSmall` and `ContentPaused` (an overlay over paused content; the ad stays ready in these three), `WrongPresentation` (a linear ad). With a player given to `track`, an overlay also ends with `onAdEnded(INTERRUPTED)` when the content stops.

**Parameters**

**insets** `androidx.core.graphics.Insets`

Space to keep free inside `container`, in pixels, on top of the system bars and display cutout that overlap it: `Insets.of(0, 0, 0, 200)` places a bottom strip 200 px above them. Defaults to `Insets.NONE`. Kept while the ad is shown, through resizes and rotations. A full-screen pause ad ignores them. See [Keeping the ad clear of your controls](https://docs.kontext.so/video/sdk/kotlin/guides.md#keeping-the-ad-clear-of-your-controls).

**show(activity)** `method`

Shows a linear ad full screen, in the SDK's own Activity started from `activity`. Back does nothing there: the creative's own controls end the ad. Throws, with nothing shown: the errors above, `PresenterUnavailable` for an Activity that is finishing or destroyed (the ad stays ready), `WrongPresentation` (an overlay or pause ad).

```kotlin theme={null}
linear.show(this)   // from your Activity
```

**dismiss()** `method`

Ends an overlay or pause ad because you say so; `onAdEnded(DISMISSED)` follows, synchronously. Before `show()` and after the end it does nothing. Throws `WrongPresentation` for a linear ad, which only the SDK dismisses.

`reject`, `show` and `dismiss` are annotated `@Throws(VideoAdException::class)`, so Java code catches the exception.

## `VideoAdListener`

Delivered on the main thread. Every method has an empty default, from Java too; implement the ones you need. Nothing is delivered before `show()`. After it: the impression and clicks, in no fixed order, then exactly one `onAdEnded`, always last. Every method gets the ad first, so one listener can serve several ads.

**onAdImpression(ad)** `method`

The impression was counted: at `show()` for an image or HTML creative, at the first frame for a video. At most once; may not come at all, when the ad ends before.

**onAdClicked(ad, click)** `method`

The viewer tapped the ad. The SDK recorded the click and started opening its target. Delivered as the opening starts, so whether anything opened, and which, is not reported. Once per tap.

**click**

**area** `VideoAd.Click.Area`

Where the viewer tapped: `Creative` (the image, the video, the card), `EndCard` (a linear ad's end card, after the video completed), or `Other(label)`, a label of the creative's own such as `"cta"`.

**onAdFailed(ad, error)** `method`

The ad broke after `show()` returned. `onAdEnded(FAILED)` follows.

**error**

**CreativeFailed** `VideoAdException`

The creative failed or stalled on screen. `code` is the VAST error code: the creative's own, or 405.

**PresenterUnavailable** `VideoAdException`

The linear ad's screen never started.

**onAdEnded(ad, reason)** `method`

Presentation is over. Exactly once per successful `show()`, always last. Resume your content here, deciding from the format and `reason`. For an overlay or pause ad it comes synchronously, even from inside your own `dismiss()` or `destroy()`. For a linear ad it comes once the ad's screen is gone, so you can start your next screen from it.

**reason**

**reason** `VideoAd.EndReason`

Who or what took the ad off the screen; see [`VideoAd.EndReason`](#videoad-endreason). Whether the creative ran to its end is `ad.isCompleted`: a linear ad whose end card the viewer closed is `CLOSED` with `isCompleted == true`.

## `VideoAd.EndReason`

| Reason | When |
| - | - |
| `COMPLETED` | The ad ended by itself: an overlay's duration elapsed, a linear video finished with no end card. |
| `SKIPPED` | The viewer tapped Skip on a linear ad before the video ended. |
| `CLOSED` | The viewer tapped the ad's close control (an overlay's or pause card's ✕, a linear end card's ✕). A pause card's ✕ leaves the content paused. |
| `RESUMED` | The viewer tapped Resume on a full-screen pause ad. |
| `FAILED` | The creative failed or stalled, or the linear ad's screen never started; `onAdFailed` came first. |
| `INTERRUPTED` | The presentation disappeared for a reason the SDK did not cause: the container left the window or became too small for the ad, or the linear ad's screen was destroyed under it. For an overlay with a player given to `track`, also the content pausing, ending or moving to another item. |
| `DISMISSED` | You called `dismiss()`. |
| `DESTROYED` | You called `destroy()`. |

## `VideoAdException`

Thrown by a call: a thrown exception means nothing happened on screen and nothing was fired, except a failed `load()`'s VAST error event. Two are also delivered through `onAdFailed` after a `show()` that succeeded: `CreativeFailed` and `PresenterUnavailable`; the ad is over then. Every one is a subclass of `VideoAdException`.

| Exception | When |
| - | - |
| `NotInitialized` | `KontextVideo.initialize` was never called. |
| `InitializationFailed` | The server could not be reached, and the retry failed too. Every `format()` retries. |
| `Destroyed` | The `VideoAds` was destroyed. Create a new one for the next content. |
| `Disabled(reason)` | The server switched the SDK off for this launch. |
| `NoFill(reason)` | The server had nothing for this format. See [`NoFillReason`](#nofillreason). |
| `Request` | The request failed and no valid ad was held for the format; `cause` says why. |
| `CreativeFailed(code)` | The creative did not load within 10 s of active time; `code` is the VAST error code. |
| `AlreadyShown` | Shown once; cannot be shown again or rejected. |
| `AnotherAdShowing` | Another ad is on screen, from any `VideoAds`. This ad stays ready. |
| `Invalidated(reason)` | The ad ended before it was shown: `Expired`, `Rejected`, `Destroyed`, `CreativeFailed(code)`. |
| `NotLoaded` | `show()` before `load()` finished. The ad stays as it was. |
| `ContentPaused` | `show(container)` on an overlay while the content of its `VideoAds` is paused. The ad stays ready: show it once the content plays. |
| `ContainerTooSmall` | The ad would have to shrink below 50 % to fit. `minimumWidthPx` and `minimumHeightPx` are the smallest container that holds it, your insets included. The ad stays ready. |
| `PresenterUnavailable` | Nothing on screen can hold the ad right now: the Activity for a linear ad is finishing or destroyed, or the container for an overlay or pause ad is not attached to a window, not shown, or has no area. The ad stays ready. |
| `WrongPresentation` | `show(activity)` on an overlay or pause ad, `show(container)` or `dismiss()` on a linear ad. |

## `NoFillReason`

| Reason | Meaning |
| - | - |
| `NoAdMatch` | No campaign matched the content, the viewer or the moment |
| `FrequencyCap` | The viewer reached the frequency cap set for your app. Only when a cap is configured |
| `ContentUnsafe` | The content is not brand safe |
| `GeoRestricted` | Not served in this country |
| `FormatNotEnabled` | The format is not enabled for your app, or not for the `labels` this `VideoAds` sends |
| `TemporaryError` | Our side; ask again later |
| `AlreadyUsed` | The SDK's own: the answer was an ad you had just shown or rejected |
| `Unknown` | The server gave no reason |
| `Other(raw)` | A reason this SDK version does not know, passed through unchanged |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.