# Approvals in Slack and Teams

> Approval requests reach people where they already are, with Approve and Deny in the message. A decision in chat is the console's decision, with every rule the console applies.

Source: https://immiscible.fly.dev/docs/guides/approvals-in-chat

When a decision is `approval_required`, Immiscible posts the request to your approvals channel in Slack or Microsoft Teams, and to each named approver by direct message in Slack. Escalations, freezes, incidents and kill-switch drill results post to the same channel.

## The same decision, not a shortcut

Approving in chat applies every rule the console applies:

- The person who clicks is mapped to a member of the workspace by **verified email** (or Entra object id in Teams). An unmapped account cannot decide.
- They must be able to decide for that agent: an owner or admin, the member it acts for, or only the named approvers when the workspace has them.
- **Separation of duties.** Above the workspace's line, neither the person the agent acts for nor whoever wrote its mandate may approve.
- A **frozen** agent's request cannot be approved.
- The request is checked against the **mandate again** at the moment of approval.

If a click is refused, the person who clicked sees why, privately, and nothing changes. Every decision records the channel (`slack` or `teams`) and the chat user id, on the approval and in the evidence ledger, and the message is updated to say who decided, however they decided.

## The chat line

Above a line you set, chat refuses to approve and links to the console instead, where the person's own signed-in session (and their two-factor) stands behind the click, not a chat account. The line is in reference pence: `5000` is about fifty pounds in any currency. A data release has no amount, so with a line set it is always approved in the console. Denying is never stepped up.

```bash
curl -X PUT "https://immiscible.fly.dev/api/w/$IMMISCIBLE_WORKSPACE/chat/settings" \
  -H "cookie: __Host-sid=$IMMISCIBLE_SESSION" -H "x-immiscible-csrf: 1" \
  -H "content-type: application/json" \
  -d '{ "chatStepUpAbove": 5000 }'
```

## Which to use

| | Slack | Microsoft Teams |
|---|---|---|
| How it connects | a Slack app, installed by OAuth | a Workflows (Power Automate) webhook and a signed callback relay |
| Direct messages to named approvers | yes | no, the channel only |
| Messages updated in place | yes, by Immiscible | by your flow, from the callback's answer |
| Slash command | `/immiscible status`, `approvals`, `freeze` | none |
| How requests from chat are trusted | Slack's v0 signature, five minute window, each accepted once | an HMAC with a per-workspace secret, plus a token on each card button bound to the approval and decision |

- [Set up Slack](https://immiscible.fly.dev/docs/guides/slack.md): Create or install the app, choose the channel, map members, set the line.
- [Set up Microsoft Teams](https://immiscible.fly.dev/docs/guides/teams.md): Build the flow, run the relay, map members by Entra object id.

## Freezing from Slack

`/immiscible freeze <agent> <reason>` freezes an agent under a **security hold**. Only the agent's kill owner, a security lead, or a workspace owner or admin can, and the reason is recorded. Lifting the hold happens in the console, under the console's rules: see [holds and drills](https://immiscible.fly.dev/docs/guides/holds-and-drills.md).

> **Note**
> Disconnecting chat never loses a request. Approvals still arrive by email and wait in the console.
