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:
- Open the Product that you want to connect to Slack.
- Go to Surfaces and click Add Surface.
- Select Slack.
- Enter a name for the Slack app.
- Click Copy manifest.
- Click Open Slack app creation.
- In Slack, under Or start your own way, select From a manifest, then Continue. Don’t choose the AI agent or Starter app template.
- In the JSON tab, replace the entire Demo App sample with the manifest. Select your Workspace on the same screen.
- Click Next, review the settings, and create the app.
- 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:
- In Slack, open the app that you created.
- Go to Basic Information and find App Credentials.
- Copy Client ID, Client Secret, and Signing Secret into the Runtype setup wizard.
- Click Add to Slack. A popup opens Slack’s authorization page.
- 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. 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:
- 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. - 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.
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:
- To test in a channel, invite the app first. In the channel, run
/invite @APP_NAME, whereAPP_NAMEis the name of your Slack app. - Send a direct message to the Slack app, or mention it in the channel.
- 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.
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:
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:
Every message, judge mode, and shadow mode (responseGateShadow, described in 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), oreager. 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.replyanswers 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, whereCHANNEL_IDis the Slack channel ID. - Call the MCP tool
get_surface_channel_context. - Call
getSurfaceChannelContextin 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, andonUncertaindecided.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 ajudgedcontext 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
judgemode, 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 withrequireandignorelists. In Every message andjudgemode, the Surface skips an unmentioned channel message that contains anignoreword. Injudgemode, the filter runs before the judgment. Whenrequirehas 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’schannelResponseModeandeagernesstake 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. Setmodetowindow(the default) to include recent channel history, tojudgedto include only the recent channel messages that a judgment model finds necessary to understand the answered message, or tononeto include no history. On Slack,maxMessages(default and maximum 20) andmaxAgeMinutes(default 360, maximum 1440) apply only injudgedmode. Injudgedmode, 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 thewindowhistory. The bot doesn’t reply to these messages.triggerOnMention: a deprecated alias forchannelResponseMode.truemeansmentionandfalsemeansalways. UsechannelResponseModeinstead.
Tool approvals
Slack displays native Approve and Deny buttons when an attached Agent uses Tool approval. 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.
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:
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?: understand how Surfaces expose Products.
- Slack integration: give Agents tools to act in Slack through the workspace that a Slack Surface connects.
- Surface orchestration modes: route requests when a Surface exposes more than one Capability.
- Taking over messaging conversations: take over a conversation from the Agent and hand it back.