ShiftLabs Start free

How to approve production deploys in Slack

This guide builds a workflow that asks for approval in Slack before a production deploy, then runs the deploy. It takes one workflow file and the Slack connection in Settings → Approvals.

What you build

Before you start

1. Connect Slack

  1. In Settings → Approvals, an admin selects Connect Slack and allows the app.
  2. Pick the channel for approval requests. For a private channel, run /invite @shiftlabs there first.
  3. Each approver selects Link my Slack account and signs in with Slack.

Choose a channel only approvers read.

2. Write the workflow

TypeScript
// workflows/deploy.ts
import { workflow } from "shiftlabs";

export default workflow<{ service: string; version: string }>(async ({ payload, step }) => {
  const decision = await step.waitForApproval("deploy", {
    message: `Deploy ${payload.service} ${payload.version} to production?`,
    timeout: "4 hours",
    approvers: ["role:admin", "role:manager"],
  });
  if (!decision.approved) return { deployed: false, note: decision.note };
  await step.exec("deploy", {
    command: ["kubectl", "set", "image", `deployment/${payload.service}`, `app=${payload.version}`, "-n", "apps"],
  });
  return { deployed: true, by: decision.by };
});

In Python:

Python
from shiftlabs import workflow


@workflow
async def deploy(ctx):
    service, version = ctx.payload["service"], ctx.payload["version"]
    decision = await ctx.step.wait_for_approval(
        "deploy",
        message=f"Deploy {service} {version} to production?",
        timeout="4 hours",
        approvers=["role:admin", "role:manager"],
    )
    if not decision["approved"]:
        return {"deployed": False, "note": decision["note"]}
    await ctx.step.exec(
        "deploy",
        command=["kubectl", "set", "image", f"deployment/{service}", f"app={version}", "-n", "apps"],
    )
    return {"deployed": True, "by": decision["by"]}

The kubectl line is an example. Swap in your own deploy command.

3. Deploy it

Shell
npx shiftlabs deploy

Start a run with Run now in the dashboard, the API, a webhook from your CI, or an agent's tool call.

What the approver sees

The request arrives in the channel with the workflow's message and Approve and Deny buttons. The approver can write a note. The message updates when the request is answered anywhere: in Slack, in the dashboard or with the API. A workflow's message never shows the value of a secret your workspace stores.

When the answer is no, or nobody answers

Require two approvers

Pass approvals: 2 to need two different members. The run waits for the last approval. An admin can also set a workspace minimum. See Two approvers.

Deploy through GitHub Actions instead

The github-actions-runbook template starts .github/workflows/shiftlabs-runbook.yml in your repository after the approval, then waits for a signed result. Your deploy credentials stay in GitHub secrets.

Shell
npx shiftlabs init --template github-actions-runbook

What it costs

On Free, each deploy is one run of the 1,000 a day. Waiting for an approval is not a step, and it counts toward the run's lifetime of 168 hours. A runner command uses no CPU time of your plan. Free allows commands up to 30 minutes; Pro allows 12 hours. See Pricing.