# Microsoft Teams integration

> Announce query requests in a Teams channel and wire approvals through an HMAC-verified action endpoint.

- Documentation: Guides
- Updated: 2026-09-07
- Source: https://queryproxy.com/docs/teams-integration/
- Language: en-US
- Author: Muhammet ŞAFAK

---
The Teams integration has two halves: **announcements** into a channel via an
incoming webhook, and an **action endpoint** your automation (an outgoing
webhook or a Power Automate flow) can call to approve or reject.

## Announcements

1. Add an **Incoming Webhook** to your Teams channel and copy its URL.
2. In QueryProxy, open **ChatOps** (as DBA), paste the webhook URL and set an
   **HMAC secret** (any strong random string — you'll use the same secret for
   the action endpoint below). Both are stored encrypted.

New requests are posted as a card with the requester, connection, statement
count, a SQL preview and a **Review in QueryProxy** link. Decisions are
announced back to the channel as they happen.

## The action endpoint

To approve or reject *from* Teams-side automation, call:

```
POST https://your-queryproxy-host/webhooks/teams/actions
X-QueryProxy-Timestamp: <unix seconds>
Authorization: HMAC <base64(HMAC_SHA256("{timestamp}:{raw_body}", secret))>
Content-Type: application/json
```

```json
{
  "action": "approve",
  "request_id": 123,
  "actor_id": "<AAD object id>",
  "reason": "optional — used for reject"
}
```

- `action` — `approve` or `reject`.
- `request_id` — the request number from the announcement card.
- `actor_id` — the Azure AD **object id** of the approver. QueryProxy resolves
  it to a user through the **Teams ID** mapping an admin sets in
  **Admin → Users** (an unlinked id is rejected); it is never resolved by a
  self-declared email. The usual rules still apply: the resolved user needs the
  DBA role in the request's team and cannot decide on their own request.
- `reason` — optional for `reject`; defaults to "Rejected via Teams by
  &lt;name&gt;".

The signature is computed over `"{timestamp}:{raw_body}"` (the same
timestamped scheme as [Slack](/docs/slack-integration/)), base64-encoded, in
the `Authorization: HMAC …` header. A Power Automate flow can compute this
header directly.

## Security

- The `X-QueryProxy-Timestamp` header is required and must be within **±5
  minutes** of server time, so a captured request cannot be replayed later.
- An invalid or missing HMAC (or a stale/absent timestamp) returns `401` and
  changes nothing.
- The approver must be pre-linked through the **Teams ID** mapping — the shared
  secret alone cannot act as an arbitrary user.
- Responses are JSON (`{"ok": true|false, "message": "…"}`); authorization
  failures return `403` with the reason.
- The endpoint is rate-limited like every webhook in QueryProxy.

> Full interactive Adaptive Card buttons (as with
> [Slack](/docs/slack-integration/)) require a Teams bot registration and
> are on the roadmap; the action endpoint covers approvals-from-Teams today.
