# Connections

> A connection syncs messages between channels, usually a Slack channel and a Microsoft Teams channel. Each channel in a connection is an endpoint. The requests on this page list, create, repair and remove connections, and change the settings of each side.

Canonical HTML page: https://conclude.io/doc/api/connections/
Last updated: 2026-10-27

---

**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 connection syncs messages between channels, usually a Slack channel and a Microsoft Teams channel. Each channel in a connection is an endpoint. The requests on this page list, create, repair and remove connections, and change the settings of each side.

The API creates [Internal](https://conclude.io/doc/connect/internal/) connections, between channels of your own organization. [External](https://conclude.io/doc/connect/external/) and [Bridge](https://conclude.io/doc/connect/bridge/) connections start with an invitation in the Dashboard. The API can read them, change your side, repair them and remove them.

## Who can change connections

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

The person the token acts for needs a permission from [**Admin console > Users and access > Roles & permissions**](https://conclude.io/admin/roles):

| Connection | Permission |
|---|---|
| Internal | **Connect channels** |
| External | **Connect external channels** |
| Bridge | **Manage bridges**, or admin rights on that bridge |

For each channel, the person must be a full member of its workspace or team. Guests don't count. For a private channel, the person must also be a member of the channel. A connection is visible when the person can access at least one of its endpoints.

A key that acts as Conclude follows extra rules:

- It holds the permissions above through its scopes.
- It only reaches channels that Conclude reaches. Those are public channels, and private channels that Conclude was added to.
- Every change needs a token with a [subject](https://conclude.io/doc/api/authentication/#acts-as). Without one, Conclude answers `subject_required`.

On an External or Bridge connection, you can change and remove only your own organization's endpoints.

## The connection object

```json
{
  "id": "Y-7f9h1j3l",
  "type": "channel",
  "settings": {},
  "endpoints": [
    {
      "orgID": "O-2a4c6e8g",
      "teamID": "T-4c6e8g0i",
      "channelID": "C-6e8g0i2k",
      "sysChannelID": "C07ABCDEF12",
      "status": "ok",
      "settings": {"syncBotMessages": true},
      "created": "2026-10-27T14:03:12.52Z",
      "teamName": "Example",
      "channelName": "it-help",
      "platform": "slack",
      "url": "https://example.slack.com/archives/C07ABCDEF12"
    },
    {
      "orgID": "O-2a4c6e8g",
      "teamID": "T-8h0j2l4n",
      "channelID": "C-9i1k3m5o",
      "sysChannelID": "19:a1b2c3@thread.tacv2",
      "status": "ok",
      "settings": {},
      "created": "2026-10-27T14:03:12.52Z",
      "teamName": "IT",
      "channelName": "Help",
      "platform": "teams",
      "url": "https://teams.microsoft.com/l/channel/19%3Aa1b2c3%40thread.tacv2/Help"
    }
  ],
  "lastActivity": "2026-10-27T15:20:00Z",
  "created": "2026-10-27T14:03:12.52Z",
  "modified": "2026-10-27T14:03:12.52Z"
}
```

| Field | Type | Description |
|---|---|---|
| `id` | string | The connection's ID. |
| `type` | string | `channel` for an Internal connection, `external` or `bridge`. |
| `bridgeID` | string | The bridge's ID, for a connection that belongs to a bridge. |
| `settings` | object | Reserved for settings of the whole connection. It is always empty today. |
| `endpoints` | array | The connection's [endpoints](#endpoint-object). Slack comes before Microsoft Teams, then they are sorted by team and channel name. |
| `lastActivity` | time | When a message last synced, to the minute. |
| `created` | time | When the connection was created. |
| `modified` | time | When the connection was last changed. |

## The endpoint object

| Field | Type | Description |
|---|---|---|
| `orgID` | string | The organization the channel belongs to. On an External or Bridge connection, it can be another organization. |
| `teamID` | string | The channel's team or workspace. |
| `channelID` | string | The channel's Conclude ID. The paths below take it as `{channel}`. |
| `sysChannelID` | string | The Slack or Microsoft Teams channel ID. |
| `platform` | string | `slack` or `teams`. |
| `status` | string | The endpoint's health. See [Status](#status). |
| `disabled` | boolean | `true` when Conclude stopped syncing this endpoint. See [Status](#status). |
| `settings` | object | The endpoint's [settings](#endpoint-settings). |
| `created` | time | When the channel joined the connection. |
| `teamName` | string | The team's or workspace's name. |
| `channelName` | string | The channel's name. |
| `url` | string | A link that opens the channel in Slack or Microsoft Teams. |
| `appInfo` | object | The Conclude app installed in the channel, with `icon`, `name` and `label`. Left out when there is none. |
| `orgName` | string | External connections only. The other organization's name. |
| `contact` | string | External connections only. The name of the endpoint's owner. |
| `email` | string | External connections only. The email address of the endpoint's owner. |

## Endpoint settings

Each endpoint has its own settings. Most of them decide what this side sends to the others.

| Field | Type | Description |
|---|---|---|
| `owner` | string | The user ID of the person responsible for this side. It must be a user with an active account in your organization. |
| `inviter` | boolean | `true` on the side that sent the External invitation. Only the invitation sets it. Conclude ignores it in requests. |
| `syncBotMessages` | boolean | `true` to sync posts from other apps' bots on this side. Conclude agents always sync. The default is `false`. |
| `syncSystemMessages` | boolean | `true` to sync system messages from this side, such as notices that someone joined. The default is `false`. |
| `attachmentPolicy` | string | What happens to files sent from this side. `copy` copies them, `useReferences` sends a link, and `ignore` leaves them out. Empty uses your organization's policy. |
| `attachmentPolicyForImages` | string | The same for images. Empty makes images follow the file policy. |
| `translate` | string | A language code, such as `de` or `fr`. Messages that arrive in this channel are translated into it. Empty turns translation off. |

The two attachment policies take effect only when an admin has turned on **Allow channel-level overrides**. Otherwise the organization's policy applies. See [File and image sharing](https://conclude.io/doc/connect/file-sharing/#overrides).

## Status

Each endpoint has a `status`. Conclude checks connections regularly and updates it.

| Status | Meaning | What to do |
|---|---|---|
| `ok` | The endpoint syncs. A new endpoint is `ok` until Conclude first checks it. | Nothing. |
| `noBot` | Slack. Conclude is not in the channel. | Add Conclude to the channel, then [repair](#repair-a-connection) the connection. |
| `noAuth` | Slack: Conclude can no longer reach the channel, for example because it was made private. Microsoft Teams: no one who signed in to Conclude can see the channel. | Slack: add Conclude to the channel. Microsoft Teams: have a member of the channel sign in to Conclude. Then repair the connection. |
| `noSub` | Microsoft Teams. Conclude doesn't receive the channel's messages. | Repair the connection. |
| `expiredSub` | Microsoft Teams. Conclude's subscription to the channel's messages has expired. | Repair the connection. |
| `deactivated` | The channel was archived or deleted. | Remove the endpoint. |
| `teamDeactivated` | Conclude was removed from the workspace, or the workspace is suspended. | Install Conclude in the workspace again. |
| `connectionError` | Conclude doesn't treat the channel as connected. | Contact support. |
| `paused` | An admin paused the connection's bridge. | Resume the bridge in the Dashboard. |
| `corrupt` | The connection has fewer than two endpoints. | Delete the connection. |

When an endpoint stays broken, Conclude stops syncing it and sets `disabled` to `true`. That happens after seven days, or after two hours for `corrupt`. A disabled endpoint neither sends nor receives messages. It turns itself back on once Conclude finds it healthy.

## List connections

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

Returns every connection with an endpoint in your organization that the token can see. The response is an array of [connection objects](#connection-object), sorted by their first endpoint. It is not paged.

```sh
curl https://api.conclude.io/v1/connections -H "Authorization: Bearer $ACCESS_TOKEN"
```

## Get a connection

`GET /v1/connections/{connection}` · scope `connections:read`

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

| Status | Code | When |
|---|---|---|
| 404 | `not_found` | The connection doesn't exist, has no endpoint in your organization, or the token can't see it. |

## Create a connection

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

Connects two or more channels of your organization. Conclude marks the channels as connected and starts to sync them. The audit log records `connect.channel.created`.

| Field | Type | Required | Description |
|---|---|---|---|
| `type` | string | Yes | Always `channel`. |
| `endpoints` | array | Yes | Two or more channels. |
| `endpoints[].channelID` | string | Yes | The channel's Conclude ID, from [`GET /v1/findChannel`](https://conclude.io/doc/api/organization/#find-channels). |
| `endpoints[].settings` | object | No | The channel's [settings](#endpoint-settings). |

```sh
curl https://api.conclude.io/v1/connections \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"type": "channel", "endpoints": [{"channelID": "C-6e8g0i2k"}, {"channelID": "C-9i1k3m5o"}]}'
```

The response is the new [connection object](#connection-object).

| Status | Code | When |
|---|---|---|
| 400 | `invalid_request` | `type` is not `channel`. There are fewer than two endpoints, or a channel appears twice. A channel belongs to another organization. A setting has the wrong value. |
| 400 | `channel_not_connectable` | A channel is archived or deleted, or is not a regular channel. |
| 403 | `forbidden` | The person the token acts for doesn't have the **Connect channels** permission. |
| 403 | `subject_required` | A key that acts as Conclude sent no subject. |
| 403 | `conclude_not_in_channel` | A key that acts as Conclude named a channel that Conclude doesn't reach. |
| 403 | `channel_access_denied` | The person the token acts for can't access a channel. |
| 403 | `connections_disabled` | Channel connections are turned off for your organization. |
| 404 | `not_found` | A channel doesn't exist. `param` names it, as in `endpoints.1.channelID`. |
| 409 | `already_connected` | A channel is already in a connection. |

## Delete a connection

`DELETE /v1/connections/{connection}` · scope `connections:readwrite`

Deletes the connection and stops syncing all its channels. On an External connection, this also removes the other organization's side. On a Bridge connection, it disconnects the channels from the bridge. The response is `200` with an empty body.

A key can delete an External or Bridge connection only when at most one of its endpoints belongs to another organization. Otherwise, [remove your own endpoints](#remove-an-endpoint).

| Status | Code | When |
|---|---|---|
| 403 | `forbidden` | The person the token acts for doesn't have the permission for this connection. Or a key tried to delete a connection with endpoints in several other organizations. |
| 403 | `subject_required` | A key that acts as Conclude sent no subject. |
| 404 | `not_found` | The connection doesn't exist, or the token can't see it. |

## Repair a connection

`POST /v1/connections/{connection}/repair` · scope `connections:readwrite`

Checks every endpoint and fixes what Conclude can fix. It reads each channel again from Slack or Microsoft Teams. It renews Conclude's subscriptions to the channels' messages. In Microsoft Teams, it also adds Conclude's integration account to a private or shared channel again. Then it checks the endpoints once more and updates their `status`.

The request has no body. Repair runs while you wait, so it can take several seconds. The response is the [connection object](#connection-object) with the new status of each endpoint.

Repair can't fix every status. See [Status](#status) for what to do. If an endpoint has been broken longer than its grace period, repair disables it, and Conclude alerts your admins.

| Status | Code | When |
|---|---|---|
| 403 | `forbidden` | The person the token acts for doesn't have the permission for this connection. |
| 403 | `subject_required` | A key that acts as Conclude sent no subject. |
| 404 | `not_found` | The connection doesn't exist, or the token can't see it. |
| 503 | `unavailable` | The repair was interrupted. Try again. |

## Add an endpoint

`POST /v1/connections/{connection}/endpoints` · scope `connections:readwrite`

Adds a channel of your organization to an Internal connection.

| Field | Type | Required | Description |
|---|---|---|---|
| `channelID` | string | Yes | The channel's Conclude ID. |
| `settings` | object | No | The channel's [settings](#endpoint-settings). |

```sh
curl https://api.conclude.io/v1/connections/Y-7f9h1j3l/endpoints \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"channelID": "C-0j2l4n6p"}'
```

The response is the [connection object](#connection-object) with the new endpoint.

| Status | Code | When |
|---|---|---|
| 400 | `invalid_request` | The connection is not an Internal connection. The channel belongs to another organization. A setting has the wrong value. |
| 400 | `channel_not_connectable` | The channel is archived or deleted, or is not a regular channel. |
| 403 | `forbidden` | The person the token acts for doesn't have the **Connect channels** permission. |
| 403 | `subject_required` | A key that acts as Conclude sent no subject. |
| 403 | `conclude_not_in_channel` | A key that acts as Conclude named a channel that Conclude doesn't reach. |
| 403 | `channel_access_denied` | The person the token acts for can't access the channel. |
| 403 | `connections_disabled` | Channel connections are turned off for your organization. |
| 404 | `not_found` | The connection or the channel doesn't exist, or the token can't see the connection. |
| 409 | `already_connected` | The channel is already in a connection. |

## Remove an endpoint

`DELETE /v1/connections/{connection}/endpoints/{channel}` · scope `connections:readwrite`

Removes a channel of your organization from the connection. `{channel}` is the channel's Conclude ID.

When fewer than two endpoints remain, Conclude deletes the whole connection. The response is then the connection with an empty `endpoints` list. Removing either side of a Bridge connection disconnects both. Otherwise the response is the [connection object](#connection-object) without the removed endpoint.

| Status | Code | When |
|---|---|---|
| 403 | `forbidden` | The person the token acts for doesn't have the permission for this connection. Or the channel belongs to another organization. |
| 403 | `subject_required` | A key that acts as Conclude sent no subject. |
| 404 | `not_found` | The connection doesn't exist, the channel is not part of it, or the token can't see it. |

## Change endpoint settings

`PUT /v1/connections/{connection}/endpoints/{channel}/settings` · scope `connections:readwrite`

Replaces the [settings](#endpoint-settings) of one of your organization's endpoints. The body is the settings object. A field you leave out goes back to its default. Leaving out `owner` keeps the current owner, and `inviter` never changes.

To change one setting, read the connection first, change the setting and send all the settings back.

```sh
curl -X PUT https://api.conclude.io/v1/connections/Y-7f9h1j3l/endpoints/C-6e8g0i2k/settings \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"syncBotMessages": true, "translate": "de"}'
```

The response is the [endpoint object](#endpoint-object) with its new settings.

| Status | Code | When |
|---|---|---|
| 400 | `invalid_request` | A setting has the wrong value. `param` names it, as in `owner`. |
| 403 | `forbidden` | The person the token acts for doesn't have the permission for this connection. Or the channel belongs to another organization. |
| 403 | `subject_required` | A key that acts as Conclude sent no subject. |
| 404 | `not_found` | The connection doesn't exist, the channel is not part of it, or the token can't see it. |

