# Chats

> A chat is a private conversation between named people across Slack and Microsoft Teams. Conclude creates a private Slack channel and a Microsoft Teams group chat, and syncs them. The requests on this page list, start, change and close chats.

Canonical HTML page: https://conclude.io/doc/api/chats/
Last updated: 2026-11-12

---

**The API is in beta.** API keys are available to organizations in the beta program. Requests and responses may still change, and we tell beta customers before they do. [Contact us](https://conclude.io/contact/) to get access.

A chat is a private conversation between named people across Slack and Microsoft Teams. Conclude creates a private Slack channel and a Microsoft Teams group chat, and syncs them. The requests on this page list, start, change and close chats.

A chat within your organization has a channel in your Slack workspace and one in your Microsoft Teams tenant. A chat with people of another organization runs over a [Bridge](https://conclude.io/doc/connect/bridge/). It has a channel in each organization, and the API shows only yours. [Cross-platform chats](https://conclude.io/doc/guides/chats/) describes how chats work in Slack and Microsoft Teams.

## Who can use chats

Reading needs the scope `chats:read`. Every change needs `chats:readwrite`.

Only a chat's members can see it. The requests on this page act for the person the token acts for:

- A key that acts as you reaches your chats
- A key that acts as Conclude, with a [subject](https://conclude.io/doc/api/authentication/#acts-as), reaches the subject's chats
- A key that acts as Conclude, without a subject, reads every chat with a channel in your organization. It can also [start chats](#start-a-chat) between the people it names. To change, check or delete a chat, it needs a subject. Without one, Conclude answers `subject_required`
- For anyone else who is not a member, a chat doesn't exist. Conclude answers `not_found`

Starting, changing and deleting a chat within your organization needs the **Create chats** permission from [**Admin console > Users and access > Roles & permissions**](https://conclude.io/admin/roles). For a key that acts as Conclude, the scope `chats:readwrite` stands in for it.

A chat with another organization uses the bridge's **Chat access** list instead of the **Create chats** permission. Everyone in the chat must be on that list, including the person the token acts for. Each organization manages its own list, so no scope can replace it. Any member of the chat can close it.

## The chat object

```json
{
  "id": "Y-7f9h1j3l",
  "topic": "Laptop refresh",
  "autoName": true,
  "status": "ok",
  "lastActivity": "2026-11-05T09:12:44Z",
  "myChannelID": "C-6e8g0i2k",
  "channels": [
    {
      "id": "C-6e8g0i2k",
      "name": "chat_ana_ben",
      "platform": "slack",
      "sysID": "C07ABCDEF12",
      "workspace": {
        "id": "T-4c6e8g0i",
        "name": "Example",
        "sysID": "T04ABCDEF12"
      },
      "url": "slack://channel?team=T04ABCDEF12&id=C07ABCDEF12"
    },
    {
      "id": "C-9i1k3m5o",
      "name": "Chat between Ana and Ben",
      "platform": "teams",
      "sysID": "19:a1b2c3d4@thread.v2",
      "workspace": {
        "id": "T-8h0j2l4n",
        "name": "Example",
        "sysID": "8f0e2d4c-6b8a-4c2e-9f1d-3b5a7c9e1f20"
      },
      "url": "https://teams.microsoft.com/l/chat/19:a1b2c3d4@thread.v2"
    }
  ],
  "members": [
    {
      "id": "U-2b4d6f8h",
      "mention": "Ana",
      "name": "Ana Silva",
      "email": "ana@example.com",
      "avatar": "https://avatars.example.com/ana.png",
      "orgID": "O-2a4c6e8g"
    },
    {
      "id": "U-3b5d7f9h",
      "mention": "Ben",
      "name": "Ben Okafor",
      "email": "ben@example.com",
      "orgID": "O-2a4c6e8g"
    }
  ]
}
```

| Field | Type | Description |
|---|---|---|
| `id` | string | The chat's ID. The paths below take it as `{chat}`. |
| `topic` | string | The chat's topic. It is empty when the chat has none. |
| `autoName` | boolean | `true` when Conclude names the channels after the members. Adding or removing members then renames them. |
| `status` | string | The chat's health. See [Status](#status). |
| `lastActivity` | time | When a message last synced, to the minute. Left out until the first message. |
| `myChannelID` | string | The chat's channel that the person the token acts for is in. Left out for a key that acts as Conclude without a subject. |
| `bridge` | object | Only on a chat with another organization. The bridge's `id`, and the other organization as `partner`, with its `id` and `name`. |
| `channels` | array | Your organization's channels of the chat, as [chat channel objects](#chat-channel-object). |
| `members` | array | The chat's members, as [user objects](#user-object). |

## The chat channel object

A chat's channel is a private Slack channel or a Microsoft Teams group chat.

| Field | Type | Description |
|---|---|---|
| `id` | string | The channel's Conclude ID. |
| `name` | string | The channel's name. |
| `platform` | string | `slack` or `teams`. |
| `sysID` | string | The Slack channel ID or the Microsoft Teams chat ID. |
| `workspace` | object | The Slack workspace or Microsoft Teams tenant the channel is in, with its `id`, `name` and `sysID`. |
| `url` | string | A link that opens the channel in Slack or Microsoft Teams. A Slack link opens the Slack app. |

## The user object

| Field | Type | Description |
|---|---|---|
| `id` | string | The person's Conclude user ID. |
| `mention` | string | The name people use to mention the person in Slack or Microsoft Teams. |
| `name` | string | The person's full name. It is the same as `mention` when Conclude doesn't know the full name. |
| `email` | string | The person's email address, when Conclude knows it. |
| `avatar` | string | The URL of the person's picture, when there is one. |
| `orgID` | string | The person's organization. In a chat with another organization, it tells the two sides apart. |

## Status

| Status | Meaning |
|---|---|
| `ok` | The chat syncs. |
| `paused` | Conclude stopped syncing one of the chat's channels after it stayed broken. The channel syncs again once Conclude finds it healthy. |

Any other value is the [status of the first broken channel](https://conclude.io/doc/api/connections/#status), such as `noAuth` or `deactivated`. The Connections page says what to do about each.

## List chats

`GET /v1/chats` · scope `chats:read`

Returns a page of chats, newest first by when they were created. It includes chats with other organizations, and leaves out chats on a bridge that an admin has paused.

| Parameter | Description |
|---|---|
| `bridge` | Optional. A bridge ID returns only the chats over that bridge. `none` returns only the chats within your organization. |
| `limit` | The number of chats on a page, from 1 to 500. The default is 50. A larger value returns 500. |
| `position` | The `nextPosition` from the previous page. |

```sh
curl -G https://api.conclude.io/v1/chats \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  --data-urlencode "bridge=none"
```

| Field | Type | Description |
|---|---|---|
| `chats` | array | The [chat objects](#chat-object) on this page. |
| `nextPosition` | string | The position of the next page. It is empty on the last page. |

To read the next page, send the same request with `position` set to `nextPosition`. URL-encode the value.

| Status | Code | When |
|---|---|---|
| 400 | `invalid_request` | `limit` is not a positive number, or `position` is not one Conclude returned. `param` names the parameter. |
| 404 | `not_found` | `bridge` is not a bridge of your organization. `param` is `bridge`. |

## Get a chat

`GET /v1/chats/{chat}` · scope `chats:read`

Returns one [chat object](#chat-object).

```sh
curl https://api.conclude.io/v1/chats/Y-7f9h1j3l -H "Authorization: Bearer $ACCESS_TOKEN"
```

| Status | Code | When |
|---|---|---|
| 404 | `not_found` | The chat doesn't exist, or the person the token acts for is not a member. Or the chat's bridge is paused. |

## Start a chat

`POST /v1/chats` · scope `chats:readwrite`

Starts a chat between the person the token acts for and the people you name. Conclude creates a private Slack channel and a Microsoft Teams group chat and adds everyone to them. The audit log records `connect.chat.created`.

A key that acts as Conclude, without a subject, starts a chat between the people you name. Name at least two, and at least one from your organization. The first of them from your organization counts as the person who started the chat. The audit log names Conclude.

| Field | Type | Required | Description |
|---|---|---|---|
| `members` | array of strings | Yes | The user IDs of the people to chat with. [`GET /v1/findUser`](https://conclude.io/doc/api/organization/#find-users) finds them in your organization. |
| `bridge` | string | No | The ID of the bridge to start the chat over, with people of another organization. See [Chats with another organization](#bridge-chats). |
| `topic` | string | No | The chat's topic. |
| `name` | string | No | The name of the channels. Without it, Conclude names them after the members. |

```sh
curl https://api.conclude.io/v1/chats \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"members": ["U-3b5d7f9h"], "topic": "Laptop refresh"}'
```

The response has these fields:

| Field | Type | Description |
|---|---|---|
| `chat` | object | The new [chat object](#chat-object). |
| `skippedMembers` | array | [User objects](#user-object) of the people Conclude didn't add, because they have no account in the chat's workspaces. Left out when Conclude added everyone. A chat with another organization never has it, so compare the chat's `members` with the people you named. |
| `warnings` | array of strings | Messages for people to read, such as how many members Conclude skipped. Left out when there are none. |

Slack and Microsoft Teams can take a few seconds to add everyone. [Check the members](#check-members) to find people they didn't add.

### Chats with another organization

To chat with people of another organization, name your bridge with that organization in `bridge`. [`GET /v1/bridges`](https://conclude.io/doc/api/bridges/#list-bridges) lists your bridges, and [`GET /v1/bridges/{bridge}/chatUsers`](https://conclude.io/doc/api/bridges/#list-chat-users) lists the people you can name. Everyone must have **Chat access** on the bridge, and at least one of them must be from the other organization. A key that acts as Conclude, without a subject, names at least two people. The first of them on your organization's side of the **Chat access** list counts as the person who started the chat. The audit log records `bridge.chat.created` instead.

```sh
curl https://api.conclude.io/v1/chats \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"members": ["U-5e7g9i1k"], "bridge": "B-1a3c5e7g", "topic": "Renewal"}'
```

Without `bridge`, everyone you name must be a user in your organization, even someone who is on a bridge's **Chat access** list.

| Status | Code | When |
|---|---|---|
| 400 | `invalid_request` | `members` is empty, or a member is not a user in your organization. `param` names the member, as in `members.1`. With `bridge`, no member is from the other organization. The person the token acts for has no account to start the chat from. A key that acts as Conclude, without a subject, named fewer than two members. |
| 400 | `chat_member_not_allowed` | Microsoft Teams wouldn't add a member, such as a guest or an account without a Teams license. Or a member doesn't have **Chat access** on the bridge. Or a key that acts as Conclude, without a subject, named no one from your side of the bridge's **Chat access** list. `param` names the member when Conclude knows which one. |
| 403 | `forbidden` | The person the token acts for doesn't have the **Create chats** permission. |
| 403 | `bridge_chat_not_allowed` | The person the token acts for doesn't have **Chat access** on the bridge. |
| 403 | `plan_limit` | Your organization's plan doesn't include chats. With `bridge`, neither organization's plan includes bridges. `param` is `chats` or `bridge`. |
| 404 | `not_found` | `bridge` is not a bridge of your organization. `param` is `bridge`. |
| 409 | `chat_needs_two_workspaces` | Your organization doesn't have two workspaces to chat across, such as a Slack workspace and a Microsoft Teams tenant. |
| 409 | `teams_license_missing` | None of the Microsoft Teams accounts that Conclude can use has a Teams license. |
| 409 | `teams_sign_in_required` | Microsoft Teams needs a member, or the [integration account](https://conclude.io/doc/admin/organization/integration-account/), to be signed in to Conclude. |
| 409 | `bridge_not_active` | An admin has paused the bridge. |
| 409 | `conflict` | Conclude couldn't create the chat's channel in one of the organizations. A member from that organization must sign in to Conclude. |

## Change a chat

`PATCH /v1/chats/{chat}` · scope `chats:readwrite`

Changes the chat's topic, the name of its channels and its members, as one change. Slack and Microsoft Teams rename the channels at most once. A field you leave out stays as it is. Changes to one chat's members run one at a time, so a request waits while another one changes the chat.

| Field | Type | Required | Description |
|---|---|---|---|
| `topic` | string | No | The new topic. An empty string removes it. |
| `name` | string | No | The new name of the channels. An empty string names them after the members again. |
| `addMembers` | array of strings | No | The user IDs of people from your organization to add. People who are already members stay as they are. |
| `removeMembers` | array of strings | No | The user IDs of members to remove. The person the token acts for can't remove themselves. |

```sh
curl -X PATCH https://api.conclude.io/v1/chats/Y-7f9h1j3l \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"topic": "Laptop refresh, phase 2", "addMembers": ["U-4c6e8g0i"], "removeMembers": ["U-3b5d7f9h"]}'
```

The response has the changed [chat object](#chat-object) as `chat`, and the people Conclude didn't add as `skippedMembers`, as when you [start a chat](#start-a-chat). When `autoName` is `true` and the members changed, Conclude renames the channels after them. The audit log records `connect.chat.updated` when people joined or left, or the channels got a new name.

| Status | Code | When |
|---|---|---|
| 400 | `invalid_request` | A person in `addMembers` is not a user in your organization. A person in `removeMembers` is not a member, or is the person the token acts for. `param` names the person, as in `removeMembers.0`. |
| 400 | `chat_member_not_allowed` | Microsoft Teams wouldn't add a member, such as a guest or an account without a Teams license, to a chat channel Conclude creates for the change. |
| 403 | `forbidden` | The person the token acts for doesn't have the **Create chats** permission. |
| 403 | `subject_required` | A key that acts as Conclude sent no subject. |
| 404 | `not_found` | The chat doesn't exist, or the person the token acts for is not a member. |
| 409 | `bridge_chat_unsupported` | The chat is with another organization. Such a chat can only be read and closed. |
| 409 | `conflict` | Another change to the chat's members is still running after a minute. Try again. |
| 500 | `internal_error` | Microsoft Teams wouldn't add a member to the chat's existing channel. Other changes may already have gone through, so [check the members](#check-members). |

## Add members

`POST /v1/chats/{chat}/members` · scope `chats:readwrite`

Adds people from your organization to the chat. People who are already members stay as they are. To add and remove people in one change, [change the chat](#change-a-chat) instead. Changes to one chat's members run one at a time, so a request waits while another one changes the chat.

| Field | Type | Required | Description |
|---|---|---|---|
| `members` | array of strings | Yes | The user IDs of the people to add. |

```sh
curl https://api.conclude.io/v1/chats/Y-7f9h1j3l/members \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"members": ["U-4c6e8g0i"]}'
```

The response has the changed [chat object](#chat-object) as `chat`, and the people Conclude didn't add as `skippedMembers`, as when you [start a chat](#start-a-chat). When `autoName` is `true`, Conclude renames the channels after the new members. The audit log records `connect.chat.updated`.

| Status | Code | When |
|---|---|---|
| 400 | `invalid_request` | `members` is empty, or a member is not a user in your organization. `param` names the member, as in `members.0`. |
| 400 | `chat_member_not_allowed` | Microsoft Teams wouldn't add a member, such as a guest or an account without a Teams license, to a chat channel Conclude creates for the change. |
| 403 | `forbidden` | The person the token acts for doesn't have the **Create chats** permission. |
| 403 | `subject_required` | A key that acts as Conclude sent no subject. |
| 404 | `not_found` | The chat doesn't exist, or the person the token acts for is not a member. |
| 409 | `bridge_chat_unsupported` | The chat is with another organization. |
| 409 | `conflict` | Another change to the chat's members is still running after a minute. Try again. |
| 500 | `internal_error` | Microsoft Teams wouldn't add a member to the chat's existing channel. Other changes may already have gone through, so [check the members](#check-members). |

## Remove a member

`DELETE /v1/chats/{chat}/members/{user}` · scope `chats:readwrite`

Removes a person from the chat. `{user}` is their user ID. The person the token acts for can't remove themselves. Another member can, or they can [delete the chat](#delete-a-chat).

```sh
curl -X DELETE https://api.conclude.io/v1/chats/Y-7f9h1j3l/members/U-4c6e8g0i \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

The response has the changed [chat object](#chat-object) as `chat`. When `autoName` is `true`, Conclude renames the channels after the remaining members. The audit log records `connect.chat.updated`.

| Status | Code | When |
|---|---|---|
| 400 | `invalid_request` | `{user}` is the person the token acts for. `param` is `user`. |
| 403 | `forbidden` | The person the token acts for doesn't have the **Create chats** permission. |
| 403 | `subject_required` | A key that acts as Conclude sent no subject. |
| 404 | `not_found` | The chat doesn't exist, or the person the token acts for is not a member. Or `{user}` is not a member. |
| 409 | `bridge_chat_unsupported` | The chat is with another organization. |
| 409 | `conflict` | Another change to the chat's members is still running after a minute. Try again. |

## Check members

`POST /v1/chats/{chat}/verify` · scope `chats:readwrite`

Reads the members of each channel from Slack and Microsoft Teams again, and returns the members who are missing from a channel. The request has no body.

```sh
curl -X POST https://api.conclude.io/v1/chats/Y-7f9h1j3l/verify \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

```json
{
  "missingMembers": []
}
```

`missingMembers` is a list of [user objects](#user-object). A member is missing when they are in the chat on one platform, but not in the channel on the other.

- Right after you start or change a chat, Slack or Microsoft Teams may still be adding someone. Wait a few seconds and check again
- To try again, [add the person](#add-members) once more. Conclude then invites everyone who is missing from a channel
- If someone stays missing, Microsoft Teams won't add them. That happens to guests and to accounts without a Teams license. [Remove them](#remove-a-member) from the chat, or leave them as they are

| Status | Code | When |
|---|---|---|
| 403 | `forbidden` | The person the token acts for doesn't have the **Create chats** permission. |
| 403 | `subject_required` | A key that acts as Conclude sent no subject. |
| 404 | `not_found` | The chat doesn't exist, or the person the token acts for is not a member. |
| 409 | `bridge_chat_unsupported` | The chat is with another organization. |

## Delete a chat

`DELETE /v1/chats/{chat}` · scope `chats:readwrite`

Closes the chat and stops syncing it. Conclude archives the Slack channel and deletes the Microsoft Teams group chat. Microsoft Teams can't archive a group chat, so the members lose its history in Microsoft Teams. The audit log records `connect.chat.deleted`.

On a chat with another organization, Conclude closes the channels of both organizations. Any member can do this, without the **Create chats** permission. The audit log records `bridge.chat.closed`.

```sh
curl -X DELETE https://api.conclude.io/v1/chats/Y-7f9h1j3l -H "Authorization: Bearer $ACCESS_TOKEN"
```

The response can have `warnings`, a list of messages for people to read. They name what Conclude couldn't clean up, such as a channel it couldn't archive.

| Status | Code | When |
|---|---|---|
| 403 | `forbidden` | The person the token acts for doesn't have the **Create chats** permission. |
| 403 | `subject_required` | A key that acts as Conclude sent no subject. |
| 404 | `not_found` | The chat doesn't exist, or the person the token acts for is not a member. |

