# Pinned Messages

Pinned messages highlight important content in a channel. Use them for announcements, key information, or temporarily promoted content. Each channel can have multiple pinned messages, with optional expiration times.

## Pinning and Unpinning Messages

Pin an existing message using `pinMessage`, or create a pinned message by setting `pinned: true` when sending.

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
// Create a pinned message
const { message } = await channel.sendMessage({
  text: "Important announcement",
  pinned: true,
  pin_expires: "2077-01-01T00:00:00Z",
});

// Pin an existing message for 120 seconds
await client.pinMessage(message, 120);

// Pin with a specific expiration date
await client.pinMessage(message, "2077-01-01T00:00:00Z");

// Pin indefinitely (remove expiration)
await client.pinMessage(message, null);

// Unpin a message
await client.unpinMessage(message);
```

</codetabs-item>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
// Create a pinned message
val pinExpirationDate = Calendar.getInstance().apply { set(2077, 1, 1) }.time
val message = Message(
  text = "Important announcement",
  pinned = true,
  pinExpires = pinExpirationDate
)

channel.sendMessage(message).enqueue { /* ... */ }

// Pin message for 120 seconds
channel.pinMessage(message, timeout = 120).enqueue { /* ... */ }

// Pin with a specific expiration date
channel.pinMessage(message, expirationDate = pinExpirationDate).enqueue { /* ... */ }

// Pin indefinitely (remove expiration)
channel.pinMessage(message, expirationDate = null).enqueue { /* ... */ }

// Unpin message
channel.unpinMessage(message).enqueue { /* ... */ }
```

</codetabs-item>

<codetabs-item value="swift" label="Swift">

```swift
let messageController = client.messageController(
  cid: .init(type: .messaging, id: "general"),
  messageId: "message-id"
)

// Pin message for 120 seconds
messageController.pin(.expirationTime(120))

// Pin to a specific date
messageController.pin(.expirationDate(Date()))

// Unpin message
messageController.unpin()
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
// Create a pinned message
final message = await channel
  .sendMessage(Message(
   text: 'Important announcement',
   pinned: true,
   pinExpires: DateTime.now().add(Duration(days: 3)),
  ))
  .then((resp) => resp.message);

// Pin message for 120 seconds
await channel.pinMessage(message, 120);

// Pin with a specific expiration date
await channel.pinMessage(message, DateTime(2077));

// Pin indefinitely (remove expiration)
await channel.pinMessage(message, null);

// Unpin message
await channel.unpinMessage(message);
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
// Android SDK
Calendar calendar = Calendar.getInstance();
calendar.set(2077, 1, 1);
Date pinExpirationDate = calendar.getTime();

Message message = new Message();
message.setText("Important announcement");
message.setPinned(true);
message.setPinExpires(pinExpirationDate);

channelClient.sendMessage(message).enqueue(result -> { /* ... */ });

// Pin message for 120 seconds
channelClient.pinMessage(message, 120).enqueue(result -> { /* ... */ });

// Pin with expiration date
channelClient.pinMessage(message, pinExpirationDate).enqueue(result -> { /* ... */ });

// Pin indefinitely
channelClient.pinMessage(message, null).enqueue(result -> { /* ... */ });

// Unpin message
channelClient.unpinMessage(message).enqueue(result -> { /* ... */ });

// Backend SDK
// Create a pinned message
chat.sendMessage(channelType, channelId, SendMessageRequest.builder()
    .message(MessageRequest.builder()
        .text("Important announcement")
        .pinned(true)
        .pinExpires(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").parse("2077-01-01T00:00:00Z"))
        .userID(userId)
        .build())
    .build()).execute();

// Pin an existing message
chat.updateMessage(messageId, UpdateMessageRequest.builder()
    .message(MessageRequest.builder()
        .pinned(true)
        .userID(userId)
        .build())
    .build()).execute();

// Unpin message
chat.updateMessage(messageId, UpdateMessageRequest.builder()
    .message(MessageRequest.builder()
        .pinned(false)
        .userID(userId)
        .build())
    .build()).execute();
```

</codetabs-item>

<codetabs-item value="python" label="Python">

```python
from getstream.models import MessageRequest

# Create a pinned message
response = channel.send_message(
    MessageRequest(pinned=True, text="Important announcement", user_id=user["id"])
)

# Pin message for 120 seconds
client.chat.update_message_partial(
    id=response.data.message.id,
    set={"pinned": True, "pin_expires": "2077-01-01T00:00:00Z"},
    user_id=user["id"],
)

# Unpin message
client.chat.update_message_partial(
    id=response.data.message.id,
    set={"pinned": False},
    user_id=user["id"],
)
```

</codetabs-item>

<codetabs-item value="php" label="PHP">

```php
// Create a pinned message
$response = $client->sendMessage("messaging", "general", new Models\SendMessageRequest(
    message: new Models\MessageRequest(
        text: "Important announcement",
        pinned: true,
        pinExpires: new \DateTime("2077-01-01T00:00:00Z"),
        userID: $userId,
    ),
));

// Pin an existing message for 120 seconds
$client->updateMessagePartial("message-id", new Models\UpdateMessagePartialRequest(
    set: (object)["pinned" => true, "pin_expires" => (new \DateTime("+120 seconds"))->format(\DateTime::ATOM)],
    userID: $userId,
));

// Unpin message
$client->updateMessagePartial("message-id", new Models\UpdateMessagePartialRequest(
    set: (object)["pinned" => false],
    userID: $userId,
));
```

</codetabs-item>

<codetabs-item value="ruby" label="Ruby">

```ruby
require 'getstream_ruby'
Models = GetStream::Generated::Models

# Create a pinned message
response = client.chat.send_message('messaging', channel_id, Models::SendMessageRequest.new(
  message: Models::MessageRequest.new(
    text: 'Important announcement',
    pinned: true,
    user_id: user_id
  )
))

# Pin an existing message using partial update
client.chat.update_message_partial(response.message.to_h['id'], Models::UpdateMessagePartialRequest.new(
  set: { 'pinned' => true, 'pin_expires' => '2077-01-01T00:00:00Z' },
  user_id: user_id
))

# Unpin message
client.chat.update_message_partial(response.message.to_h['id'], Models::UpdateMessagePartialRequest.new(
  set: { 'pinned' => false },
  user_id: user_id
))
```

</codetabs-item>

<codetabs-item value="go" label="Go">

```go
// Create a pinned message
messageResp, err := channel.SendMessage(ctx, &getstream.SendMessageRequest{
  Message: getstream.MessageRequest{
    Text:   getstream.PtrTo("Important announcement"),
    Pinned: getstream.PtrTo(true),
    UserID: getstream.PtrTo(userID),
  },
})

// Pin an existing message using partial update
client.Chat().UpdateMessagePartial(ctx, msgID, &getstream.UpdateMessagePartialRequest{
  Set:    map[string]any{"pinned": true, "pin_expires": "2077-01-01T00:00:00Z"},
  UserID: getstream.PtrTo(userID),
})

// Unpin message
client.Chat().UpdateMessagePartial(ctx, msgID, &getstream.UpdateMessagePartialRequest{
  Set:    map[string]any{"pinned": false},
  UserID: getstream.PtrTo(userID),
})
```

</codetabs-item>

<codetabs-item value="csharp" label="C#">

```csharp
// Create a pinned message
var resp = await chat.SendMessageAsync("messaging", channelId,
    new SendMessageRequest
    {
        Message = new MessageRequest
        {
            Text = "Important announcement",
            UserID = userId,
            Pinned = true
        }
    });

// Pin an existing message
await chat.UpdateMessagePartialAsync(messageId,
    new UpdateMessagePartialRequest
    {
        Set = new Dictionary<string, object>
        {
            ["pinned"] = true,
            ["pin_expires"] = "2077-01-01T00:00:00Z"
        },
        UserID = userId
    });

// Unpin message
await chat.UpdateMessagePartialAsync(messageId,
    new UpdateMessagePartialRequest
    {
        Set = new Dictionary<string, object> { ["pinned"] = false },
        UserID = userId
    });
```

</codetabs-item>

<codetabs-item value="unity" label="Unity">

```csharp
// Pin until unpinned
await message.PinAsync();

// Pin for 7 days
await message.PinAsync(new DateTime().AddDays(7));

// Unpin previously pinned message
await message.UnpinAsync();
```

</codetabs-item>

</codetabs>

### Pin Parameters

| Name        | Type    | Description                                                            | Default | Optional |
| ----------- | ------- | ---------------------------------------------------------------------- | ------- | -------- |
| pinned      | boolean | Whether the message is pinned                                          | false   | ✓        |
| pinned_at   | string  | Timestamp when the message was pinned                                  | -       | ✓        |
| pin_expires | string  | Timestamp when the pin expires. Null means the message does not expire | null    | ✓        |
| pinned_by   | object  | The user who pinned the message                                        | -       | ✓        |

<admonition type="info">

Pinning a message requires the `PinMessage` permission. See [Permission Resources](/chat/docs/<framework>/permissions_reference/) and [Default Permissions](/chat/docs/<framework>/chat_permission_policies/) for details.

</admonition>

## Retrieving Pinned Messages

Query a channel to retrieve the 10 most recent pinned messages from `pinned_messages`.

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
const channelState = await client.channel("messaging", channelId).query();
const pinnedMessages = channelState.pinned_messages;
```

</codetabs-item>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
channelClient.query(QueryChannelRequest()).enqueue { result ->
  if (result is Result.Success) {
    val pinnedMessages: List<Message> = result.value.pinnedMessages
  } else {
    // Handle Result.Failure
  }
}
```

</codetabs-item>

<codetabs-item value="swift" label="Swift">

```swift
let channelController = client.channelController(for: .init(type: .messaging, id: "general"))

channelController.synchronize() { error in
  if error == nil {
    channelController.channel?.pinnedMessages
  }
}
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
final channelState = await channel.query();
final pinnedMessages = channelState.pinnedMessages;
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
// Android SDK
channelClient.query(new QueryChannelRequest()).enqueue(result -> {
  if (result.isSuccess()) {
    List<Message> pinnedMessages = result.data().getPinnedMessages();
  } else {
    // Handle result.error()
  }
});

// Backend SDK
var resp = chat.getOrCreateChannel("type", "id",
    GetOrCreateChannelRequest.builder().state(true).build()).execute();
var messages = resp.getData().getPinnedMessages();
```

</codetabs-item>

<codetabs-item value="python" label="Python">

```python
response = channel.get_or_create(state=True)
pinned_messages = response.data.pinned_messages
```

</codetabs-item>

<codetabs-item value="php" label="PHP">

```php
$response = $client->getOrCreateChannel("messaging", "general", new Models\ChannelGetOrCreateRequest(
    state: true,
));
$pinnedMessages = $response->getData()->pinnedMessages;
```

</codetabs-item>

<codetabs-item value="ruby" label="Ruby">

```ruby
require 'getstream_ruby'
Models = GetStream::Generated::Models

response = client.chat.get_or_create_channel('messaging', channel_id, Models::ChannelGetOrCreateRequest.new(
  state: true
))
pinned_messages = response.pinned_messages
```

</codetabs-item>

<codetabs-item value="go" label="Go">

```go
resp, err := channel.GetOrCreate(ctx, &getstream.GetOrCreateChannelRequest{
  State: getstream.PtrTo(true),
})
pinnedMessages := resp.Data.PinnedMessages
```

</codetabs-item>

<codetabs-item value="csharp" label="C#">

```csharp
var resp = await chat.GetOrCreateChannelAsync("messaging", channelId,
    new ChannelGetOrCreateRequest
    {
        State = true
    });

var pinnedMessages = resp.Data.PinnedMessages;
```

</codetabs-item>

</codetabs>

## Paginating Pinned Messages

Use the dedicated pinned messages endpoint to retrieve all pinned messages with pagination.

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
// First page, newest first
const page1 = await channel.getPinnedMessages({ limit: 10 }, { pinned_at: -1 });

// Next page
const lastMsg = page1.messages[page1.messages.length - 1];
const page2 = await channel.getPinnedMessages(
  { limit: 10, id_lt: lastMsg.id },
  { pinned_at: -1 },
);

// Oldest first
const ascPage = await channel.getPinnedMessages({ limit: 10 });
const ascLastMsg = ascPage.messages[ascPage.messages.length - 1];
const ascPage2 = await channel.getPinnedMessages({
  limit: 10,
  id_gt: ascLastMsg.id,
});
```

</codetabs-item>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
// First page, newest first
channelClient.getPinnedMessages(
  limit = 10,
  sort = QuerySortByField.descByName("pinnedAt"),
  pagination = PinnedMessagesPagination.BeforeDate(
    date = Date(),
    inclusive = false,
  ),
).enqueue { result ->
  if (result is Result.Success) {
    val pinnedMessages: List<Message> = result.value
  } else {
    // Handle Result.Failure
  }
}

// Next page using pinnedAt from previous response
val nextDate = Date()
channelClient.getPinnedMessages(
  limit = 10,
  sort = QuerySortByField.descByName("pinnedAt"),
  pagination = PinnedMessagesPagination.BeforeDate(
    date = nextDate,
    inclusive = false,
  ),
).enqueue { /* ... */ }
```

</codetabs-item>

<codetabs-item value="swift" label="Swift">

```swift
channelController.loadPinnedMessages(
  pageSize: 10,
  sorting: [.init(key: .pinnedAt, isAscending: false)],
  pagination: .earlier(Date(), inclusive: true)) { result in
    switch result {
    case .success(let pinnedMessages):
      print(pinnedMessages)
    case .failure(let failure):
      // Handle error
    }
  }

// Next page using message ID
let fetchedPinnedMessageId = ...
channelController.loadPinnedMessages(
  pagination: .before(fetchedPinnedMessageId, inclusive: false)) { result in
    // Handle result
  }
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
// Android SDK
channelClient.getPinnedMessages(
  10,
  QuerySortByField.descByName("pinnedAt"),
  new PinnedMessagesPagination.BeforeDate(new Date(), false)
).enqueue(result -> {
  if (result.isSuccess()) {
    List<Message> pinnedMessages = result.data();
  } else {
    // Handle result.error()
  }
});

// Next page
Date nextDate = new Date();
channelClient.getPinnedMessages(
  10,
  QuerySortByField.descByName("pinnedAt"),
  new PinnedMessagesPagination.BeforeDate(nextDate, false)
).enqueue(result -> { /* ... */ });

// Backend SDK
chat.search(SearchRequest.builder()
    .Payload(SearchPayload.builder()
        .filterConditions(Map.of("cid", "messaging:general"))
        .messageFilterConditions(Map.of("pinned", true))
        .build())
    .build()).execute();
```

</codetabs-item>

</codetabs>


---

This page was last updated at 2026-03-12T14:13:49.812Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/android/pinned_messages/](https://getstream.io/chat/docs/android/pinned_messages/).