> ## Documentation Index
> Fetch the complete documentation index at: https://docs.auto.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Slack Events

> Every Slack event an agent trigger can listen on: messages, mentions, edits, and reactions, with payload placeholders, where filters, and bind-to-thread routing.

Slack messages and reactions reach agents as provider-neutral `chat.*` events — the same event keys Discord and Telegram use. A trigger scopes itself to one Slack workspace with `connection:`, and (when the agent listens on more than one chat provider) discriminates with a `where` filter on `$.chat.provider`. This page covers the Slack-specific behavior: how mentions address an agent, thread semantics, and the payload fields you can template and filter on.

## Event keys

| Event                                           | Fires when                                                                                                                                                                                                |
| ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `chat.message.mentioned`                        | A message mentions an agent through a real Slack mention entity — either the agent's own bot user or the Auto app plus an `@auto.<alias>` token. Delivered as an addressed copy targeting that one agent. |
| `chat.message.direct`                           | A direct message to the connection's Slack app (the shared Auto app or an agent's dedicated bot user). The thread is subscribed automatically.                                                            |
| `chat.message.subscribed`                       | A new message in a thread the workspace connection is subscribed to (after a mention, a DM, or an explicit subscribe from a session).                                                                     |
| `chat.message.channel`                          | Any channel message the connection can see — the broadcast copy, and the downgrade for text that merely looks like a mention.                                                                             |
| `chat.message.edited`                           | A message was edited (Slack `message_changed`). Carries the new and previous text; never spawns mention sessions.                                                                                         |
| `chat.reaction.added` / `chat.reaction.removed` | An emoji reaction was added to or removed from a message.                                                                                                                                                 |

Trigger `event` strings are not validated against a closed catalog — a misspelled key applies cleanly and never fires — so copy the keys above exactly.

## Scoping to a workspace

`connection:` on the trigger names the Slack connection (the workspace grant). It may be omitted only when the project has exactly one active chat connection across Slack, Discord, and Telegram; with zero or several, apply fails with an error asking you to set `trigger.connection`. Add `optional: true` to let apply silently skip the trigger while the connection does not exist yet — it activates on the next apply once the connection is set up. See [connections and identities](/concepts/connections-and-identities).

```yaml theme={null}
triggers:
  - event: chat.message.mentioned
    connection: slack
    where:
      $.chat.provider: slack
      $.auto.authored: false
    routing:
      kind: spawn
```

## How mentions address an agent

Two mention forms produce an addressed `chat.message.mentioned` event for a specific agent:

* **The agent's own bot user** — when the agent has a Slack identity, `@AgentName` mentions its dedicated bot user directly.
* **The Auto app with an alias prefix** — mention the shared Auto app and address an agent by alias token: `@auto.<alias>`, where `<alias>` is the agent's identity `username` (falling back to the agent name), matched case-insensitively. The delimiter may be `.`, `:`, `/`, or `-`.

Only entity-backed mentions count. Plain text that merely looks like a mention (for example a bot crediting `@auto-dot-sh[bot]` in a deploy notice) is downgraded to `chat.message.channel` with `message.isMention` cleared, and does not subscribe the thread.

An addressed mention is delivered only to the mentioned agent — it does not fan out to other agents subscribed to the same thread. A message that mentions several agents produces one addressed copy per agent. When a mention addresses an Auto agent, the thread is subscribed, so later replies arrive as `chat.message.subscribed`.

## Payload

Trigger `message` templates, `initialPrompt`, and `where` filters all read the same normalized payload. `chat.message.channel`, `.direct`, `.mentioned`, and `.subscribed` share this shape:

<ParamField path="chat" type="object">
  Event envelope: `provider` (`"slack"` here), `kind` (the message kind), `threadId` (canonical thread id — the thread root), `channelId`, `isDirectMessage`, and `messageId` (the Slack message timestamp).
</ParamField>

<ParamField path="message" type="object">
  The message itself: `text`, `author` (Slack-shaped object; `author.userName` and `author.isMe` are the commonly used fields), `isMention`, `dateSent` (ISO 8601), `attachments`, and `links`.
</ParamField>

<ParamField path="auto" type="object">
  Auto's own metadata: `mentioned` (an agent was addressed), `contextual` (thread-participation context rather than an explicit mention), `authored` (an Auto session or agent bot wrote this message — filter it out to avoid echo loops), `context.kind` (`explicit_mention` or `thread_participation`), `attribution` / `attributions` (the session(s) already attached to this thread, each `{ sessionId, agentName }`), and on addressed events `agent` (the mentioned agent identity, with `addressing.kind` one of `slack_auto_prefix`, `slack_bot_user`, `discord_app_alias`).
</ParamField>

<ParamField path="raw" type="object">
  The raw Slack event, for anything the normalized fields do not carry.
</ParamField>

### `chat.message.edited`

Same envelope with `chat.kind: "edited"`, plus `message.previousText` (when Slack supplies it) and `message.editedAt`. `message.isMention` and `auto.mentioned` are always `false` — edits never address agents and never spawn sessions. Route edits with `deliver` + `attributedSessions` so the session already holding the conversation sees the correction.

### `chat.reaction.added` / `chat.reaction.removed`

<ParamField path="chat" type="object">
  `provider`, `threadId`, `channelId`, and `messageId` — the id of the message that was reacted to.
</ParamField>

<ParamField path="reaction" type="object">
  `added` (boolean), `emojiName` (canonical name, e.g. `thumbsup`), `rawEmoji` (as Slack sent it), `emoji` (rendered character when resolvable), and `user` (the reacting user; `user.isMe` marks the connection's own reactions).
</ParamField>

<ParamField path="message" type="object">
  The reacted-to message when available, including `author` — filter `$.message.author.isMe: true` to react only to reactions on the agent's own messages.
</ParamField>

## Template placeholders

Placeholders render `{{dot.path}}` tokens against the payload above — see [variables and templating](/reference/variables-and-templating). The ones you will use most:

```yaml theme={null}
message: |
  {{message.author.userName}} mentioned you on {{chat.provider}}:

  {{message.text}}

  Channel: {{chat.channelId}}
  Thread: {{chat.threadId}}
```

Objects and arrays render as JSON; a missing path renders as an empty string. Do not prefix paths with `payload.` — apply rejects it.

## Filters

The full filter grammar lives in the [triggers reference](/reference/triggers). Filters that matter on Slack events:

| Filter                                   | Use                                                                                                                                           |
| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `$.chat.provider: slack`                 | Pin the trigger to Slack when the agent also listens on Discord or Telegram.                                                                  |
| `$.auto.authored: false`                 | Suppress the agent's own messages. **Required** on every `attributedSessions` deliver trigger over `chat.message.*` — apply fails without it. |
| `$.auto.attributions: { exists: false }` | On spawn triggers: only start a session when no session is already attached to the thread.                                                    |
| `$.auto.attributions: { exists: true }`  | On deliver triggers: only deliver into threads that already have an attributed session.                                                       |
| `$.chat.isDirectMessage: true`           | Match DMs only.                                                                                                                               |
| `$.reaction.emojiName: thumbsup`         | Match a specific reaction.                                                                                                                    |

When a spawn trigger and an `attributedSessions` deliver trigger listen on the same event key, apply requires them to carry the mutually exclusive `$.auto.attributions` filters shown above — otherwise one message would both spawn a new session and deliver into the old one.

## Threads and routing

Every Slack message and reaction carries a thread-level routing target: the pair (workspace connection, `chat.threadId`) canonicalizes to a `slack.thread` binding target at ingest. That gives you three routing patterns:

* **`spawn`** — start a fresh session per conversation. Add `bind: { target: slack.thread }` so the new session owns the thread and later `bind`-routed events resolve back to it.
* **`deliver` + `routeBy: { kind: attributedSessions }`** — the standard follow-up route: thread replies, edits, and reactions flow to the session(s) already attributed to the thread.
* **`bind` with `target: slack.thread`** — resolve the event to the one session bound to its thread; `onUnmatched` (default `drop`) decides what happens when no session holds it.

Two declarative conveniences on the agent spec:

* `bindings: { slack.thread: { bind: onMention } }` — after the router delivers an addressed `chat.message.mentioned` event, the delivered session is automatically bound to the thread. Only addressed mentions auto-bind; DMs, subscribed messages, and reactions never do.
* Spawned mention sessions claim their thread attribution in the session-creation transaction, so the follow-up deliver route works without any tool call from the agent.

Delivered chat messages interrupt the session's running turn immediately (unlike CI-completion events, which wait for idle).

## Example: a channel assistant

A complete agent that answers mentions, keeps each conversation in its Slack thread, and receives follow-ups in the same session. Adapted from the [chat assistant example](/examples/chat-assistant).

```yaml .auto/agents/assistant.yaml theme={null}
name: assistant
systemPrompt: |
  You are the team's Slack assistant. Reply in the thread you were
  addressed in using chat.send with target provider `slack`, the
  triggering channel, and the triggering thread. Keep replies short.
  Never reply to your own messages.
initialPrompt: |
  Someone mentioned you on Slack.

  - Channel: {{chat.channelId}}
  - Thread: {{chat.threadId}}
  - Author: {{message.author.userName}}
  - Message: {{message.text}}

  Reply in that thread with chat.send.
tools:
  auto:
    kind: local
    implementation: auto
  chat:
    kind: local
    implementation: chat
    auth:
      kind: connection
      provider: slack
      connection: slack
bindings:
  slack.thread:
    bind: onMention
triggers:
  # New conversation: an addressed mention with no session on the thread yet.
  - event: chat.message.mentioned
    connection: slack
    where:
      $.chat.provider: slack
      $.auto.authored: false
      $.auto.attributions:
        exists: false
    routing:
      kind: spawn
      bind:
        target: slack.thread
  # Follow-ups: replies in a thread this agent already owns.
  - events:
      - chat.message.mentioned
      - chat.message.subscribed
    connection: slack
    where:
      $.chat.provider: slack
      $.auto.authored: false
      $.auto.attributions:
        exists: true
    message: |
      {{message.author.userName}} replied in your conversation:

      {{message.text}}

      Channel: {{chat.channelId}}
      Thread: {{chat.threadId}}

      Reply in that thread with chat.send.
    routing:
      kind: deliver
      routeBy:
        kind: attributedSessions
      onUnmatched: drop
  # Reactions to the agent's own messages.
  - events:
      - chat.reaction.added
      - chat.reaction.removed
    connection: slack
    where:
      $.chat.provider: slack
      $.message.author.isMe: true
      $.reaction.user.isMe: false
    message: |
      {{reaction.user.userName}} reacted with {{reaction.rawEmoji}} to your
      message {{chat.messageId}}. Respond in the thread only if it warrants it.
    routing:
      kind: deliver
      routeBy:
        kind: attributedSessions
      onUnmatched: drop
```

## See also

* [Triggers reference](/reference/triggers) — routing kinds, filter grammar, fallback triggers
* [Telegram events](/reference/events/telegram) — the same `chat.*` contract on Telegram
* [Chat tools](/runtime/chat-tools) — how a session replies, reads history, and reacts
