A self-hosted Docker service that connects Plane.so to Telegram via AI. Ask questions about your projects, issues, cycles, and more — right from Telegram.
Supports two deployment modes:
- Personal mode (default) — one workspace, one user, zero setup friction
- Multi-user mode — any Telegram user can
/loginwith their own Plane credentials; ideal for shared team bots or public deployments
Telegram → Bot (python-telegram-bot) → AI (Claude/GPT) + Plane Tools → Plane.so
The AI uses Plane tools to query and interact with your workspace. It decides which tools to call based on your natural language questions. Tools are built on the official plane-sdk with lean schemas to minimise token usage, backed by an SQLite cache for workspace data.
- Plane PAT (API Key) — Generate from your Plane workspace settings → API Tokens
- Telegram Bot Token — Create a bot via @BotFather on Telegram
- AI API Key — From Anthropic or OpenAI
- Docker — Installed on your server
- Open Telegram and search for @BotFather
- Send
/newbotand follow the prompts:- Bot name: Choose a display name (e.g., "Plane AI Assistant")
- Bot username: Must end in "bot" (e.g., "my_plane_ai_bot")
- BotFather will give you a bot token — save it for
TELEGRAM_BOT_TOKEN
Note: No need to set bot commands in BotFather. The bot registers all commands with Telegram automatically on startup.
Optional extras via BotFather:
/setdescription— Description shown in the chat header/setabouttext— Text shown in the bot's profile/setuserpic— Profile picture
Run the pre-built Docker image:
docker run -d \
--name plane-telegram \
-v $(pwd)/data:/app/data \
-e PLANE_PAT=your_plane_api_key \
-e PLANE_WORKSPACE_SLUG=your-workspace-slug \
-e TELEGRAM_BOT_TOKEN=your_telegram_bot_token \
-e AI_PROVIDER=anthropic \
-e AI_API_KEY=your_ai_api_key \
--restart unless-stopped \
mguptahub/plane-telegram:latestThat's it! Replace the values with your credentials, find your bot on Telegram, and send /start.
Add MULTI_USER=true and omit PLANE_PAT / PLANE_WORKSPACE_SLUG:
docker run -d \
--name plane-telegram \
-v $(pwd)/data:/app/data \
-e MULTI_USER=true \
-e TELEGRAM_BOT_TOKEN=your_telegram_bot_token \
-e AI_PROVIDER=anthropic \
-e AI_API_KEY=your_ai_api_key \
--restart unless-stopped \
mguptahub/plane-telegram:latestEach user (or group chat) runs /login to connect their own Plane account. See Multi-User Mode for details.
docker run -d \
--name plane-telegram \
-v $(pwd)/data:/app/data \
-e PLANE_PAT=your_plane_api_key \
-e PLANE_WORKSPACE_SLUG=your-workspace-slug \
-e TELEGRAM_BOT_TOKEN=your_telegram_bot_token \
-e AI_PROVIDER=openai \
-e AI_API_KEY=your_ai_api_key \
-e AI_MODEL=gpt-4o \
-e AI_BASE_URL=https://api.openai.com/v1 \
-e MAX_HISTORY_MESSAGES=15 \
-e SESSION_TIMEOUT_MINUTES=60 \
-e DEBUG_LOGGING=false \
--restart unless-stopped \
mguptahub/plane-telegram:latest| Variable | Required | Default | Description |
|---|---|---|---|
MULTI_USER |
No | false |
Enable multi-user mode — each Telegram chat registers its own Plane account |
PLANE_PAT |
Yes* | — | Plane API key / Personal Access Token (*not required when MULTI_USER=true) |
PLANE_WORKSPACE_SLUG |
Yes* | — | Default workspace slug (*not required when MULTI_USER=true) |
PLANE_BASE_URL |
If self-hosted | https://app.plane.so |
Your self-hosted Plane URL |
TELEGRAM_BOT_TOKEN |
Yes | — | Telegram bot token from BotFather |
AI_PROVIDER |
Yes | — | anthropic or openai (any OpenAI-compatible) |
AI_API_KEY |
Yes | — | API key for your AI provider |
AI_MODEL |
No | Auto | Model name (defaults to claude-sonnet-4-20250514 or gpt-4o) |
AI_BASE_URL |
No | Provider default | Optional base URL override |
AI_APP_REFERER |
No | — | HTTP-Referer header (used by OpenRouter for attribution) |
AI_APP_TITLE |
No | Plane Telegram AI |
X-Title header (used by OpenRouter for attribution) |
MAX_HISTORY_MESSAGES |
No | 15 |
Number of messages to include as context |
SESSION_TIMEOUT_MINUTES |
No | 60 |
Auto-expire old messages from context |
LOG_LEVEL |
No | INFO |
Logging level |
DEBUG_LOGGING |
No | false |
Write per-request debug logs to data/debug_logs/ |
| Provider | AI_PROVIDER | AI_BASE_URL |
|---|---|---|
| Anthropic (default) | anthropic |
— |
| OpenAI (default) | openai |
— |
| Groq | openai |
https://api.groq.com/openai/v1 |
| OpenRouter | openai |
https://openrouter.ai/api/v1 |
| Together AI | openai |
https://api.together.xyz/v1 |
| LiteLLM proxy | openai |
http://localhost:4000/v1 |
| Azure OpenAI | openai |
https://your-resource.openai.azure.com/ |
| Tool | Description |
|---|---|
get_me |
Get current user info |
get_workspace_members |
List all workspace members |
list_projects |
List all projects |
get_project |
Get a project by UUID |
get_project_members |
List members of a project |
list_states |
List workflow states for a project |
list_labels |
List labels for a project |
list_work_items |
List work items (filterable by state, priority, assignee, label) |
get_work_item |
Get a work item by UUID or identifier (e.g. PROJ-42) |
create_work_item |
Create a new work item |
update_work_item |
Update an existing work item |
list_initiatives |
List workspace initiatives |
The bot registers all commands with Telegram automatically — they appear in the / menu.
| Command | Description |
|---|---|
/login |
Connect your Plane account (PAT + workspace slug) |
/logout |
Remove your credentials and clear all chat data |
/status |
Show connection status, cache state, and active project |
| Command | Description |
|---|---|
/start |
Show welcome message |
/help |
Show help and command reference |
/reset |
Clear conversation history and start fresh |
/tools |
List all available Plane tools |
/status |
Show connection status and active context |
| Command | Description |
|---|---|
/workspace |
Show the currently active workspace and project |
/workspace show |
Same as above |
/workspace <slug> |
Switch workspace, rebuild cache, and clear active project |
Examples:
/workspace
/workspace my-company
/workspace show
Each chat session has its own active workspace. The default is set by PLANE_WORKSPACE_SLUG. Switching workspace rebuilds the cache and resets the active project (projects are workspace-scoped).
Set an active project once so you don't need to type the identifier on every /workitems call.
| Command | Description |
|---|---|
/project |
Show the currently active project |
/project show |
Same as above |
/project <IDENTIFIER> |
Set the active project (validated against cache) |
Examples:
/project
/project BACK
/project INVESTOS
The active project is cleared automatically when you switch workspace.
Explicit mode — project identifier always required, ignores active project context. Useful for one-off lookups across projects.
| Command | Description |
|---|---|
/list projects |
List all projects in the active workspace |
/list workitems <PROJ> [query] |
List work items for a project |
/list states <PROJ> |
List workflow states for a project |
/list labels <PROJ> |
List labels for a project |
/list members <PROJ> |
List members of a project |
Context mode — uses the active project set by /project. Set it once, then query freely.
| Command | Description |
|---|---|
/workitems [query] |
List work items using the active project context |
/states |
List workflow states for the active project |
/labels |
List labels for the active project |
/members |
List members of the active project |
<PROJ> is the project identifier (e.g. BACK, INVESTOS). The optional [query] for work items is plain English — the AI resolves it into filters (state, priority, assignee, label). Only one filter value is supported per field.
Examples:
/list projects
# Explicit — project always required
/list workitems BACK
/list workitems BACK todo
/list workitems BACK high priority
/list workitems BACK assigned to me
/list workitems BACK labeled as James
/list states BACK
/list labels BACK
/list members BACK
# Context — /project BACK must be set first
/workitems
/workitems todo
/workitems in progress
/workitems urgent
/workitems assigned to me
/workitems labeled as James
/states
/labels
/members
The bot caches workspace data (user info, projects, states, labels, members) in SQLite to reduce API calls and speed up responses.
| Command | Description |
|---|---|
/cache |
Show all cache entries (same as show) |
/cache show |
List cache entries with size and age |
/cache update |
Refresh all cached data (top-level + existing project data) |
/cache clean |
Delete project-level caches, refresh top-level only |
Project states, labels, and members are lazily cached on first use and reused for subsequent calls. Use /cache clean after making structural changes in Plane (new states, members, etc.).
These are natural language queries you can send directly to the bot (no commands needed):
Who am I?
What workspace am I in?
List all my projects
How many projects do I have?
Tell me about the Backend project
What are the open issues in project Backend?
Show me all urgent items in INVESTOS
What issues are assigned to me in BACK?
Get me issue BACK-42
What's the status of PROJ-7?
Create an issue titled "Fix auth bug" with high priority in project Backend
Create a new todo in INVESTOS: "Update pricing page"
Update BACK-42 to Done
Change the priority of PROJ-7 to urgent
Assign BACK-15 to John
Who are the members of the BACK project?
List all workspace members
What states does the INVESTOS project have?
What labels are available in BACK?
Set MULTI_USER=true to allow any Telegram user (or group chat) to connect their own Plane account. The bot stores credentials per-chat and isolates all data (cache, history, active workspace) between chats.
User: /login
Bot: Step 1 — reply with your Plane API token
User: plane_pat_abc123... ← bot deletes this message for security
Bot: ✅ Verified as John (john@example.com)
Step 2 — reply with your workspace slug
User: my-company
Bot: ✅ Registered! Found 5 projects in workspace my-company.
| Command | Description |
|---|---|
/login |
Connect a Plane account (PAT + workspace slug) |
/logout |
Remove credentials and clear all chat data |
- Each chat gets its own credential store and cache namespace in SQLite
PLANE_PATandPLANE_WORKSPACE_SLUGenvironment variables are optional whenMULTI_USER=true- In groups, the
/loginflow uses Telegram'sForceReplyso the bot receives replies even with privacy mode enabled /logoutremoves the registration, all conversation history, and all cache entries for that chat
In multi-user mode, each chat also has its own active workspace. The registered workspace is the default — users can switch with /workspace <slug> at any time. Each chat tracks its own:
- Active workspace — default is the registered workspace slug
- Active project — set with
/project <IDENTIFIER> - Cache — scoped to the chat, rebuilt on
/loginand/cache clean
- Self-hosted: Your Plane PAT, AI keys, and conversation data never leave your infrastructure
- SQLite: Chat history and cache are stored locally in
./data/chat_history.db(volume-mapped) - No telemetry: The bot doesn't phone home
Pull the latest image and restart:
docker pull mguptahub/plane-telegram:latest
docker stop plane-telegram
docker rm plane-telegram
# Then run the docker run command from Quick StartYour chat history and cache persist in the ./data volume.
Bot not responding?
- Check logs:
docker logs -f plane-telegram - Verify your Telegram bot token is correct
- Make sure the bot is not blocked or hasn't been stopped
Plane connection fails?
- Verify your
PLANE_PATis valid and has the right permissions - Verify your
PLANE_WORKSPACE_SLUGis correct (the slug, not the full URL) - For self-hosted: ensure
PLANE_BASE_URLis reachable from inside the container - Check logs:
docker logs -f plane-telegram
AI errors?
- Verify your
AI_API_KEYis valid and has credits - Check the model name in
AI_MODELis correct for your provider
Work item filters not working?
- Run
/cache cleanto ensure states and members are freshly cached - Try
/cache showto inspect what's cached - Note: each filter (state, priority, assignee, label) accepts only one value at a time
Commands not showing in Telegram?
- Send
/helpto force re-registration of commands - Force-close and reopen Telegram (the commands menu is cached client-side)
git clone https://github.com/mguptahub/plane-telegram-ai
cd plane-telegram-ai
cp .env.example .env
# Edit .env with your credentialsdocker compose up -d --buildCheck logs:
docker compose logs -fSet DEBUG_LOGGING=true to write per-request JSON logs to data/debug_logs/. Each file captures the full AI exchange including tool calls, token usage, and cache hits.
MIT