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

# Presence

Presence enables you to track which clients are present on a channel, including their state and custom data. Each channel has a presence set containing all currently present members.

## Presence Object

Access presence through a channel's `presence` property:

<Code>
  ```javascript theme={null}
  const channel = realtime.channels.get('chat-room');
  const presence = channel.presence;
  ```

  ```python theme={null}
  channel = realtime.channels.get('chat-room')
  presence = channel.presence
  ```
</Code>

## Properties

<ParamField path="syncComplete" type="boolean">
  Indicates whether the presence set is synchronized with the server. Returns `true` when sync is complete.
</ParamField>

## Methods

### enter

`enter(data?, callback?): Promise<void>`

Enter the presence set with optional data.

<Code>
  ```javascript theme={null}
  // Enter without data
  await presence.enter();

  // Enter with data
  await presence.enter({
    status: 'online',
    name: 'Alice'
  });
  ```

  ```python theme={null}
  # Enter without data
  await presence.enter()

  # Enter with data
  await presence.enter({
      'status': 'online',
      'name': 'Alice'
  })
  ```
</Code>

<ParamField path="data" type="any">
  Optional data to associate with this member. Supported types: string, JSON object, binary data.
</ParamField>

<ParamField path="callback" type="function">
  Optional callback called on completion.
</ParamField>

Requirements:

* Client must have a `clientId` configured
* Client must have presence capability on the channel
* Channel will be implicitly attached if not already

### update

`update(data?, callback?): Promise<void>`

Update the data for this member without leaving and re-entering.

<Code>
  ```javascript theme={null}
  await presence.update({
    status: 'away',
    lastSeen: Date.now()
  });
  ```

  ```python theme={null}
  await presence.update({
      'status': 'away',
      'lastSeen': time.time()
  })
  ```
</Code>

<ParamField path="data" type="any">
  New data to associate with this member.
</ParamField>

### leave

`leave(data?, callback?): Promise<void>`

Leave the presence set.

<Code>
  ```javascript theme={null}
  // Leave without data
  await presence.leave();

  // Leave with data
  await presence.leave({
    reason: 'logged out'
  });
  ```

  ```python theme={null}
  # Leave without data
  await presence.leave()

  # Leave with data
  await presence.leave({
      'reason': 'logged out'
  })
  ```
</Code>

<ParamField path="data" type="any">
  Optional data emitted with the leave event.
</ParamField>

### get

`get(options?): Promise<PresenceMessage[]>`

Get the current presence set.

<Code>
  ```javascript theme={null}
  const members = await presence.get();

  members.forEach(member => {
    console.log(member.clientId, member.data);
  });

  // Filter by clientId or connectionId
  const specificMember = await presence.get({
    clientId: 'user-123'
  });
  ```

  ```python theme={null}
  members = await presence.get()

  for member in members:
      print(member.client_id, member.data)

  # Filter by clientId or connectionId
  specific_member = await presence.get(
      client_id='user-123'
  )
  ```
</Code>

<ParamField query="clientId" type="string">
  Filter by specific client ID.
</ParamField>

<ParamField query="connectionId" type="string">
  Filter by specific connection ID.
</ParamField>

<ParamField query="waitForSync" type="boolean" default="true">
  Wait for presence sync to complete before returning.
</ParamField>

### subscribe

`subscribe(action?, listener): Promise<void>`

Subscribe to presence events.

<Code>
  ```javascript theme={null}
  // Subscribe to all presence events
  await presence.subscribe((presenceMessage) => {
    console.log(
      presenceMessage.action,
      presenceMessage.clientId,
      presenceMessage.data
    );
  });

  // Subscribe to specific action
  await presence.subscribe('enter', (presenceMessage) => {
    console.log(presenceMessage.clientId, 'entered');
  });

  // Subscribe to multiple actions
  await presence.subscribe(['enter', 'leave'], (presenceMessage) => {
    console.log(
      presenceMessage.clientId,
      presenceMessage.action
    );
  });
  ```

  ```python theme={null}
  # Subscribe to all presence events
  async def on_presence(presence_message):
      print(
          presence_message.action,
          presence_message.client_id,
          presence_message.data
      )

  await presence.subscribe(on_presence)

  # Subscribe to specific action
  async def on_enter(presence_message):
      print(presence_message.client_id, 'entered')

  await presence.subscribe('enter', on_enter)
  ```
</Code>

<ParamField path="action" type="string | string[]">
  Optional action(s) to filter events: `enter`, `leave`, `update`, or `present`.
</ParamField>

<ParamField path="listener" type="function" required>
  Callback function called for each presence event.
</ParamField>

### unsubscribe

`unsubscribe(action?, listener?): void`

Unsubscribe from presence events.

<Code>
  ```javascript theme={null}
  // Unsubscribe specific listener
  presence.unsubscribe('enter', myListener);

  // Unsubscribe all listeners for action
  presence.unsubscribe('enter');

  // Unsubscribe all listeners
  presence.unsubscribe();
  ```
</Code>

### history

`history(options?): Promise<PaginatedResult<PresenceMessage>>`

Retrieve presence event history.

<Code>
  ```javascript theme={null}
  const result = await presence.history({
    limit: 50,
    direction: 'backwards'
  });

  result.items.forEach(event => {
    console.log(
      event.action,
      event.clientId,
      event.timestamp
    );
  });
  ```

  ```python theme={null}
  result = await presence.history(
      limit=50,
      direction='backwards'
  )

  for event in result.items:
      print(
          event.action,
          event.client_id,
          event.timestamp
      )
  ```
</Code>

<ParamField query="start" type="integer">
  Start time in milliseconds since epoch.
</ParamField>

<ParamField query="end" type="integer">
  End time in milliseconds since epoch.
</ParamField>

<ParamField query="limit" type="integer" default="100">
  Maximum number of events to return (max 1000).
</ParamField>

<ParamField query="direction" type="string" default="backwards">
  Query direction: `forwards` or `backwards`.
</ParamField>

## PresenceMessage

Presence messages contain:

<ParamField path="action" type="string">
  Presence action: `enter`, `leave`, `update`, or `present`.
</ParamField>

<ParamField path="clientId" type="string">
  Client ID of the member.
</ParamField>

<ParamField path="connectionId" type="string">
  Connection ID of the member.
</ParamField>

<ParamField path="data" type="any">
  Data associated with this member.
</ParamField>

<ParamField path="timestamp" type="integer">
  Timestamp of the presence event.
</ParamField>

<ParamField path="id" type="string">
  Unique event ID.
</ParamField>

## Presence Actions

<ResponseField name="enter" type="action">
  Member entered the presence set.
</ResponseField>

<ResponseField name="leave" type="action">
  Member left the presence set.
</ResponseField>

<ResponseField name="update" type="action">
  Member updated their data.
</ResponseField>

<ResponseField name="present" type="action">
  Member was already present (sent during sync).
</ResponseField>

## Examples

### User Status

Track user online status:

<Code>
  ```javascript theme={null}
  // Enter presence when user logs in
  await presence.enter({
    status: 'online',
    username: 'alice'
  });

  // Update status
  await presence.update({
    status: 'away'
  });

  // Leave when user logs out
  await presence.leave();
  ```
</Code>

### Display Active Users

Show list of active users:

<Code>
  ```javascript theme={null}
  // Get current users
  const members = await presence.get();
  updateUserList(members);

  // Subscribe to changes
  await presence.subscribe((presenceMsg) => {
    if (presenceMsg.action === 'enter') {
      addUserToList(presenceMsg);
    } else if (presenceMsg.action === 'leave') {
      removeUserFromList(presenceMsg);
    }
  });
  ```
</Code>

### Typing Indicators

Implement typing indicators:

<Code>
  ```javascript theme={null}
  let typingTimeout;

  // User starts typing
  input.addEventListener('keydown', () => {
    presence.update({ typing: true });
    
    clearTimeout(typingTimeout);
    typingTimeout = setTimeout(() => {
      presence.update({ typing: false });
    }, 3000);
  });

  // Show who is typing
  await presence.subscribe('update', (msg) => {
    if (msg.data.typing) {
      showTypingIndicator(msg.clientId);
    } else {
      hideTypingIndicator(msg.clientId);
    }
  });
  ```
</Code>

## Related Resources

* [Presence Guide](/docs/presence-occupancy/presence)
* [Channels API](/docs/api/realtime-sdk/channels)
* [Occupancy](/docs/presence-occupancy/occupancy)
* [Identified Clients](/docs/auth/identified-clients)
