> 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/events.md).

# Events

Reference for events the SDK emits on your behalf and events you track yourself.

## Event types

Every event the SDK records has a `type` of either `system` or `custom`:

| Type     | Who emits it               | Example             |
| -------- | -------------------------- | ------------------- |
| `system` | SDK, automatically         | `SessionStarted`    |
| `custom` | Your app, via `track(...)` | `PurchaseCompleted` |

Both flow through the same pipeline and appear together in `getRecentEvents(...)`. System events are also delivered to `SystemEventsListener` as they fire.

***

## Standard events (SDK-emitted)

These fire automatically. You don't need to track them. This is the complete list. All of them reach your system-events listener: subscribe with `setSystemEventsListener(...)` (iOS/Android) or `addSystemEventListener(...)` (React Native). `CampaignResolved` mainly feeds Amply's per-campaign stats and never counts toward frequency limits.

| Name                    | When it fires                                                                                                                                         | Properties                                                                                | Observable |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ---------- |
| `SdkInitialized`        | Once per session, after the SDK finishes setting up and before the first campaign fetch.                                                              | none                                                                                      | yes        |
| `ConfigFetchStarted`    | Just before the SDK requests the campaign configuration.                                                                                              | none                                                                                      | yes        |
| `ConfigFetchFinished`   | When the campaign fetch completes (success or failure).                                                                                               | `campaignCount`, `success`, `campaigns`, `configSource` and `bundleGeneratedAt` (0.16.0+) | yes        |
| `SessionStarted`        | When a new session begins — either cold (first launch after install or full restart) or warm (app returning to foreground after timeout).             | `type`                                                                                    | yes        |
| `SessionFinished`       | When the current session ends.                                                                                                                        | none                                                                                      | yes        |
| `CampaignShown`         | When a campaign is activated and delivered in response to an event.                                                                                   | `source`, `campaignId`, `campaignName` (0.15.0+)                                          | yes        |
| `EventTriggered`        | After every `track(...)` call, mirroring the custom event that was just recorded.                                                                     | `sourceEvent`, `properties`                                                               | yes        |
| `CustomPropertyChanged` | When you set, update, remove, or clear a custom property. Usable as a campaign **trigger** so you can react to the moment a trait changes.            | `key`, `oldValue`, `newValue`                                                             | yes        |
| `CampaignResolved`      | When a presented campaign action resolves, or a campaign that was chosen is skipped. **0.5.0+.** Mainly for stats — counted per campaign server-side. | `campaignId`, `campaignName` (0.15.0+), `result`, `unavailableReason` (0.5.2+)            | yes        |

### SdkInitialized

Fires once after construction/initialization finishes. Use this to know when it's safe to read `getDataSetSnapshot(...)` or query cached campaigns.

```
name:       "SdkInitialized"
type:       "system"
properties: {}
```

### ConfigFetchStarted

Fires before the network request that loads the campaign configuration.

```
name:       "ConfigFetchStarted"
type:       "system"
properties: {}
```

### ConfigFetchFinished

Fires after the campaign configuration has been fetched (or failed to fetch). The cached configuration has already been applied by the time this event fires.

```
name:       "ConfigFetchFinished"
type:       "system"
properties: {
  "campaignCount": 3,
  "success":       true,
  "campaigns": [
    { "id": "c_abc", "name": "Welcome offer" },
    { "id": "c_def", "name": "Day-2 check-in" }
  ]
}
```

| Property            | Type    | Description                                                                                                                                                                                                                                                                                                           |
| ------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `campaignCount`     | number  | Number of campaigns now active in memory.                                                                                                                                                                                                                                                                             |
| `success`           | boolean | `true` if the fetch completed without error.                                                                                                                                                                                                                                                                          |
| `campaigns`         | array   | One entry per active campaign with `id` and `name`.                                                                                                                                                                                                                                                                   |
| `configSource`      | string  | **0.16.0+.** Where the campaigns in use came from: `network` (this download), `cache` (the last download saved on the device), `bundle` ([built-in campaigns](/developer-guide/built-in-campaigns.md) shipped in the app), `forcedBundle` (the bundle, because Amply switched this app back to it) or `empty` (none). |
| `bundleGeneratedAt` | string  | **0.16.0+.** When the built-in bundle was made. Present only when the campaigns in use come from the bundle.                                                                                                                                                                                                          |

From 0.16.0, `success` describes this download only. When the download fails and the app falls back to saved or built-in campaigns, `success` is `false` and `configSource` says what is in use. Earlier versions reported `true` after a fallback.

### SessionStarted

Fires at the start of every session.

```
name:       "SessionStarted"
type:       "system"
properties: { "type": "cold" }
```

| Property | Type   | Description                                                                                                                                                     |
| -------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`   | string | `"cold"` for the first session of an install or a full process restart, `"warm"` for a session resumed after the app was backgrounded past the session timeout. |

### SessionFinished

Fires when the current session ends.

```
name:       "SessionFinished"
type:       "system"
properties: {}
```

### EventTriggered

Fires immediately after each `track(...)` call. The original event is mirrored in the `sourceEvent` and `properties` fields. This exists so downstream listeners (dashboards, analytics forwarders) see a uniform system-event stream for every tracked action.

```
name:       "EventTriggered"
type:       "system"
properties: {
  "sourceEvent": "PurchaseCompleted",
  "properties":  { "sku": "premium_yearly", "price": 59.99 }
}
```

| Property      | Type   | Description                                      |
| ------------- | ------ | ------------------------------------------------ |
| `sourceEvent` | string | The `name` of the custom event that was tracked. |
| `properties`  | object | The exact property map passed to `track(...)`.   |

### CampaignShown

Fires when a campaign is activated in response to a triggering event.

```
name:       "CampaignShown"
type:       "system"
properties: {
  "source":       "PurchaseCompleted",
  "campaignId":   "c_abc",
  "campaignName": "Welcome offer"
}
```

| Property       | Type   | Description                                                                                                                     |
| -------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `source`       | string | Name of the event that triggered the campaign.                                                                                  |
| `campaignId`   | string | Identifier of the activated campaign — the same value `{@campaign.id}` puts into a campaign link.                               |
| `campaignName` | string | **0.15.0+.** The campaign's name as you set it in the dashboard, so your own analytics can label the campaign without a lookup. |

For a campaign with [Show after](/user-guide/campaigns.md#show-after) set, `CampaignShown` fires when the campaign actually appears, after the pause — never for a show that was skipped during it.

### CustomPropertyChanged

Fires whenever a custom property is set, updated, removed, or cleared — i.e. on every `setCustomProperty(...)`, `removeCustomProperty(...)`, and `clearCustomProperties()`. Because it carries the key and both the previous and new value, you can use it as a campaign **trigger** to react to the moment a trait changes, not just to the trait's current value.

```
name:       "CustomPropertyChanged"
type:       "system"
properties: {
  "key":      "plan_tier",
  "oldValue": "pro",
  "newValue": "free"
}
```

| Property   | Type                | Description                                                                 |
| ---------- | ------------------- | --------------------------------------------------------------------------- |
| `key`      | string              | The custom property that changed.                                           |
| `oldValue` | primitive \| absent | The value before the change. Absent when the property is newly set.         |
| `newValue` | primitive \| absent | The value after the change. Absent when the property is removed or cleared. |

### CampaignResolved

**0.5.0+.** Fires when a campaign's presented action finishes — completed, dismissed, unavailable — or when a campaign that was chosen ends up not showing. It is emitted mainly for Amply's per-campaign stats rather than as a signal your app reacts to, and it is never a frequency-cap input. Your system-events listener receives it, so you can forward it to your own analytics.

```
name:       "CampaignResolved"
type:       "system"
properties: {
  "campaignId":        "c_abc",
  "campaignName":      "Export — rewarded ad",
  "result":            "Unavailable",
  "unavailableReason": "TimedOut"
}
```

| Property            | Type   | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| ------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `campaignId`        | string | Identifier of the campaign whose action resolved — the same value `CampaignShown` reports.                                                                                                                                                                                                                                                                                                                                                                        |
| `campaignName`      | string | **0.15.0+.** The campaign's name as you set it in the dashboard.                                                                                                                                                                                                                                                                                                                                                                                                  |
| `result`            | string | What happened — see the table below.                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `unavailableReason` | string | **0.5.2+.** Present when `result` is `Unavailable`, to say why the gate failed open: `TimedOut` (the deadline elapsed before the action reported), `PresenterFailed` (the action handler threw), or `PresenterReported` (the handler itself reported it couldn't proceed). For a mini-app it is a short code for why it could not show or run — for example `readyTimeout`, `busy` or `notCached`. Also present on `SkippedNoPresenter`, as `PresenterWithdrawn`. |

| `result`                | Meaning                                                                                                                                                                                                     |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Completed`             | The action finished. For a mini-app (sheet, story, fullscreen flow) this means the mini-app's own flow ended — including a flow that closes itself — not necessarily that the user reached its last screen. |
| `Dismissed`             | The user closed the action without completing it.                                                                                                                                                           |
| `Unavailable`           | The action could not run; `unavailableReason` says why.                                                                                                                                                     |
| `SkippedNoPresenter`    | The app had withdrawn the handler for this gated action, so nothing was shown.                                                                                                                              |
| `SkippedNoContinuation` | A gated campaign was reached from a plain `track(...)` call, which has nothing to wait on, so it was not shown.                                                                                             |

[**Show after**](/user-guide/campaigns.md#show-after) **skips (0.15.0+).** None counts as a show:

| `result`                  | Meaning                                                                                                                           |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `SkippedPreempted`        | A campaign created earlier, or a gated action the app was waiting on, took the screen during the pause.                           |
| `SkippedWhileDelayed`     | This campaign triggered while a campaign created earlier was already waiting to appear, so it did not show this time.             |
| `SkippedBackgrounded`     | The app went to the background before the pause ended.                                                                            |
| `SkippedSessionEnded`     | The session ended before the pause ended.                                                                                         |
| `SkippedNoLongerEligible` | When the pause ended, the user no longer matched the audience, reached a show limit, or its content could not be shown right now. |
| `SkippedCampaignChanged`  | The campaign was edited or stopped in the dashboard, and the device received the change, during the pause.                        |

***

## Custom events

Custom events are the events you define to describe what users do in your app.

### Targeting on events

Events aren't only triggers — campaign audience rules can target them directly: how many times an event has happened for a user (or whether it ever has), when they first or last did it, and property filters so that only matching occurrences count ("`Purchase` where `product` equals `pro`"). Requires apps built with SDK 0.6.1 or later; on older builds such campaigns simply never match. See [Targeting and audiences](/concepts/targeting-and-audiences.md#targeting-on-past-behavior) for the full model.

### Naming

* Use a short, stable string. Once an event name is in the wild, changing it breaks every campaign targeting it.
* **Recommended:** `PascalCase` for event names and `snake_case` for property keys. Names are just strings — the SDK accepts any value — but this house convention keeps things readable: events read like action constants (`PurchaseCompleted`, `WorkoutCompleted`), properties read like data fields (`plan_tier`, `source_screen`).
* Describe an action, not a screen. `PurchaseCompleted` is useful; `PurchaseScreen` isn't — you'll want to track both the screen view and the purchase as separate events.
* Reserve the system event names above for the SDK. Don't emit an event called `SessionStarted` yourself.

### Suggested naming patterns

| Pattern             | Example                             | Use for                                     |
| ------------------- | ----------------------------------- | ------------------------------------------- |
| `<Object><Verb>`    | `PaywallViewed`, `TrialStarted`     | Lifecycle or funnel steps.                  |
| `<Feature><Action>` | `ExportClicked`, `FilterApplied`    | Feature-level interactions.                 |
| `<Outcome>`         | `PurchaseCompleted`, `SignupFailed` | Terminal events you'll target campaigns on. |

### Property conventions

* Keys are strings. We recommend `snake_case` for property keys (e.g. `plan_tier`, `source_screen`) so they read differently from the `PascalCase` event-name recommendation.
* Keep the set of keys per event name stable. If `PurchaseCompleted` sometimes has `price` and sometimes doesn't, campaigns that depend on `price` will behave inconsistently.
* Prefer machine-readable values. `"currency": "USD"` beats `"currency": "US Dollars"`.
* Don't pack structured blobs into a single string. Use multiple keys instead.

### Property value types

The SDK accepts primitive values only. What counts as a primitive depends on the platform.

| Platform         | Accepted value types                                  |
| ---------------- | ----------------------------------------------------- |
| iOS (native)     | `String`, `Int`, `Int64`, `Float`, `Double`, `Bool`   |
| Android (native) | `String`, `Int`, `Long`, `Float`, `Double`, `Boolean` |
| React Native     | `string`, `number`, `boolean`                         |

Arrays, nested objects, `null`, and date objects are not supported as property values. Serialize dates to a number (Unix milliseconds) or a string (ISO 8601) before passing them.

### Example

{% tabs %}
{% tab title="iOS" %}

```swift
amply.track(
  event: "PurchaseCompleted",
  properties: [
    "sku": "premium_yearly",
    "price": 59.99,
    "currency": "USD",
    "is_trial": false
  ]
)
```

{% endtab %}

{% tab title="Android" %}

```kotlin
amply.track(
  "PurchaseCompleted",
  mapOf(
    "sku" to "premium_yearly",
    "price" to 59.99,
    "currency" to "USD",
    "is_trial" to false
  )
)
```

{% endtab %}

{% tab title="React Native" %}

```ts
await amply.track({
  name: "PurchaseCompleted",
  properties: {
    sku: "premium_yearly",
    price: 59.99,
    currency: "USD",
    is_trial: false,
  },
});
```

{% endtab %}
{% endtabs %}

***

## Related

* [SDK reference — iOS](/reference/sdk-ios.md) — full API, including `track` and `setSystemEventsListener`.
* [SDK reference — Android](/reference/sdk-android.md) — same for Android.
* [API reference](/reference/api-reference.md) — how events travel from the SDK to the server.
