> For the complete documentation index, see [llms.txt](https://docs.amply.tools/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.amply.tools/reference/sdk-android.md).

# Android SDK

Public API surface for the Amply Android SDK. Kotlin signatures are copied verbatim from the source.

## At a glance

| Group           | Function                                                                                                                                                                  |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Initialization  | `Amply(config, application)`, `AmplyConfig(...)`, `amplyConfig { ... }`                                                                                                   |
| Event tracking  | `track(event, properties)`                                                                                                                                                |
| User attributes | `setUserId(userId)`, `setCustomProperty(key, value)`, `setCustomProperties(properties)`, `getCustomProperty(key)`, `removeCustomProperty(key)`, `clearCustomProperties()` |
| Deeplinks       | `registerDeepLinkListener(listener)` → token, `removeDeepLinkListener(token)`                                                                                             |
| System events   | `setSystemEventsListener(listener)` → token, `clearSystemEventsListener(token)`                                                                                           |
| Data inspection | `getRecentEvents(limit)`, `getDataSetSnapshot(type)`                                                                                                                      |
| Logging         | `setLogLevel(level)`, `getLogLevel()`, `setLogListener(listener)`                                                                                                         |

## Detaching a listener

Registering a listener returns a **token**. Keep it — passing that token back is how you detach:

```
val token = amply.registerDeepLinkListener(router)
// later, when whatever owns `router` goes away:
amply.removeDeepLinkListener(token)
```

Detaching with a token that is no longer the current registration does nothing. That is deliberate: if a screen is replaced by a new one that registers first, the departing screen cannot silence its replacement on the way out.

**Who needs to do this.** A listener set up once at launch and kept for the life of the app never has to detach. Anything shorter-lived does — a screen, a feature module. A listener left attached after its owner is gone keeps that owner alive and the SDK keeps calling it.

**Detach where your teardown actually runs.** Do not rely on the object's deallocation hook: while the SDK still holds the listener, that hook does not run. Use whatever explicit teardown point your host gives you.

**Gates work the same way.** `registerGate` returns a token and `unregisterGate` takes it. A gate left registered by a screen that has gone away makes the next gated call on that URL wait out its full timeout with nothing on screen.

## Initialization

### `class Amply(val config: AmplyConfig, val application: Application)`

Construct the SDK. Construction **is** initialization — the `init` block wires internal components, starts the first session, and begins tracking the activity lifecycle. No separate `initialize()` call is required.

Parameters:

| Name          | Type                      | Required | Description                                                              |
| ------------- | ------------------------- | -------- | ------------------------------------------------------------------------ |
| `config`      | `AmplyConfig`             | yes      | Credentials and optional network overrides.                              |
| `application` | `android.app.Application` | yes      | Application instance used for lifecycle callbacks, storage, and context. |

Hold the returned instance for the lifetime of the process (commonly on the `Application` subclass).

### `class AmplyConfig`

Credential and network configuration.

```kotlin
class AmplyConfig(
    val appId: String,
    val apiKeyPublic: String,
    val apiKeySecret: String,
    val defaultConfig: String?,
    val configBaseUrl: String? = null,
    val backendBaseUrl: String? = null,
)
```

| Name             | Type      | Required       | Description                                                                                      |
| ---------------- | --------- | -------------- | ------------------------------------------------------------------------------------------------ |
| `appId`          | `String`  | yes            | Application identifier from the Amply dashboard.                                                 |
| `apiKeyPublic`   | `String`  | yes            | Public API key.                                                                                  |
| `apiKeySecret`   | `String`  | yes            | Secret API key. Keep it out of source control.                                                   |
| `defaultConfig`  | `String?` | yes (nullable) | JSON string of a fallback configuration used until the remote config loads. Pass `null` to skip. |
| `configBaseUrl`  | `String?` | no             | Override for the configuration service base URL.                                                 |
| `backendBaseUrl` | `String?` | no             | Override for the events backend base URL.                                                        |

### `fun amplyConfig(initializer: AmplyConfigBuilder.() -> Unit): AmplyConfig`

Kotlin DSL for building an `AmplyConfig`. Enforces that `appId`, `apiKeyPublic`, and `apiKeySecret` are non-null and throws `IllegalArgumentException` otherwise.

```kotlin
val config = amplyConfig {
    api {
        appId = "your.app.id"
        apiKeyPublic = "your-public-key"
        apiKeySecret = "your-secret-key"
    }
    network {
        configBaseUrl = "https://config.example.com"
        backendBaseUrl = "https://events.example.com"
    }
    defaultConfig = "{ ... }"
}
```

The `api { }` block sets credentials; the `network { }` block sets optional overrides. Returns a fully validated `AmplyConfig`.

## Event tracking

### `fun track(event: String, properties: Map<String, Any> = emptyMap())`

Record a custom event.

| Name         | Type               | Required | Description                                 |
| ------------ | ------------------ | -------- | ------------------------------------------- |
| `event`      | `String`           | yes      | Event name.                                 |
| `properties` | `Map<String, Any>` | no       | Event properties. Defaults to `emptyMap()`. |

Returns `Unit`. Events are persisted locally and flushed by the SDK; no network call is tied to this call site.

## Gating

Gate a user-facing step on a campaign's outcome — for example, a rewarded ad before an export. See [Gating actions](/developer-guide/gating-actions.md) for the full flow.

### `suspend fun trackGated(event: String, properties: Map<String, Any> = emptyMap()): GateDecision`

Track an event and suspend until any gating campaign on it resolves, then return the decision. If no gate applies, returns `GateDecision.Proceed(ProceedReason.FailOpen)` immediately. Call from a coroutine.

| Name         | Type               | Required | Description                     |
| ------------ | ------------------ | -------- | ------------------------------- |
| `event`      | `String`           | yes      | Event name a campaign may gate. |
| `properties` | `Map<String, Any>` | no       | Event properties.               |

### `fun registerGate(baseUrl: String, presenter: CampaignPresenter, onAbort: AbortPolicy = AbortPolicy.Cancel, timeoutMs: Long = 60_000): ListenerToken`

Register the single gate presenter once at startup. The SDK calls the presenter when a gated campaign needs to show UI, and calls its `dismiss()` if the gate is abandoned.

| Name        | Type                | Required | Description                                              |
| ----------- | ------------------- | -------- | -------------------------------------------------------- |
| `baseUrl`   | `String`            | yes      | Base URL the gate resolves against.                      |
| `presenter` | `CampaignPresenter` | yes      | Your presenter (see below).                              |
| `onAbort`   | `AbortPolicy`       | no       | What a user dismiss means. Default `AbortPolicy.Cancel`. |
| `timeoutMs` | `Long`              | no       | Fail-open deadline in milliseconds. Default `60_000`.    |

### `fun unregisterGate(token: ListenerToken)`

Withdraws the gate registered with that token. The URL stays gate-able, so a gated call still resolves immediately rather than falling through as an ordinary deeplink - it resolves the way the gate was registered to resolve, and costs the campaign no impression.

A gate registered at launch and kept for the life of the app needs no withdrawal. One owned by something shorter-lived does: the registry outlives whatever registered into it.

### `sealed class GateDecision`

| Variant                                       | Meaning                                                                     |
| --------------------------------------------- | --------------------------------------------------------------------------- |
| `GateDecision.Proceed(reason: ProceedReason)` | Do the next step. `reason` distinguishes a satisfied gate from a fail-open. |
| `GateDecision.Cancelled`                      | User deliberately dismissed a `Cancel`-policy gate. Suppress the next step. |

`when (decision) { is GateDecision.Proceed -> … ; is GateDecision.Cancelled -> … }`.

### `enum class ProceedReason`

`ProceedReason.Completed` — the campaign's value-exchange was satisfied (e.g. reward earned). `ProceedReason.FailOpen` — nothing to wait on, or the gate couldn't run to a decision; do not reward.

### `enum class AbortPolicy`

`AbortPolicy.Cancel` (default) — a dismiss returns `GateDecision.Cancelled` (true gating, for value-exchange actions). `AbortPolicy.Proceed` — a dismiss fails open (for consent-style flows).

### `interface CampaignPresenter`

```kotlin
interface CampaignPresenter {
    fun present(params: Map<String, String>, info: Map<String, Any>, resolution: CampaignResolution)
    fun dismiss()
}
```

`present` shows the gated UI and reports the result through the `resolution` handle; `dismiss` tears it down without producing a result (the SDK calls it when the gate is abandoned). Both are required.

### `CampaignResolution` / `enum class CampaignResult`

The `resolution` handle passed to `present(...)` reports the outcome exactly once:

```kotlin
resolution.resolve(CampaignResult.Completed)    // value-exchange satisfied → proceeds (ProceedReason.Completed)
resolution.resolve(CampaignResult.Dismissed)    // user backed out → honors the onAbort policy
resolution.resolve(CampaignResult.Unavailable)  // couldn't run (no fill / error) → fail-open
```

`CampaignResult`: `Completed` / `Dismissed` / `Unavailable`.

## User attributes

### `fun setUserId(userId: String?)`

Associate subsequent events with a user identifier.

| Name     | Type      | Required | Description                                 |
| -------- | --------- | -------- | ------------------------------------------- |
| `userId` | `String?` | yes      | Stable user identifier, or `null` to clear. |

Returns `Unit`. Survives across sessions until changed.

### `fun setCustomProperty(key: String, value: Any)`

Set one custom property on the current user.

| Name    | Type     | Required | Description                                                                     |
| ------- | -------- | -------- | ------------------------------------------------------------------------------- |
| `key`   | `String` | yes      | Property key.                                                                   |
| `value` | `Any`    | yes      | One of: `String`, `Int`, `Long`, `Float`, `Double`, `Boolean`, `DateTimeValue`. |

Returns `Unit`. Use `DateTimeValue(epochMillis)` to persist a value as a datetime rather than as a number.

### `fun setCustomProperties(properties: Map<String, Any>)`

Set multiple custom properties at once.

| Name         | Type               | Required | Description               |
| ------------ | ------------------ | -------- | ------------------------- |
| `properties` | `Map<String, Any>` | yes      | Keys to supported values. |

Returns `Unit`. Equivalent to calling `setCustomProperty` per entry.

### `suspend fun getCustomProperty(key: String): Any?`

Read the current value of a custom property.

| Name  | Type     | Required | Description   |
| ----- | -------- | -------- | ------------- |
| `key` | `String` | yes      | Property key. |

Returns `Any?` — the stored value, or `null` if unset. `suspend` because storage access runs off the main thread.

### `fun removeCustomProperty(key: String)`

Delete a single custom property.

| Name  | Type     | Required | Description             |
| ----- | -------- | -------- | ----------------------- |
| `key` | `String` | yes      | Property key to remove. |

Returns `Unit`.

### `fun clearCustomProperties()`

Remove every custom property on the current user. Returns `Unit`. Takes no parameters.

## Deeplinks

### `fun registerDeepLinkListener(listener: DeepLinkListener): ListenerToken`

Register a handler that receives campaign deep link URLs.

| Name       | Type               | Required | Description                                            |
| ---------- | ------------------ | -------- | ------------------------------------------------------ |
| `listener` | `DeepLinkListener` | yes      | Handler implementing `onDeepLink(url, info): Boolean`. |

Returns `Unit`. Multiple listeners can be registered; each is invoked in registration order.

### `interface DeepLinkListener`

```kotlin
interface DeepLinkListener {
    fun onDeepLink(url: String, info: Map<String, Any>): Boolean
}
```

Return `true` if your code handled the URL; `false` to allow other listeners to handle it.

A listener that throws is treated as "did not handle it" — the URL still reaches the listeners registered after it.

### `fun removeDeepLinkListener(token: ListenerToken)`

Withdrawing takes the **token** returned at registration, not the listener object. A token that is no longer the current registration is ignored, so a component being torn down cannot detach the one that replaced it.

Stop sending deep links to a previously registered listener.

| Name       | Type               | Required | Description               |
| ---------- | ------------------ | -------- | ------------------------- |
| `listener` | `DeepLinkListener` | yes      | The listener to withdraw. |

Returns `Unit`. Registering **adds** a listener rather than replacing one, so anything you register from an object that can be torn down and rebuilt must be withdrawn when that object goes away — otherwise the old listeners stay registered and every later deep link is delivered to all of them.

## System events

### `fun setSystemEventsListener(listener: SystemEventsListener): ListenerToken`

Receive SDK lifecycle events (session start, config fetch, campaign shown).

| Name       | Type                   | Required | Description                               |
| ---------- | ---------------------- | -------- | ----------------------------------------- |
| `listener` | `SystemEventsListener` | yes      | Handler with one `onEvent(event)` method. |

Returns `Unit`. Replaces any previously set listener.

### `interface SystemEventsListener`

```kotlin
interface SystemEventsListener {
    fun onEvent(event: EventInterface)
}
```

### `fun clearSystemEventsListener(token: ListenerToken)`

Withdrawing takes the **token** returned at registration, not the listener object. A token that is no longer the current registration is ignored, so a component being torn down cannot detach the one that replaced it.

Stop sending system events to a previously set listener.

| Name       | Type                   | Required | Description               |
| ---------- | ---------------------- | -------- | ------------------------- |
| `listener` | `SystemEventsListener` | yes      | The listener to withdraw. |

Returns `Unit`. The listener is cleared **only if it is still the one currently set** — so if a replacement has already registered itself, withdrawing the old one leaves the new one in place. Call it when the object you registered is being torn down.

## Data inspection

### `suspend fun getRecentEvents(limit: Int = 30): List<EventInterface>`

Fetch the most recent events tracked in the current install. Intended for in-app debug tools.

| Name    | Type  | Required | Description                               |
| ------- | ----- | -------- | ----------------------------------------- |
| `limit` | `Int` | no       | Maximum number of events. Defaults to 30. |

Returns `List<EventInterface>` newest-first.

### `suspend fun getDataSetSnapshot(type: DataSetType): Map<String, Any>`

Return a snapshot of one dataset the SDK uses for targeting.

| Name   | Type          | Required | Description       |
| ------ | ------------- | -------- | ----------------- |
| `type` | `DataSetType` | yes      | Dataset selector. |

Returns `Map<String, Any>`.

### `sealed class DataSetType`

| Variant                                                        | Value             | Contents                                                                          |
| -------------------------------------------------------------- | ----------------- | --------------------------------------------------------------------------------- |
| `DataSetType.Device`                                           | `@device`         | Advertising ID, model, OS version, locale, timezone.                              |
| `DataSetType.User`                                             | `@user`           | User ID and identity attributes.                                                  |
| `DataSetType.Custom`                                           | `@custom`         | All custom properties you've set.                                                 |
| `DataSetType.Session`                                          | `@session`        | Current session metadata.                                                         |
| `DataSetType.TriggeredEvent(countStrategy, params, eventName)` | `@triggeredEvent` | Aggregated counts for a specific event. `CountStrategy` is `GLOBAL` or `SESSION`. |
| `DataSetType.Events(data)`                                     | `@events`         | Counts for a list of events. Each `Event(name, type, params)`.                    |

## Logging

### `fun setLogLevel(level: LogLevel)`

Set the SDK log level.

| Name    | Type       | Required | Description                                           |
| ------- | ---------- | -------- | ----------------------------------------------------- |
| `level` | `LogLevel` | yes      | `LogLevel.NONE`, `ERROR`, `WARN`, `INFO`, or `DEBUG`. |

Returns `Unit`. Defaults to `NONE`.

### `fun setLogLevel(level: String?)`

String overload. Accepts `"none"`, `"error"`, `"warn"`, `"info"`, `"debug"` (case-insensitive). Any other value — including `null` — maps to `NONE`. Returns `Unit`.

### `fun getLogLevel(): LogLevel`

Return the current log level. Takes no parameters.

### `fun setLogListener(listener: LogListener): ListenerToken`

No longer accepts `null`. Detach with `clearLogListener(token)`.

Install a handler that receives every SDK log entry above the current level. Pass `null` to remove the existing listener.

| Name       | Type           | Required | Description                                      |
| ---------- | -------------- | -------- | ------------------------------------------------ |
| `listener` | `LogListener?` | yes      | Handler with `onLog(entry)`, or `null` to clear. |

Returns `Unit`.

### `interface LogListener`

```kotlin
interface LogListener {
    fun onLog(entry: LogEntry)
}
```

### `data class LogEntry`

| Field       | Type                | Description                                                      |
| ----------- | ------------------- | ---------------------------------------------------------------- |
| `level`     | `LogLevel`          | Severity of the entry.                                           |
| `category`  | `String`            | SDK-assigned category (e.g. `"sdk"`, `"session"`, `"deeplink"`). |
| `message`   | `String`            | Human-readable message.                                          |
| `timestamp` | `Long`              | UTC epoch milliseconds.                                          |
| `details`   | `Map<String, Any>?` | Structured context, if provided.                                 |

## Types

### `enum class LogLevel`

| Value   | Int level |
| ------- | --------- |
| `NONE`  | 0         |
| `ERROR` | 1         |
| `WARN`  | 2         |
| `INFO`  | 3         |
| `DEBUG` | 4         |

Higher values include all lower-severity entries.

### `class DateTimeValue(val epochMillis: Long)`

Wrapper passed to `setCustomProperty` to mark a value as a datetime rather than as a number.

### `interface EventInterface`

| Field        | Type               | Description                               |
| ------------ | ------------------ | ----------------------------------------- |
| `name`       | `String`           | Event name.                               |
| `timestamp`  | `Long`             | UTC epoch milliseconds.                   |
| `properties` | `Map<String, Any>` | Event properties.                         |
| `type`       | `EventType`        | `EventType.CUSTOM` or `EventType.SYSTEM`. |

### System event names

Well-known values on `EventInterface.name` when `type == EventType.SYSTEM`:

| Constant                               | Value                   | When it fires                                                                          |
| -------------------------------------- | ----------------------- | -------------------------------------------------------------------------------------- |
| `SystemEvents.SDK_INITIALIZED`         | `SdkInitialized`        | SDK finished initialization.                                                           |
| `SystemEvents.CONFIG_FETCH_STARTED`    | `ConfigFetchStarted`    | Remote configuration fetch began.                                                      |
| `SystemEvents.CONFIG_FETCH_FINISHED`   | `ConfigFetchFinished`   | Remote configuration fetch completed.                                                  |
| `SystemEvents.SESSION_START`           | `SessionStarted`        | Session started. `properties["type"]` is `"cold"` or `"warm"`.                         |
| `SystemEvents.SESSION_END`             | `SessionFinished`       | Session ended.                                                                         |
| `SystemEvents.CAMPAIGN_SHOWN`          | `CampaignShown`         | Campaign impression recorded.                                                          |
| `SystemEvents.EVENT_TRIGGERED`         | `EventTriggered`        | A custom event matched a campaign trigger.                                             |
| `SystemEvents.CUSTOM_PROPERTY_CHANGED` | `CustomPropertyChanged` | A custom property was set, updated, removed, or cleared. Usable as a campaign trigger. |
| `SystemEvents.CAMPAIGN_RESOLVED`       | `CampaignResolved`      | A presented (blocking) campaign action resolved or was skipped (0.5.0+). Stats only.   |

## Related

* [iOS SDK reference](/reference/sdk-ios.md) — Swift surface on the other native platform.
* [React Native SDK reference](/reference/sdk-react-native.md) — TypeScript surface for RN apps.
