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

# Slack

> Web API, OAuth v2, incoming webhooks, interactivity, events, and Socket Mode.

Stateful Slack Web API emulation with channels, messages, threads, reactions, user profiles, presence, modern file uploads, pins, bookmarks, views, OAuth v2, incoming webhooks, interactivity, slash commands, and Socket Mode. Chat writes preserve common rich message fields such as `blocks`, `attachments`, `metadata`, formatting flags, unfurl flags, and client message ids. Conversation writes update archive state, names, topics, purposes, membership, DMs, MPIMs, and read cursors. User writes update profile fields, status, custom fields, and deterministic active or away presence. File writes support the current external upload flow with local upload URLs, file share messages, reads, lists, downloads, and deletes. Pin and bookmark writes support channel message pins and link bookmarks. View writes support App Home publishing and modal stacks. Seeded OAuth apps and OAuth installs create bot users and installation records. OAuth exchanges and explicit token seeds create scoped token records. State changes dispatch `event_callback` payloads to configured webhook URLs.

Slack message text is limited to 40,000 Unicode characters across chat writes, incoming webhooks, and file upload initial comments. Longer text is truncated at a Unicode code point boundary before it is stored or dispatched. Successful Web API responses include `warning: "message_truncated"` and `response_metadata` with the matching warning and explanatory message. Rich fields such as `blocks` and `attachments` are preserved unchanged.

## At a glance

| | |
| - | - |
| Package | `@deploybaseline/slack` |
| Port | `4003` in the default set, `4000` when started alone |
| Config key | `slack` |
| HTTP operations | 81, browsable at `http://localhost:3999/docs/slack` while it runs ([API reference](/api-reference.md)) |
| WebSocket endpoints | 1 |

```bash CLI theme={null}
npx @deploybaseline/cli start --service slack
```

```ts Programmatic theme={null}
import { createEmulator } from "@deploybaseline/cli";

const slack = await createEmulator({ service: "slack" });
// slack.url is http://localhost:4000
```

```json baseline.config.json theme={null}
{
  "slack": {
    "team": {
      "name": "My Workspace",
      "domain": "my-workspace"
    },
    "users": [
      {
        "name": "developer",
        "real_name": "Developer",
        "email": "dev@example.com",
        "profile": {
          "title": "Local Developer",
          "status_text": "Testing locally",
          "status_emoji": ":computer:"
        },
        "presence": "active"
      }
    ],
    "channels": [
      {
        "name": "general",
        "topic": "General discussion"
      },
      {
        "name": "random",
        "topic": "Random stuff"
      }
    ],
    "bots": [
      {
        "name": "my-bot"
      }
    ],
    "oauth_apps": [
      {
        "client_id": "12345.67890",
        "client_secret": "example_client_secret",
        "app_id": "A000000001",
        "name": "My Slack App",
        "redirect_uris": ["http://localhost:3000/api/auth/callback/slack"],
        "scopes": [
          "chat:write",
          "channels:read",
          "channels:history",
          "channels:join",
          "channels:manage",
          "channels:write",
          "groups:read",
          "groups:history",
          "groups:write",
          "im:read",
          "im:history",
          "im:write",
          "mpim:read",
          "mpim:history",
          "mpim:write",
          "users:read",
          "users:read.email",
          "users.profile:read",
          "users.profile:write",
          "users:write",
          "files:read",
          "files:write",
          "pins:read",
          "pins:write",
          "bookmarks:read",
          "bookmarks:write",
          "reactions:read",
          "reactions:write",
          "team:read"
        ],
        "user_scopes": ["users:read", "users.profile:read"],
        "bot_name": "my-bot"
      }
    ],
    "strict_scopes": false
  }
}
```

## Start

```bash theme={null}
npx @deploybaseline/cli start --service slack
```

When all services are started, Slack listens on `http://localhost:4003`; alone, it starts on `http://localhost:4000`. Point `@slack/web-api` or Bolt at it with `slackApiUrl: "http://localhost:4003/api/"`. For Socket Mode, start with `npx -p @deploybaseline/cli -p @deploybaseline/ws baseline start --service slack` (see [Socket Mode](#socket-mode)).

## Auth

* `POST /api/auth.test` — Test authentication

All Web API endpoints require `Authorization: Bearer <token>`. Seeded OAuth apps create local installation records, and the OAuth v2 flow with its user picker UI creates scoped bot tokens.

Web API arguments are read from the query string, a form body, or a JSON body, as Slack accepts them; when an argument is in both, the body wins.

Scope checks are relaxed by default for local development. Set `slack.strict_scopes: true` in seed config to make supported Web API methods return Slack-style `missing_scope` errors with `needed` and `provided` fields. Strict mode checks `chat:write`, `channels:read`, `channels:history`, `channels:join`, `channels:manage`, `channels:write`, `groups:read`, `groups:history`, `groups:write`, `im:read`, `im:history`, `im:write`, `mpim:read`, `mpim:history`, `mpim:write`, `users:read`, `users:read.email`, `users.profile:read`, `users.profile:write`, `users:write`, `files:read`, `files:write`, `pins:read`, `pins:write`, `bookmarks:read`, `bookmarks:write`, `reactions:read`, `reactions:write`, `team:read`, and `admin.conversations:write`. Slack lists no method-specific scopes for `views.publish`, `views.open`, `views.update`, or `views.push`, so the emulator requires auth but does not add strict-scope checks for those methods.

## OAuth

* `GET /oauth/v2/authorize` — Authorization endpoint (shows user picker)
* `POST /oauth/v2/authorize/callback` — User picker callback: creates the auth code
* `POST /api/oauth.v2.access` — Token exchange
* `POST /api/apps.uninstall` — Uninstall an app and revoke its workspace tokens

When no OAuth apps are configured, the emulator accepts any `client_id`. With apps configured, strict validation is enforced.

`oauth.v2.access` returns a bot token, `bot_user_id`, `app_id`, team details, and the `authed_user` object. When `user_scope` is requested, `authed_user` also includes a user token and granted user scopes.

`POST /api/apps.uninstall` accepts `client_id`, `client_secret`, and an installation's user token in the Authorization header or POST `token` field. It removes that app's installation in the token's workspace and revokes all associated OAuth bot and user tokens, including earlier grants. Revoked credentials return `token_revoked` on Web API calls even with permissive authentication. Other apps and workspaces keep their credentials. Granular bot tokens cannot uninstall an app and return `no_permission`; configured development tokens without an app binding may uninstall a local installation. No method scope is required.

Uninstall dispatches `app_uninstalled` and `tokens_revoked` to matching event subscriptions with the removed app's `api_app_id`. `tokens_revoked.tokens.oauth` and `.bot` contain deduplicated user IDs, never token strings. Credentials are invalid before callbacks are delivered. Handlers should accept either event order. The app definition remains available for a new OAuth installation; reinstalling issues fresh tokens without reactivating revoked ones.

## Chat

* `POST /api/chat.postMessage` — Post message with text or rich payload fields (supports threads via `thread_ts` and DM user IDs)
* `POST /api/chat.postEphemeral` — Post ephemeral message outside channel history
* `POST /api/chat.update` — Update message text and rich payload fields
* `POST /api/chat.delete` — Delete message
* `GET /api/chat.getPermalink` / `POST /api/chat.getPermalink` — Get message permalink
* `POST /api/chat.scheduleMessage` — Schedule pending message
* `POST /api/chat.deleteScheduledMessage` — Delete pending scheduled message
* `POST /api/chat.scheduledMessages.list` — List pending scheduled messages
* `POST /api/chat.meMessage` — Send a /me message

## Conversations

* `POST /api/conversations.list` — List conversations (cursor pagination, `types`, `exclude_archived`)
* `POST /api/conversations.info` — Get channel info
* `POST /api/conversations.create` — Create channel
* `POST /api/conversations.archive` — Archive channel
* `POST /api/conversations.unarchive` — Restore archived channel
* `POST /api/conversations.rename` — Rename channel
* `POST /api/conversations.setTopic` — Update channel topic
* `POST /api/conversations.setPurpose` — Update channel purpose
* `POST /api/conversations.history` — Channel history with rich message fields (top-level messages)
* `POST /api/conversations.replies` — Thread replies with rich message fields
* `POST /api/conversations.join` — Join channel
* `POST /api/conversations.leave` — Leave channel
* `POST /api/conversations.invite` — Invite user to a channel
* `POST /api/conversations.kick` — Remove user from a channel
* `POST /api/conversations.open` — Open or resume DM/MPIM
* `POST /api/conversations.close` — Close DM/MPIM
* `POST /api/conversations.mark` — Set read cursor
* `POST /api/conversations.members` — List members

### Channel privacy conversions

`POST /api/admin.conversations.convertToPrivate` and `POST /api/admin.conversations.convertToPublic` accept `channel_id`. Use a user token belonging to a seeded admin; bot and test tokens return `not_allowed_token_type`, and non-admin users return `missing_scope`. With `slack.strict_scopes: true`, the token also needs `admin.conversations:write`. For example:

```json theme={null}
{
  "slack": {
    "tokens": [
      {
        "token": "local-admin-user",
        "type": "user",
        "user": "admin",
        "scopes": ["admin.conversations:write", "channels:read", "groups:read"]
      }
    ]
  }
}
```

Conversions preserve the channel ID, members, and history, update visibility in conversation and file reads, and append a `message` with subtype `channel_convert_to_private` or `channel_convert_to_public`. Event callbacks include the acting user and the new `channel_type` (`group` or `channel`). Admins need not belong to the channel. DMs, MPIMs, and repeated conversions are rejected; `#general` cannot become private.

These methods operate on the emulated workspace without Slack's Enterprise plan or organization-wide installation requirements. Enterprise roles, channel-management policies, and MPIM conversion are not simulated.

### Slack Connect fixtures

Seed a user with a different `team_id` to test messages from another workspace. `users.info` and `users.list` return that user's workspace and `is_stranger` flag. Omitted user teams default to the emulated workspace. `is_stranger` defaults to `false` and can be seeded to model a user outside the app's visible shared channels.

```json theme={null}
{
  "slack": {
    "users": [
      {
        "name": "partner",
        "team_id": "T_PARTNER",
        "is_stranger": false
      }
    ],
    "tokens": [
      {
        "token": "xoxp-partner",
        "type": "user",
        "user": "partner",
        "scopes": ["chat:write"]
      }
    ]
  }
}
```

Use the seeded user token to act as the external user. Invite the user and your app's bot to a channel with `conversations.invite`, then post a message mentioning the bot. The app-mention event includes the author's `user_team` and `source_team`; the outer `team_id` identifies the receiving installation workspace selected by the token, which defaults to the emulated workspace. Ordinary message callbacks retain the author user ID, which can be resolved with `users.info`.

A channel with external members returns `is_ext_shared: true` and `is_shared: true`; its event callbacks include `is_ext_shared_channel: true`. Seed `channels[].is_ext_shared: true` to model an already shared channel. Sharing remains set after the last external member leaves or is removed, including across saved state. Slack Connect invitation and approval flows, workspace disconnection, and per-workspace channel settings are not simulated.

## Users

* `POST /api/users.list` — List users (cursor pagination)
* `POST /api/users.info` — Get user info
* `POST /api/users.lookupByEmail` — Look up by email
* `GET /api/users.profile.get` / `POST /api/users.profile.get` — Get user profile fields
* `POST /api/users.profile.set` — Update profile fields, status, and custom fields
* `GET /api/users.getPresence` / `POST /api/users.getPresence` — Get active or away presence
* `POST /api/users.setPresence` — Set presence: the authed user to away or automatic

## Reactions

* `POST /api/reactions.add` — Add reaction
* `POST /api/reactions.remove` — Remove reaction
* `POST /api/reactions.get` — Get reactions for a message

## Files

* `POST /api/files.getUploadURLExternal` — Create a local external upload session
* `POST /upload/v1/:fileId` — Receive raw uploaded file bytes
* `POST /api/files.completeUploadExternal` — Complete uploads: optionally share file messages
* `GET /api/files.info` / `POST /api/files.info` — Get file metadata
* `GET /api/files.list` / `POST /api/files.list` — List completed files
* `GET /files-pri/:fileId/:filename` — Download file bytes: needs a bearer token that can access the file
* `POST /api/files.delete` — Delete a completed file

## Pins and bookmarks

* `POST /api/pins.add` — Pin a message to a channel
* `GET /api/pins.list` / `POST /api/pins.list` — List pinned message items for a channel
* `POST /api/pins.remove` — Remove a message pin from a channel
* `POST /api/bookmarks.add` — Add a link bookmark to a channel
* `POST /api/bookmarks.edit` — Update a link bookmark
* `POST /api/bookmarks.list` — List channel bookmarks
* `POST /api/bookmarks.remove` — Remove a bookmark from a channel

## Views

* `POST /api/views.publish` — Publish or update an App Home view for a user
* `POST /api/views.open` — Open a modal view
* `POST /api/views.update` — Update a view by `view_id` or `external_id`
* `POST /api/views.push` — Push a modal view onto the current modal stack
* `POST /api/views.generateTriggerId` — Generate trigger id: local helper for tests that need a modal trigger id

Modal opens and pushes accept `trigger_id` or `interactivity_pointer` from an interaction payload or `/api/views.generateTriggerId`. Generate push values with an existing `view_id`. Triggers expire after 3 seconds by default; set `slack.view_trigger_ttl_seconds` to a positive number to allow more time in local tests.

## Team

* `POST /api/team.info` — Get workspace info

## Bots

* `POST /api/bots.info` — Get bot info

## Incoming webhooks

* `POST /services/:teamId/:botId/:webhookId` — Post via incoming webhook: text or rich payload fields (supports `channel` override and `thread_ts`)

## Interactivity

Buttons and modal submissions can invoke your app's Interactivity Request URL. Configure `interactivity_url` on a seeded `slack.oauth_apps` entry and set `slack.signing_secret` to the same secret used by your receiver:

```json theme={null}
{
  "slack": {
    "signing_secret": "local-signing-secret",
    "oauth_apps": [
      {
        "app_id": "A_MY_APP",
        "client_id": "my-client",
        "client_secret": "my-secret",
        "name": "My App",
        "redirect_uris": [],
        "interactivity_url": "http://localhost:3000/slack/interactions"
      }
    ]
  }
}
```

* `POST /_slack/simulate/block-actions` — Simulate a button click: accepts `channel`, `message_ts`, and a button selector (`action_id`, exact `text`, or `url`), plus the acting `user` ID or name. It supports buttons in actions blocks and section accessories, including ephemeral messages visible to that user. The simulator requires an authenticated emulator token and the acting user must belong to the conversation.
* `POST /_slack/simulate/view-submission` — Simulate a modal submission: accepts `view_id`, optional `user`, and `values` keyed by input block ID. Text inputs take strings; static selects and radio buttons take an option value; multi-selects and checkboxes take arrays of option values. Omitted values retain previous input or initial values. Other input elements return `unsupported_element`.

Callbacks are `application/x-www-form-urlencoded` with one `payload` JSON field, signed over the exact encoded request body. Button payloads include an app-bound, single-use `trigger_id` that can open a modal for the acting user. App ownership is preserved for messages posted with an app's bot or user token. Simulators return the payload and `delivery_id`; attempts and delivery results are saved in `slack.interaction_deliveries`.

A button payload's `response_url` accepts JSON without a bearer token, up to five times within 30 minutes. It posts an ephemeral reply by default, or a public reply with `response_type: "in_channel"`. Use `replace_original: true` or `delete_original: true` to modify the source message. Replacement retains the source message's visibility. Rich blocks and thread replies are supported.

A modal receiver must acknowledge within three seconds. An empty HTTP 200 closes the submitted view, while `response_action: "clear"` closes its stack. `errors` retains the view and returns block errors; `update` replaces its contents; `push` adds a new view subject to the three-view limit. Delivery failures and invalid acknowledgements leave the modal open. Shortcuts, non-button actions, `view_closed`, and `block_suggestion` are not simulated. Slash commands work for Socket Mode and HTTP apps; see [Socket Mode](#socket-mode).

## Socket Mode

Set `socket_mode_enabled: true` on an `oauth_apps` entry and that app receives its events, button clicks, modal submissions, and slash commands over a WebSocket instead of HTTP, as a Bolt app with `socketMode: true` expects. It works with a plain `npx @deploybaseline/cli start`; see [WebSockets](/websockets.md).

```json theme={null}
{
  "slack": {
    "oauth_apps": [
      {
        "app_id": "A0DEPLOYBOT",
        "client_id": "deploy-bot",
        "client_secret": "deploy-bot-secret",
        "name": "Deploy Bot",
        "redirect_uris": [],
        "bot_user_id": "U0DEPLOYBOT",
        "socket_mode_enabled": true,
        "app_token": "xapp-1-A0DEPLOYBOT-local",
        "slash_commands": [
          {
            "command": "/deploy"
          }
        ]
      }
    ],
    "tokens": [
      {
        "token": "xoxb-deploy-bot-local",
        "type": "bot",
        "user_id": "U0DEPLOYBOT",
        "app_id": "A0DEPLOYBOT",
        "bot_user_id": "U0DEPLOYBOT"
      }
    ]
  }
}
```

* `app_token`: optional; default `xapp-1-<app_id>-baseline`

```js theme={null}
import { App } from "@slack/bolt";

const app = new App({
  token: "xoxb-deploy-bot-local",
  appToken: "xapp-1-A0DEPLOYBOT-local",
  socketMode: true,
  clientOptions: { slackApiUrl: "http://localhost:4003/api/" },
});
await app.start();
```

Bolt passes `clientOptions.slackApiUrl` to its Socket Mode client, so `apps.connections.open` and the socket both go to the emulator. Setting `app_token` alone also turns Socket Mode on.

* `POST /api/apps.connections.open` — Open a Socket Mode connection: takes the app-level token from the Authorization header only, as Slack does. It returns `not_authed` without one, `not_allowed_token_type` for a bot, user, or development token, and `invalid_auth` for an unknown token. The URL is `ws://` (or `wss://` when the base URL is `https://`), ends in `/link/?ticket=...&app_id=...`, and its ticket works once within 60 seconds.
* On connect the emulator sends `hello` with `num_connections`, `debug_info`, and `connection_info.app_id`, and pings every 10 seconds. Add `debug_reconnects=true` to the URL to get a `refresh_requested` disconnect after 360 seconds.
* Events arrive as `events_api` envelopes carrying the same `event_callback` payload and `event_id` an HTTP subscriber gets, plus `retry_attempt` and `retry_reason`. The app must ack with `{ "envelope_id": "..." }` within 3 seconds. A missing ack, or no open connection, records a failed delivery in the inspector's Events tab, and `POST /_slack/simulate/event-retry` resends it with `retry_attempt: 1` and the given `retry_reason`. With several connections, envelopes rotate between them.
* A Socket Mode app's events go only to its connections, never to HTTP event subscriptions. Apps without Socket Mode keep HTTP delivery.
* Button clicks (`POST /_slack/simulate/block-actions`) and modal submissions (`POST /_slack/simulate/view-submission`) arrive as `interactive` envelopes, so no `interactivity_url` is needed. A `view_submission` ack's `payload` is the modal response, e.g. `{ "response_action": "errors", "errors": { ... } }`; Bolt's `ack({...})` sends it.
* `POST /_slack/simulate/slash-command` — Run a slash command: runs a declared command as a user would type it. It accepts `command`, `text`, `channel` (ID or name), and optional `user` and `app_id`. Socket Mode apps get a `slash_commands` envelope; other apps need `slash_commands[].url` and get a signed form POST there. The payload carries `trigger_id` and `response_url`. A reply in the ack (or HTTP response body) with `text`, `blocks`, or `attachments` is posted ephemeral to the user, or in the channel with `response_type: "in_channel"`. Errors: `command_not_found`, `ambiguous_command`, `command_url_not_configured`, `app_not_installed`, and `command_delivery_failed`.
* `POST /_slack/simulate/socket-mode-disconnect` — Simulate a disconnect: sends `disconnect` to open connections, for one `app_id` or all apps. `reason` is `refresh_requested` (default), `warning`, or `link_disabled`. Slack clients reconnect when they get it; for `refresh_requested` and `link_disabled` the emulator also closes the connection after a second. `GET /_slack/socket-mode/connections` lists open connections, and the inspector has a Socket Mode tab.
* Socket Mode works in the CLI and in `createEmulator`.

Not simulated: Slack's limit of 10 connections per app, per-app event subscriptions (a Socket Mode app receives every event its installation would), shortcuts, and `apps.connections.open` rate limits.

## Events API

For supported Slack writes that emit events, the emulator dispatches `event_callback` payloads to configured webhook URLs matching Slack's Events API format.

Each callback includes outer `token`, `api_app_id`, `authorizations`, `team_id`, `event_id`, and integer Unix-seconds `event_time` alongside the inner `event`. For authenticated Web API writes, `team_id` comes from the presented Slack token's installation. Development tokens without a stored Slack record fall back to the affected channel, user, or file's team, then the seeded workspace team (or `T000000001`). Incoming webhook posts use their webhook record's team, or the target channel's team when no record matches. Each logical event gets a distinct `event_id` shared by deliveries to multiple subscribers.

Callbacks identify each workspace installation with its recipient `api_app_id` and bot authorization. With multiple installed apps, each receives a separate envelope; configured webhook subscriptions are shared and can route by `api_app_id`. Without an installation, callbacks use `A000000001` and a local user authorization. The legacy `token` field defaults to `baseline-verification-token`; set `slack.verification_token` to match receivers that check it. Use `slack.signing_secret` for request signature verification.

Every inner event includes `event_ts`. Message events include `channel_type` (`channel`, `group`, `im`, or `mpim`). Posts made with an issued or seeded bot token, and incoming webhook and interaction replies, include the bot's `bot_id`, `app_id`, and `bot_profile` when the corresponding bot record exists. These fields persist in history, replies, and updates so handlers can identify their own messages. Removing an installed bot with `conversations.kick` or `conversations.leave` also emits `channel_left` or `group_left`, with `actor_id` and `event_ts`, for the affected app.

To test bot handlers, invite an installed app's `bot_user_id` with `conversations.invite`, subscribe to `app_mention`, then post text containing `<@BOT_USER_ID>` with `chat.postMessage`. Channel mentions produce one callback per mentioned app, with outer `api_app_id`, inner `event_ts`, and the original message and thread identity. DMs and MPIMs use `message` events.

* `message` events on `chat.postMessage`
* `app_mention` on `chat.postMessage` when channel message text mentions an installed member bot
* `app_uninstalled` / `tokens_revoked` on successful `apps.uninstall`
* `message` with `subtype: message_changed` on `chat.update`
* `message` with `subtype: message_deleted` on `chat.delete`
* rich message fields are included on posted `message` events when present
* `reaction_added` / `reaction_removed` events on `reactions.add` / `reactions.remove`
* `pin_added` / `pin_removed` events on `pins.add` / `pins.remove`
* `message` with `subtype: bot_message` on incoming webhook posts
* `channel_archive` / `channel_unarchive` for public lifecycle archive writes
* `group_archive` / `group_unarchive` for private lifecycle archive writes
* `channel_rename` / `group_rename` and matching name message subtypes on `conversations.rename`
* `message` with public `channel_topic` / `channel_purpose` or private `group_topic` / `group_purpose` subtypes on topic and purpose writes
* `member_joined_channel` / `member_left_channel` on invite, join, leave, and kick writes
* `channel_left` / `group_left` when an installed bot leaves or is removed
* `im_created`, `im_open`, `im_close`, `im_marked`, and group open/close/marked events for DM and MPIM writes
* `user_change` on profile writes
* `presence_change` on presence writes
* `file_created`, `file_shared`, and `file_deleted` on file writes
* `message` with `subtype: file_share` on shared file uploads

Set `slack.signing_secret` in seed config to sign every outbound event subscription callback, including subscriptions that already exist. Signed callbacks include `X-Slack-Request-Timestamp` and `X-Slack-Signature`. The signature is `v0=<HMAC-SHA256(secret, "v0:<timestamp>:<raw-body>")>`, calculated from the exact serialized request body. Configure the receiver with the same secret and verify the unparsed body. When the secret is absent or empty, callbacks are unsigned.

### Event retries

Use `POST /_slack/simulate/event-retry` with an authenticated emulator token and JSON `{ "event_id": "Ev...", "reason": "http_timeout" }` to test event deduplication. Get the event ID from a received callback or the inspector's Events tab. The simulator resends the latest recorded delivery for that event to its original subscription, preserving the entire payload and event ID. If several subscribers received it, only the latest recipient is retried.

Each attempt gets an increasing `X-Slack-Retry-Num`, starting at `1`, and `X-Slack-Retry-Reason` (default `http_timeout`). Reasons also accept `http_error`, `connection_failed`, `ssl_error`, `too_many_redirects`, and `unknown_error`. When `slack.signing_secret` is configured, the retry gets a fresh request timestamp and signature. The response includes `delivery_id`, `retry_num`, `success`, and `status_code`; a failed callback is recorded and still consumes a retry number.

Retries are explicit, with no automatic scheduling or three-attempt limit. Only the latest 1,000 event deliveries are retained in memory, so retries are unavailable after their history is evicted or the emulator restarts. Unknown events return `event_not_found`, and an inactive subscription returns `subscription_inactive`.

## Seed config

```json theme={null}
{
  "slack": {
    "team": {
      "name": "My Workspace",
      "domain": "my-workspace"
    },
    "users": [
      {
        "name": "developer",
        "real_name": "Developer",
        "email": "dev@example.com",
        "is_admin": true,
        "profile": {
          "title": "Local Developer",
          "status_text": "Testing locally",
          "status_emoji": ":computer:"
        },
        "presence": "active"
      }
    ],
    "channels": [
      {
        "name": "general",
        "topic": "General discussion"
      },
      {
        "name": "random",
        "topic": "Random stuff"
      },
      {
        "name": "engineering",
        "topic": "Engineering discussions",
        "is_private": true
      }
    ],
    "bots": [
      {
        "name": "my-bot"
      }
    ],
    "oauth_apps": [
      {
        "client_id": "12345.67890",
        "client_secret": "example_client_secret",
        "app_id": "A000000001",
        "name": "My Slack App",
        "redirect_uris": ["http://localhost:3000/api/auth/callback/slack"],
        "scopes": [
          "chat:write",
          "channels:read",
          "channels:history",
          "channels:join",
          "channels:manage",
          "channels:write",
          "groups:read",
          "groups:history",
          "groups:write",
          "im:read",
          "im:history",
          "im:write",
          "mpim:read",
          "mpim:history",
          "mpim:write",
          "users:read",
          "users:read.email",
          "users.profile:read",
          "users.profile:write",
          "users:write",
          "files:read",
          "files:write",
          "pins:read",
          "pins:write",
          "bookmarks:read",
          "bookmarks:write",
          "reactions:read",
          "reactions:write",
          "team:read"
        ],
        "user_scopes": ["users:read", "users.profile:read"],
        "bot_name": "my-bot"
      }
    ],
    "tokens": [
      {
        "token": "xoxb-local-test",
        "user": "developer",
        "scopes": [
          "chat:write",
          "channels:read",
          "channels:history",
          "channels:join",
          "channels:manage",
          "channels:write",
          "groups:read",
          "groups:history",
          "groups:write",
          "im:read",
          "im:history",
          "im:write",
          "mpim:read",
          "mpim:history",
          "mpim:write",
          "users:read",
          "users:read.email",
          "users.profile:read",
          "users.profile:write",
          "users:write",
          "files:read",
          "files:write",
          "pins:read",
          "pins:write",
          "bookmarks:read",
          "bookmarks:write",
          "reactions:read",
          "reactions:write",
          "team:read"
        ]
      }
    ],
    "incoming_webhooks": [
      {
        "channel": "general",
        "label": "CI Notifications"
      }
    ],
    "strict_scopes": false,
    "signing_secret": "my_signing_secret"
  }
}
```

* `signing_secret`: optional; signs event callbacks
* `socket_mode_enabled` (not set above, for example `true`): deliver over Socket Mode
* `slash_commands` (not set above, for example `[{ "command": "/deploy", "url": "http://localhost:3000/slack/commands" }]`).

| Key | Purpose |
| - | - |
| `team` | Workspace `name` and `domain` |
| `users` | Workspace users: `name`, `real_name`, `email`, `profile`, `presence`, `is_admin`, `team_id`, `is_stranger` ([Slack Connect fixtures](#slack-connect-fixtures)) |
| `channels` | Channels: `name`, `topic`, `is_private`, `is_ext_shared` |
| `bots` | Bot users by `name` |
| `oauth_apps` | Installed apps: `client_id`, `client_secret`, `app_id`, `name`, `redirect_uris`, `scopes`, `user_scopes`, `bot_name`, `bot_user_id`, plus `interactivity_url` ([Interactivity](#interactivity)) and `socket_mode_enabled`, `app_token`, `slash_commands` ([Socket Mode](#socket-mode)). When none are configured, any `client_id` is accepted |
| `tokens` | Explicit tokens: `token`, `type`, `user`, `user_id`, `app_id`, `bot_user_id`, `scopes` |
| `incoming_webhooks` | Incoming webhooks: `channel` and `label` |
| `strict_scopes` | Return `missing_scope` errors (see [Auth](#auth)) |
| `signing_secret` | Signs event and interactivity callbacks (see [Events API](#events-api)) |
| `verification_token` | Legacy `token` field on event callbacks (default `baseline-verification-token`) |
| `view_trigger_ttl_seconds` | Modal trigger lifetime (default 3 seconds) |

## Inspector

* `GET /` — Tabbed local inspector for conversations, messages, files, views, auth records, incoming webhooks, event subscriptions, event deliveries, and Socket Mode connections

## Limits

Slack Connect invitation and approval flows, per-workspace channel settings, other Enterprise Grid admin APIs, Audit Logs API, SCIM, Legal Holds, shortcuts, interaction types other than button actions and modal submissions, user groups, reminders, stars, calls, canvases, lists, functions, workflows, chat streaming, legacy `files.upload`, exact rate limiting, and paid-plan behavior are not implemented.

## Conformance

| | |
| - | - |
| Routes | 82 supported or partial, 8 planned |
| Route gate | fail |
| Spec gate | no public spec |
| SDKs tested | `@slack/bolt@5.1.0`, `@slack/socket-mode@3.1.0`, `@slack/web-api@8.2.0` |
| Dual mode | not yet |

See [CONFORMANCE.md](https://github.com/deploybaseline/cli/blob/main/CONFORMANCE.md) for every provider.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.