<!-- Generated by `just docs` from catalog/toolkits/slack.yaml, catalog/expansions/slack.yaml, catalog/evals/scorecard.json. Edit the source, not this file. -->

# Slack

Team messaging. Send and manage messages, work with channels, threads, and direct messages, look up people, and search history.

| Property | Value |
| --- | --- |
| Slug | `slack` |
| Definition version | `0.1.0` |
| Base URL | `https://slack.com/api` |
| Auth schemes | `oauth2` |
| Action tools | 35 |
| By class | 15 read, 16 write, 4 destructive |
| Triggers | 2 |
| Provider rate limit | 50 requests per 1m, per account |

## Measured routing accuracy

33 golden cases replayed through the router over the whole index: measured over corpus `ea4f12ad2948` (65 toolkits, 2283 tools indexed and 13 declared uncallable), 13 cases written by hand and 17 cases from the paraphrase pass, plus 3 context cases whose intent names no app and is decided by the session. A case counts as top-1 when its gold tool ranked first and top-8 when it reached the slate at all.

| Measure | Cases | Share |
| --- | --- | --- |
| top-1 | 6/33 | 18.2% |
| top-8 | 23/33 | 69.7% |

The sweep is offline: the reranker is a deterministic identity fake that returns candidates in the order retrieval produced them, so top-1 measures retrieval order rather than a reranked slate. `just eval-live` measures the same cases through the live reranker.

## Authentication

Connect an entity with `ConnectionsService.InitiateConnection`, naming this toolkit's slug. Credentials stay in the connections vault; callers hold connected-account ids only.

### `oauth2`

| Property | Value |
| --- | --- |
| Authorization URL | `https://slack.com/oauth/v2/authorize` |
| Token URL | `https://slack.com/api/oauth.v2.access` |
| Default scopes | `chat:write`, `channels:read`, `channels:history`, `users:read` |
| Refresh tokens | no, so the end user reauthorizes when the token expires |

## Tools

35 action tools. The catalog-wide slug is `slack.<tool>`, which is what `search_tools` returns and `call_tool` takes.

### `slack.send_message`

Send a message to a channel, DM, or thread now, immediately. Use when the user wants to post, tell, notify, announce, share, or reply in Slack. Pass thread_ts to reply inside a thread. Not for a later time; for that use schedule_message. For a message only one person can see, use send_ephemeral_message instead.

Class `write` (writes, no confirmation needed). Scopes `chat:write`. Calls `POST /chat.postMessage`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | Channel, DM, or group DM id, e.g. "C0123456789". Resolve a channel name with list_channels; open a DM id with open_dm. |
| `reply_broadcast` | boolean | no | When replying in a thread, also show the reply in the channel. |
| `text` | string | **yes** | Message text. Slack mrkdwn is supported. |
| `thread_ts` | string | no | Parent message ts, e.g. "1712345678.000200". Set to reply in that message's thread instead of posting to the channel. |
| `unfurl_links` | boolean | no | Whether Slack expands link previews. Defaults to true. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `channel` | string |  |
| `text` | string |  |
| `ts` | string |  |

Also retrieved by: "drop a note in the channel", "ping the team about something", "give everyone a heads up in there", "shout out to the channel", "let the group know what's going on", "ask something in front of the whole channel".

### `slack.send_ephemeral_message`

Send a message in a channel that only one member can see. Use for a private hint, warning, or confirmation that should not be visible to the whole channel. The message disappears on reload and cannot be edited. For a normal message everyone sees, use send_message.

Class `write` (writes, no confirmation needed). Scopes `chat:write`. Calls `POST /chat.postEphemeral`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | Channel id the message appears in, e.g. "C0123456789". |
| `text` | string | **yes** | Message text. Slack mrkdwn is supported. |
| `thread_ts` | string | no | Parent message ts to show the ephemeral message in a thread. |
| `user` | string | **yes** | User id of the only person who will see it, e.g. "U0123456789". |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `message_ts` | string |  |

Also retrieved by: "flag something to just one teammate without the rest seeing", "give someone a quiet heads up nobody else notices", "send a note only that person can see", "whisper a warning to one member", "pop up a private note that vanishes after they read it".

### `slack.update_message`

Edit the text of a message that was already posted. Use when the user wants to fix, correct, or change an existing Slack message. Identify the message by its channel and ts timestamp. Ephemeral messages cannot be edited.

Class `write` (writes, no confirmation needed). Scopes `chat:write`. Calls `POST /chat.update`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | Channel id containing the message. |
| `text` | string | **yes** | Replacement text for the whole message. |
| `ts` | string | **yes** | Timestamp of the message to edit, e.g. "1712345678.000200". |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `channel` | string |  |
| `text` | string |  |
| `ts` | string |  |

Also retrieved by: "fix a typo in something I posted", "reword what I already sent", "clean up the wording in my last post", "change what I typed earlier", "tweak the text of something I sent".

### `slack.delete_message`

Delete a message from a channel or DM. Use when the user wants to remove, retract, or take down a message. Permanent; the message cannot be restored. Identify the message by its channel and ts timestamp.

Class `destructive` (held by the mutation gate until the call's own arguments carry `"confirm": true`). Scopes `chat:write`. Calls `POST /chat.delete`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | Channel id containing the message. |
| `ts` | string | **yes** | Timestamp of the message to delete, e.g. "1712345678.000200". |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `channel` | string |  |
| `ts` | string |  |

Also retrieved by: "take back something I posted", "erase a post I regret sending", "get rid of that note in the channel", "pull down what I just wrote", "undo something I sent by mistake".

### `slack.schedule_message`

Send a message later, at a chosen future time in a channel or DM. Use when the user says send later, schedule, or remind the channel at a given time. post_at is a Unix timestamp, at most 120 days ahead. To post now, use send_message.

Class `write` (writes, no confirmation needed). Scopes `chat:write`. Calls `POST /chat.scheduleMessage`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | Channel or DM id to deliver the message to. |
| `post_at` | integer | **yes** | Unix timestamp (seconds) for delivery, e.g. 1785302400. Must be in the future and within 120 days. |
| `text` | string | **yes** | Message text. Slack mrkdwn is supported. |
| `thread_ts` | string | no | Parent message ts to deliver the message into a thread. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `channel` | string |  |
| `post_at` | integer |  |
| `scheduled_message_id` | string |  |

Also retrieved by: "queue up a note to go out first thing tomorrow", "line up something to send while I'm asleep", "have a reminder post itself at 9am", "set something to go out automatically later", "prep a note that fires off at a specific time".

### `slack.list_scheduled_messages`

List pending scheduled posts not yet delivered, with their ids and delivery times. Use to see what is queued, or to find the id needed by cancel_scheduled_message. Optionally filter by channel or a time window. Does not search message history; for that use search_messages.

Class `read` (reads only). No scopes beyond the connection's defaults. Calls `POST /chat.scheduledMessages.list`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | no | Only return messages scheduled for this channel id. |
| `cursor` | string | no | Pagination cursor from a previous page's next_cursor. |
| `latest` | string | no | Only messages scheduled before this Unix timestamp. |
| `limit` | integer | no | Maximum results per page. Default 100. |
| `oldest` | string | no | Only messages scheduled after this Unix timestamp. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `next_cursor` | string |  |
| `scheduled_messages` | array of object |  |
| `scheduled_messages[].channel_id` | string |  |
| `scheduled_messages[].id` | string |  |
| `scheduled_messages[].post_at` | integer |  |
| `scheduled_messages[].text` | string |  |

Also retrieved by: "see what's queued up to go out later", "check what's about to be posted automatically", "show me the upcoming sends I set up", "what reminders are still pending", "review what I lined up for later".

### `slack.cancel_scheduled_message`

Cancel a scheduled message before it is sent. Use when the user wants to stop or undo a message they scheduled. Requires the scheduled_message_id from schedule_message or list_scheduled_messages. Fails within 60 seconds of the delivery time. The cancellation is permanent.

Class `destructive` (held by the mutation gate until the call's own arguments carry `"confirm": true`). Scopes `chat:write`. Calls `POST /chat.deleteScheduledMessage`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | Channel id the message was scheduled for. |
| `scheduled_message_id` | string | **yes** | Id returned when the message was scheduled, e.g. "Q1298393284". |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `ok` | boolean |  |

Also retrieved by: "pull back something I queued up before it goes out", "call off something I lined up for later", "scrap that reminder before it fires", "stop the queued note from going out", "I changed my mind, don't send that later note".

### `slack.get_message_permalink`

Get a shareable permalink URL for a specific message. Use when the user wants a link to a Slack message to share, bookmark, or reference elsewhere. Identify the message by its channel and message_ts timestamp.

Class `read` (reads only). No scopes beyond the connection's defaults. Calls `GET /chat.getPermalink`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | Channel id containing the message. |
| `message_ts` | string | **yes** | Timestamp of the message, e.g. "1712345678.000200". |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `channel` | string |  |
| `permalink` | string |  |

Also retrieved by: "grab a link to that post so I can share it", "get the url for a specific post", "copy the address of what someone said", "give me something I can paste elsewhere pointing to that post", "I need a link back to that exact post".

### `slack.list_channels`

List channels in the workspace, including private channels and DMs the connected account can see. Use to browse channels, find a channel id from its name, or see what channels exist. For channels a specific person belongs to, use list_user_conversations.

Class `read` (reads only). Scopes `channels:read`, `groups:read`, `im:read`, `mpim:read`. Calls `GET /conversations.list`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `cursor` | string | no | Pagination cursor from a previous page's next_cursor. |
| `exclude_archived` | boolean | no | Leave out archived channels. Default false. |
| `limit` | integer | no | Maximum results per page, up to 1000. Default 100. |
| `types` | string | no | Comma-separated conversation types to include, from public_channel, private_channel, mpim, im. Default public_channel. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `channels` | array of object |  |
| `channels[].id` | string |  |
| `channels[].is_archived` | boolean |  |
| `channels[].is_private` | boolean |  |
| `channels[].name` | string |  |
| `channels[].num_members` | integer |  |
| `next_cursor` | string |  |

Also retrieved by: "show me every room that exists here", "what rooms do we have", "browse everything available in the workspace", "give me the full rundown of rooms", "see all the places to talk in this workspace".

### `slack.get_channel_info`

Get details about one channel, DM, or group DM: name, topic, purpose, member count, and archived state. Use when the user asks about a specific channel. For the actual member list, use list_channel_members.

Class `read` (reads only). Scopes `channels:read`, `groups:read`, `im:read`, `mpim:read`. Calls `GET /conversations.info`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | Channel id, e.g. "C0123456789". |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `id` | string |  |
| `is_archived` | boolean |  |
| `is_private` | boolean |  |
| `name` | string |  |
| `num_members` | integer |  |
| `purpose` | string |  |
| `topic` | string |  |

Also retrieved by: "tell me about this particular room", "what's this room actually for", "give me the details on one room", "how many people are in this one room", "is this room archived or still active".

### `slack.get_channel_history`

Fetch recent messages from a channel or DM, newest first. Use when the user wants to read, catch up on, or summarize what was said in a channel. Returns top-level messages only; use get_thread_replies for the messages inside a thread.

Class `read` (reads only). Scopes `channels:history`, `groups:history`, `im:history`, `mpim:history`. Calls `GET /conversations.history`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | Channel or DM id to read from. |
| `cursor` | string | no | Pagination cursor from a previous page's next_cursor. |
| `latest` | string | no | Only messages before this ts timestamp. |
| `limit` | integer | no | Maximum messages per page, up to 999. Default 100. |
| `oldest` | string | no | Only messages after this ts timestamp. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `has_more` | boolean |  |
| `messages` | array of object |  |
| `messages[].reply_count` | integer |  |
| `messages[].text` | string |  |
| `messages[].thread_ts` | string |  |
| `messages[].ts` | string |  |
| `messages[].user` | string |  |
| `next_cursor` | string |  |

Also retrieved by: "pull up the last 20 messages from the support room", "what did I miss in there today", "catch me up on what's been said recently", "scroll back through recent chatter", "recap what happened this week", "show me what was said lately".

### `slack.get_thread_replies`

Fetch the messages in one thread, including the parent message. Use when the user wants to read a thread or its replies. Identify the thread by its channel and the parent message's ts. For a channel's top-level messages, use get_channel_history.

Class `read` (reads only). Scopes `channels:history`, `groups:history`, `im:history`, `mpim:history`. Calls `GET /conversations.replies`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | Channel id containing the thread. |
| `cursor` | string | no | Pagination cursor from a previous page's next_cursor. |
| `limit` | integer | no | Maximum messages per page. Default 100. |
| `ts` | string | **yes** | The parent message's ts, e.g. "1712345678.000200". |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `has_more` | boolean |  |
| `messages` | array of object |  |
| `messages[].text` | string |  |
| `messages[].ts` | string |  |
| `messages[].user` | string |  |
| `next_cursor` | string |  |

Also retrieved by: "what did people say back on that thread", "show me the whole conversation under that post", "see every reply under one post", "read the back and forth on that thread", "expand that thread so I can see the replies".

### `slack.create_channel`

Create a new channel, public or private. Use when the user wants to make, start, or set up a channel. Names must be lowercase without spaces or periods, up to 80 characters. A private channel cannot be made public later.

Class `write` (writes, no confirmation needed). Scopes `channels:manage`, `groups:write`. Calls `POST /conversations.create`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `is_private` | boolean | no | Create as a private channel. Default false. |
| `name` | string | **yes** | Channel name, e.g. "incident-2026-07". Lowercase letters, numbers, hyphens, and underscores only. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `id` | string |  |
| `is_private` | boolean |  |
| `name` | string |  |

Also retrieved by: "spin up a new room for this", "start a place for people to talk about it", "set up a room for the incident responders", "open a fresh room for the launch", "get a new room going".

### `slack.archive_channel`

Archive a channel, hiding it and blocking new messages for every member. Use when the user wants to archive, retire, shut down, or close down a channel for good. History stays searchable and unarchive_channel reverses it, but archiving disrupts all members immediately.

Class `destructive` (held by the mutation gate until the call's own arguments carry `"confirm": true`). Scopes `channels:manage`, `groups:write`. Calls `POST /conversations.archive`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | Channel id to archive. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `ok` | boolean |  |

Also retrieved by: "shut down a room we don't need anymore", "wind down the old hackathon room", "put this room away for good", "mothball a room nobody uses", "close out a room we're done with".

### `slack.unarchive_channel`

Restore an archived channel so members can see it and post again. Use when the user wants to reopen, reactivate, or bring back a channel that was archived.

Class `write` (writes, no confirmation needed). Scopes `channels:manage`, `groups:write`. Calls `POST /conversations.unarchive`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | Channel id to unarchive. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `ok` | boolean |  |

Also retrieved by: "bring back a room that got shelved", "revive an old room we put away", "reopen something we closed down before", "un-shelve that room from before", "make that shelved room active again".

### `slack.rename_channel`

Rename an existing channel, changing its name only. Use when the user wants to change a channel's name. Does not close or archive it. Same naming rules as create_channel: lowercase, no spaces or periods, up to 80 characters. Mentions and links using the old name are not redirected.

Class `write` (writes, no confirmation needed). Scopes `channels:manage`, `groups:write`. Calls `POST /conversations.rename`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | Channel id to rename. |
| `name` | string | **yes** | New channel name. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `id` | string |  |
| `name` | string |  |

Also retrieved by: "call this room something else", "relabel it with a new name", "rebrand this room", "swap out the name of a room", "give this room a different name".

### `slack.set_channel_topic`

Set or change a channel's topic, the short line shown in the channel header. Use when the user wants to update what the channel is currently about. For the longer standing description of the channel, use set_channel_purpose.

Class `write` (writes, no confirmation needed). Scopes `channels:manage`, `groups:write`, `im:write`, `mpim:write`. Calls `POST /conversations.setTopic`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | Channel id to update. |
| `topic` | string | **yes** | New topic text. Plain text; up to 250 characters. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `topic` | string |  |

Also retrieved by: "put a quick headline on the room", "update what's pinned at the top", "change the one-liner shown up top", "set what shows next to the room's name", "update the short blurb everyone sees first".

### `slack.set_channel_purpose`

Set or change a channel's purpose, the standing description of why the channel exists. Use when the user wants to update a channel's description. For the short header line that changes day to day, use set_channel_topic.

Class `write` (writes, no confirmation needed). Scopes `channels:manage`, `groups:write`, `im:write`, `mpim:write`. Calls `POST /conversations.setPurpose`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | Channel id to update. |
| `purpose` | string | **yes** | New purpose text. Plain text; up to 250 characters. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `purpose` | string |  |

Also retrieved by: "explain what this room is for", "write up why this room exists", "describe the point of this room", "set the long-term description for a room", "fill in what this room is meant for".

### `slack.invite_to_channel`

Invite one or more people to a channel. Use when the user wants to add members to a channel. Pass user ids comma separated, up to 1000 at once. Resolve ids with find_user_by_email or list_users.

Class `write` (writes, no confirmation needed). Scopes `channels:manage`, `groups:write`. Calls `POST /conversations.invite`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | Channel id to invite into. |
| `users` | string | **yes** | Comma-separated user ids, e.g. "U0123456789,U0987654321". |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `id` | string |  |
| `name` | string |  |

Also retrieved by: "add someone to a room", "bring a new person into a room", "loop a teammate into a room", "get a coworker added to this room", "pull someone into the conversation".

### `slack.remove_from_channel`

Remove a person from a channel. Use when the user wants to kick or take someone out of a channel. The person loses access immediately and must be re-invited to return.

Class `destructive` (held by the mutation gate until the call's own arguments carry `"confirm": true`). Scopes `channels:manage`, `groups:write`. Calls `POST /conversations.kick`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | Channel id to remove the person from. |
| `user` | string | **yes** | User id of the person to remove. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `ok` | boolean |  |

Also retrieved by: "kick someone out of a room", "boot a person from this room", "cut off someone's access to a room", "take a teammate out of a room", "remove one person's access".

### `slack.join_channel`

Join a public channel. Use when the user wants to join, follow, or start participating in a channel. Works on public channels only; joining a private channel requires an invitation from a member.

Class `write` (writes, no confirmation needed). Scopes `channels:join`. Calls `POST /conversations.join`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | Public channel id to join. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `id` | string |  |
| `name` | string |  |

Also retrieved by: "hop into a public room", "get myself added to a room", "start following along in a room", "sign myself up for a room", "become part of that room".

### `slack.leave_channel`

Leave a channel. Use when the user wants to leave or exit a channel they are in. A public channel can be rejoined later with join_channel; leaving a private channel requires a new invitation to return.

Class `write` (writes, no confirmation needed). Scopes `channels:manage`, `groups:write`, `im:write`, `mpim:write`. Calls `POST /conversations.leave`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | Channel id to leave. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `ok` | boolean |  |

Also retrieved by: "step out of a room I'm in", "drop out of a room I don't need anymore", "get myself out of this room", "stop being part of a room", "exit a conversation I'm done with".

### `slack.open_dm`

Open a direct message with one person or a group DM with up to eight people. Use when the user wants to start or resume a DM. Returns the conversation id to pass as channel to send_message. Pass user ids comma separated.

Class `write` (writes, no confirmation needed). Scopes `im:write`, `mpim:write`. Calls `POST /conversations.open`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `users` | string | **yes** | Comma-separated user ids, e.g. "U0123456789" for a DM or "U0123456789,U0987654321" for a group DM. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `channel_id` | string |  |

Also retrieved by: "start a private chat with a coworker", "message someone directly instead of a room", "open a one on one conversation", "get a small group chat going with a few people", "begin a direct line to someone".

### `slack.close_dm`

Close a direct message or group DM, removing it from the sidebar. Use when the user wants to close or hide a DM. No history is deleted; opening the conversation again brings it back.

Class `write` (writes, no confirmation needed). Scopes `im:write`, `mpim:write`. Calls `POST /conversations.close`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | DM or group DM id to close, e.g. "D0123456789". |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `ok` | boolean |  |

Also retrieved by: "hide a direct chat from my sidebar", "tidy up my list of conversations", "get a private chat out of my view", "put away a private chat I'm not using", "clear a conversation off my sidebar".

### `slack.list_channel_members`

List the user ids of everyone in a channel. Use when the user asks who is in a channel or how many people it has. Resolve the ids to names with get_user_info.

Class `read` (reads only). Scopes `channels:read`, `groups:read`, `im:read`, `mpim:read`. Calls `GET /conversations.members`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `channel` | string | **yes** | Channel id to list members of. |
| `cursor` | string | no | Pagination cursor from a previous page's next_cursor. |
| `limit` | integer | no | Maximum results per page. Default 100. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `members` | array of string |  |
| `next_cursor` | string |  |

Also retrieved by: "who is in the platform oncall channel right now", "see everyone in a room", "channel roster", "who's part of this room", "how many people are actually in here", "give me the full lineup for a room".

### `slack.list_users`

List everyone in the workspace with their ids, names, and profile basics. Use to browse members or resolve a display name to a user id. To look someone up by email address, use find_user_by_email instead of paging this list.

Class `read` (reads only). Scopes `users:read`. Calls `GET /users.list`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `cursor` | string | no | Pagination cursor from a previous page's next_cursor. |
| `limit` | integer | no | Maximum results per page, up to 1000. Default is the full list. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `members` | array of object |  |
| `members[].deleted` | boolean |  |
| `members[].id` | string |  |
| `members[].is_bot` | boolean |  |
| `members[].name` | string |  |
| `members[].real_name` | string |  |
| `next_cursor` | string |  |

Also retrieved by: "show me everyone on the team", "pull up the whole company directory", "see every person in the workspace", "who works here", "browse the full roster of people".

### `slack.get_user_info`

Get a workspace member's details by user id: real name, display name, title, email, time zone, and bot or deactivated flags. Use when the user asks who someone is. If you only have an email address, use find_user_by_email.

Class `read` (reads only). Scopes `users:read`. Calls `GET /users.info`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `user` | string | **yes** | User id, e.g. "U0123456789". |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `deleted` | boolean |  |
| `display_name` | string |  |
| `email` | string |  |
| `id` | string |  |
| `is_bot` | boolean |  |
| `real_name` | string |  |
| `title` | string |  |
| `tz` | string |  |

Also retrieved by: "look someone up by their id", "what's this person's job title", "find out someone's time zone", "get the lowdown on a teammate", "who exactly is this person".

### `slack.find_user_by_email`

Find a workspace member by their email address and return their user id and profile. Use when the user identifies a person by email, or when you need a user id for messaging or inviting and only have an email.

Class `read` (reads only). Scopes `users:read.email`. Calls `GET /users.lookupByEmail`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `email` | string | **yes** | The email address to look up, e.g. "priya@acme.com". |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `display_name` | string |  |
| `email` | string |  |
| `id` | string |  |
| `real_name` | string |  |

Also retrieved by: "who in the workspace has this email address", "match an email address to a real person", "figure out whose email this is", "look someone up using just their email", "find the account tied to an address".

### `slack.get_user_presence`

Check whether a person is currently active or away in Slack. Use when the user asks if someone is online, around, or available right now.

Class `read` (reads only). Scopes `users:read`. Calls `GET /users.getPresence`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `user` | string | **yes** | User id to check, e.g. "U0123456789". |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `presence` | string, one of active, away |  |

Also retrieved by: "is she online right now", "check if someone is at their desk", "see if a teammate is free to chat", "is he around at the moment", "find out if someone's active right now".

### `slack.set_user_presence`

Set the connected account's own presence to away, or back to auto so it follows their activity again. Use when the user wants to appear away or reset their presence. To set a status message, use set_user_status.

Class `write` (writes, no confirmation needed). Scopes `users:write`. Calls `POST /users.setPresence`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `presence` | string, one of auto, away | **yes** | "away" to appear away, "auto" to follow real activity. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `ok` | boolean |  |

Also retrieved by: "make it look like I stepped away", "go invisible for a bit", "turn my status back to automatic", "stop showing myself as active", "reset my own online indicator".

### `slack.get_user_profile`

Get a person's profile fields, including their current status text and status emoji. Use when the user asks what someone's status is or wants full profile details. For basic identity plus email, get_user_info is usually enough.

Class `read` (reads only). Scopes `users.profile:read`. Calls `GET /users.profile.get`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `user` | string | no | User id to fetch. Omit for the connected account's own profile. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `display_name` | string |  |
| `email` | string |  |
| `real_name` | string |  |
| `status_emoji` | string |  |
| `status_text` | string |  |
| `title` | string |  |

Also retrieved by: "what does someone's status say right now", "check a teammate's bio", "see the emoji next to someone's name", "pull up someone's full details", "what is this person's current status".

### `slack.set_user_status`

Set the connected account's Slack status: the text and emoji shown next to their name. Use when the user wants to set, change, or clear their status. Pass empty strings to clear it. To appear away instead, use set_user_presence.

Class `write` (writes, no confirmation needed). Scopes `users.profile:write`. Calls `POST /users.profile.set`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `status_emoji` | string | no | Emoji shorthand, e.g. ":palm_tree:". Empty string with empty text clears it. |
| `status_expiration` | integer | no | Unix timestamp when the status clears itself. 0 or omitted means it does not expire. |
| `status_text` | string | **yes** | Status text, e.g. "On vacation". Empty string clears the status. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `status_emoji` | string |  |
| `status_text` | string |  |

Also retrieved by: "put a little note next to my name", "add an emoji badge to my profile", "clear whatever status I have up", "change what shows beside my name right now", "let people know I'm on vacation with an emoji".

### `slack.list_user_conversations`

List the channels and DMs a specific person is a member of. Use when the user asks what channels someone is in. Defaults to the connected account when user is omitted. For every channel in the workspace regardless of membership, use list_channels.

Class `read` (reads only). Scopes `channels:read`, `groups:read`, `im:read`, `mpim:read`. Calls `GET /users.conversations`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `cursor` | string | no | Pagination cursor from a previous page's next_cursor. |
| `exclude_archived` | boolean | no | Leave out archived channels. Default false. |
| `limit` | integer | no | Maximum results per page, up to 1000. Default 100. |
| `types` | string | no | Comma-separated conversation types, from public_channel, private_channel, mpim, im. Default public_channel. |
| `user` | string | no | User id whose conversations to list. Omit for the connected account. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `channels` | array of object |  |
| `channels[].id` | string |  |
| `channels[].is_private` | boolean |  |
| `channels[].name` | string |  |
| `next_cursor` | string |  |

Also retrieved by: "which rooms is a specific person in", "where does this teammate spend their time", "see every room someone belongs to", "what conversations is this person part of", "find all the rooms one coworker joined".

### `slack.search_messages`

Search messages across the whole workspace by keyword. Use when the user wants to find, look up, or dig up what someone said, a decision, or a link, and does not know which channel it is in. Supports modifiers like from:@name, in:#channel, before:, after:. Runs as the connected user. For files and uploads, use search_files.

Class `read` (reads only). Scopes `search:read`. Calls `GET /search.messages`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `count` | integer | no | Results per page, up to 100. Default 20. |
| `page` | integer | no | Page number, up to 100. Default 1. |
| `query` | string | **yes** | Search terms, with optional modifiers, e.g. "q3 budget from:@sam in:#finance". |
| `sort` | string, one of score, timestamp | no | Sort by relevance (score) or recency (timestamp). Default score. |
| `sort_dir` | string, one of asc, desc | no | Sort direction. Default desc. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `matches` | array of object |  |
| `matches[].channel_name` | string |  |
| `matches[].permalink` | string |  |
| `matches[].text` | string |  |
| `matches[].ts` | string |  |
| `matches[].user` | string |  |
| `total` | integer |  |

Also retrieved by: "look up what someone said", "find where something was mentioned", "dig through old chats for a specific word", "track down who said what about a topic", "hunt for a conversation I can't remember the room of", "find where a decision or number was shared".

### `slack.search_files`

Search files shared in the workspace by keyword: documents, images, snippets, and uploads. Use when the user is looking for a file rather than a conversation. Supports modifiers like from:@name and in:#channel. Runs as the connected user. For messages, use search_messages.

Class `read` (reads only). Scopes `search:read`. Calls `GET /search.files`.

Arguments:

| Argument | Type | Required | Notes |
| --- | --- | --- | --- |
| `count` | integer | no | Results per page, up to 100. Default 20. |
| `page` | integer | no | Page number, up to 100. Default 1. |
| `query` | string | **yes** | Search terms, with optional modifiers, e.g. "roadmap pdf in:#planning". |
| `sort` | string, one of score, timestamp | no | Sort by relevance (score) or recency (timestamp). Default score. |
| `sort_dir` | string, one of asc, desc | no | Sort direction. Default desc. |

Result fields (the payload is trimmed to these before it reaches the model):

| Field | Type | Notes |
| --- | --- | --- |
| `files` | array of object |  |
| `files[].filetype` | string |  |
| `files[].id` | string |  |
| `files[].name` | string |  |
| `files[].permalink` | string |  |
| `files[].title` | string |  |
| `files[].user` | string |  |
| `total` | integer |  |

Also retrieved by: "find that pdf someone shared", "track down an attachment from a while back", "dig up a document someone uploaded", "look for an image someone posted", "locate a file without remembering who sent it".

## Triggers

Triggers are app-initiated. They are not retrievable as tools and cannot be called; subscribe with `TriggersService.CreateSubscription` and events arrive as signed deliveries.

### `message_posted`

Fires when a message is posted in a channel the app is a member of. Carries the channel, author, text, and thread timestamp.

Payload fields (anything the schema does not declare never reaches a subscriber):

| Field | Type | Notes |
| --- | --- | --- |
| `event` | object |  |
| `event.channel` | string |  |
| `event.channel_type` | string |  |
| `event.text` | string |  |
| `event.thread_ts` | string |  |
| `event.ts` | string |  |
| `event.type` | string |  |
| `event.user` | string |  |
| `team_id` | string |  |

### `app_mention`

Fires when someone mentions the app in a channel. Carries the channel, author, full message text, and thread timestamp.

Payload fields (anything the schema does not declare never reaches a subscriber):

| Field | Type | Notes |
| --- | --- | --- |
| `event` | object |  |
| `event.channel` | string |  |
| `event.text` | string |  |
| `event.thread_ts` | string |  |
| `event.ts` | string |  |
| `event.type` | string |  |
| `event.user` | string |  |
| `team_id` | string |  |

## Inbound webhooks

Providers post to `/webhooks/slack`. Posts that fail verification are rejected before anything is stored, and the ingest path refuses every post while the secret variable is unset.

| Property | Value |
| --- | --- |
| Verification scheme | `slack_v0` |
| Shared secret | `AUTOMATON_SLACK_SIGNING_SECRET` (deployment environment) |
| Signature header | `X-Slack-Signature` |
| Signature format | `v0={sig}` |
| Timestamp header | `X-Slack-Request-Timestamp` |
| Timestamp tolerance | 300s |

Mappings are tried in order and the first match wins.

| Trigger | Matches when | Entity resolved from |
| --- | --- | --- |
| `app_mention` | payload `event.type` = `app_mention` | event.user |
| `message_posted` | payload `event.type` = `message` | event.user |
