Messages Overview

Last Edit: Nov 27 2020

Below is a detailed example of how to send a message using Stream Chat:

const message = await channel.sendMessage({
    text: '@Josh I told them I was pesca-pescatarian. Which is one who eats solely fish who eat other fish.',
    attachments: [
            type: 'image',
            asset_url: '',
            thumb_url: '',
            myCustomField: 123
    mentioned_users: [],
    anotherCustomField: 234

Message message = new Message();
message.setText("Josh I told them I was pesca-pescatarian. Which is one who eats solely fish who eat other fish.");
message.getExtraData().put("anotherCustomField", 234);

// add an image attachment to the message
Attachment attachment = new Attachment();
attachment.setFallback("test image");
// add some custom data to the attachment
attachment.getExtraData().put("myCustomField", 123);


message.getMentionedUsers().add(new User(userId));

channelController.sendMessage(message).enqueue(result -> Unit.INSTANCE);

let channel = .messaging, id: "general")

// First, let's create an extra data for a channel.
struct Product: Codable {
    let id: String
    let name: String
    let price: Int
struct AttachmentData: Codable {
    let info: String
// Update your extra data types for the decoding.
Message.extraDataType = Product.self
Attachment.extraDataType = AttachmentData.self

// Create extra data for message.
let iPhone = Product(id: "iPhone12,3", name: "iPhone 11 Pro", price: 999)
// Create extra data for the attachment.
let fish = AttachmentData(info: "Just a fish to increase sales.")

// Create attachment.
let attachment = Attachment(type: .image,
                            title: "A fish",
                            imageURL: URL(string: "")!,
                            extraData: fish)
// Create a message with the extra data and attachments.
let message = Message(text: "We have a new iPhone 11 Pro. Do you want to buy it?",
                      attachments: [attachment],
                      mentionedUsers: [User(id: "josh-id", name: "Josh")],
                      extraData: iPhone)
// Send the message
channel.send(message: message) { (result) in
    do {
        let response = try result.get()
    } catch {
        print("Error when sending message: \(error)")

val message = Message()
message.text =
    "Josh I told them I was pesca-pescatarian. Which is one who eats solely fish who eat other fish."
message.extraData["anotherCustomField"] = 234

// add an image attachment to the message
val attachment = Attachment()
attachment.type = "image"
attachment.imageUrl = ""
attachment.fallback = "test image"
// add some custom data to the attachment
attachment.extraData["myCustomField"] = 123


// include the user id of the mentioned user

channel.sendMessage(message).enqueue {
    val message =

final message = Message(
  text: '@Josh I told them I was pesca-pescatarian. Which is one who eats solely fish who eat other fish.',
  attachments: [
      type: "image",
      assetUrl: "",
      thumbUrl: "",
      extraData: {
        "myCustomField": 123,
  mentionedUsers: [
    User(id: "josh")
  extraData: {
    "anotherCustomField": 234,

await channel.sendMessage(message);

// Sending a message from jenny mentioning bob-1

$message = $channel->sendMessage([
	'text' => '@Bob I told them I was pesca-pescatarian. Which is one who eats solely fish who eat other fish.',
	'attachments' =>
				'type' => 'image',
				'asset_url' => '',
				'thumb_url' => '',
				'myCustomField' => 123

	'mentioned_users' => ['bob-1'],
	'anotherCustomField' => 456

message = {
    "text": "@Bob I told them I was pesca-pescatarian. Which is one who eats solely fish who eat other fish.",
    "attachments": [
            "type": "image",
            "asset_url": "",
            "thumb_url": "",
            "myCustomField": 123,
    "mentioned_users": ["bob-1"],
    "anotherCustomField": 456,

channel.send_message(message, user_id)

message := &stream_chat.Message{
	Text: "@Bob I told them I was pesca-pescatarian. Which is one who eats solely fish who eat other fish.",
	Attachments: []*stream_chat.Attachment{
			Type:     "image",
			ThumbURL: "",
			AssetURL: "",
			ExtraData: map[string]interface{}{
				"myCustomField": 123,

	ExtraData: map[string]interface{}{
		"anotherCustomField": 456,

	MentionedUsers: []*stream_chat.User{
		&stream_chat.User{Name: "bob-1"},

filledMessage, err := channel.SendMessage(message, userID)
Note: calling the Send Message endpoint using server-side auth (i.e setting the user in the message object) will also trigger a user update. Please make sure to include all necessary user fields in order not to lose any information.

There are five built-in fields for the message:

Name Type Description Default Optional
text string The text of the chat message (Stream chat supports markdown and automatically enriches URLs).
attachments array A list of attachments (audio, videos, images, and text). Max is 10 attachments per message. Each attachment can have up to 5KB.
user object This value is automatically set in client-side mode. You only need to send this value when using the server-side APIs.
mentioned_users array A list of users mentioned in the message. You send this as a list of user IDs and receive back the full user data.
message custom data object Extra data for the message. Must not exceed 5KB in size.

Note that both the message and the attachments can contain custom fields. By default Stream’s frontend components support the following attachment types:

  • Audio
  • Video
  • Image
  • Text

You can specify different types as long as you implement the frontend rendering logic to handle them. Common use cases include:

  • Embedding products (photos, descriptions, outbound links, etc.)
  • Sharing of a users location

The React tutorial for Stream Chat explains how to customize the Attachment component.

Get a Message

You can get a single message by its ID using the getMessage call:

await client.getMessage(messageID);

channelController.getMessage(messageId).enqueue(result -> {
    Message message =;
    return Unit.INSTANCE;

Client.shared.message(withId: <#String#>) { (result) in
    // handle result

channelController.getMessage(messageId).enqueue {
    val message =

final message = await client.getMessage("message-id");

$message = $client->getMessage('message-id');

response = client.get_message(msg_id)

msg, err := client.GetMessage(msgID)

Update a Message

You can edit a message by calling updateMessage and including a message with an ID – the ID field is required when editing a message:

const message = { id: 123, text: 'the edited version of my text' };
const update = await client.updateMessage(message);

message.setText("my updated text");
channelController.updateMessage(message).enqueue(result -> {
    Message updatedMessage =;
    return Unit.INSTANCE;

let channel = .messaging, id: "general")
let editedMessage = Message(id: <#Message ID to be edited#>, text: "Hello World!")
channel.send(message: editedMessage) { (result) in
    // handle result

// update some field of the message
message.text = "my updated text"
// send the message to the channel
channelController.updateMessage(message).enqueue {
    val message =

await client.updateMessage(Message(id: "123", text: "the edited version of my text"));

$message = [
	'user_id' => 'jenny',
	'id' => 'message-id',
	'text' => 'the edited version of my text'

$update = $client->updateMessage($message);

        "id": msg_id,
        "text": "the edited version of my text",
        "user_id": user_id,

updatedMessage := &stream_chat.Message{
	ID:   msgID,
	Text: "the edited version of my text",
	User: &stream_chat.User{ID: userID},

filledMessage, err := client.UpdateMessage(updatedMessage, msgID)

Delete A Message

You can delete a message by calling deleteMessage  and including a message with an ID. Messages can be soft deleted or hard deleted. Unless specified via the hard parameter, messages are soft deleted.

await client.deleteMessage(messageID);

// hard delete the message (works only server-side)
await client.deleteMessage(messageID, true);


let message = Message(text: "Hello") // or with id
message.delete { (result) in
    // handle result

channelController.deleteMessage(messageId).enqueue { 
    val deletedMessage =

await client.deleteMessage("123");

// soft delete the message

// hard delete the message
$client->deleteMessage('message-id', ['hard' => true]);

# soft delete a message

# hard delete a message
client.delete_message(msg_id, hard=True)

// Soft Delete
err := client.DeleteMessage(msgID)
Example of a soft deleted message in a conversation

Soft delete

  1. Can be done client-side by users

  2. Message is still returned in the message list and all its data is kept as it is

  3. Message type is set to "deleted"

  4. Reactions and replies are kept in place

Hard delete

  1. Can only be done server-side

  2. The message is removed from the channel and its data wiped

  3. All reactions are deleted

  4. All replies and their reactions are deleted

By default messages are soft deleted, this is a great way to keep the channel history consistent.