Skip to main content
Available from Chat SDK v5.0.7. These APIs require cometchat_sdk v5.0.7 or later. See Setup to upgrade.
Pinning highlights an important message in a conversation. A pin is conversation-wide and visible to everyone in that conversation, so it is the right tool for announcements, rules or a link everyone keeps asking for. A pinned message carries a pinnedAt timestamp and the pinnedBy UID of the member who pinned it.
Pinning is a moderation action. In a group, only an Admin or Moderator — a group owner included — may pin or unpin; in a one-on-one conversation both participants can. The server is the authority: a call the user may not make is rejected with ERR_PERMISSION_DENIED.

Pin a Message

Call pinMessage() with the message’s ID. On success, onSuccess receives the full updated message, with pinnedAt and pinnedBy set.
Pinning is idempotent, and a message has a single pinner: re-pinning an already pinned message updates pinnedBy and pinnedAt to the most recent pinner rather than failing.
A just-sent message may not be pinnable yet. On an app with moderation enabled, the server marks a new message as moderation-pending and clears it a moment later. While it is pending, the call is rejected with ERR_MESSAGE_NO_ACCESS — the same code returned for a genuine access refusal, so you cannot tell the two apart from the error alone.The window runs from send, not from the user’s tap, and clears within a few seconds. Do not disable the control on this error; prefer a retry or a transient “not ready yet” message over telling the user they lack access. Moderation is configured per app, so this never reproduces on an app that has it switched off.

Unpin a Message

Call unpinMessage(). Any participant with pin permission can unpin a message — not just the one who pinned it. The returned message comes back with its pin fields cleared to null, so you can swap it straight into your list.

Fetch Pinned Messages

Build a MessagesRequest with the pinned field of the MessagesRequestBuilder set to true. A pinned list belongs to one conversation, so pair it with uid for a one-on-one conversation or guid for a group — exactly one of the two.
The list is ordered by pin time, most recently pinned first — not by when the messages were sent. Render it in the order the SDK returns it. Use fetchPrevious() for this list. fetchNext() needs a message ID or timestamp cursor and fails with ERR_FILTERS_MISSING without one. The setPinned(true) setter is equivalent to the pinned field. A MessagesRequest returns at most 100 messages per call, which also covers the whole list at the default cap of 100 pins per conversation.

Check if a Message is Pinned

Every fetched or received BaseMessage carries its pin state. A null pinnedAt means the message is not pinned.

Real-time Pin Events

Register a MessageListener using addMessageListener() and override the onMessagePinned() and onMessageUnpinned() callbacks. Each callback receives the full updated message, so you can replace the message in your list without a follow-up fetch.
The device that performed the action also receives these callbacks as soon as the call succeeds — the SDK fans them out locally and drops the server’s echo of the same action — so a single code path can update your UI for your own pins and for pins made by other members. Remove the listener with CometChat.removeMessageListener("listenerId") when it is no longer needed.

Pin Limit

A conversation holds a capped number of pins, configurable per app. Read the cap rather than hard-coding it — it is tenant-overridable and will drift.
getPinnedMessagesLimit() is synchronous and returns null when the backend did not serve a limit — show generic copy in that case rather than guessing a number. The server still enforces its own cap, so treat the value as a display hint, not a gate.

Error Handling

pinMessage() and unpinMessage() report failures through onError with a CometChatException. Branch on e.code rather than the message text: On the limit error, the exception’s errorParams map carries the authoritative cap as {"limit": n}, so you can name the exact number without a second call:

Feature Availability

Check whether Pin Message is enabled for your app before showing pin actions. isPinMessageEnabled() is synchronous and safe to call from build() — it never throws.
The flag is served on the logged-in user’s login payload, with the app settings as a fallback. When neither carries it, isPinMessageEnabled() returns true, so the feature is not disabled on a backend that predates the flag — the server still rejects the calls if the feature is genuinely off.

Next Steps

Save A Message

Bookmark a message privately, across conversations

Pin A Conversation

Pin a conversation to the top of the list

All Real Time Listeners

Every listener the SDK exposes, in one place

Additional Message Filtering

Filter messages by pinned, saved, type, tags and more