# Microsoft Teams

Connect a Microsoft Teams bot so chats and channels start and continue Kortix sessions.

Canonical page: https://dosco.live/docs/connect/teams

Microsoft Teams is a channel: an `@`-mention in a Teams chat or channel starts
a [session](/docs/work/sessions), and the agent replies right there with live
progress. It follows the same session, identity, and credential rules as
[Slack](/docs/connect/slack); this page covers what is specific to Teams.

Teams is experimental — enable **Microsoft Teams** under
[Settings → Feature flags](/docs/feature-flags) on the project first.

## Connect

Two ways, both from **Settings → Channels**:

- **One click (managed).** A Teams **tenant admin** clicks **Add to Teams**,
  signs in, and consents. Kortix publishes the app to your org's Teams catalog
  automatically — there is nothing to build or paste. The row then offers
  **Add to Teams** (a deep link into the Teams client) so anyone can add the
  bot to a chat or channel. If a later version of the app changes, click
  **Publish to your Teams catalog** again to push the update.
- **Bring your own bot.** Click **Use your own Microsoft Teams app** for a
  three-step wizard: register an Azure Bot, connect it to Kortix with its app
  id, client secret and tenant id, then download the generated app package and
  upload it to Teams. Use this on self-hosted deployments or when you run your
  own registration.

  The order matters. Kortix builds the manifest from **your** app id, so it can
  only generate the package after step 2 — the wizard shows you the messaging
  endpoint to paste into Azure before it asks for anything else.

The CLI mirrors the dashboard:

```bash
kortix channels connect --platform teams      # prints the admin-consent URL
kortix channels status --platform teams        # tenant + catalog + publish state
kortix channels disconnect --platform teams
```

## Chats, channels, and mentions

- **Personal chat:** every message reaches the bot.
- **Team channel / group chat:** a new post needs an `@`-mention. After that,
  a reply in a conversation the bot already owns continues the session
  **without** one, the way a Slack thread does. That needs the app's
  `ChannelMessage.Read.Group` (channels) and `ChatMessage.Read.Chat` (group
  chats) permissions, which the tenant admin consents to when the app is added.
  If the app was added before these were requested, remove and re-add it to
  pick up the new consent.

Each chat or channel conversation is its own session. The first message creates
it; later messages continue it, even after the sandbox stops and resumes. A
tenant connected to more than one project shows a **project picker** on the
first unrouted message, and remembers the choice.

In a channel, every new post starts its own session. A personal or group chat
is one conversation for its whole life, so it keeps one session until you send
`/new`. Put a message after it (`/new summarize today's deploys`) to start the
new session with that message. The previous session stays in Kortix, with its
full history, and the confirmation links to it.

## Who may join a session

Each conversation has a join policy, set with `/policy` in Teams or from the
bindings table on the Channels page:

- **open** (default) — any linked project member may join a session started here.
- **approval** — the session owner gets an Approve / Deny card for each new person.
- **owner** — only the person who started the session.

## Identity linking

The sender links their Kortix account once with `/login`; the agent then runs
as them, with their own credentials, secrets, and connected apps. An unlinked
sender, or one without access to the project, gets a card to connect or request
access instead of a session.

## Commands

Type these in a chat with the bot (in a channel, `@`-mention it first):

| Command | What it does |
| --- | --- |
| `/help` | List the commands |
| `/login`, `/logout`, `/whoami` | Link, unlink, or show your Kortix account |
| `/status` | The effective project, agent and model here, and the run in progress |
| `/stop` | Stop the run in progress here |
| `/new [message]` | Start a new session in this chat (personal and group chats) |
| `/models`, `/model <ref>` | Pick the model for this conversation |
| `/agents`, `/agent <name>` | Pick the agent for this conversation |
| `/projects`, `/use <name>` | List connected projects, or point this one at another |
| `/policy [open\|approval\|owner]` | Who may join sessions started here |

## While the agent works

The agent answers in one card: its steps while it works, then the reply. A
long reply that does not fit in one Teams message is cut at a line, marked as
cut, and links to the full session in Kortix.

Send another message while it works and it waits for the current run, then
gets its own card. A run can take as long as it needs: a card is closed as
dead only when the runtime reports that the run has ended, not because a long
step posted nothing for a while.

## Stopping a run

While the agent works, its live card carries a **Stop** button. `/stop` does
the same thing after the card has scrolled out of reach. Either ends the run at
the runtime, not just on the card — a run that is left open swallows the next
message in the conversation instead of answering it.

The person who sent the message being answered can stop it, as can anyone
already working in that session. A bystander in the channel cannot.

## Files

The agent sends files with `teams send --file`, and Kortix picks the delivery
for the conversation:

- **An image is shown inline** — in a personal chat, a group chat, or a
  channel. If it is too large for Teams to carry inline, a personal chat falls
  back to a consent card (accept, and it lands in your OneDrive) and a channel
  falls back to the team's files. A group chat cannot receive file transfers,
  so an oversized image there comes back to the agent as "send a smaller one".
- **Any other file** arrives as a consent card in a personal chat, or as a
  SharePoint link when it is shared to a team.

Inbound files and pasted images are available to the agent to download and
work with — including images pasted into a channel or a group chat, which Teams
delivers inside the message body rather than as an attachment.

Paste a screenshot and ask about it and the turn runs on a model that can read
images, even when the conversation's own model is text-only — the session's
model pick is left alone.

## Questions and forms

The agent asks with controls, not prose — the same shape as a question in the
Kortix app:

- **One question, up to six options** — a button per option. One tap answers.
- **Several questions, a pick-several question, or a longer list** — one form:
  each question with its options listed, a box for an answer of your own, and a
  **Send answers** button.
- **A date, a time, a number, or an on/off choice** — a form with the matching
  input.

Your answer reaches the agent as your next message, labelled with the question
it answers, and the session continues from there. You can always ignore the
card and type your answer instead.

## Approvals

When an agent needs a human decision before it proceeds — a risky step, a
change it wants signed off — it files a review item. If the session was started
from this conversation, the decision lands here as a card: the title, a summary,
a risk level, and **Approve**, **Request changes** and **Deny**, plus a link
into Kortix.

The card carries an optional feedback box. Whatever you type is stored with the
decision and passed to the agent with it, so "Request changes" does not cost a
second round trip to ask what you meant. It is optional by design — a plain
Approve should not need typing.

Anyone in the conversation sees the card, but acting on it needs a linked
Kortix account with access to the project.

## Posting on its own

Besides replying where it was asked, the agent can post into any chat or
channel the bot is already in for this project — a nightly summary, an alert,
a hand-off. It cannot reach a conversation it was never added to.

## When a session will not start

Kortix says what is wrong instead of asking you to try again, because most
start failures never clear on a retry: a sandbox template that no longer
exists, an account that is not linked, a project you cannot reach, or a
workspace out of credits. Each one names the fix and the command that applies
it.

If the agent set for the conversation has been deleted or renamed, the failure
arrives as a picker: choose a live agent, then send your message again. The
list only offers agents you have access to.

## Reading a channel

In a channel the bot only receives messages that mention it, so the discussion
it was asked about is not in its session. When it needs that context, the agent
reads the thread it was mentioned in, or the channel's recent posts, through
the `ChannelMessage.Read.Group` permission the app requests when it is added to
a team. A team that added the app before that permission was requested must
remove and re-add it. In a personal chat every message already reaches the
session, so there is nothing earlier to read.

## Limits

Teams bots cannot add message reactions, send ephemeral (only-you) messages, or
search messages — those Slack affordances have no Teams equivalent. The agent
can read a channel only in a team the app was added to.
