Skip to content

Answer gates from Slack

This guide makes realm agent post each human gate to a Slack channel and take the answer from a reply in the thread. At the end, a person approves or rejects from Slack and the run carries on within a second or two.

You need:

  • a workflow with a human gate, driven by realm agent. See Add a human gate;
  • a Slack workspace where you may create an app.

When a gate opens, realm agent posts a message to a channel through a Slack app you own, and opens a connection to Slack to listen for replies in that message’s thread. This connection is Slack’s Socket Mode: it goes out from your machine, so you need no public address. When someone replies with one of the gate’s choices, Realm records the answer and the run continues.

Go to api.slack.com/apps and create an app in your workspace. Give it four settings:

Where in the app’s settings Setting
OAuth & Permissions → Bot Token Scopes chat:write and channels:history
Socket Mode Enable Socket Mode: on
Event Subscriptions Enable Events: on
Event Subscriptions → Subscribe to bot events message.channels

The last two are easy to miss. Without the event subscription, Slack never tells Realm about a reply, and the gate stays open.

Then install the app to the workspace, and add it to the channel where gates should appear.

  • The bot token, from OAuth & Permissions. It starts with xoxb-.
  • An app-level token, from Basic Information → App-Level Tokens. Socket Mode uses it to open the connection. It starts with xapp-.
  • The channel ID, from the channel’s details in Slack. It looks like C08V0S1ABCD.

Put them in a file named .env beside your workflow. Keep this file out of version control.

SLACK_BOT_TOKEN=xoxb-your-bot-token
SLACK_APP_TOKEN=xapp-your-app-token
SLACK_CHANNEL_ID=C08V0S1ABCD

Create realm.yaml beside your workflow, or add this block to it:

version: 1
notifiers:
slack_gate:
type: slack
config:
bot_token: '${secret:SLACK_BOT_TOKEN}'
channel_id: '${secret:SLACK_CHANNEL_ID}'
app_token: '${secret:SLACK_APP_TOKEN}'

${secret:NAME} is replaced with the value from .env. Realm reads Slack settings only from this block. Setting SLACK_… variables in your shell has no effect.

This guide uses a workflow with one gate:

id: slack-test
name: Slack gate test
version: 1
steps:
review:
description: A person decides from Slack whether the message goes out.
execution: auto
trust: human_confirmed
gate:
choices: [send, discard]
message: |
Realm docs test. Reply in this thread with one word: send or discard.
resolution_messages:
send: 'Recorded: send.'
discard: 'Recorded: discard.'
send:
description: Send the message.
execution: auto
depends_on: [review]
when: "review.choice == 'send'"

Start it with realm agent. Use --register, so that the gate can also be answered from the command line:

Terminal window
realm agent --workflow ./ --register --provider anthropic --model claude-sonnet-5-5

This workflow’s steps call no model, but realm agent still needs a model provider key and --model: it checks them before it reads the workflow. The command uses Anthropic and the model claude-sonnet-5-5; with an OpenAI key, use --provider openai --model <a model from OpenAI's list> (OpenAI’s models). Version 0.45.0 has a default model, so --model can be left out there.

It prints:

Realm Agent — Slack gate test v1
Run ID: 71b0ed55-f198-41b1-a6e2-e9234b1fab1c
→ [auto] review
⏸ Gate: review | ID: cc12fb7f-4157-4502-b41b-e07e2582f919
Realm docs test. Reply in this thread with one word: send or discard.
Waiting for approval...
ℹ Socket Mode connected.

Notice Socket Mode connected. Realm is now listening. In Slack, the channel shows a message headed “Workflow gate waiting for approval”, with the gate’s name, your gate message, and the choices.

In Slack, open the message’s thread and reply with one of the choices:

send

Realm replies in the thread with the line you set in resolution_messages:

Recorded: send.

In the terminal, the run carries on:

→ [auto] send
✓ → completed
Run complete: 71b0ed55-f198-41b1-a6e2-e9234b1fab1c

Two rules about replies:

  • The reply must be in the thread. A new message in the channel is ignored.
  • The reply must be exactly one of the choices. Capital letters do not matter, so Send works. yes or looks good, send it do not. For a reply that matches no choice, Realm answers in the thread with Please reply with one of: send, discard, at most twice.

The run record keeps the choice. It does not keep who replied. Anyone who can post in the channel can answer a gate, so post gates to a channel whose members you trust to decide.

If nobody answers, Realm posts a reminder in the thread after 10 minutes and an escalation after 30. Change either under the same config block, in milliseconds:

reminder_interval_ms: 300000
escalation_threshold_ms: 900000

These timers live in the realm agent process. If that process stops, the reminders stop.

Every Slack gate can also be answered with realm run respond. The Slack message includes the command for each choice. realm agent notices the answer and carries on, whichever way it arrived.

  • Post only. Leave out app_token. Realm posts the gate to Slack and prints that it is using the terminal: the answer has to come from realm run respond. With only webhook_url set, Realm posts through a Slack incoming webhook in the same one-way manner.
  • Events API. Set signing_secret and events_port in place of app_token. Slack then sends replies to an address you expose, and Realm checks each request’s signature. Use this only if you cannot use Socket Mode. app_token and signing_secret cannot be set together.
  • Error: realm agent requires an LLM API key. Set OPENAI_API_KEY or ANTHROPIC_API_KEY. Nothing was started. realm agent needs a model provider key to start, even for a workflow whose steps need no model. Set one of the two variables.
  • Error: --model is required: realm has no default model. ANTHROPIC_API_KEY is set, so the provider is Anthropic; name one of its models (Anthropic lists them at https://platform.claude.com/docs/en/models/overview). Nothing was started. realm agent needs --model to start, even for a workflow whose steps need no model. Add --model with a model the provider lists. The message names the provider Realm chose; it reads differently when another key is set or --provider is given. Version 0.45.0 does not print it: it has a default model.
  • Workflow not found: slack-test — most often this run was created from a file without --register. You answered with realm run respond, but the workflow was started from a file and never registered. Run realm workflow register ./, then answer again.
  • The gate message appears in Slack, but a reply does nothing. Check that the reply is in the thread, that it is exactly one of the choices, and that the app’s Event Subscriptions include message.channels.