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

# Pin A Conversation

> Pin and unpin conversations, fetch the pinned conversation list, and listen for conversation pin events with the CometChat JavaScript SDK.

Pinning a conversation keeps it at the top of the logged-in user's conversation list. The pin is **private to that user** — nobody else sees it — and it syncs to their other devices.

<Note>
  This is separate from an **admin-global pin**, which is managed from the
  [CometChat Dashboard](https://app.cometchat.com) and shows for every user. Those
  cannot be created or removed from the SDK, only observed.
</Note>

## Pin a Conversation

A conversation is addressed by its peer — the other user's UID for a one-on-one conversation, or the GUID for a group — together with the conversation type.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    CometChat.pinConversation("cometchat-uid-1", CometChat.RECEIVER_TYPE.USER).then(
      (conversation: CometChat.Conversation) => {
        console.log("Conversation pinned:", conversation);
      },
      (error: CometChat.CometChatException) => {
        console.log("Failed to pin conversation:", error);
      }
    );
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    CometChat.pinConversation("cometchat-guid-1", CometChat.RECEIVER_TYPE.GROUP).then(
      (conversation) => {
        console.log("Conversation pinned:", conversation);
      },
      (error) => {
        console.log("Failed to pin conversation:", error);
      }
    );
    ```
  </Tab>
</Tabs>

It resolves with the full updated `Conversation`, with `pinnedAt` and `pinnedBy` set. Pinning is idempotent.

<Note>
  Addressing by peer rather than by `conversationId` is deliberate: it lets you
  pin a conversation that has no messages yet.
</Note>

## Unpin a Conversation

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    CometChat.unpinConversation(
      "cometchat-uid-1",
      CometChat.RECEIVER_TYPE.USER
    ).then(
      (conversation: CometChat.Conversation) => {
        console.log("Conversation unpinned:", conversation);
      },
      (error: CometChat.CometChatException) => {
        console.log("Failed to unpin conversation:", error);
      }
    );
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    CometChat.unpinConversation(
      "cometchat-uid-1",
      CometChat.RECEIVER_TYPE.USER
    ).then(
      (conversation) => {
        console.log("Conversation unpinned:", conversation);
      },
      (error) => {
        console.log("Failed to unpin conversation:", error);
      }
    );
    ```
  </Tab>
</Tabs>

A user cannot unpin an admin-global pin — that call is rejected with `ERR_ACTION_NOT_ALLOWED`. Hide or disable the unpin control for conversations you know are system-pinned.

## Fetch Pinned Conversations

The default conversation list is already **pin-ordered** by the server: admin-global pins first, then the user's own pins, then everything else. Fetch it as you normally would and no extra filter is needed.

To narrow the list to pins only, use `setPinnedBy()` with the tokens on `CometChat.PINNED_BY`.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    let conversationsRequest: CometChat.ConversationsRequest =
      new CometChat.ConversationsRequestBuilder()
        .setPinnedBy([CometChat.PINNED_BY.SYSTEM, CometChat.PINNED_BY.ME])
        .setLimit(30)
        .build();

    conversationsRequest.fetchNext().then(
      (conversations: CometChat.Conversation[]) => {
        console.log("Pinned conversations:", conversations);
      },
      (error: CometChat.CometChatException) => {
        console.log("Failed to fetch conversations:", error);
      }
    );
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    let conversationsRequest = new CometChat.ConversationsRequestBuilder()
      .setPinnedBy([CometChat.PINNED_BY.ME])
      .setLimit(30)
      .build();

    conversationsRequest.fetchNext().then(
      (conversations) => {
        console.log("Pinned conversations:", conversations);
      },
      (error) => {
        console.log("Failed to fetch conversations:", error);
      }
    );
    ```
  </Tab>
</Tabs>

| Token                        | Meaning                                             |
| ---------------------------- | --------------------------------------------------- |
| `CometChat.PINNED_BY.ME`     | Conversations the logged-in user pinned themselves. |
| `CometChat.PINNED_BY.SYSTEM` | Conversations pinned globally by an admin.          |

Passing an empty array is the same as not calling `setPinnedBy()` at all — you get the default, pin-ordered list.

## Check if a Conversation is Pinned

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    if (conversation.isPinned()) {
      console.log("Pinned at:", conversation.getPinnedAt());
      console.log("Pinned by:", conversation.getPinnedBy());
    }
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    if (conversation.isPinned()) {
      console.log("Pinned at:", conversation.getPinnedAt());
      console.log("Pinned by:", conversation.getPinnedBy());
    }
    ```
  </Tab>
</Tabs>

As with messages, the presence of `pinnedAt` *is* the boolean — an unpinned conversation has no pin attributes at all.

## Real-time Conversation Pin Events

Conversation pins arrive on a dedicated `ConversationListener`, **not** on `MessageListener` — the payload is a `Conversation`, not a message.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    CometChat.addConversationListener(
      "UNIQUE_LISTENER_ID",
      new CometChat.ConversationListener({
        onConversationPinned: (conversation: CometChat.Conversation) => {
          console.log("Conversation pinned:", conversation);
        },
        onConversationUnpinned: (conversation: CometChat.Conversation) => {
          console.log("Conversation unpinned:", conversation);
        },
      })
    );
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    CometChat.addConversationListener(
      "UNIQUE_LISTENER_ID",
      new CometChat.ConversationListener({
        onConversationPinned: (conversation) => {
          console.log("Conversation pinned:", conversation);
        },
        onConversationUnpinned: (conversation) => {
          console.log("Conversation unpinned:", conversation);
        },
      })
    );
    ```
  </Tab>
</Tabs>

These fire both when the logged-in user pins from another device and when an admin pins globally. Remove the listener when you are done:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    CometChat.removeConversationListener("UNIQUE_LISTENER_ID");
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    CometChat.removeConversationListener("UNIQUE_LISTENER_ID");
    ```
  </Tab>
</Tabs>

## Pin Limit

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    let limit: number | null = await CometChat.getPinnedConversationsLimit();
    let systemLimit: number | null =
      await CometChat.getSystemPinnedConversationsLimit();
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    CometChat.getPinnedConversationsLimit().then((limit) => {
      console.log("Pinned conversations limit:", limit);
    });
    ```
  </Tab>
</Tabs>

Both resolve to `null` when the app settings carry no value. `getSystemPinnedConversationsLimit()` is the separate admin/global cap, enforced independently.

## Feature Availability

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    let enabled: boolean = await CometChat.isPinConversationEnabled();

    if (enabled) {
      // Show the Pin Conversation option
    }
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    CometChat.isPinConversationEnabled().then((enabled) => {
      if (enabled) {
        // Show the Pin Conversation option
      }
    });
    ```
  </Tab>
</Tabs>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Retrieve Conversations" icon="comments" href="/docs/sdk/javascript/retrieve-conversations">
    Fetch and order the conversation list
  </Card>

  <Card title="Pin A Message" icon="thumbtack" href="/docs/sdk/javascript/pin-message">
    Highlight a message for everyone in a conversation
  </Card>

  <Card title="Save A Message" icon="bookmark" href="/docs/sdk/javascript/save-message">
    Bookmark a message privately, across conversations
  </Card>

  <Card title="All Real Time Listeners" icon="tower-broadcast" href="/docs/sdk/javascript/all-real-time-listeners">
    Every listener the SDK exposes, in one place
  </Card>
</CardGroup>
