# Approving SQL queries from Slack and Teams, safely

> How to approve production SQL from Slack or Teams without weakening the control, covering signatures, replay protection, tokens and identity mapping.

- Published: 2026-10-13
- Tags: chatops, access-control, audit
- Source: https://queryproxy.com/blog/approve-sql-queries-in-slack-teams/
- Language: en-US
- Author: Muhammet Şafak

---
You can approve production SQL from Slack or Teams safely if the button is not
trusted on its own. A click is only an HTTP callback, so the safety lives in what
the server checks before it acts: who signed it, how old it is, whether it
answers a real message, and which person it maps to. That is where chat
approvals usually go wrong, and where QueryProxy spends its effort.

## Why approve in chat at all?

Because a queue nobody opens is a queue that blocks. A DBA already lives in
chat, so the request arrives where they are, with the requester, the connection
and a preview of the SQL, and they decide in place. The
[approval workflow](/sql-query-approval-workflow/) is the same either way;
chat only changes where the decision is taken.

## What can go wrong with a chat button?

- **Forgery.** Anyone who finds the callback URL can post to it.
- **Replay.** A genuine approval, captured and sent again later.
- **Wrong person.** A valid signature says the caller holds a secret. It does not
  say a particular person clicked.
- **A stale message.** Someone decides a request twice from an old card.

## How does Slack approval work in QueryProxy?

New requests are announced in a channel with **Approve**, **Reject** and **Open
in QueryProxy**, and after a decision the message is updated so the buttons
disappear. Setup is in the
[Slack integration guide](/docs/slack-integration/). Each callback is verified
before anything happens:

- **Signature.** The `X-Slack-Signature` header must match the HMAC-SHA256 of
  the timestamp and raw body, signed with the app's signing secret. Slack
  describes the scheme in its guide to
  [verifying requests from Slack](https://docs.slack.dev/authentication/verifying-requests-from-slack/),
  including the `v0=` prefix. (Read on 2026-10-13.)
- **Replay window.** Slack's guide says to reject a request whose timestamp
  differs from local time by more than five minutes, and QueryProxy rejects
  requests outside plus or minus five minutes. (Read on 2026-10-13.)
- **Single-use token.** Each button carries a token bound to one request, valid
  for 24 hours and spent on the first recorded decision. A click that is turned
  away by authorization does not use it up.
- **Identity.** The Slack user must be mapped to a QueryProxy user with the DBA
  role in the request's team. An administrator fills each reviewer's Slack
  member ID, which is what ties a click to a person.
- **Rules.** No self-approval, exactly as on the web.

A forged or expired callback is refused and changes no state.

## Why is the secret administrator-only?

Because of what an HMAC can and cannot say. The
[guide](/docs/slack-integration/) is blunt about it: it proves the caller knows
the secret, never that a particular person approved. If a DBA could choose the
secret, they could sign a callback approving their own request in a colleague's
name, and the trail would record the colleague. So only a system administrator
can set or rotate it, and rotating needs the current one.

## What about Microsoft Teams?

Teams works differently, and it is worth being plain about it. QueryProxy posts
a card for each new request with a **Review in QueryProxy** link, and announces
decisions back to the channel. Approving from Teams happens through an
[action endpoint](/docs/teams-integration/) that your automation, such as a
Power Automate flow or an outgoing webhook, calls with an HMAC-signed request.
The approver is identified by an Azure AD object ID that an administrator has
linked to a user. Full interactive card buttons, as in Slack, need a Teams bot
registration and are on the roadmap.

If you want a click-to-approve flow in Teams today, you build it in your
automation. If you do not want to, approve from the web.

## Does the trail record chat decisions?

Yes: a decision is written to the audit log with its channel, so a reviewer can
tell a web approval from a Slack one. See
[what a production query audit trail should record](/blog/production-query-audit-trail/)
and the [SOC 2 page](/soc-2-database-access-controls/) for how that maps to
evidence.

## What are the limits?

A rejection from Slack records only a generic reason, so write a justification
in the web UI when one matters. The SQL preview in a card is cut at 400
characters, and you can turn it off with `QUERYPROXY_CHAT_INCLUDE_SQL=false` if
chat history is not a place you want statements to land. Buttons in messages
posted before the single-use tokens were introduced no longer work.
