Slack Bolt for Python -- a framework for building Slack apps in Python.
-
Foundation: Built on top of
slack_sdk(seepyproject.tomlconstraints). -
Execution Models: Supports both synchronous (
App) and asynchronous (AsyncAppusingasyncio) execution. Async mode requiresaiohttpas an additional dependency. -
Framework Adapters: Features built-in adapters for web frameworks (Flask, FastAPI, Django, Tornado, Pyramid, and many more) and serverless environments (AWS Lambda, Google Cloud Functions).
-
Python Version: Requires Python 3.7+ as defined in
pyproject.toml. -
Repository: https://github.com/slackapi/bolt-python
-
Documentation: https://docs.slack.dev/tools/bolt-python/
-
Current version: defined in
slack_bolt/version.py(referenced bypyproject.tomlvia[tool.setuptools.dynamic])
A python virtual environment (venv) should be activated before running any commands.
# Create a venv (first time only)
python -m venv .venv
# Activate
source .venv/bin/activate
# Install all dependencies
./scripts/install.shYou can verify the venv is active by checking echo $VIRTUAL_ENV. If tools like black, flake8, mypy or pytest are not found, ask the user to activate the venv.
Always use the project scripts instead of calling pytest directly:
# Install all dependencies and run all tests (formats, lints, tests, typechecks)
./scripts/install_all_and_run_tests.sh
# Run a single test file
./scripts/run_tests.sh tests/scenario_tests/test_app.py
# Run a single test function
./scripts/run_tests.sh tests/scenario_tests/test_app.py::TestApp::test_name# Format (black, line-length=125)
./scripts/format.sh --no-install
# Lint (flake8, line-length=125, ignores: F841,F821,W503,E402)
./scripts/lint.sh --no-install
# Type check (mypy)
./scripts/run_mypy.sh --no-installIncoming requests flow through a middleware chain before reaching listeners:
- SSL Check -> Request Verification (signature) -> URL Verification -> Authorization (token injection) -> Ignoring Self Events -> Custom middleware
- Listener Matching --
ListenerMatcherimplementations check if a listener should handle the request - Listener Execution -- listener-specific middleware runs, then
ack()is called, then the handler executes
For FaaS environments (process_before_response=True), long-running handlers execute as "lazy listeners" in a thread pool after the ack response is returned.
App/AsyncApp(slack_bolt/app/) -- Central class. Registers listeners via decorators (@app.event(),@app.action(),@app.command(),@app.message(),@app.view(),@app.shortcut(),@app.options(),@app.function()). Dispatches incoming requests through middleware to matching listeners.Middleware(slack_bolt/middleware/) -- Abstract base withprocess(req, resp, next). Built-in: authorization, request verification, SSL check, URL verification, assistant, self-event ignoring.Listener(slack_bolt/listener/) -- Has matchers, middleware, and an ack/handler function.CustomListeneris the main implementation.ListenerMatcher(slack_bolt/listener_matcher/) -- Determines if a listener handles a given request. Built-in matchers for events, actions, commands, messages (regex), shortcuts, views, options, functions.BoltContext(slack_bolt/context/) -- Dict-like object passed to listeners withclient,say(),ack(),respond(),complete(),fail(), plus event metadata (user_id,channel_id,team_id, etc.).BoltRequest/BoltResponse(slack_bolt/request/,slack_bolt/response/) -- Request/response wrappers. Request hasmodeof "http" or "socket_mode".
Listeners receive arguments by parameter name. The framework inspects function signatures and injects matching args: body, event, action, command, payload, context, client, ack, say, respond, logger, complete, fail, agent, etc. Defined in slack_bolt/kwargs_injection/args.py.
Each adapter in slack_bolt/adapter/ converts between a web framework's request/response types and BoltRequest/BoltResponse. Adapters exist for: Flask, FastAPI, Django, Starlette, Sanic, Bottle, Tornado, CherryPy, Falcon, Pyramid, AWS Lambda, Google Cloud Functions, Socket Mode, WSGI, ASGI, and more.
This is the most important pattern in this codebase. Almost every module has both a sync and async variant. When you modify one, you almost always must modify the other.
File naming convention: Async files use the async_ prefix alongside their sync counterpart:
slack_bolt/middleware/custom_middleware.py # sync
slack_bolt/middleware/async_custom_middleware.py # async
slack_bolt/context/say/say.py # sync
slack_bolt/context/say/async_say.py # async
slack_bolt/listener/custom_listener.py # sync
slack_bolt/listener/async_listener.py # async
slack_bolt/adapter/fastapi/async_handler.py # async-only (no sync FastAPI adapter)
slack_bolt/adapter/flask/handler.py # sync-only (no async Flask adapter)
Which modules come in sync/async pairs:
slack_bolt/app/--app.py/async_app.pyslack_bolt/middleware/-- every middleware has anasync_counterpartslack_bolt/listener/--listener.py/async_listener.py, plus error/completion/start handlersslack_bolt/listener_matcher/--builtins.py/async_builtins.pyslack_bolt/context/-- each subdirectory (e.g.,say/,ack/,respond/) hasasync_variantsslack_bolt/kwargs_injection/--args.py/async_args.py,utils.py/async_utils.py
Adapters are an exception: Most adapters are sync-only or async-only depending on the framework. Async-native frameworks (FastAPI, Starlette, Sanic, Tornado, ASGI, Socket Mode) have async_handler.py. Sync-only frameworks (Flask, Django, Bottle, CherryPy, Falcon, Pyramid, AWS Lambda, Google Cloud Functions, WSGI) have handler.py.
BoltAgent (slack_bolt/agent/) provides chat_stream(), set_status(), and set_suggested_prompts() for AI-powered agents. Assistant middleware (slack_bolt/middleware/assistant/) handles assistant thread events.
- Implement the sync version in
slack_bolt/middleware/(subclassMiddleware, implementprocess()) - Implement the async version with
async_prefix (subclassAsyncMiddleware, implementasync_process()) - Export built-in middleware from
slack_bolt/middleware/__init__.py(sync) andasync_builtins.py(async)
Each context utility lives in its own subdirectory under slack_bolt/context/:
slack_bolt/context/my_util/
__init__.py
my_util.py # sync implementation
async_my_util.py # async implementation
internals.py # shared logic (optional)
Then wire it into BoltContext (slack_bolt/context/context.py) and AsyncBoltContext (slack_bolt/context/async_context.py).
- Create
slack_bolt/adapter/<framework>/ - Add
__init__.pyandhandler.py(orasync_handler.pyfor async frameworks) - The handler converts the framework's request to
BoltRequest, callsapp.dispatch(), and convertsBoltResponseback - Add the framework to
requirements/adapter.txtwith version constraints - Add adapter tests in
tests/adapter_tests/(ortests/adapter_tests_async/)
- Add the new arg to
slack_bolt/kwargs_injection/args.pyandasync_args.py - Update the
Argsclass with the new property - Populate the arg in the appropriate context or listener setup code
The core package has a single required runtime dependency: slack_sdk (defined in pyproject.toml). Do not add runtime dependencies.
requirements/ directory structure:
async.txt-- async runtime deps (aiohttp,websockets)adapter.txt-- all framework adapter deps (Flask, Django, FastAPI, etc.)testing.txt-- test runner deps (pytest,pytest-asyncio, includesasync.txt)testing_without_asyncio.txt-- test deps without async (pytest,pytest-cov)adapter_testing.txt-- adapter-specific test deps (moto,boddle,sanic-testing)tools.txt-- dev tools (mypy,flake8,black)
When adding a new dependency: add it to the appropriate requirements/*.txt file with version constraints, never to pyproject.toml dependencies (unless it's a core runtime dep, which is very rare).
tests/scenario_tests/-- Integration-style tests with realistic Slack payloadstests/slack_bolt/-- Unit tests mirroring the source structuretests/adapter_tests/andtests/adapter_tests_async/-- Framework adapter teststests/mock_web_api_server/-- Mock Slack API server used by tests- Async test variants use
_asyncsuffix directories
Where to put new tests: Mirror the source structure. For slack_bolt/middleware/foo.py, add tests in tests/slack_bolt/middleware/test_foo.py. For async variants, use the _async suffix directory or file naming pattern. Adapter tests go in tests/adapter_tests/ (sync) or tests/adapter_tests_async/ (async).
Mock server: Many tests use tests/mock_web_api_server/ to simulate Slack API responses. Look at existing tests for usage patterns rather than making real API calls.
- Black formatter configured in
pyproject.toml(line-length=125) - Flake8 linter configured in
.flake8(line-length=125, ignores: F841,F821,W503,E402) - MyPy configured in
pyproject.toml - pytest configured in
pyproject.toml
.github/-- GitHub-specific configuration and documentation.github/workflows/-- Continuous integration pipeline definitions that run on GitHub Actions.github/maintainers_guide.md-- Maintainer workflows and release process