Slack free
Slack connects as an app installed into a workspace. There are three ways to do it, and they trade setup time for control: a one-click shared app, your own branded app, or a distributed app you push into your customers' workspaces. All three land on the same on_message handler.
Slack is free: no signup, no paid credit. An anonymous sandbox key is enough. See the Quickstart if you don't have a key yet.
Which option?
| Option | Setup | App to create | Best for |
|---|---|---|---|
| Quick | one click | none (uses Caspian's shared app) | getting into your own workspace fast, still under your name and icon |
| Branded | ~5 min | your own Slack app | owning the app name and the @mention in your workspace |
| Distribute | ~10 min once | your own published app | installing into many customer workspaces, one link each |
In every case the agent replies only when it is @-mentioned in a channel, or in a direct message. It stays quiet on ordinary channel chatter.
a) Quick: the shared app
Call install_slack() with the name (and optional icon) your agent should post under. It installs Caspian's shared Slack app into a workspace, but every message goes out under your name and icon; the plumbing stays invisible. Nothing to build on api.slack.com.
from caspian_sdk import CommClient
client = CommClient()
result = client.install_slack(
display_name="Acme Support",
icon_url="https://acme.com/bot.png", # optional
)
print("Add to Slack:", result["authorize_url"])
@client.on_message
def handle(message):
message.reply(f"You said: {message.text}")
client.listen()import { CommClient } from "caspian-sdk";
const client = new CommClient();
const result = await client.installSlack({
displayName: "Acme Support",
iconUrl: "https://acme.com/bot.png", // optional
});
console.log("Add to Slack:", result.authorize_url);
client.onMessage(async (message) => {
await message.reply(`You said: ${message.text}`);
});
await client.listen();Open the returned authorize_url ("Add to Slack"), pick your workspace, and authorize. The connection flips to active on its own once the install completes, and messages in that workspace start reaching your handler.
If install_slack() returns a 400, the shared app isn't configured on this gateway. Fall back to the branded option below with your own Slack app.
b) Branded: your own app in your workspace
Create a Slack app you own, then hand its credentials to connect_slack(). You control the app name and the @mention. This is a one-time ~5 minute setup at api.slack.com/apps.
Create the app
Go to api.slack.com/apps → Create New App → From scratch, then:
- OAuth & Permissions → Redirect URLs. Add the redirect URL and save:
https://api.trycaspianai.com/v1/oauth/slack/callback - OAuth & Permissions → Bot Token Scopes. Add:
chat:write chat:write.customize channels:history im:history app_mentions:read - Event Subscriptions. Toggle On and set the Request URL (it verifies instantly):
Then under Subscribe to bot events add and save:
https://api.trycaspianai.com/internal/providers/slack/webhooksmessage.channels message.im app_mention - Basic Information → App Credentials. Copy the Client ID, Client Secret, and Signing Secret.
Connect it
result = client.connect_slack(
slack_client_id="1234567890.1234567890",
slack_client_secret="xxxxxxxxxxxxxxxxxxxxxxxx",
slack_signing_secret="xxxxxxxxxxxxxxxxxxxxxxxx",
)
print("Add to Slack:", result["authorize_url"])const result = await client.connectSlack({
slackClientId: "1234567890.1234567890",
slackClientSecret: "xxxxxxxxxxxxxxxxxxxxxxxx",
slackSigningSecret: "xxxxxxxxxxxxxxxxxxxxxxxx",
});
console.log("Add to Slack:", result.authorize_url);Open the returned authorize_url, pick your workspace, and Allow. The connection goes active and inbound messages route to the same handler.
c) Distribute: install into your customers' workspaces
Same app as (b), but you also turn on public distribution so it can install into other workspaces, the same way Caspian ships its shared app. Then you call connect_slack() once per customer to get one "Add to Slack" link per workspace.
Do everything in (b), then add one step before copying credentials:
- Manage Distribution. Finish the checklist, then Activate Public Distribution. This is what lets the app install into workspaces other than your own.
Now call connect_slack() with the same app credentials but a different customer_id per customer. Each call returns its own authorize_url:
APP = dict(
slack_client_id="1234567890.1234567890",
slack_client_secret="xxxxxxxxxxxxxxxxxxxxxxxx",
slack_signing_secret="xxxxxxxxxxxxxxxxxxxxxxxx",
)
for customer in ("acme-inc", "globex", "initech"):
result = client.connect_slack(customer_id=customer, **APP)
print(customer, "->", result["authorize_url"])const APP = {
slackClientId: "1234567890.1234567890",
slackClientSecret: "xxxxxxxxxxxxxxxxxxxxxxxx",
slackSigningSecret: "xxxxxxxxxxxxxxxxxxxxxxxx",
};
for (const customer of ["acme-inc", "globex", "initech"]) {
const result = await client.connectSlack({ customerId: customer, ...APP });
console.log(customer, "->", result.authorize_url);
}Send each customer their own link; they pick their workspace and Allow. Every customer's workspace routes back to the same on_message handler, isolated by customer_id. Inbound from all of them shares the one Event Subscriptions URL; the gateway routes each message by app and team automatically.
Change the name or icon the agent posts under any time after connecting (no re-install) with update_branding(connection_id, display_name=..., icon_url=...) (Python) / updateBranding(connectionId, { displayName, iconUrl }) (JS). It takes effect on the next message.
How replies behave
Slack is noisy, so the agent is deliberately quiet. It fires on_message and replies only in two cases:
- In a channel: when the bot is directly @-mentioned.
- In a DM: every direct message.
Everything else in a channel is ignored. Reply with message.reply() and it lands back in the same Slack thread; the channels:history and im:history scopes let the agent read surrounding context. See Behavior guides for Slack-specific etiquette to inject into your agent's prompt.