> 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. Most are **observable** via the system-events listener — subscribe with `setSystemEventsListener(...)` (iOS/Android) or `addSystemEventListener(...)` (React Native). One (`CampaignResolved`) is recorded for server-side stats only and is never a frequency-cap input; the **Observable** column says which events are delivered to your listener.

| 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`              | 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`                               | 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 (blocking) campaign action resolves or is skipped. **0.5.0+.** Mainly for stats — counted per campaign server-side.       | `campaignId`, `result`, `unavailableReason` (0.5.2+) | stats      |

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

### 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"
}
```

| Property     | Type   | Description                                    |
| ------------ | ------ | ---------------------------------------------- |
| `source`     | string | Name of the event that triggered the campaign. |
| `campaignId` | string | Identifier of the activated campaign.          |

### 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 (blocking) action finishes — completed, dismissed, unavailable, or skipped. It is emitted mainly for server-side observability (counted per campaign) rather than as a signal your app reacts to, and it is never a frequency-cap input.

```
name:       "CampaignResolved"
type:       "system"
properties: {
  "campaignId":        "c_abc",
  "result":            "Unavailable",
  "unavailableReason": "TimedOut"
}
```

| Property            | Type   | Description                                                                                                                                                                                                                                                                     |
| ------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `campaignId`        | string | Identifier of the campaign whose action resolved.                                                                                                                                                                                                                               |
| `result`            | string | One of `Completed`, `Dismissed`, `Unavailable`, `SkippedNoPresenter`, `SkippedNoContinuation`.                                                                                                                                                                                  |
| `unavailableReason` | string | **0.5.2+.** Present only 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). |

***

## Custom events

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

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


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.amply.tools/reference/events.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
