# 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). |
| `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` are specific to subscriptions. `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 — login, logout, checkout, or session restore. This is the main event for keeping your app in sync with the user's state.

```javascript
tiun.on('userChange', (data) => {
  // data.event — what triggered the change: 'init', 'login', 'checkout', 'logout', 'update'
  // data.isAuthenticated — whether the user has a valid session
  // data.user — { userId, email, productAccess } or null
});
```

{% 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: 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.tiun.io/sdk/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.
