---
url: https://talkjs.com/docs/REST_API/Messages/
title: "Messages | REST API | TalkJS Docs"
---

For AI agents: Find a documentation index at https://talkjs.com/llms.txt (full content at https://talkjs.com/llms-full.txt). Get a markdown version of any page by appending .md to its URL path.

# Messages

Send, edit, and react to messages in a conversation.

Messages are a sub-resource of conversations.

GETPOST/v1/{appId}/conversations/{conversationId}/messages

## Send a message

There are currently two types of messages you can send to a conversation. You can either send messages to a conversation on behalf of one user or on behalf of your app. Users in the conversation must have had their [data synchronized](https://talkjs.com/docs/REST_API/Users/#creating-or-updating-a-user) first.

The request payload of this endpoint must always be an array, even if you send only one message. This way it's possible for you to send a batch of consecutive messages in a single request. It's also possible to combine user messages and system messages in one request. The maximum amount of messages sent in one request is 10.

This endpoint returns an array of objects, one for each message, that contain the ids of the messages.

```json
[
  {"id": "msg_abcdefghijklm"},
  {"id": "msg_nopqrstuvwxyz"},
  ...
]
```

### Send a message on behalf of a user

You can programmatically send messages on behalf of a user in a conversation. The behavior is identical to that when sending messages via the UI; notifications are triggered based on the participating user's role.

POST/v1/{appId}/conversations/{conversationId}/messages

Payload StructureExample Payload

```typescript
[
  {
    // Must specify either `text` or `content`, not both
    text: string,
    content: [SendContentBlock],

    sender: string,
    type: "UserMessage",
    referencedMessageId?: string,
    custom?: Record<string, string>,
    idempotencyKey?: string
  },
  ...
]
```

`sender` has to be a participant in the conversation. `type` must be `"UserMessage"` when `sender` is provided or the request fails.

The `referencedMessageId` property is used when the message you are sending is meant to be a reply to another message. You cannot reference a message from a different conversation.

To learn more about sending messages with `content`, see the [Message content documentation](https://talkjs.com/docs/Concepts/Message_Content/#sending-message-content) or the [SendContentBlock reference](https://talkjs.com/docs/JavaScript_Data_API/Message_Content/#SendContentBlock).

### System messages

System messages are messages which are not sent by a participant, but programmatically by your application. A `SystemMessage` is displayed differently in the UI, and doesn't trigger notifications for any user in the conversation. You are expected to handle these notifications in your application if needed. These messages support attachments but don't currently support locations.

System Messages support [formatting](https://talkjs.com/docs/Features/Messages/Formatting/).

Some example use cases in a marketplace for System Messages are 'order received' and 'payment failed', but these can be anything from payment confirmations to "User left the group" notifications.

POST/v1/{appId}/conversations/{conversationId}/messages

Payload StructureExample Payload

```typescript
[
  {
    // Must specify either `text` or `content`, not both
    text: string,
    content: [SendContentBlock],

    type: "SystemMessage",
    custom?: Record<string, string>,
    idempotencyKey?: string
  },
  ...
]
```

The `text` field is parsed the same as text sent via the TalkJS UI. `sender` must not be provided when sending system messages.

### Ensure exactly-once delivery of messages

If an initial request fails, you may want to retry the request. To avoid message duplication on retries, you can use the `idempotencyKey` field when sending the message. If you use an `idempotencyKey`, you can try a request any number of times and TalkJS guarantees that the message gets sent exactly once.

Make sure that the `idempotencyKey` you use is unique within the conversation in which the message is sent. If two messages in the same conversation have the same `idempotencyKey`, the system treats those two messages as if they're the same request, and only the first message with that `idempotencyKey` gets processed.

TalkJS associates a message with an `idempotencyKey` for 24 hours. After 24 hours, the `idempotencyKey` expires and you can reuse the key for a different message.

## Fetch a single message

You can check the status of a certain message from a conversation (for example, after some time to read a `readBy` status), by calling:

GET/v1/{appId}/conversations/{conversationId}/messages/{messageId}

Payload StructureExample Response

```typescript
type MessageId = string;
type UserId = string;

type UnixMilliseconds = number;

type Message = {
  id: MessageId;
  conversationId: string;

  // null when `type: "SystemMessage"`
  senderId: UserId | null;
  type: 'UserMessage' | 'SystemMessage';

  origin: 'rest' | 'email' | 'import' | 'web';
  createdAt: UnixMilliseconds;
  editedAt: UnixMilliseconds | null;

  text: string;
  attachment: FileBlock | null;
  location: [number, number] | null;
  content: ContentBlock[];

  custom: { [name: string]: string };
  readBy: UserId[];
  referencedMessageId: string | null;

  reactions: { [emoji: string]: {
    count: number;
    createdAt: number;
  }}
};
```

Note that the `readBy` field is only used for conversations with at most 300 participants. For conversations with 301 or more participants, the `readBy` field will be empty.

To learn more about message content and `ContentBlock`, read the [Message content introduction](https://talkjs.com/docs/Concepts/Message_Content/) or the [reference documentation](https://talkjs.com/docs/JavaScript_Data_API/Message_Content/);

## Edit a message

This endpoint lets you edit the text field and the custom data in existing message. You first need to know the id of the message you want to edit. Please use the [listing messages](https://talkjs.com/docs/REST_API/Messages/#listing-messages-from-a-conversation) endpoint to obtain a list of messages along with their IDs or get it via a [Webhook](https://talkjs.com/docs/Webhooks/). Afterwards, you have to call the following endpoint:

You can also pass a field called `markEdited` (defaulting to false). When this field is set to true, the message's `editedAt` field will be set to the time of this change, and depending on your theme, an indicator that the message was edited may appear in the UI.

PUT/v1/{appId}/conversations/{conversationId}/messages/{messageId}

Payload Structure

```typescript
{
  // Do not specify both `text` and `content` together
  text?: string,
  content?: [SendContentBlock],
  custom?: Record<string, string | null> | null,
  markEdited?: boolean
}
```

## Delete a message

Use this endpoint to irrevocably delete a message of the given conversation. Besides deleting the message from the TalkJS database, it is also deleted in real-time from any potentially connected clients. Additionally, a [message.deleted](https://talkjs.com/docs/Webhooks/) webhook event is sent. If it contains the attachment it is removed from the storage.

DELETE/v1/{appId}/conversations/{conversationId}/messages/{messageId}

## Add or remove an emoji reaction

This endpoint lets you edit the emoji reactions on a message. You need to know the id of the message you want to react to, in addition to the emoji you want to use, and the id of the user whose reaction you are editing.

To add the reaction:

PUT/v1/{appId}/conversations/{conversationId}/messages/{messageId}/reactions/{emoji}/{userId}

Payload Structure

```typescript
{
}
```

To remove the reaction:

DELETE/v1/{appId}/conversations/{conversationId}/messages/{messageId}/reactions/{emoji}/{userId}

In the url, `emoji` can either be a single unicode emoji such as "🚀", or a custom emoji such as ":cat\_roomba:".

## List messages from a conversation

You can list messages written in conversations by users in TalkJS. In order to do that you need to call the following endpoint:

GET/v1/{appId}/conversations/{conversationId}/messages

Response StructureExample Response

```typescript
{
  "data": Array<Message>
}
```

Note that the `readBy` field is only used for conversations with at most 300 participants. For conversations with 301 or more participants, the `readBy` field will be empty.

#### Limits and pagination

Similarly to other list requests, pagination and limits are applicable to [listing messages](https://talkjs.com/docs/REST_API/#listing). The maximum `limit` you can fetch at once is 100 messages.

#### Filtering messages

You can filter the listed messages based on the message and sender attributes. First create a [MessagePredicate](https://talkjs.com/docs/UI_Components/JavaScript/Classic/Other_Interfaces/#MessagePredicate) object. Then convert that JSON object to a string, URL-encode it, and pass the resulting string in the `filter` query parameter:

```typescript
const filter = { type: ['==', 'UserMessage'] };
const encodedFilter = encodeURIComponent(JSON.stringify(filter));
const url = `https://api.talkjs.com/v1/${appId}/conversations/${conversationId}/messages?filter=${encodedFilter}`;
```

#### Examples

```typescript
GET https://api.talkjs.com/v1/{appId}/conversations/{conversationId}/messages
GET https://api.talkjs.com/v1/{appId}/conversations/{conversationId}/messages?limit=50
GET https://api.talkjs.com/v1/{appId}/conversations/{conversationId}/messages?startingAfter=msg_123
GET https://api.talkjs.com/v1/{appId}/conversations/{conversationId}/messages?startingAfter=msg_123&limit=10

// Filtered to only show user messages, not system messages
GET https://api.talkjs.com/v1/{appId}/conversations/{conversationId}/messages?filter=%7B%22type%22%3A%5B%22%3D%3D%22%2C%22UserMessage%22%5D%7D
```

## List users who reacted to a message

You can list the users who reacted to a message with a specific emoji. To do that, need to call the following endpoint:

GET/v1/{appId}/conversations/{conversationId}/messages/{messageId}/reactions/{emoji}

Response StructureExample Response

```typescript
{
  "data": Array<{
    // ID of the user who reacted with the specified emoji
    "userId": string;
    // Unix epoch milliseconds when the user reacted
    "createdAt": number;
  }>
}
```

In the URL, `emoji` should be either a unicode emoji like 🚀 or a TalkJS custom emoji like `:cat_roomba:`. The reactions in the response are sorted chronologically, starting with the oldest one.

#### Limits and pagination

By default, this endpoint will return up to 30 users. You can set the limit anywhere from 1 to 100 using the `limit` query parameter.

This endpoint does not currently support pagination. Get in touch if you have more than 100 users adding a single reaction to a message.

#### Examples

```typescript
GET https://api.talkjs.com/v1/{appId}/conversations/{conversationId}/messages/{messageId}/reactions/🚀
GET https://api.talkjs.com/v1/{appId}/conversations/{conversationId}/messages/{messageId}/reactions/:cat_roomba:
GET https://api.talkjs.com/v1/{appId}/conversations/{conversationId}/messages/{messageId}/reactions/🧑🏽‍💼?limit=50
```

## Send a file

Sending files (images, documents, etc) is a two-step process. First, you upload the file to our servers. Then, you send a message in the same way as a text message, but with a reference to the uploaded file.

### 1\. Upload a file

You upload a file using the `files` endpoint:

POST/v1/{appId}/files

Example Response

```typescript
{
    "fileToken": "L0oQOg5DVENGFVQZABpOQVNRCxs...EFFbHxhiXVEYUBw"
}
```

This is the only resource on the TalkJS REST API that does not accept a JSON payload. Instead, send `multipart/form-data` formatted data with two **required** parameters:

1.  `file` - This is the file data you want to upload.
2.  `filename` - The name of the file.

This is the usual way files are uploaded over HTTP. Check the docs of your favorite HTTP client for information on how to upload files with it. For example: [JavaScript](https://stackoverflow.com/a/40826943/103395), [Python (Requests)](https://requests.readthedocs.io/en/latest/user/quickstart/#post-a-multipart-encoded-file), [PHP](http://code.stephenmorley.org/php/sending-files-using-curl/), [Ruby (RestClient)](https://github.com/rest-client/rest-client#multipart).

A file uploaded with those two parameters does not have any additional metadata and, when sent, appears in the message `content` as a `GenericFileBlock`. For your file to be received as a more-specific file block variant like `ImageBlock`, you must set some additional parameters:

3.  `subtype` (optional) - One of "video", "image", "audio", or "voice".
4.  `width` (optional) - the width of a video or image in pixels.
5.  `height` (optional) - the height of a video or image in pixels.
6.  `duration` (optional) - the duration of a video / audio file / voice recording in seconds.

This endpoint returns a JSON object with a single property, `fileToken`.

#### Troubleshooting file upload issues

If you get a `400 Bad Request` response, try the following:

-   Check if your code is explicitly setting a `Content-type: multipart/form-data` header somewhere. If so, **remove it**. Most HTTP libraries will autogenerate this header when uploading files, and they will usually set the header to something like `Content-Type: multipart/form-data; boundary=a3eb698cd620e766f278e52886c572edcb0e4a97`. Without the boundary, TalkJS can't parse the data.
-   For TalkJS to be able to detect that you're uploading a file, the file needs to have a `filename` directive set. Many HTTP libraries do this automatically. If not, there will be a way for you to set it explicitly. Doing so causes the library to generate a header such as `Content-Disposition: form-data; file="This is a txt file."; filename="sample.txt"` inside the HTTP body. You shouldn't need to set this header manually; find a facility in your HTTP library to give an uploaded file a filename.

If you get a `500 Server Error` response, reach out through the TalkJS support chat.

### 2\. Send a message with an attachment

Now, use the usual `messages` resource to send a message, specifying your file token in the content field:

POST/v1/{appId}/conversations/{conversationId}/messages

Payload Structure

```typescript
[
  {
    content: [{ type: 'file', fileToken: string }],
    attachmentToken: string, // deprecated, use `content`
    sender: string,
    type: 'UserMessage',
  },
];
```

Alternatively, files can be sent using a system message by omitting the sender and using `SystemMessage` as the message type.

```typescript
[
  {
    content: [{ type: 'file', fileToken: string }],
    attachmentToken: string, // deprecated, use `content`
    type: 'SystemMessage',
  },
];
```

## Delete a file

When you delete a message with a file attachment, the file is not deleted from our servers. This is because you can use the `fileToken` repeatedly to send the same attachment on multiple messages.

Use this endpoint to permanently delete a file from our servers:

DELETE/v1/{appId}/files

Payload StructureExample Response

```typescript
{
  // Pick one
  url?: string;
  fileToken?: string;
}
```

This endpoint is intended for regulatory compliance, chat moderation, and generally when you need to fully purge the existence of a file. If there are any messages with this file as an attachment, those messages will show an error in the attachment preview, and the attachment link will stop working.

## Delete all messages from a conversation

Use this endpoint to irrevocably delete all messages of the given conversation. Besides deleting the messages from the TalkJS database, it is also deleted in real-time from any potentially connected clients. Note that this endpoint does not generate any webhooks.

DELETE/v1/{appId}/conversations/{conversationId}/messages
