---
title: "Recipe: Subscribe to Pull events"
description: "Open a live channel from a Bitrix24 frame app and react to push events using useB24Helper + the Pull client."
canonical_url: "https://bitrix24.github.io/b24jssdk/docs/examples/pull-subscribe-frame"
last_updated: "2026-06-02"
---
# Recipe: Subscribe to Pull events

> Open a live channel from a Bitrix24 frame app and react to push events using useB24Helper + the Pull client.

> [!WARNING]
> We are still updating this page. Some data may be missing here — we will complete it shortly.

## Goal

A frame app that gets real-time updates from Bitrix24 — for example, the badge counter goes up when a new chat message arrives or when a CRM activity changes hands. The Pull client multiplexes WebSocket and long-polling transports; you only see one callback.

## Stack & Prerequisites

- Vue 3 + Vite (or Nuxt) running inside a Bitrix24 placement
- `@bitrix24/b24jssdk@^1.0.0`
- The portal must have the Pull module enabled (it is on every cloud and most on-premise installations)

```bash
pnpm add @bitrix24/b24jssdk
```

## Full Example

`src/components/PullPanel.vue`:

```vue
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from 'vue'
import {
  initializeB24Frame,
  LoadDataType,
  LoggerFactory,
  useB24Helper,
  type B24Frame,
  type TypePullMessage
} from '@bitrix24/b24jssdk'

const logger = LoggerFactory.createForBrowser('PullPanel', import.meta.env.DEV)
const events = ref<Array<{ at: string, command: string }>>([])
let $b24: B24Frame | null = null

const {
  initB24Helper,
  destroyB24Helper,
  usePullClient,
  useSubscribePullClient,
  startPullClient
} = useB24Helper()

function handleEvent(message: TypePullMessage) {
  logger.info('pull', message)
  events.value.unshift({
    at: new Date().toLocaleTimeString(),
    command: message.command
  })
  // Keep the visible log short.
  if (events.value.length > 20) events.value.length = 20
}

onMounted(async () => {
  $b24 = await initializeB24Frame()

  // App + Profile are required so the helper can register a Pull channel.
  await initB24Helper(
    $b24,
    [LoadDataType.App, LoadDataType.Profile],
    'pull-panel-init'
  )

  usePullClient()
  // Listen to events from the `crm` module. Use `'application'` (default) for
  // your own app's custom events.
  useSubscribePullClient(handleEvent, 'crm')
  startPullClient()
})

onBeforeUnmount(() => {
  destroyB24Helper()
  $b24 = null
})
</script>

<template>
  <section>
    <h2>Live events</h2>
    <ul v-if="events.length">
      <li v-for="(event, index) in events" :key="index">
        <code>{{ event.at }}</code> — {{ event.command }}
      </li>
    </ul>
    <p v-else>Waiting for events…</p>
  </section>
</template>
```

## Run It

Open the app inside Bitrix24, then trigger a CRM event in another tab — drag a deal to a new stage, add an activity, change the responsible person. Within a second the event command (e.g. `crm_deal_update`) appears at the top of the list. The component keeps the last 20 events visible.

## How It Works

- `useB24Helper()` returns a closure over a single `B24HelperManager` instance. It is **not** a Vue composable — it doesn't read Vue lifecycle hooks. You wire it into `onMounted` / `onBeforeUnmount` yourself.
- `initB24Helper($b24, dataTypes, requestId)` loads the data the Pull client needs to register a channel. Without `LoadDataType.App` and `LoadDataType.Profile` the subsequent `usePullClient()` call throws.
- `usePullClient()` instantiates the Pull transport (WebSocket with long-polling fallback). `useSubscribePullClient(handler, moduleId)` registers your callback for that module. `startPullClient()` actually opens the connection — call it last.
- `destroyB24Helper()` tears down the transport and unregisters subscribers. Calling it on unmount prevents the WebSocket from leaking when the app slider closes.

## Limitations

- The order **must** be `initB24Helper` → `usePullClient` → `useSubscribePullClient` → `startPullClient`. Calling `useSubscribePullClient` before `usePullClient` throws `PullClient is not initialized`. The error is intentional — it surfaces wiring mistakes early.
- `useB24Helper()` returns one shared instance per closure. If you create a second `useB24Helper()` and call `initB24Helper` again, the second call re-uses the existing manager rather than building a new one.
- `moduleId` defaults to `'application'`. To listen to portal modules (`'crm'`, `'tasks'`, `'im'`, …) pass them explicitly. Subscribing to multiple modules requires multiple `useSubscribePullClient` calls.
- Pull events are best-effort. They are great for "wake the UI up", not for "this is the source of truth". Always re-fetch via REST when the user takes an action that depends on freshness.

## See also

- [Recipe: Frame app skeleton](https://bitrix24.github.io/b24jssdk/raw/docs/examples/frame-app-skeleton.md) — minimal frame app this builds on top of.
- [`B24Frame`](https://bitrix24.github.io/b24jssdk/raw/docs/working-with-the-rest-api/frame.md) — the iframe entry point.

## Sitemap

See the full [sitemap](/b24jssdk/sitemap.md) for all pages.
