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

# Slack

> Configure a Slack bot distribution, map Slack events into A2A, and use distribution-bound Slack tools.

<Note>
  Slack distributions are in preview. Customer-managed Slack apps are the default setup mode. Aion-managed apps appear
  only in environments where API provisioning is enabled, and commercial production use remains separately gated.
</Note>

Slack distributions connect one Slack bot installation to one Aion distribution. Slack sends subscribed Events API
callbacks to Aion, Aion maps accepted events into the Distribution/Messaging A2A extension, and the agent can reply in
the same Slack conversation. The distribution also contributes a first-party `mcp.slack` server for bounded Slack API
reads and writes.

## Overview

An active Slack distribution supports two complementary paths:

1. **Push delivery:** Slack sends message, membership, reaction, and lifecycle callbacks through the
   [Events API](https://docs.slack.dev/apis/events-api/). Aion verifies, durably accepts, normalizes, and dispatches
   eligible callbacks to the agent.
2. **Agent-initiated tools:** the agent calls the distribution-bound `mcp.slack` server for conversation metadata,
   recent history, thread replies, message writes, direct messages, and reactions.

Both paths use the bot identity and workspace installation selected by the distribution. Agents never receive the Slack
bot token or provide a static distribution id to the Slack tools.

For the protocol contracts, see
[Distribution](/a2a/extensions/aion/distribution/1.0.0),
[Event](/a2a/extensions/aion/event/1.0.0), and
[Distribution/Messaging](/a2a/extensions/aion/distribution/messaging/1.0.0).

### Identity and installation model

Slack and Aion use several related objects. They are not interchangeable.

| Object                     | Owner             | Purpose                                                                 |
| -------------------------- | ----------------- | ----------------------------------------------------------------------- |
| Aion principal identity    | Aion organization | Identifies the agent that receives distribution requests.               |
| Slack bot service identity | Aion organization | Represents one logical Slack app and bot profile in Aion.               |
| Managed Slack app          | Customer or Aion  | Holds the Slack manifest, app credentials, signing secret, and profile. |
| Workspace installation     | Slack workspace   | Holds workspace-local OAuth grants, bot token, and Slack bot user id.   |
| Slack user identity        | Slack workspace   | Identifies an installer or an inbound Slack user in that workspace.     |

The principal identity does not supply or synchronize the bot profile. Set the Slack app name, bot display name,
description, color, and icon during Slack setup. Later profile changes use Aion's editable service-identity surface when
the provisioning mode can apply them; otherwise Aion returns an explicit Slack owner action.

In v1, one managed app and service identity have one active workspace installation. Reauthorizing the same workspace
updates that installation. An OAuth callback for a different workspace is rejected. Slack Connect does not create a
second installation.

## Default Request Loop

```mermaid theme={null}
sequenceDiagram
participant User as Slack user
participant Slack as Slack Events API
participant Aion as Aion Slack distribution
participant Agent as A2A agent
participant Tools as mcp.slack

User->>Slack: Send DM, channel message, thread reply, or mention
Slack->>Aion: POST signed Events API callback
Aion->>Aion: Verify, durably accept, authorize, and normalize
Aion->>Agent: SendMessage in the mapped long-lived context
Agent-->>Aion: Return Message, Task, or streaming task events
Aion-->>Slack: Post or update the selected Slack reply
Agent->>Tools: Optionally read history or perform a Slack action
Tools->>Slack: Call the Web API with the selected installation
Tools-->>Agent: Return structured result and freshness metadata
```

Aion acknowledges an accepted Slack callback after durable handoff. Parsing, routing, A2A dispatch, and agent execution
continue asynchronously so the Events API endpoint can respond promptly.

## Configuration

### Choose a provisioning mode

The Slack setup modal presents the independently gated modes available in the current Aion environment.

| Mode             | Default | App owner | Configuration                                          |
| ---------------- | ------- | --------- | ------------------------------------------------------ |
| Customer-managed | Yes     | Customer  | Guided generated manifests and Slack owner actions.    |
| Aion-managed     | No      | Aion      | API-applied manifests when the environment is enabled. |

Both modes produce the same runtime bot capabilities. They differ in app ownership, who can change provider
configuration, and whether Aion can apply profile and manifest updates directly.

Customer-managed availability is controlled separately from Aion-managed availability. Aion is validating the
customer-managed single-customer policy classification directly with Slack; the guide does not present that
classification as Slack-approved. Aion-managed commercial production use remains disabled until the required Slack
authorization is recorded.

### Set up a customer-managed app

Use this mode when the customer should own the Slack app and its provider credentials.

1. Open **Integrations**, select **Slack**, and add a Slack bot.
2. Choose **Customer-managed Slack app** and enter the bot service profile.
3. Open the Slack owner action. Select the intended workspace, review the generated bootstrap manifest, and create the
   app [from the manifest](https://docs.slack.dev/app-manifests/configuring-apps-with-app-manifests).
4. Return to Aion and enter the app id, client id, client secret, and signing secret from Slack's app settings. Treat
   these values as secrets.
5. Apply the second generated manifest in Slack. This stage adds the opaque Aion Events URL after Aion can resolve the
   app and signing secret. Save the manifest, complete Slack's separate Request URL verification, and confirm the step
   in Aion.
6. Continue to Slack OAuth, choose the same workspace, review the requested bot grants, and approve the installation.
   Workspace app-approval policy may place the request in an admin-approval-pending state.
7. When setup completes, return to the draft distribution and explicitly select the new Slack bot service identity.

The two manifest stages prevent Slack from sending a URL-verification callback before Aion has safely persisted the
app's signing secret and opaque ingress key. Aion never asks the customer to share a workspace-scoped app
configuration token.

### Set up an Aion-managed app

Use this mode only when the Aion-managed option is enabled for the current environment.

1. Open the Slack setup modal, choose **Aion-managed Slack app**, and enter the bot service profile.
2. Aion creates a dedicated Slack app, persists its app credentials, and then applies the event-enabled manifest.
3. Continue to Slack OAuth and approve the installation in the intended workspace.
4. Select the resulting Slack bot service identity on the draft distribution.

Aion-managed provisioning uses a two-stage manifest sequence for the same callback-ordering reason as the
customer-managed flow. The app remains dedicated to one bot identity and one active workspace installation in v1.

### Activate the distribution

A Slack distribution can activate when all of the following are true:

* a Slack bot service identity is selected;
* its workspace installation is connected;
* the required baseline OAuth grants are present;
* the Events API subscriptions and Request URL projection are healthy; and
* the installation is not revoked, uninstalled, or waiting for reauthorization.

Channel membership is not an activation requirement. A bot with zero channel memberships can still receive direct
messages. Membership is checked for the particular channel when an event or MCP call uses it.

A configuration change that expands OAuth grants moves the installation to **Reauthentication required**. Reinstall
the app before activating the expanded capability.

### Add the bot to channels

Aion does not maintain a channel allowlist or production channel picker in v1. Slack membership is the channel
observation control.

* **Public channel:** invite the bot in Slack, for example with `/invite @bot-name`. Remove the bot to stop observing
  that channel.
* **Private channel:** an existing channel member must invite the bot. Workspace policy may require admin approval.
* **Direct message:** no channel membership is required.
* **Multiparty direct message:** add the bot to the MPIM before expecting delivery or history access.
* **Slack Connect:** use the same invitation model, then enable **Allow externally shared channels** on the
  distribution. External shared channels remain blocked by default.

The external-channel flag is a privacy backstop, not an installation control. A Slack Connect channel can contain
members from another workspace while the app remains installed only in the authorized workspace.

### Baseline and optional capabilities

The generated manifest is the source of truth for exact scopes and event subscriptions. Aion derives it from the
Slack module's typed capability model instead of maintaining a separate documentation-only matrix.

The baseline includes app mentions, member-channel messages, private member-channel messages, one-to-one DMs,
multiparty DMs, outbound chat, membership events, and app lifecycle events. Notable provider requirements include:

* `groups:history` with `message.groups` for invited private channels;
* `mpim:history`, `mpim:read`, and `message.mpim` for multiparty direct messages;
* `im:history`, `im:read`, and `message.im` for one-to-one direct messages;
* `channels:history`, `channels:read`, and `message.channels` for joined public channels; and
* `app_mentions:read`, `chat:write`, membership events, and uninstall/token-revocation events.

Scopes do not grant access to every private conversation. Slack still limits private-channel and MPIM access to
conversations where the bot is a member.

The manifest uses Slack's conventional writable
[App Home Messages tab](https://docs.slack.dev/surfaces/app-home/) for direct conversations. Slack's native Agent View,
agent-container navigation, suggested prompts, and agent-context events are deferred.

Reaction events, reaction writes, direct-message initiation, native streaming, and authorization reconciliation are
optional capabilities. Commands, interactive payloads, and card-to-Block-Kit rendering are planned rather than
available in this release.

## Message Mapping

### Inbound and outbound payloads

For a message-like callback, Aion sends:

* a normal text part when Slack supplied visible text;
* a normalized `MessageEventPayload` with provider conversation coordinates; and
* a `SourceSystemEventPayload` whose `event` is the complete parsed outer Events API callback.

The source-system payload is semantic JSON preservation. It retains unknown JSON fields from Slack's parsed callback,
but not raw HTTP bytes, headers, signature input, insignificant whitespace, or an invalid body.

By default, a normal agent response returns to the inbound DM, channel, or thread. Explicit
Distribution/Messaging action payloads and `mcp.slack` writes can select another permitted target.

### Context and message identifiers

Slack has no standalone thread id. The root message timestamp, `thread_ts`, is Slack's thread key.

| Slack location                        | Payload `contextId` | Payload `parentContextId` | `messageId`  | `replyToMessageId` |
| ------------------------------------- | ------------------- | ------------------------- | ------------ | ------------------ |
| Channel, private channel, DM, or MPIM | Conversation id     | Absent                    | Message `ts` | Absent             |
| Thread root                           | Root `thread_ts`    | Enclosing conversation id | Root `ts`    | Absent             |
| Thread reply                          | Root `thread_ts`    | Enclosing conversation id | Reply `ts`   | Root `thread_ts`   |

These Slack-native fields belong to the Distribution/Messaging payload. The top-level A2A `Message.contextId` is a
separate opaque Aion identifier mapped to the same immediate context. Agents must not parse it or assume it equals a
Slack channel or timestamp.

Channel, private-channel, DM, MPIM, and thread contexts are long-lived in v1. Aion reuses the mapped A2A context for
later events in the same Slack context. There is no inactivity timeout. Changing the distribution binding retires the
old mapping.

### Thread mention example

This example shows both the normalized Slack coordinates and the complete parsed outer callback:

```json theme={null}
{
  "message": {
    "role": "ROLE_USER",
    "messageId": "msg_01JZSLACK0001",
    "contextId": "ctx_01JZSLACKTHREAD",
    "extensions": [
      "https://docs.aion.to/a2a/extensions/aion/distribution/1.0.0",
      "https://docs.aion.to/a2a/extensions/aion/event/1.0.0",
      "https://docs.aion.to/a2a/extensions/aion/distribution/messaging/1.0.0"
    ],
    "parts": [
      {
        "text": "<@U0AIONBOT> summarize this thread"
      },
      {
        "data": {
          "userId": "U0USER123",
          "contextId": "1715182600.001900",
          "parentContextId": "C0CHANNEL1",
          "messageId": "1715182634.002500",
          "replyToMessageId": "1715182600.001900",
          "trajectory": "reply"
        },
        "mediaType": "application/json",
        "metadata": {
          "https://docs.aion.to/a2a/extensions/aion/event/1.0.0": {
            "schema": "https://docs.aion.to/a2a/extensions/aion/distribution/messaging/1.0.0#MessageEventPayload"
          }
        }
      },
      {
        "data": {
          "provider": "slack",
          "event": {
            "type": "event_callback",
            "team_id": "T0WORKSPACE1",
            "api_app_id": "A0APP123",
            "event": {
              "type": "app_mention",
              "user": "U0USER123",
              "channel": "C0CHANNEL1",
              "text": "<@U0AIONBOT> summarize this thread",
              "ts": "1715182634.002500",
              "thread_ts": "1715182600.001900",
              "event_ts": "1715182634.002500"
            },
            "authorizations": [
              {
                "team_id": "T0WORKSPACE1",
                "user_id": "U0AIONBOT",
                "is_bot": true,
                "is_enterprise_install": false
              }
            ],
            "event_id": "Ev0EVENT123",
            "event_time": 1715182634,
            "is_ext_shared_channel": false,
            "context_team_id": "T0WORKSPACE1",
            "context_enterprise_id": null
          }
        },
        "mediaType": "application/json",
        "metadata": {
          "https://docs.aion.to/a2a/extensions/aion/event/1.0.0": {
            "schema": "https://docs.aion.to/a2a/extensions/aion/distribution/messaging/1.0.0#SourceSystemEventPayload"
          }
        }
      }
    ],
    "metadata": {
      "https://docs.aion.to/a2a/extensions/aion/event/1.0.0": {
        "type": "to.aion.distribution.message.1.0.0",
        "source": "aion://distribution/slack-distribution-123",
        "id": "Ev0EVENT123"
      }
    }
  }
}
```

### Task continuity and retention

Every inbound Slack message starts a new A2A task in the reused context. This includes a message that appears to answer
an earlier `input-required` task. Aion does not infer a target task from participants, timing, or thread position.
Agents can consult prior tasks or their own state for the shared context, but the new request does not identify one
earlier task as its continuation.

Persisted A2A messages are immutable audit snapshots. Slack edits and deletions can update the bounded MCP
current-state cache, but they do not rewrite or delete an already dispatched A2A task. The common retention policy
defaults to 365 days from a task's first terminal transition, then physically deletes the complete task graph.

### Distribution-bound Slack MCP

Use the provider coordinates from `MessageEventPayload` with the runtime-bound `mcp.slack` tools. A thread history
request uses both the enclosing conversation id and root timestamp:

```json theme={null}
{
  "name": "conversations.replies",
  "arguments": {
    "channel": "C0CHANNEL1",
    "ts": "1715182600.001900",
    "limit": 15
  }
}
```

A history result identifies whether Slack, the bounded cache, or both contributed to the response:

```json theme={null}
{
  "messages": [
    {
      "ts": "1715182634.002500",
      "threadTs": "1715182600.001900",
      "user": "U0USER123",
      "botId": null,
      "text": "summarize this thread",
      "subtype": null,
      "replyCount": null,
      "occurredAt": "2026-07-13T19:20:08Z",
      "revisedAt": "2026-07-13T19:20:08Z",
      "edited": false,
      "files": [],
      "attachments": []
    }
  ],
  "metadata": {
    "source": "cache",
    "providerFetchedAt": "2026-07-13T19:20:00Z",
    "eventIngestedThrough": "2026-07-13T19:20:08Z",
    "coverageStart": "1715182600.001900",
    "coverageEnd": "1715182634.002500",
    "partial": true,
    "cacheExpiresAt": "2026-07-13T19:25:00Z",
    "cacheRemainingTtlSeconds": 252,
    "fallbackReason": "provider_rate_limit",
    "providerRetryAt": "2026-07-13T19:21:00Z",
    "retryAfterSeconds": 52
  }
}
```

History reads are live-first. A successful provider read refreshes the cache. When Slack rate-limits or temporarily
rejects a read, Aion can return bounded cached coverage and marks its source, freshness, coverage, partial status, and
retry timing. Never treat a cached partial result as authoritative Slack history.

New commercially distributed non-Marketplace apps can be limited to one
[`conversations.history` or `conversations.replies` request per minute][slack-history-limits].
Aion measures the effective tier for each installation instead of inferring it from provisioning mode.

The history tools return text, author ids, timestamps, thread coordinates, and reviewed file/attachment metadata. They
do not download file contents. SDK `Thread.history()` is not implemented; load and call the distribution-bound Slack
MCP tools instead. See [LangGraph integration patterns](/sdk/langgraph/guides/integration-patterns).

## Features

### Mentions

Aion identifies mentions by the installed bot's exact Slack user id, such as `<@U0AIONBOT>`. Usernames and display
names are not identity keys.

Slack can represent one mentioned channel message as both a generic `message.channels` or `message.groups` callback
and an `app_mention` callback. When the generic event contains the exact installed-bot mention, Aion terminally
suppresses it and dispatches the `app_mention` representation. There is no arbitration timeout. If Slack never sends
the expected `app_mention`, Aion deliberately sends no A2A request for that message.

<Tabs borderBottom>
  <Tab title="LangGraph">
    ```python theme={null}
    from aion.langgraph.authoring import Message, Thread, create_event_router


    async def handle_message(thread: Thread, message: Message) -> None:
        await thread.reply(f"Slack message received: {message.text}")


    slack_events = create_event_router(on_message=handle_message)
    ```
  </Tab>

  <Tab title="Google ADK">
    ```python theme={null}
    from google.adk.events import Event

    from aion.adk.authoring.invocation import AionInvocationContext, Thread


    async def _run_async_impl(self, ctx: AionInvocationContext):
        thread = Thread.from_context(ctx.aion_runtime_context)
        text = thread.message.text if thread.message else ""
        yield Event(
            author=self.name,
            content={"parts": [{"text": f"Slack mention received: {text}"}]},
        )
    ```
  </Tab>

  <Tab title="A2A">
    Inspect the `MessageEventPayload` for normalized coordinates and the `SourceSystemEventPayload` when provider
    fields such as `app_mention`, `thread_ts`, or the outer authorization envelope matter.
  </Tab>
</Tabs>

### Direct and multiparty messages

One-to-one DMs use the writable App Home Messages tab and `message.im`. Multiparty DMs use `message.mpim` and require
the app to be added to that MPIM. Both are baseline message contexts.

<Tabs borderBottom>
  <Tab title="LangGraph">
    Plain LangGraph receives the inbound text in `state.messages`. SDK-aware LangGraph receives the same text plus the
    runtime-scoped `Thread` and message metadata.
  </Tab>

  <Tab title="Google ADK">
    `Thread.from_context(ctx.aion_runtime_context)` resolves the inbound DM or MPIM target. A normal final text event
    replies to that same context by default.
  </Tab>

  <Tab title="A2A">
    ```json theme={null}
    {
      "userId": "U0USER123",
      "contextId": "D0DIRECT1",
      "messageId": "1715182700.000100",
      "trajectory": "direct-message"
    }
    ```
  </Tab>
</Tabs>

### Member-channel observation

An active distribution observes supported messages in every public or private channel where its bot is a member.
There is no separate observation toggle. Removing the bot from a channel stops future event delivery for that channel.

Thread broadcasts are an optional event-policy family. Ordinary edits, deletions, joins, and unsupported message
subtypes do not create new conversational tasks.

### Reactions

Reaction callbacks and reaction writes are optional. Enabling them can require expanded scopes and workspace
re-consent.

<Tabs borderBottom>
  <Tab title="LangGraph">
    Route inbound reactions with `create_event_router(on_reaction=handler)`. Read the normalized reaction event instead
    of treating it as a user text turn.
  </Tab>

  <Tab title="Google ADK">
    Inspect the invocation's normalized event metadata. Use an explicit reaction action only when the agent should
    change the Slack message rather than post a text reply.
  </Tab>

  <Tab title="A2A">
    ```json theme={null}
    {
      "userId": "U0USER123",
      "contextId": "C0CHANNEL1",
      "messageId": "1715182634.002500",
      "reactionKey": "thumbsup",
      "displayValue": ":thumbsup:",
      "action": "added",
      "isCustom": false
    }
    ```
  </Tab>
</Tabs>

### Replies and streaming

The default reply target preserves the inbound context. A distribution can instead select a thread, channel, direct
message, or no-reply mode.

Streaming is off by default. When enabled and eligible, Aion can use Slack's native streaming methods. Otherwise it
posts one message and coalesces updates to that message. The implementation records why it selected native streaming
or the conventional post/update path so operators can diagnose provider eligibility and fallback behavior.

<Tabs borderBottom>
  <Tab title="LangGraph">
    Use `Thread.reply(...)` for the inbound context and `Thread.post(...)` for a new message in the enclosing
    conversation. Runtime streaming events can feed the same reply when streaming is enabled.
  </Tab>

  <Tab title="Google ADK">
    Partial ADK events can contribute to a streaming response. The final event still determines the completed text and
    target.
  </Tab>

  <Tab title="A2A">
    Aion consumes streaming task updates, selects the final response text, and completes provider delivery before
    reporting a successful Slack response phase.
  </Tab>
</Tabs>

## Operations and limitations

### Troubleshoot a missing mention

A suppressed generic mention is never released as a fallback. If the bot ignores a mention:

1. Compare generic-mention suppression and `app_mention` receipt counters for the installation.
2. Verify the app subscribes to `app_mention` and still has `app_mentions:read`.
3. Check installation, re-consent, Request URL, and Events API acknowledgement health.
4. Repair the Slack configuration. Do not replay the suppressed generic event.

Mention diagnostics contain installation ids and low-cardinality result codes, not message text.

### Interpret installation health

| State                     | Meaning                                                | Action                                    |
| ------------------------- | ------------------------------------------------------ | ----------------------------------------- |
| No channel memberships    | DMs remain available; no joined channels are observed. | Invite the bot from Slack.                |
| Reauthentication required | Enabled capabilities exceed the current OAuth grant.   | Reinstall and approve scopes.             |
| Admin approval pending    | Slack is waiting for workspace approval.               | Ask a workspace admin to approve the app. |
| Configuration required    | Manifest or Request URL setup is incomplete.           | Resume setup and finish the owner action. |
| Revoked or uninstalled    | Slack no longer authorizes the installation.           | Reconnect or create a replacement setup.  |

### Current limitations

* One active workspace installation is allowed per Slack bot service identity.
* Enterprise Grid organization-wide installs are unsupported.
* Externally shared channels require a distribution-wide opt-in and are subject to stricter privacy checks.
* Native Agent View, commands, interactive payloads, and card-to-Block-Kit rendering are deferred.
* Native streaming remains preview until each supported Slack target is manually validated.
* Slack edits and deletions update bounded current-state history only; persisted A2A audit history is immutable.
* Customer-managed profile updates can require a Slack owner action. Aion-managed profile updates remain subject to
  provisioning entitlement and provider support.

See [Feature Support](/docs/distributions/messaging/feature-support) for release-state labels across messaging
providers.

[slack-history-limits]: https://docs.slack.dev/changelog/2025/05/29/rate-limit-changes-for-non-marketplace-apps/
