---
title: "Slack"
description: "Send messages and collect responses via Slack channels or DMs"
icon: "slack"
---

{/* Missing image */}

<Frame>
  <img
    src="/images/slack-conversation.png"
    alt="HumanLayer Slack Approval Flow"
  />
</Frame>

## Configuration

### Option 1: Using the HumanLayer Slack App

<Frame>
  <img src="/images/slack-humanlayer.png" alt="HumanLayer Slack App" />
</Frame>

The quickest way to get started with Slack integration:

1. Head to the [HumanLayer dashboard](https://app.humanlayer.dev)
2. Navigate to "Integrations"
3. Click "HumanLayer App" under the Slack section
4. Follow the Slack Approval Workflow
5. Congrats - your HumanLayer agents will now contact you in Slack for approvals

### Option 2: Using Your Own Slack App

<Frame>
  <img src="/images/slack-byo.png" alt="Use your own Slack App" />
</Frame>

For teams that need more customization, you can use your own Slack app. This option is ideal for:

- Customizing the bot's display name, icon, and branding
- Using a consistent bot identity across multiple integrations
- Managing app distribution and permissions at an organizational level
- Custom approval UI with your brand's look and feel
- Enterprise policy compliance

1. **Create a Slack App** - [Create a new Slack app](https://api.slack.com/quickstart) in your workspace with the following manifest:

<Frame>
  <img
    src="/images/slack-from-manifest.png"
    alt="Setting up a Slack app from a manifest"
  />
</Frame>

```json
{
  "display_information": {
    "name": "My App",
    "description": "My App",
    "background_color": "#000000"
  },
  "features": {
    "bot_user": {
      "display_name": "My App",
      "always_online": false
    }
  },
  "oauth_config": {
    "scopes": {
      "bot": [
        "app_mentions:read",
        "users.profile:read",
        "users:read",
        "commands",
        "channels:history",
        "channels:read",
        "chat:write",
        "groups:history",
        "groups:write",
        "im:history",
        "im:read",
        "im:write"
      ]
    }
  },
  "settings": {
    "event_subscriptions": {
      "request_url": "https://api.humanlayer.dev/humanlayer/v1/slack/events",
      "bot_events": [
        "message.channels",
        "message.groups",
        "message.im",
        "app_mention"
      ]
    },
    "interactivity": {
      "is_enabled": true,
      "request_url": "https://api.humanlayer.dev/humanlayer/v1/slack/interactions"
    },
    "org_deploy_enabled": false,
    "socket_mode_enabled": false,
    "token_rotation_enabled": false
  }
}
```

2. **Configure Your App in HumanLayer**:

   - In the HumanLayer dashboard, navigate to your project's settings
   - Navigate to "Integrations". Select "Use Your Own" under the Slack section.
   - Enter your Slack app's Signing Secret and App ID
   - Save the configuration

3. **Get Your OAuth Token**:

   - For testing, you can use the Bot User OAuth Token from your Slack app settings
   - For production, implement a proper OAuth flow following [Slack's documentation](https://api.slack.com/quickstart)

4. **Use Your Bot Token with HumanLayer SDK**:
   - When initializing HumanLayer, provide your bot token in the contact channel configuration:

<CodeGroup>
```python example.py
import os
from humanlayer import HumanLayer, ContactChannel, SlackContactChannel, FunctionCallSpec

bot_token = "...YOUR BOT TOKEN"

hl = HumanLayer(
api_key=os.getenv("HUMANLAYER_API_KEY"),
verbose=True,
contact_channel=ContactChannel(
slack=SlackContactChannel(
bot_token=bot_token,
channel_or_user_id="", # leave blank to dm whoever installed the app
experimental_slack_blocks=True,
)
),
)

hl.fetch_approval(
spec=FunctionCallSpec(
fn="add",
kwargs={"x": 2, "y": 5},
)
)

````

```typescript example.ts
import { humanlayer } from "@humanlayer/sdk";

const botToken = "...YOUR BOT TOKEN";

const hl = humanlayer({
  apiKey: process.env.HUMANLAYER_API_KEY,
  verbose: true,
  contactChannel: {
    slack: {
      bot_token: botToken,
      channel_or_user_id: "", // leave blank to dm whoever installed the app
      experimental_slack_blocks: true,
    },
  },
});

(async () => {
  const status = await hl.fetchHumanApproval({
    spec: {
      fn: "add",
      kwargs: { x: 2, y: 5 },
    },
  });
})();
````

</CodeGroup>

For a complete example that makes use of a custom Slack app, see [06-custom_bot_token.py](https://github.com/humanlayer/humanlayer/tree/main/examples/langchain/06-custom_bot_token.py) in our `examples` directory. Be sure you have a Slack application setup before attempting to run the script.

### Troubleshooting

If you encounter issues with your custom Slack app integration:

1. **Verify Scopes** - Ensure your app has all the required scopes listed in the manifest
2. **Check Request URLs** - The event subscription and interactivity URLs must point to the HumanLayer API
3. **Verify Signing Secret** - Double-check the signing secret entered in the HumanLayer dashboard
4. **Update/Reauthorize Slack Apps** - If you're updating an existing Slack app and have added new scopes, you'll need to reauthorize the app via the OAuth flow you've built. Be sure to take a look at the embedded markup Slack provides in the "Manage Distribution" section of your app's settings as these links and markup can change once you've updated scopes.

## Customizing Channels

The Slack contact channel allows agents to send messages and collect responses through Slack channels or direct messages.
Configure a Slack channel using the `SlackContactChannel` model:

```python
from humanlayer import ContactChannel, SlackContactChannel

dm_with_ceo = ContactChannel(
    slack=SlackContactChannel(
        channel_or_user_id="U123456",  # Must be an ID like C123456 or U123456, not #channel or @user
        context_about_channel_or_user="a DM with the CEO",  # Optional context for the LLM
        experimental_slack_blocks=True,  # Optional - enables rich message formatting
        allowed_responder_ids=["U123456"],  # Optional - restrict who can respond
    )
)

hl = HumanLayer(contact_channel=dm_with_ceo)
```

### Allowed Responders

You can restrict which Slack users are allowed to respond to a message using `allowed_responder_ids`:

```python
# Only allow specific users to approve add operations
add_approver_channel = ContactChannel(
    slack=SlackContactChannel(
        channel_or_user_id="C123456",
        context_about_channel_or_user="the channel for add operations",
        allowed_responder_ids=["U123456"],  # Only this user can approve
    )
)

@hl.require_approval(contact_channel=add_approver_channel)
def add(a: float, b: float) -> float:
    """Add two numbers"""
    return a + b
```

The `allowed_responder_ids` must be Slack user IDs (starting with `U`). Messages from other users will be ignored.

See [01-math-example-allowed-repliers.py](https://github.com/humanlayer/humanlayer/tree/main/examples/langchain/01-math_example_allowed_repliers.py) for a complete example.

### Thread Messages

You can specify a thread to post messages in using the `thread_ts` parameter:

```python
# Post in a specific Slack thread
threaded_channel = ContactChannel(
    slack=SlackContactChannel(
        channel_or_user_id="C123456",
        thread_ts="1234567890.123456",  # Timestamp of the parent message
    )
)
```

The `thread_ts` parameter accepts a Slack message timestamp (e.g. "1234567890.123456") which identifies the parent message of the thread. When specified, all messages will be posted as replies in that thread instead of as new messages in the channel.

This is useful for organizing related approvals or keeping discussions contained within specific threads.

### Channel/User ID

The `channel_or_user_id` must be a Slack ID:

- Channel IDs start with `C` (e.g. `C123456`)
- User IDs start with `U` (e.g. `U123456`)
- Do not use channel names (`#general`) or usernames (`@user`)

You can get a channel ID in slack by right-clicking the channel, going to "Channel Details", and copying the ID at the bottom of the modal.

You can also override the contact channel at the per-request level:

```python
hl = HumanLayer()

@hl.require_approva(contact_channel=dm_with_ceo)
def send_email():
    ...
```

Or in one or more human-as tool instances

```python
tools = [
  hl.human_as_tool(contact_channel=channel_with_sre_team),
  hl.human_as_tool(contact_channel=dm_with_ceo),
]
```

### Context

The optional `context_about_channel_or_user` field helps the LLM understand who it's talking to:

```python
# Good context examples
"a DM with the CEO"
"the channel with the SRE team"
"a private channel for approvals"
```

## Usage

Use the Slack channel with either core feature:

```python
# With require_approval
@hl.require_approval(contact_channel=dm_with_ceo)
def send_email(to: str, subject: str, body: str) -> str:
    """Send an email to a customer"""
    return f"Email sent to {to}"

# With human_as_tool
tools = [
    langchain_tools.StructuredTool.from_function(
        hl.human_as_tool(
            contact_channel=dm_with_ceo,
        )
    ),
]
```

## Next Steps

- [Configure response options](/core/customize-response-options)
- [Try composite channels](/channels/composite-channels)
