# 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
Message message =
  Message.send(channelType, channelId)
    .message(
      MessageRequestObject.builder()
        .text("Important announcement")
        .pinned(true)
        .pinExpires(
          new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
            .parse("2077-01-01T00:00:00Z"))
        .userId(userId)
        .build())
    .request()
    .getMessage();

// Unpin message
MessageRequestObject messageRequestObject = MessageRequestObject.buildFrom(message);
messageRequestObject.setPinned(false);
Message.update(message.getId()).message(messageRequestObject).request();
```

</codetabs-item>

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

```python
# Create a pinned message
response = channel.send_message({"pinned": True, "text": "Important announcement"}, user["id"])

# Pin message for 120 seconds
client.pin_message(response["message"]["id"], user["id"], 120)

# Unpin message
client.unpin_message(response["message"]["id"], user["id"])
```

</codetabs-item>

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

```php
$response = $channel->sendMessage([
  'text' => 'Important announcement',
  'pinned' => true,
  'pin_expires' => "2077-01-01T00:00:00Z",
], $user["id"]);

// Pin message for 120 seconds
$client->pinMessage($response["message"]["id"], $user["id"], 120);

// Unpin message
$client->unPinMessage($response["message"]["id"], $user["id"]);
```

</codetabs-item>

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

```ruby
response = channel.send_message({pinned: true, text: "Important announcement"}, user["id"])

# Pin message for 120 seconds
client.pin_message(response["message"]["id"], user["id"], 120)

# Unpin message
client.unpin_message(response["message"]["id"], user["id"])
```

</codetabs-item>

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

```go
msg := &Message{Text: "Important announcement", Pinned: true}
messageResp, err := channel.SendMessage(ctx, msg, user.ID)

// Pin message for 120 seconds
exp := time.Now().Add(time.Second * 120)
client.PinMessage(ctx, messageResp.Message.ID, user.ID, &exp)

// Unpin message
client.UnPinMessage(ctx, msg.ID, user.ID)
```

</codetabs-item>

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

```csharp
var msg = new MessageRequest { Text = "Important announcement", Pinned = true };
var response = await messageClient.SendMessageAsync(channel.Type, channel.Id, msg, user.Id);

// Pin message for 120 seconds
await messageClient.PinMessageAsync(response.Message.Id, user.Id, TimeSpan.FromSeconds(120));

// Unpin message
await messageClient.UnpinMessageAsync(response.Message.Id, user.Id);
```

</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
ChannelGetResponse resp = Channel.getOrCreate("type", "id").request();
List<Message> messages = resp.getPinnedMessages();
```

</codetabs-item>

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

```python
channel_state = channel.query(watch=False, state=False, presence=False)
pinned_messages = channel_state["pinned_messages"]
```

</codetabs-item>

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

```php
$response = $channel->query([
  "watch" => false,
  "state" => false,
  "presence" => false
]);
$pinnedMessages = $response["pinned_messages"];
```

</codetabs-item>

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

```ruby
channel_state = channel.query(watch: false, state: false, presence: false)
pinned_messages = channel_state['pinned_messages']
```

</codetabs-item>

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

```go
_, err = channel.Query(ctx, map[string]interface{}{"watch": false, "state": true})
msgs := channel.PinnedMessages
```

</codetabs-item>

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

```csharp
var currentChannelState = await _channelClient.GetOrCreateAsync(_channel.Type, _channel.Id, new ChannelGetRequest
{
  State = true,
  Watch = false,
});

var pinnedMessages = currentChannelState.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
Message.search()
  .filterCondition("pinned", true)
  .request();
```

</codetabs-item>

</codetabs>


---

This page was last updated at 2026-03-05T19:05:18.483Z.

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