> For the complete documentation index, see [llms.txt](/llms.txt).
> A full single-fetch corpus is available at [llms-full.txt](/llms-full.txt).
---
title: Slack
description: Start AGNT5 workflows automatically from Slack messages, mentions, and reactions using the Slack Events API.
last_verified: 2026-06-11
---

Connect Slack to AGNT5 and your workflows will start automatically whenever a Slack event fires. Every delivery is signature-verified before any workflow runs.

---

## Setup

### 1. Create the integration in Studio

1. Go to **Settings → Integrations** and click **Add Integration**.
2. Pick **Slack**.
3. Studio walks you through creating the Slack app and choosing the target environment.

<DocsImage
  src="/docs/integrations/slack-integration-light.png"
  darkSrc="/docs/integrations/slack-integration-dark.png"
  alt="Studio Add Slack App panel showing app name, OAuth scopes, and bot events configuration."
  caption="Enter an app name, select the OAuth scopes and bot events you need, then click Create Slack App."
  width={1746}
  height={1060}
/>

### 2. Copy the app credentials from Slack

Go to **Basic Information → App Credentials** and copy the following values

<Callout type="info">
| Field | Used for |
|---|---|
| **App ID** | Identifying your Slack app |
| **Client ID** | Identifies your app during OAuth authorization; included in the redirect URL when requesting user permissions |
| **Client Secret** | Paired with Client ID to exchange an authorization code for an access token, keep this server-side and never expose it publicly |
| **Signing Secret** | AGNT5 webhook verification (required) |
</Callout>

Paste each value into the required fields in Studio when prompted.


### 3. Complete the setup in Studio

Paste the credentials into the required fields and save. Studio registers the webhook URL automatically.

---

## Events

The event name your trigger matches is `slack.<event_type>`, where `event_type` is the value of the Slack `event.type` field inside the payload.

| Event | Full event name | Required scope |
|---|---|---|
| `app_mention` | `slack.app_mention` | `app_mentions:read` |
| `message` (channels) | `slack.message` | `channels:history` |
| `message` (groups) | `slack.message` | `groups:history` |
| `message` (DMs) | `slack.message` | `im:history` |
| `reaction_added` | `slack.reaction_added` | `reactions:read` |
| `reaction_removed` | `slack.reaction_removed` | `reactions:read` |
| `member_joined_channel` | `slack.member_joined_channel` | `channels:read` |

A single workflow can subscribe to multiple event types at once by passing multiple `event()` calls in `triggers`.

---

## Workflow examples

### Logging messages and mentions

1. Workflows receive the envelope as `**envelope`.
2. Subscribe to both `slack.message` and `slack.app_mention` to cover channels and mentions in one workflow.
3. `body` may be a JSON string or a dict, so parse defensively.
4. Filter out bot messages (`bot_id`, `subtype`) to avoid reply loops.

```python
import json
from agnt5 import WorkflowContext, workflow
from agnt5.types import event


def _parse_body(envelope: dict) -> dict:
    body = envelope.get("body", {})
    if isinstance(body, str):
        try:
            body = json.loads(body)
        except json.JSONDecodeError:
            return {}
    return body if isinstance(body, dict) else {}


@workflow(
    name="slack_message_logger",
    triggers=[event("slack.message"), event("slack.app_mention")],
)
async def slack_message_logger(ctx: WorkflowContext, **envelope) -> dict:
    body = _parse_body(envelope)
    slack_event = body.get("event", {})

    # skip bot messages to avoid reply loops
    if slack_event.get("bot_id") or slack_event.get("subtype"):
        ctx.logger.info("Slack message skipped: bot or unsupported subtype")
        return {"status": "skipped"}

    user = slack_event.get("user", "unknown")
    text = slack_event.get("text", "")
    channel = slack_event.get("channel", "")

    ctx.logger.info("Slack message from %s in %s: %s" % (user, channel, text[:200]))

    # add your processing logic here, e.g. call an agent, look up data
    return {"status": "logged", "user": user, "channel": channel, "text": text}
```

### Viewing triggered runs in Studio

After a Slack event fires, the run appears in **Studio → Runs** within seconds. Open it to inspect the envelope, extracted fields, and any reply output.

_[screenshot: Studio Runs list showing a completed slack_message_logger run triggered by a slack.app_mention event, with the run detail open showing the event body and workflow output]_

---

## Envelope structure

Every Slack-triggered run receives this envelope as `**envelope`:

```json
{
  "_webhook": true,
  "source": "slack",
  "integration_id": "int_abc123",
  "event_type": "slack.app_mention",
  "timestamp": 1733337600,
  "headers": { "x-slack-signature": "v0=…", "x-slack-request-timestamp": "…" },
  "body": "{\"type\":\"event_callback\",\"event\":{\"type\":\"app_mention\",\"user\":\"U123\",\"text\":\"<@U456> hello\",\"channel\":\"C789\",\"ts\":\"…\"}}"
}
```

`body` is the raw verified request body. Parse it with `json.loads`. Slack does not include an `idempotency_key` (see [Delivery semantics](#delivery-semantics)).

---

## Signature verification

- Uses `X-Slack-Signature` (`v0=<hex>`) with `X-Slack-Request-Timestamp`.
- 5-minute replay window enforced.
- Requests with an invalid or expired signature are rejected with `401`.

---

## Delivery semantics

- Slack retries up to 3 times on non-2xx responses.
- **Retries are not deduplicated.** Slack provides no stable per-delivery id.
- Make your workflow idempotent using an id from the event body (e.g. `event.client_msg_id`).

---

## Related

- [Event sources overview](/docs/integrations/event-sources/overview.md)
- [Webhooks](/docs/build/webhooks.md): trigger mechanism, signature verification, and delivery semantics
- [Workflows](/docs/build/workflows.md): how to write and run workflows
