> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.runtype.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.runtype.com/_mcp/server.

# Setting up a Slack Surface

Set up a Slack Surface to connect a Product to Slack and route incoming messages to a Capability.

## Before you begin

Create a Product and decide which Capability handles Slack messages. Make sure that you can create and install a Slack app in your Slack workspace. You must install the app to the same workspace that you select when you create it.

## Create the Slack Surface

To create the Slack Surface and its Slack app, follow these steps:

1. Open the Product that you want to connect to Slack.
2. Go to **Surfaces** and click **Add Surface**.
3. Select **Slack**.
4. Enter a name for the Slack app.
5. Click **Copy manifest**.
6. Click **Open Slack app creation**.
7. In Slack, under **Or start your own way**, select **From a manifest**, then **Continue**. Don't choose the AI agent or Starter app template.
8. In the **JSON** tab, replace the entire Demo App sample with the manifest. Select your **Workspace** on the same screen.
9. Click **Next**, review the settings, and create the app.
10. Return to Runtype and click **App created, continue**. You don't need to wait for an automatic webhook check.

If you already created the app, keep using it. Update its settings under **Features > App Manifest** instead of creating another app.

## Connect the Slack app

To connect the Slack app to the Surface, follow these steps:

1. In Slack, open the app that you created.
2. Go to **Basic Information** and find **App Credentials**.
3. Copy **Client ID**, **Client Secret**, and **Signing Secret** into the Runtype setup wizard.
4. Click **Add to Slack**. A popup opens Slack's authorization page.
5. Approve the requested permissions in Slack.

Runtype completes the install and shows the connected workspace when setup finishes. Runtype stores the bot token on the server and doesn't send it through your browser.

## Connect with a coding agent

You can start the same setup from a coding agent connected to the [Runtype MCP server](/developer-guides/integrations/runtype-mcp-server). Ask the agent to connect your Slack Surface, and it follows the `slack-setup` documentation topic, which is the full runbook. The agent's API key must have the `INTEGRATIONS:WRITE` scope. Test keys don't have it.

The agent does the tool work. You do two things in your browser:

1. Open the connect page returned by `get_slack_app_manifest`. Surface-linked connect pages use the same setup wizard as the Product panel: name the app, copy its manifest, and create it in Slack. If you already created it from the agent's manifest, reuse that app and continue to the connection step.
2. Paste the **Client ID**, **Client Secret**, and **Signing Secret** into the wizard's connection step and click **Add to Slack**.

The connect page is a normal signed-in page in your dashboard. It has no shareable setup link, because the page collects secrets. If the connect link expires, ask the agent for a new one. You don't need to create the Slack app again.

The agent then calls `get_surface_setup` with the `product_id` and `surface_id` until it reports `ready`. `ready` means that the install finished and the Surface has the workspace details. To check every Surface on the Product at once, the agent calls `get_product_setup`.

> **Warning**
>
> Never paste your Slack Client Secret, Signing Secret, or bot token into a chat with an agent. The
> connect page is the only place those values belong, and no Runtype tool asks for them. If one ends
> up in a conversation, finish setup and then rotate it from the Surface's Reconfigure panel.

If the Surface was paused, the install still records the workspace details but leaves it paused. Un-pause it yourself when you're ready for it to take traffic.

## Test the connection

To verify the connection, follow these steps:

1. To test in a channel, invite the app first. In the channel, run `/invite @APP_NAME`, where `APP_NAME` is the name of your Slack app.
2. Send a direct message to the Slack app, or mention it in the channel.
3. Confirm that Slack displays the response.

If you work with a coding agent, it can confirm that the message arrived with `list_messaging_conversations`. It can show what the Surface did with the message with `trace_conversation`.

Start with direct messages during testing. After the response looks right, expand to channels or mention-based workflows.

## Choose where the Surface listens

A Slack Surface receives direct messages, channel messages, mentions, and thread replies. After you connect the Surface, attach the Capability that handles incoming Slack messages. If you haven't attached a Capability, see [Adding capabilities to a Product](/user-guide/products-surfaces/adding-capabilities-to-a-product).

The Surface's inbound settings control which Slack messages reach the Capability. The Surface ignores a message that these settings filter out, and Slack shows no notice. The following table lists the settings:

| Dashboard setting        | API field           | Default | Behavior                                                                                                                             |
| ------------------------ | ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| **Listen to DMs**        | `listenDms`         | On      | Receives direct messages to the app.                                                                                                 |
| **Continue in threads**  | `continueThreads`   | On      | Keeps replying in threads where the bot was already mentioned. Shown when the mode isn't **Every message**.                          |
| **Specific Channels**    | `listenChannels`    | Empty   | Takes comma-separated channel IDs, such as `C0123456789`, not channel names. Empty means all channels.                               |
| **Trigger on reactions** | `triggerOnReaction` | Off     | Sends an emoji reaction to the Capability as a message such as `:thumbsup:`.                                                         |
| **Allowed Reactions**    | `reactionAllowList` | Empty   | Takes comma-separated emoji names that trigger the Capability. Empty means all reactions. Shown when **Trigger on reactions** is on. |

## Choose how the Surface responds

The **Channel response mode** setting decides when the Surface answers a channel message that doesn't mention the bot. Set it in the Surface's inbound settings, or set `inbound.channelResponseMode` when you create or update the Surface through the API. The following table lists the modes:

| Dashboard option                                            | API value | Behavior                                                                                                                                                                                                                                                          |
| ----------------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Mention only**                                            | `mention` | Answers mentions, direct messages, and thread replies in threads where the bot already has a conversation. This is the default for new Surfaces.                                                                                                                  |
| **Every message**                                           | `always`  | Answers every message in the channels that the bot listens to.                                                                                                                                                                                                    |
| **Judged (preview)**, shown only when the API set this mode | `judge`   | Set through the API, in preview. Answers mentions, direct messages, and continued threads. For any other channel message, a judgment model decides whether a colleague in the bot's role would reply now. If the judgment fails, the Surface waits for a mention. |

> **Warning**
>
> **Every message**, `judge` mode, and shadow mode (`responseGateShadow`, described in [Judge mode](#judge-mode)) require the Slack app to be installed with the `channels:history` scope.
> Without it, the Surface only receives mentions, so `judge` behaves as **Mention only** and shadow
> mode records nothing, and the setup status shows a warning. Reinstall the Slack app from the
> dashboard to grant the scope.

### Judge mode

In `judge` mode, one judgment call decides each unmentioned channel message. The judgment reads the product's name and description, the Surface's capabilities, the start of the Agent's instructions, your rules, and the recent channel messages. These optional `inbound` fields shape the decision:

* **`responseRules`**: free text, up to 2,000 characters, that tells the judgment when the bot should speak. For example: `Answer questions about resetting Acme account passwords. Stay out of social chat.`
* **`eagerness`**: `conservative`, `balanced` (the default), or `eager`. A more eager setting replies at a lower judged probability.
* **`onUncertain`**: what to do when the judgment falls between its reply and silence thresholds. `silent` (the default) skips the message. `reply` answers it.

A skipped message creates no conversation. You can read the recent decisions for a channel in any of these ways:

* Call `GET /v1/surfaces/{surfaceId}/channel-context?channelId=CHANNEL_ID`, where `CHANNEL_ID` is the Slack channel ID.
* Call the MCP tool `get_surface_channel_context`.
* Call `getSurfaceChannelContext` in Code Mode.

Each message carries its decision and, when the judgment answered, the probability. The decision is one of these values:

* `reply`: the bot answered.
* `silent`: the bot stayed silent.
* `band`: the judgment fell between the reply and silence thresholds, and `onUncertain` decided.
* `not_judged`: no judgment answered. Either the mode doesn't ask for one, or the judgment failed and the Surface waited for a mention.

When a judgment was asked and didn't answer, the message has a `failure` field with one of these values:

* `error`: the model call failed for a reason other than a provider server error, for example a missing credential.
* `upstream`: the model provider returned a server error.
* `timeout`: the judgment ran out of time.
* `unanswered`: the model returned no decision.
* `no_assistant`: the Surface has no Product to describe.
* `preparation`: loading the judgment's inputs failed or ran out of time.
* `circuit_open`: repeated provider errors paused judgments, so none was asked.

In every case, the Surface waits for a mention.

The response also has a `labels` array. Runtype adds a label when a later message in the channel contradicts a decision. Use these labels to adjust `responseRules` or `eagerness`. The label is one of these values:

* `gate_false_negative`: the bot stayed silent, and someone mentioned it soon after.
* `gate_false_positive`: the bot replied, and someone pushed back, for example by saying that it wasn't being addressed.
* `backfill_false_negative`: after a `judged` context backfill, someone said that the answer was already given earlier in the channel.

To test the judgment without changing what the bot does, set `responseGateShadow` to `true` on a Surface in `mention` or `always` mode. The Surface judges each unmentioned channel message and records the decision, but answers exactly as its mode says. Each judgment is billed like other model usage.

### Surfaces with several Capabilities

A messaging Surface runs one Capability for each message. When a Surface item is marked as the entry point (`isEntryPoint`), or when the Surface has only one enabled Capability, the Surface runs that Capability. When a Surface has two or more enabled Capabilities and no entry point, a judgment model chooses the Capability:

* In `judge` mode, the judgment that decides whether to reply also chooses the Capability, in the same call. The bot replies only when the judgment chooses one Capability clearly. When no Capability fits, or two are too close to call, the bot stays silent.
* For a mention, a direct message, a continued thread, or a message in **Every message** mode, one routing judgment runs when the bot starts to answer. It reads the recent conversation. Each routing judgment is billed like other model usage. When no Capability fits, two are too close to call, or the judgment fails, the bot answers with the fallback Capability: the enabled Capability that was added to the Surface first. A Chat Surface in multi-capability mode falls back the same way.
* In shadow mode (`responseGateShadow`), the judgment records the Capability it would choose but doesn't use it.

A conversation keeps the Capability it first ran. Later messages in the same conversation, for example a follow-up such as "ok, thanks", go to that Capability without another routing judgment. Two exceptions apply: in `judge` mode, a new judgment that chooses a different Capability for a message moves the conversation to it, and if the Capability is disabled, the next message is routed again.

The judgment chooses among at most 20 Capabilities. A Surface with more than 20 enabled Capabilities and no entry point skips the routing judgment and answers every message with the fallback Capability. To choose the Capability yourself, mark one Surface item as the entry point.

The judgment chooses only among the Surface's own enabled Capabilities, by each Capability's exposed name and description. Channel messages can't add a Capability or name one directly. Each Capability keeps its own tool approvals. If a Capability is disabled after the judgment chooses it and before the bot answers, the Surface doesn't run it and routes the message again. To skip the judgment, mark one Surface item as the entry point.

The channel context also records the choice. On such a Surface, each judged decision carries a `capability` object with an `outcome` (`route`, `clarify`, `decline`, or `unanswered`), the chosen `capabilityId`, and its `probability`.

### Filter and override the mode

These optional `inbound` fields refine the mode:

* **`keywords`**: an object with `require` and `ignore` lists. In **Every message** and `judge` mode, the Surface skips an unmentioned channel message that contains an `ignore` word. In `judge` mode, the filter runs before the judgment. When `require` has words, the Surface answers only unmentioned messages that contain at least one of them. Matching is case-insensitive and uses whole words.
* **`channelOverrides`**: a map keyed by Slack channel ID, for example `{ "C0123456789": { "channelResponseMode": "judge", "eagerness": "conservative" } }`. An override's `channelResponseMode` and `eagerness` take precedence over the Surface-level values for that channel.
* **`contextBackfill`**: controls the earlier channel messages that the bot receives as context when it starts a conversation. Set `mode` to `window` (the default) to include recent channel history, to `judged` to include only the recent channel messages that a judgment model finds necessary to understand the answered message, or to `none` to include no history. On Slack, `maxMessages` (default and maximum 20) and `maxAgeMinutes` (default 360, maximum 1440) apply only in `judged` mode. In `judged` mode, the bot always keeps messages from the person who activated it, messages that mention the bot, and its own messages, and a marker shows where messages were left out. A reply in a thread still receives the whole thread. If the judgment fails, the bot receives the `window` history. The bot doesn't reply to these messages.
* **`triggerOnMention`**: a deprecated alias for `channelResponseMode`. `true` means `mention` and `false` means `always`. Use `channelResponseMode` instead.

## Tool approvals

Slack displays native **Approve** and **Deny** buttons when an attached Agent uses [**Tool approval**](/user-guide/agents/creating-and-configuring-agents). For a Runtype Agent, enable **Agent Loop** before you use these controls and set **Max turns** to 2 or higher. A Managed external Agent already supports multi-turn tool approval and doesn't need **Agent Loop** or **Max turns** configured.

When you click a button, the same execution resumes at the reviewed tool call instead of starting the Agent again.

Each Slack decision applies only to the reviewed tool call. The Slack prompt doesn't offer **Always allow**. It doesn't create a remembered approval, even when the Agent configuration enables that choice for another attended UI.

Slack uses explicit buttons to capture approvals. SMS and iMessage capture decisions from replies. For a comparison with other messaging Surfaces, see [Approvals on messaging surfaces](/user-guide/products-surfaces/approvals-on-messaging-surfaces).

When an approval is pending, later messages in the same Slack conversation don't start another Agent run. Answer the pending prompt first. Approval prompts expire after 1 hour. On expiration, Runtype doesn't run the tool, removes the prompt's buttons, and lets the Agent post a follow-up. A later button selection has no effect. Runtype reports an authoring warning for an approval-gated single-pass Runtype Agent because a single-pass Agent can't resume after an approval decision.

## Troubleshooting

The following table lists common setup problems and how to fix them:

| Symptom                                                             | Cause                                                                                 | Fix                                                                                                                                       |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| Slack says that the request URL isn't verified.                     | Slack's check of the events URL failed or timed out.                                  | Open **Features > Event Subscriptions**, enable events, and use the events request URL from the setup wizard. Click **Retry**, then save. |
| The Surface card shows **Rejecting events**.                        | The Signing Secret doesn't match the Slack app.                                       | In the Surface's settings, click **Reconfigure** and enter the Signing Secret again.                                                      |
| The authorization popup says that the link expired.                 | The authorization step has a 10-minute limit.                                         | Click **Add to Slack** again. The Slack app stays valid.                                                                                  |
| The authorization popup says that the setup session expired.        | The setup wasn't finished before it expired.                                          | Restart the setup from the Surface's settings.                                                                                            |
| The Surface is connected, but a mention in a channel gets no reply. | The bot isn't a member of the channel, or the channel isn't in **Specific Channels**. | Run `/invite @APP_NAME` in the channel. If **Specific Channels** is set, add the channel ID.                                              |
| The bot is missing permissions after it goes live.                  | The manifest was edited before the app was created.                                   | Copy a fresh manifest, update the app under **Features > App Manifest**, and reinstall the app.                                           |

A successful URL verification confirms the events endpoint only. It doesn't mean that the app is installed. This setup doesn't need an Incoming Webhook or Socket Mode.

## Next steps

Continue with these guides:

* [What are Surfaces?](/user-guide/products-surfaces/what-are-surfaces): understand how Surfaces expose Products.
* [Slack integration](/user-guide/integrations/slack-integration): give Agents tools to act in Slack through the workspace that a Slack Surface connects.
* [Surface orchestration modes](/user-guide/products-surfaces/surface-orchestration-modes): route requests when a Surface exposes more than one Capability.
* [Taking over messaging conversations](/user-guide/products-surfaces/taking-over-conversations): take over a conversation from the Agent and hand it back.