> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Team chat: operator workflows for channels, threads, and huddles

> Walk through the real operator workflows on Team Chat — on-call handoffs, support escalations paired with Inbox tickets, permissions, and notifications — with copy-pasteable curl sequences.

# Team Chat

Team Chat is internal messaging for the people running your Orbit workspace — support agents, admins, developers — not for customers. It sits alongside the customer-facing [Inbox](/api-reference/inbox) but is deliberately a separate surface: nothing posted in a team-chat channel or DM reaches a customer, and no outbound customer message is touched by any team-chat endpoint. Use it for the coordination that happens *around* customer work — an on-call handoff, a "heads up, deploy going out" note, a quick huddle while triaging an incident — without leaving Orbit for a separate chat tool.

This guide walks through the workflows operators run on it every day, then the permissions, notification, and read-state model underneath those workflows. For the full request/response schema, see the [Team Chat API reference](/api-reference/team-chat).

## Workflow 1 — On-call handoff: a channel the responders live in

The most common pattern: one standing channel per on-call rotation, `[On-Call](/api-reference/oncall)` decides who is responding, and the responders coordinate in the channel.

1. **Resolve who is on call** with the [On-Call API](/api-reference/oncall) — `POST /api/v1/oncall/resolve` returns the current member and the next handoff instant (see the [on-call alerting guide](/guides/oncall-alerting) for the full rotation setup).
2. **Post the handoff note in the rotation's channel** so both the outgoing and incoming responder see it, with a thread for anything that needs follow-up.
3. **Start a huddle in the same channel** the moment a text thread gets too slow and someone just needs to talk it through.

Create the channel once, up front — the creator becomes its owner:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/team-chat/channels \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "name": "on-call-platform", "description": "Platform team on-call chatter" }'
```

Add both responders (owner/admin only — see the [permissions matrix](#permissions-model) below):

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/team-chat/channels/tch_abc123/members \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "user_id": "usr_2", "role": "member" }'
```

Post the handoff note when shift changes:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/team-chat/channels/tch_abc123/messages \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "body": "Handoff 09:00 UTC — incident inc_123 still open, watching error rates. Ticket is in the Inbox." }'
```

Keep anything that needs a back-and-forth in a thread off the main channel, so the channel stays scannable during an incident. Reply with `POST .../messages/{messageId}/replies`, and load the root plus its replies with `GET .../messages/{messageId}/thread`:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/team-chat/channels/tch_abc123/messages/tmsg_9f8e7d/replies \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "body": "Error rates flattened after the rollback — resolving the ticket now." }'
```

When the handoff turns into a live triage, start a huddle in the channel:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/team-chat/channels/tch_abc123/huddle \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

A huddle is an ad-hoc voice/video room scoped to one channel. Anyone in the channel can start one or join the one already running; it ends automatically when the last participant leaves — there is no separate "end meeting" step. A teammate can check whether a channel has a live huddle with `GET .../channels/{channelId}/huddle`, and leave theirs with `POST .../channels/{channelId}/huddle/leave`.

## Workflow 2 — Support escalation: a channel paired with an Inbox ticket

The second everyday pattern: an agent working a customer conversation in the [Inbox](/api-reference/inbox) hits something they cannot resolve, and escalates to a specialist channel — without the customer ever seeing the internal discussion.

1. The agent keeps the customer thread open in the [Inbox](/api-reference/inbox).
2. They post the escalation in an internal channel — `#support-escalations`, or a specialist channel like `#billing-specialists` — linking or pasting the conversation id.
3. A specialist answers in a thread on the escalation post, or opens a huddle for anything that needs a live conversation.
4. The agent takes the answer back into the Inbox conversation and replies to the customer there.

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/team-chat/channels/tch_esc456/messages \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "body": "Escalation from Inbox conversation cnv_7891 — customer reports double charge on invoice inv_2210. Need a billing specialist to confirm before I reply." }'
```

Because Team Chat never touches customer-facing surfaces, the specialist channel discussion is invisible to the customer; the only thing the customer sees is the answer posted back into their Inbox conversation.

## Workflow 3 — Direct messages: etiquette and edge cases

DMs are for the 1:1 conversations that do not belong in a channel — a private handoff detail, a heads-up to one specific person. `POST /team-chat/dms` opens (or returns the existing) thread between the caller and another user, and `GET /team-chat/dms` lists the caller's threads:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/team-chat/dms \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "user_id": "usr_4" }'

curl -X POST https://api.orbit.devotel.io/api/v1/team-chat/dms/tdm_1122/messages \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "body": "Can you cover my on-call window tomorrow 09:00–12:00 UTC?" }'
```

Two rules keep DMs tidy. First, only the two participants can read, post to, or delete a DM thread — no admin can browse someone else's DM. Second, a DM you send is *not* marked read for the recipient until they call `POST /team-chat/dms/{threadId}/read` — so an unread DM badge means the other person has genuinely not opened it yet.

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/team-chat/dms/tdm_1122/read \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

Team Chat also carries emoji reactions (`POST`/`DELETE .../messages/{messageId}/reactions` on channels and DMs), so a 👍 on a handoff post can close the loop without another message in the channel.

## Permissions model

Team Chat has two role layers, and the difference matters in practice:

* **Per-channel role** (`owner` / `admin` / `member`) — set on the channel's membership. The creator of a channel becomes its `owner`; everyone else is added as `member` unless the owner promotes them.
* **Workspace role** (the org-level Admin or Owner set in **Settings > Team**) — moderates *every* team channel, independent of channel membership role.

| Action                                            | Channel member | Channel owner/admin | Workspace Admin/Owner                |
| ------------------------------------------------- | -------------- | ------------------- | ------------------------------------ |
| Read and post in channels they belong to          | Yes            | Yes                 | Yes                                  |
| Search messages across channels they belong to    | Yes            | Yes                 | Yes                                  |
| Start or join a huddle in channels they belong to | Yes            | Yes                 | Yes                                  |
| Edit or delete their own messages                 | Yes            | Yes                 | Yes                                  |
| Delete another member's channel message           | No             | Yes                 | Yes (any channel they belong to)     |
| Add or remove channel members                     | No             | Yes                 | No (unless also channel owner/admin) |
| Delete a channel                                  | No             | Owner only          | No (unless also the channel owner)   |
| Read or post to channels they do not belong to    | No             | No                  | No                                   |
| Read, post to, or delete a DM they are not in     | No             | No                  | No                                   |

Membership is the authorization boundary everywhere: a DM endpoint returns `403` for a non-participant, channel reads for a non-member return nothing, and search drops a channel out of results the moment you are removed from it — there is no separate revocation path to manage.

## Notifications and read state

Reading and attention tracking are designed for async teams:

* **Header notification** — when someone posts a channel message, every *other* member of that channel gets a notification in the app header (the bell and its counter). The same goes for a new DM. This is how a handoff post reaches the incoming responder even while they are looking at the Inbox.
* **Unread counts** — `GET /team-chat/channels` returns each channel with `unread_count` and `last_message_at`, so a list call alone is enough to render a badge. The unread count is measured against the caller's per-channel read cursor.
* **Mark a channel read** — `POST /team-chat/channels/{channelId}/read` advances the read cursor to now; the channel's unread count drops to zero until the next message arrives. DMs have their own equivalent, `POST /team-chat/dms/{threadId}/read`.
* **Presence and typing** — `POST /team-chat/presence/heartbeat` keeps the caller visible as online (and accepts a `status` such as away); `GET /team-chat/presence` returns the online snapshot plus last-seen timestamps; `POST /team-chat/presence` (delete-style) marks the caller offline on a graceful disconnect. `POST /team-chat/typing` broadcasts a typing indicator to a channel. These power the "online" dots and typing row in the UI — they carry no message content.

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/team-chat/presence/heartbeat \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "status": "online" }'
```

## Events and webhooks

Team Chat currently emits **no outbound webhooks** — there is no webhook event for a new channel message, a huddle, or a DM. The attention surfaces above (the header notification, unread counts, presence) are the integration contract. Two consequences for builders:

* To drive an external system (for example, pushing on-call chatter into your own status page), poll `GET /team-chat/channels` for unread counts and fetch message history per channel — search and history are keyset-paginated, so a poll loop with a cursor is cheap.
* Never point a customer-facing webhook consumer at Team Chat expecting events; the webhook catalog in [Webhooks](/webhooks/overview) covers customer-facing surfaces (messages, calls, inbox), not internal chat.

## Searching back through an incident

`GET /team-chat/search` finds messages by keyword across every channel the caller belongs to, ranked by relevance and keyset-paginated:

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/team-chat/search?q=inc_123" \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

Add `channel_id` to narrow to one channel, or `from`/`to` to bound the search to a date range — the classic post-incident move is "everything in `#on-call-platform` mentioning the incident id between its start and resolution." Search only ever covers channels the caller is a member of — it never surfaces messages from a channel they are not in.

## Endpoints

| Method             | Path                                                                            | Purpose                                                                                       |
| ------------------ | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `GET` / `POST`     | `/api/v1/team-chat/channels`                                                    | List your channels (with unread counts) / create a channel                                    |
| `DELETE`           | `/api/v1/team-chat/channels/{channelId}`                                        | Delete a channel (owner only)                                                                 |
| `GET` / `POST`     | `/api/v1/team-chat/channels/{channelId}/members`                                | List or add members (owner/admin)                                                             |
| `DELETE`           | `/api/v1/team-chat/channels/{channelId}/members/{userId}`                       | Remove a member (owner/admin)                                                                 |
| `GET`              | `/api/v1/team-chat/search`                                                      | Full-text search messages across your channels                                                |
| `GET` / `POST`     | `/api/v1/team-chat/channels/{channelId}/messages`                               | Message history / post                                                                        |
| `PATCH` / `DELETE` | `/api/v1/team-chat/channels/{channelId}/messages/{messageId}`                   | Edit / soft-delete your own message                                                           |
| `GET` / `POST`     | `/api/v1/team-chat/channels/{channelId}/messages/{messageId}/thread`            | Load a thread (root + replies)                                                                |
| `POST`             | `/api/v1/team-chat/channels/{channelId}/messages/{messageId}/replies`           | Reply in-thread                                                                               |
| `POST` / `DELETE`  | `/api/v1/team-chat/channels/{channelId}/messages/{messageId}/reactions[/emoji]` | Add / remove a reaction                                                                       |
| `POST`             | `/api/v1/team-chat/channels/{channelId}/read`                                   | Mark the channel read                                                                         |
| `GET` / `POST`     | `/api/v1/team-chat/channels/{channelId}/huddle`                                 | Get / start or join the active huddle                                                         |
| `POST`             | `/api/v1/team-chat/channels/{channelId}/huddle/leave`                           | Leave the huddle (ends it when the room empties)                                              |
| `GET` / `POST`     | `/api/v1/team-chat/dms`                                                         | List / open DM threads                                                                        |
| `DELETE`           | `/api/v1/team-chat/dms/{threadId}`                                              | Delete a DM thread (participant only)                                                         |
| `GET` / `POST`     | `/api/v1/team-chat/dms/{threadId}/messages`                                     | DM history / send                                                                             |
| `POST`             | `/api/v1/team-chat/dms/{threadId}/read`                                         | Mark a DM thread read                                                                         |
| `POST` / `GET`     | `/api/v1/team-chat/presence`                                                    | Presence heartbeat is `POST /presence/heartbeat`; `GET /presence` returns the online snapshot |
| `POST`             | `/api/v1/team-chat/typing`                                                      | Broadcast a typing indicator                                                                  |
| `POST`             | `/api/v1/team-chat/attachments`                                                 | Upload a file, then reference it in a message's `attachments` array                           |

## See also

* [Team Chat API reference](/api-reference/team-chat) — full endpoint and field reference
* [On-Call alerting guide](/guides/oncall-alerting) — build the rotation that pages the responders you coordinate with here
* [On-Call API](/api-reference/oncall) — resolve who is responding before starting a huddle
* [Inbox API](/api-reference/inbox) — the customer-facing conversation surface Team Chat is deliberately separate from
