Skip to content
Anlyon
Esc
↑↓navigate↵open⌘Jpreview
On this page

Slack approvals

Post each environment's approval requests to a Slack channel with Approve and Deny buttons, so your team decides where it already works, with every click checked like a vote in the console.

An approval request can be posted to a Slack channel with Approve and Deny buttons. The message shows the exact request the approver is deciding. A click is a vote, cast through the same path as a vote in the console, so expiry, quorum, one vote per decider, the binding and the self-approval rules all apply unchanged. Slack only adds one question: which workspace member is clicking.

You decide an approval with the buttons on a Slack request, in the console, or through the API. Email, Discord and webhook alerts only notify. Replying to a message does not decide anything.

Why decide in Slack

A request that waits for someone to open the console waits longer, and a request that waits too long expires and is not sent. Posting it where your team already works shortens the wait without weakening the check: the person clicking is matched to a workspace member, their role in that environment is checked, and the self-approval rule applies exactly as in the console. You do not build a Slack bot, an interaction endpoint or an identity mapping.

Slack approvals carry your team’s approvals only. An approval your end user decides (decidedBy: "end_user") is never posted to Slack.

Set up Slack approvals

  1. Connect Slack. An admin connects the Slack workspace in the console under Settings → Notification channels → Slack and Discord. This is the same Slack app that posts alerts. It asks for the bot scopes users:read and users:read.email, so Anlyon can read a clicker’s Slack profile.
  2. Choose a channel per environment. Under Settings → Notification channels → Slack approvals, pick a channel for each environment that should post there. An environment has at most one channel. An environment with no channel posts nothing interactive. The bot joins a public channel by itself. For a private channel, invite the bot to it.
  3. Link Slack accounts where needed. A full member of the connected Slack workspace is matched by email (below) and needs no step. Anyone else links once: Link your Slack account on the same screen.

A Slack app installed before 2026-10-19 lacks users:read.email. Until an admin reconnects Slack, members are matched only by an explicit link.

Read what the message shows

The message carries what the console’s approval inbox shows:

  • The environment first and in words. A production environment is flagged.
  • The action (or “Custom request”), the approvals so far against the number required, when the request expires, and the approval id.
  • For a governed action, its bound preview: the preview’s fields and text diffs, the expected impact, the impact limits at review time, and the start of the binding digest. For an action without a preview, the method, the URL template and the input. Anything too long to show ends with a pointer to the console.
  • The buttons: Approve, Deny, Decide with a note and Open in Anlyon. Approve asks for a confirmation that names the environment. A note is at most 1,000 characters.

Every string an agent supplied is escaped, so a payload cannot mention @channel, render a link that looks like a button, or close a code block.

Approve is offered only while the bound preview is loaded, matches the approval’s binding digest, and has not expired. That is the same rule as the console’s preview gate. It is checked again when the click arrives, so a stale or forged Approve is refused. Deny is always available.

The message is updated in place on every vote, decision and expiry, wherever the decision was made. A closed message names who decided, as the member and the Slack account that clicked, and shows the note.

Control who can decide from Slack

Anlyon maps the Slack user to a member of the workspace, in this order:

  1. An explicit link. The member signed in with Slack from the console. A link can only be started from the console, by the member it names. Anlyon never sends a link from Slack.
  2. A verified email, for a full member of the connected Slack workspace only. Slack must report the address as confirmed, and it must equal (ignoring case) the verified email of an Anlyon user who is a member of this workspace.

A Slack guest is never matched by email, and nor is a user from another Slack organisation in a shared channel. They link their account first. A click that cannot be matched gets a reply only the clicker sees, pointing to the console to link.

Then the same checks as the console apply:

  • Role in the approval’s environment. The member’s role there, including a per-environment role, must allow deciding: admin, member or approver. A viewer, or an approver granted only in another environment, is refused before any vote is cast.
  • Requesters. The key or app that requested an approval never decides it. The member who requested it from the console, or whose app or API key requested it, does not decide it either, with one exception: when they are the only member who can decide approvals in that environment, they may decide it directly, in the console or with a Slack button. That vote is marked Self-approved (only approver in this environment) on the Slack message, in the console, in the API (selfApproved: true) and in the audit record. Once a second member can decide in that environment, the request answers with the requester refusal.
  • Single sign-on. In a workspace that requires single sign-on, deciding from Slack needs a current, unexpired sign-in to Anlyon through the workspace’s identity provider. Admins with emergency access are exempt. Someone the identity provider signed out stops deciding from Slack when that session ends.
  • Races. An approval already decided elsewhere, expired, or already voted on by this member is refused with a precise reply. A vote in the console and a vote in Slack by the same member are one vote.

Every attempt, recorded or refused, is kept with the Slack user, the member it was matched to, how (link or email), and the reason for a refusal.

A Slack decision is as strong as the Slack account that makes it. If you do not trust Slack account security for production approvals, do not route production to Slack, or require more than one approval with a policy.

Avoid duplicate Slack messages

When an environment has a Slack approvals channel, the plain “approval requested” alert skips that same channel, so it is not posted twice. Every other Slack channel, Discord, email, the notification webhook and the in-app inbox still get the alert. If Slack approvals cannot run (the console says so on the Slack approvals screen), nothing interactive is posted and the alert goes to that channel as before.

Posting is best effort and never slows or fails an approval. If a post fails, the approval is still waiting in the console.

Removing an environment’s channel stops posting and also stops Slack decisions for that environment, including on messages already posted. A click then answers that Slack approvals are off for the environment.

Configure Slack approvals over the API

These routes are console operations that also accept an API key. A key needs integrations:read to read and integrations:write to change, both privileged scopes that no key has by default, and changes only its own environment’s channel. A console session needs the admin role to change a channel.

# Readiness, the connected Slack workspaces, each environment's channel, and your own links
curl https://api.anlyon.com/api/v2/slack-approvals \
  -H "Authorization: Bearer $ANLYON_OPERATOR_KEY"

# Post this environment's approval requests to a channel
curl -X PUT https://api.anlyon.com/api/v2/slack-approvals/routes/$ENVIRONMENT_ID \
  -H "Authorization: Bearer $ANLYON_OPERATOR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "integrationId": "SLACK_CONNECTION_UUID", "channelId": "C0123456789", "channelName": "prod-approvals" }'

# Stop posting, and stop accepting decisions from Slack, for this environment
curl -X DELETE https://api.anlyon.com/api/v2/slack-approvals/routes/$ENVIRONMENT_ID \
  -H "Authorization: Bearer $ANLYON_OPERATOR_KEY"

integrationId is the Slack install listed under installs in the GET response. channelId is a Slack channel id (starting with C or G). The PUT response says whether the bot joined the channel and whether it is private.

Linking takes a signed-in console session, because an API key has no Slack account:

  • POST /api/v2/slack-approvals/identity/start returns an authUrl for Sign in with Slack. Finish it in the same browser, signed in as the same member. The Slack account must belong to a Slack workspace connected to this Anlyon workspace.
  • DELETE /api/v2/slack-approvals/identity removes your links in this workspace.

Fix what goes wrong

What you see Why What to do
A private reply asking you to link your Slack account Your Slack user is a guest, from another Slack organisation, or has no confirmed email matching a member. Use Link your Slack account in the console.
A private reply that you cannot decide Your role in the approval’s environment is viewer, or you requested it and another member can decide. Ask a member with a deciding role.
No Approve button, only Deny The bound preview is not loaded, does not match the approval’s digest, or has expired. Open the approval in the console, which shows the preview state. Deny stays available.
A reply that Slack approvals are off The environment’s channel was removed. Decide in the console, or set the channel again.
Nothing is posted The environment has no channel, or the deployment cannot take Slack decisions (the Slack approvals screen says why). The plain alert still goes to the channel. Set a channel; reconnect Slack if the screen asks.
403 AUTHORIZATION_ERROR from the API The key lacks integrations:write, or the console member is not an admin. Use an operator key or an admin session.

Was this page helpful?