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

# Events

## Usage

Subscribe to events with `tiun.on()` to react when something happens in the SDK.

```javascript
tiun.on('ready', () => {
  console.log('tiun is ready');
});

tiun.on('userChange', (data) => {
  console.log('user state changed:', data.event);
});
```

***

## Event reference

| Event         | Payload                                                               | Description                                                 |
| ------------- | --------------------------------------------------------------------- | ----------------------------------------------------------- |
| `ready`       | —                                                                     | Snippet has initialized and is ready to use.                |
| `userChange`  | `{ event: string, isAuthenticated: boolean, user: UserInfo \| null }` | User state changed (init, login, checkout, logout, update). |
| `login`       | `{ user: UserInfo }`                                                  | User has logged in.                                         |
| `logout`      | —                                                                     | User has logged out.                                        |
| `paywallShow` | `{ isConnected: boolean }`                                            | Paywall should be shown (user doesn't have access).         |
| `paywallHide` | `{ sessionId: string, isConnected: boolean }`                         | User has access, hide the paywall.                          |
| `error`       | `{ code: string, message: string, details?: any }`                    | An error occurred.                                          |

{% hint style="info" %}
`userChange`, `login`, and `logout` apply to checkout-based products — subscriptions and one-time purchases. `paywallShow` and `paywallHide` are specific to time-based billing.
{% endhint %}

### `ready`

Fires once when the snippet has fully loaded and is ready to use. Use this to delay any SDK calls that depend on the snippet being available.

```javascript
tiun.on('ready', () => {
  // Safe to call checkout, login, start, etc.
});
```

### `userChange`

Fires on every user state change — session restore, login, checkout, logout, and any other change to the user. This is the main event for keeping your app in sync with the user's state.

Use `userChange` as the only listener, and call [`tiun.getUser()`](/sdk/web-sdk/core-methods.md) each time it fires. The user object carries both auth state and `productAccess`.

```javascript
tiun.on('userChange', () => {
  const { isAuthenticated, user } = tiun.getUser();
  // isAuthenticated — whether the user has a valid session
  // user — { userId, email, productAccess } or null
});
```

| `data.event` | When it fires                               |
| ------------ | ------------------------------------------- |
| `'init'`     | Session restored once the snippet is ready. |
| `'login'`    | The user signed in.                         |
| `'checkout'` | Checkout completed.                         |
| `'logout'`   | The session was cleared. `user` is `null`.  |
| `'update'`   | The user's ID or email changed.             |

{% hint style="info" %}
**`userChange` fires once with `event: 'init'` after the snippet is ready.** As long as your handler is registered **before** (or synchronously after) `tiun.init`, you'll receive the initial state automatically — no need to manually read `tiun.user` from a `waitForReady()` callback.

```javascript
// Correct — initial state arrives via the listener
tiun.on('userChange', syncStateFromTiun);
tiun.init({ snippetId });
```

{% endhint %}

### `login`

Fires when the user has successfully logged in.

```javascript
tiun.on('login', (data) => {
  // data.user — the authenticated user's info
});
```

### `logout`

Fires when the user session has been cleared.

```javascript
tiun.on('logout', () => {
  // User is logged out
});
```

### `paywallShow`

Fires when the user does not have access and the paywall should be displayed. Use this to show your paywall UI or gate content.

```javascript
tiun.on('paywallShow', (data) => {
  // data.isConnected — whether the user has a payment method connected
  showPaywall();
});
```

### `paywallHide`

Fires when the user has access and the paywall should be hidden. Use this to reveal content.

```javascript
tiun.on('paywallHide', (data) => {
  // data.sessionId — the active session ID
  // data.isConnected — whether the user has a payment method connected
  hidePaywall();
});
```

### `error`

Fires when an error occurs in the SDK.

```javascript
tiun.on('error', (err) => {
  // err.code — error code
  // err.message — human-readable message
  // err.details — optional additional info
});
```

{% hint style="info" %}
**`err.code` is a string, but there is no published enum of values.** Codes can change between SDK versions — display `err.message` to users and log `err.code` for support, rather than branching application logic off specific codes.
{% endhint %}

***

## Unsubscribing

`tiun.on()` returns an unsubscribe function. Call it if you need to remove a specific listener — for example, inside a component that mounts and unmounts.

```javascript
const unsubscribe = tiun.on('userChange', handler);

// Later, to stop listening:
unsubscribe();
```

For most integrations where listeners are set up once at app startup, you don't need to unsubscribe manually — `tiun.destroy()` cleans up everything.

***

## Listening once

Use `tiun.once()` to listen for an event a single time. The listener is automatically removed after it fires.

```javascript
tiun.once('ready', () => {
  console.log('First ready event');
});
```


---

# 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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.tiun.io/sdk/web-sdk/events.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
