> ## Documentation Index
> Fetch the complete documentation index at: https://docs.almedia.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# `AlmediaLinkSDK` — Events

> The AlmediaLink API — releases before SDK 1.3.0. Synced from the source repository.

All events are static. Subscribe before calling `Initialize` if you want to observe every transition. Components that mount later can recover the current state by reading `CurrentStatus` and then subscribing to `OnStatusChanged` for further transitions.

All events fire on the **Unity main thread**, so handlers can touch any Unity API (UI, `GetComponent`, `transform`, scene loading) directly. See [Threading](https://docs.almedia.dev/technical/integration-guide/threading.md) in the integration guide for the underlying mechanism.

### `static event Action<AlmediaStatus> OnStatusChanged`

Fires on every status transition. The first emission carries the first terminal `AlmediaStatus` (any of `Eligible`, `Linked`, `NotAvailable`, `Blocked`, `Disabled`); subsequent emissions reflect later transitions. Use it both for one-shot setup that should happen once the SDK has resolved (gate on `status != NotInitialized` and your own guard flag) and for live UI that mirrors the current state. The bundled `LinkButtonController` subscribes to this event and to `OnScreenAvailabilityChanged`, and shows or hides itself from both. `StartLinking`, `FetchNotifications`, and `StartNotificationPolling` called before this first emission are safe no-ops (a warning is logged), so a mistimed call is harmless rather than a crash.

Can fire more than once for `NotAvailable` when `NotAvailableReason` changes. It does not re-fire for a screen-availability-only change - that is what `OnScreenAvailabilityChanged` is for.

### `static event Action<AlmediaScreenAvailability> OnScreenAvailabilityChanged`

Fires when `ScreenAvailability` changes, carrying the new snapshot. This is the signal that a linked player has gained or lost the ability to use `ShowRewardHub()` or `ShowOffer()`.

```csharp theme={"theme":{"light":"github-light","dark":"night-owl"}}
AlmediaLinkSDK.OnScreenAvailabilityChanged += availability =>
    offerButton.SetActive(availability.CanShowOffer);
```

**Ordering.** One native update produces at most two events. `CurrentStatus`, `NotAvailableReason`, and `ScreenAvailability` are all current **before either** fires, so a handler for one may read the others and see a consistent picture. When both fire, `OnStatusChanged` comes first.

| What changed in the native update | `OnStatusChanged` | `OnScreenAvailabilityChanged` |
| - | - | - |
| Status (with or without availability) | fires | fires if availability also changed |
| Reason only, status still `NotAvailable` | fires | - |
| Availability only | - | fires |
| Nothing (identical payload) | - | - |

A handler that throws is logged through `OnLog` and does not prevent the other event from firing.

### `static event Action<string> OnLinkCompleted`

Fires when the player completes account linking flow. The argument is an ISO-8601 timestamp string from the backend, e.g. `"2026-05-13T14:23:01Z"`.

Does **not** fire for players who were already linked at `Initialize` time - they receive `AlmediaStatus.Linked` via `OnStatusChanged` instead. Use this event when you need to distinguish a fresh link from an already-linked session (e.g. celebration UX, one-time analytics).

### `static event Action<List<AlmediaNotification>> OnNotificationsReceived`

Fires when a polling tick or `FetchNotifications()` returns one or more notifications. Empty results do not fire the event. Notifications are delivered in arrival order (oldest first).

### `static event Action<AlmediaInGameRewardGrant> OnInGameRewardGrantRequested`

Fires when the backend instructs the game to grant in-game rewards. Credit the player and celebrate here.

One event per grant. A grant is a bundle of one or more rewards granted together ([`AlmediaInGameRewardGrant.Rewards`](https://docs.almedia.dev/technical/api-reference/almedialink/models#almediaingamerewardgrant)): "250 gems and 3 spins for finishing the chapter" arrives as one event with one id and deserves one celebration. Three separate grants arriving in the same polling tick raise three events.

**Delivery is at-least-once.** The same grant can arrive more than once. The server-to-server reward postback remains the authoritative record of what was granted; treat this event as the real-time convenience, not the ledger. Each grant has a unique [`Id`](https://docs.almedia.dev/technical/api-reference/almedialink/models#almediaingamerewardgrant) that a redelivery repeats, so deduplicate on it when a repeat credit matters to your economy. A short memory of recent ids suffices:

```csharp theme={"theme":{"light":"github-light","dark":"night-owl"}}
private readonly HashSet<string> _credited = new HashSet<string>();

AlmediaLinkSDK.OnInGameRewardGrantRequested += grant =>
{
    if (!_credited.Add(grant.Id)) return;   // redelivered; already credited
    foreach (var reward in grant.Rewards)
        Wallet.Credit(reward.Code, reward.Amount);
};
```

### `static event Action<AlmediaProgress> OnProgressUpdated`

Fires when [`Progress`](https://docs.almedia.dev/technical/api-reference/almedialink/almedialinksdk-properties#static-almediaprogress-progress--get-) changes. The argument is the new snapshot, or `null` when the native SDK cleared the snapshot with its message stream token. The accessor already holds the new value. A handler can read the argument or the accessor. Hide the progress UI on `null`.

The SDK applies only a strictly newer snapshot. The new snapshot replaces the previous one completely. This includes [`Username`](https://docs.almedia.dev/technical/api-reference/almedialink/models#almediaprogress): a newer snapshot without a username clears the previous username.

### `static event Action<AlmediaTaskCompletion> OnTaskCompleted`

Fires when the server reports a completed task. The argument carries the message id and the completion, with the reward the completion paid. See [`AlmediaTaskCompletion`](https://docs.almedia.dev/technical/api-reference/almedialink/models#almediataskcompletion).

The event is historical and best-effort. The task is not always in the current snapshot. The event does not update the snapshot. The same completion can arrive more than once, also after the SDK resets its stream token. The server never reuses an `Id`. Deduplicate on it:

```csharp theme={"theme":{"light":"github-light","dark":"night-owl"}}
private readonly HashSet<string> _seen = new HashSet<string>();

AlmediaLinkSDK.OnTaskCompleted += completion =>
{
    if (!_seen.Add(completion.Id)) return;   // replayed; already celebrated
    Celebrate(completion.Task.Task.Title, completion.Task.ActualReward);
};
```

### `static event Action<AlmediaBalanceChange> OnBalanceChanged`

Fires when the server reports a balance change. The argument carries the balance after the change and the signed change. See [`AlmediaBalanceChange`](https://docs.almedia.dev/technical/api-reference/almedialink/models#almediabalancechange). The change is negative for a reversal.

The event is historical and best-effort, like `OnTaskCompleted`. The balance can differ from the current snapshot. The event does not update the snapshot. A replay carries the same `Id`.

### `static event Action<AlmediaError> OnErrorOccurred`

Fires for both synchronous (local validation) and asynchronous (native, network) errors. See [`AlmediaErrorCode`](https://docs.almedia.dev/technical/api-reference/almedialink/models#almediaerrorcode) for the full set.

Connectivity loss in work the SDK runs on its own - notification polling, event delivery, status refreshes, a `FetchNotifications` that finds no network - does not fire it. Those are logged at `Warning` through [`OnLog`](https://docs.almedia.dev/technical/api-reference/almedialink/almedialinksdk-events#static-event-action%3Calmedialoglevel-string%3E-onlog) and retried by the SDK. A 4xx response, an undecodable response, or a failure of work the game asked for still does.

### `static event Action<AlmediaScreen> OnScreenPresented`

Fires when an SDK screen ([`AlmediaScreen`](https://docs.almedia.dev/technical/api-reference/almedialink/models#almediascreen): the linking webview, the reward hub, or an offer) is now on top of the game - pause gameplay here. It fires at the moment the native container commits to presenting, **before** the page loads, so a slow network cannot delay the pause.

The event fires for every trigger that actually presents a screen: the API methods (`StartLinking`, `ShowRewardHub`, `ShowOffer`), the `LinkPopup`'s CTA after the `LinkButton` prefab or `ShowLink()` opened it, and `Engage()` routing. The popup itself is not an SDK screen and fires neither event.

**Matched-pair guarantee.** Every screen that appears produces exactly one `OnScreenPresented` and, later, exactly one matching [`OnScreenDismissed`](https://docs.almedia.dev/technical/api-reference/almedialink/almedialinksdk-events#static-event-action%3Calmediascreen-inappscreenresult%3E-onscreendismissed):

| Scenario | `OnScreenPresented` | `OnScreenDismissed` |
| - | - | - |
| A screen actually appears | exactly one | exactly one (later, matching) |
| Call opens nothing (wrong state, missing URL, a screen already open, `Engage()` no-op) | never | never |
| System-browser linking (web-browser strategy) | never | never - only the link callbacks fire |

### `static event Action<AlmediaScreen, InAppScreenResult> OnScreenDismissed`

Fires when the screen reported by [`OnScreenPresented`](https://docs.almedia.dev/technical/api-reference/almedialink/almedialinksdk-events#static-event-action%3Calmediascreen%3E-onscreenpresented) is gone - resume gameplay here. Exactly one per `OnScreenPresented` (see the matched-pair table above). The [`InAppScreenResult`](https://docs.almedia.dev/technical/api-reference/almedialink/models#inappscreenresult) argument reports how the screen was dismissed - `Completed` (closed by the web client), `Cancelled` (user closed it), or `Failed` (it could not load, with the detail in `Error`).

Native completes its sync-on-close **before** this fires, so state read in the handler (e.g. `CurrentStatus`) already reflects anything that happened inside the screen. For webview linking the pair fires in addition to the link callbacks, and `OnScreenDismissed` fires before the outcome callbacks (`OnLinkCompleted` / `OnErrorOccurred`).

### `static event Action<AlmediaLogLevel, string> OnLog`

Fires for every log line emitted by the SDK, including forwarded logs from the native plugins. No subscribers means no console output - the SDK does not call `UnityEngine.Debug` directly. Wire this into your own logging pipeline as needed.

***


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