[{"data":1,"prerenderedAt":1895},["ShallowReactive",2],{"navigation":3,"/docs/integrations/messaging":156,"/docs/integrations/messaging-surround":1890},[4,23,83,103,118,133,141],{"title":5,"path":6,"stem":7,"children":8,"icon":22},"Getting Started","/docs/getting-started","docs/1.getting-started/1.index",[9,12,17],{"title":10,"path":6,"stem":7,"icon":11},"What is TaskView","i-lucide-house",{"title":13,"path":14,"stem":15,"icon":16},"Installation","/docs/getting-started/installation","docs/1.getting-started/2.installation","i-lucide-download",{"title":18,"path":19,"stem":20,"icon":21},"Quick Start","/docs/getting-started/usage","docs/1.getting-started/3.usage","i-lucide-rocket",false,{"title":24,"icon":22,"path":25,"stem":26,"children":27,"page":22},"Features","/docs/features","docs/2.features",[28,33,38,43,48,53,58,63,68,73,78],{"title":29,"path":30,"stem":31,"icon":32},"Projects and Lists","/docs/features/projects-and-lists","docs/2.features/1.projects-and-lists","i-lucide-folder",{"title":34,"path":35,"stem":36,"icon":37},"Organizations","/docs/features/organizations","docs/2.features/10.organizations","i-lucide-building-2",{"title":39,"path":40,"stem":41,"icon":42},"SSO (Single Sign-On)","/docs/features/sso","docs/2.features/11.sso","i-lucide-shield-check",{"title":44,"path":45,"stem":46,"icon":47},"Tasks","/docs/features/tasks","docs/2.features/2.tasks","i-lucide-check-square",{"title":49,"path":50,"stem":51,"icon":52},"Kanban Board","/docs/features/kanban","docs/2.features/3.kanban","i-lucide-columns-3",{"title":54,"path":55,"stem":56,"icon":57},"Dependency Graph","/docs/features/graph","docs/2.features/4.graph","i-lucide-git-branch",{"title":59,"path":60,"stem":61,"icon":62},"Dashboard","/docs/features/dashboard","docs/2.features/5.dashboard","i-lucide-layout-dashboard",{"title":64,"path":65,"stem":66,"icon":67},"Notifications","/docs/features/notifications","docs/2.features/6.notifications","i-lucide-bell",{"title":69,"path":70,"stem":71,"icon":72},"Webhooks","/docs/features/webhooks","docs/2.features/7.webhooks","i-lucide-webhook",{"title":74,"path":75,"stem":76,"icon":77},"API Tokens","/docs/features/api-tokens","docs/2.features/8.api-tokens","i-lucide-key-round",{"title":79,"path":80,"stem":81,"icon":82},"Sessions & Devices","/docs/features/sessions","docs/2.features/9.sessions","i-lucide-monitor-smartphone",{"title":84,"icon":22,"path":85,"stem":86,"children":87,"page":22},"Integrations","/docs/integrations","docs/3.integrations",[88,93,98],{"title":89,"path":90,"stem":91,"icon":92},"GitHub & GitLab Setup","/docs/integrations/setup","docs/3.integrations/1.setup","i-lucide-git-pull-request",{"title":94,"path":95,"stem":96,"icon":97},"MCP Server","/docs/integrations/mcp","docs/3.integrations/2.mcp","i-lucide-bot",{"title":99,"path":100,"stem":101,"icon":102},"Telegram & Slack Setup","/docs/integrations/messaging","docs/3.integrations/3.messaging","i-lucide-send",{"title":104,"icon":22,"path":105,"stem":106,"children":107,"page":22},"Configuration","/docs/configuration","docs/4.configuration",[108,113],{"title":109,"path":110,"stem":111,"icon":112},"Environment Variables","/docs/configuration/environment-variables","docs/4.configuration/1.environment-variables","i-lucide-settings",{"title":114,"path":115,"stem":116,"icon":117},"Authentication","/docs/configuration/authentication","docs/4.configuration/2.authentication","i-lucide-lock",{"title":119,"icon":22,"path":120,"stem":121,"children":122,"page":22},"Collaboration","/docs/collaboration","docs/5.collaboration",[123,128],{"title":124,"path":125,"stem":126,"icon":127},"Team Members","/docs/collaboration/members","docs/5.collaboration/1.members","i-lucide-users",{"title":129,"path":130,"stem":131,"icon":132},"Roles and Permissions","/docs/collaboration/roles-and-permissions","docs/5.collaboration/2.roles-and-permissions","i-lucide-shield",{"title":134,"path":135,"stem":136,"children":137,"icon":22},"FAQ","/docs/faq","docs/6.faq/1.index",[138],{"title":139,"path":135,"stem":136,"icon":140},"Frequently Asked Questions","i-lucide-circle-help",{"title":142,"icon":22,"path":143,"stem":144,"children":145,"page":22},"Guides","/docs/guides","docs/7.guides",[146,151],{"title":147,"path":148,"stem":149,"icon":150},"Deploy TaskView on a VPS with Nginx","/docs/guides/deploy-vps-nginx","docs/7.guides/1.deploy-vps-nginx","i-lucide-server",{"title":152,"path":153,"stem":154,"icon":155},"TaskView for Freelancers","/docs/guides/taskview-for-freelancers","docs/7.guides/2.taskview-for-freelancers","i-lucide-briefcase",{"id":157,"title":99,"body":158,"description":1884,"extension":1885,"meta":1886,"navigation":1887,"path":100,"seo":1888,"stem":101,"__hash__":1889},"docs/docs/3.integrations/3.messaging.md",{"type":159,"value":160,"toc":1852},"minimark",[161,165,182,189,194,201,206,241,245,261,337,340,388,392,408,417,431,507,518,534,538,560,606,609,613,627,629,633,637,676,680,687,711,715,721,731,734,764,773,777,780,829,832,848,865,869,875,880,912,917,948,951,959,966,972,1008,1010,1014,1021,1035,1039,1091,1095,1184,1188,1243,1247,1253,1259,1295,1301,1305,1349,1353,1359,1400,1437,1442,1471,1486,1490,1504,1506,1513,1606,1608,1612,1658,1661,1663,1667,1704,1706,1710,1724,1737,1747,1756,1766,1769,1800,1816,1829,1835,1848],[162,163,164],"p",{},"TaskView messaging integrations deliver task events to messengers. There are two levels of connection:",[166,167,168,176],"ul",{},[169,170,171,175],"li",{},[172,173,174],"strong",{},"Personal"," — a user links their own account and receives direct messages about their tasks (assigned, deadline, etc.).",[169,177,178,181],{},[172,179,180],{},"Project"," — an admin connects a group/channel, and events for that project are posted to it for the whole team.",[162,183,184,185,188],{},"The bot credentials are configured ",[172,186,187],{},"per instance"," (via environment variables), so the official TaskView SaaS ships an official bot and self-hosted installs supply their own — no code changes, only configuration.",[190,191,193],"h2",{"id":192},"notification-model-event-delivery","Notification model & event delivery",[162,195,196,197,200],{},"Understanding ",[172,198,199],{},"who receives what"," matters, because it is not \"every subscriber gets every event\".",[202,203,205],"h3",{"id":204},"connection-scope","Connection scope",[166,207,208,230],{},[169,209,210,212,213,216,217,221,222,225,226,229],{},[172,211,174],{}," connections are keyed to a ",[172,214,215],{},"user"," (",[218,219,220],"code",{},"ownerType = user","), not to a project or organization. One personal connection covers ",[172,223,224],{},"all"," your projects ",[172,227,228],{},"and"," organizations — switching the organization you view in the app does not change delivery. You receive DMs about your tasks everywhere you are a member.",[169,231,232,212,234,216,237,240],{},[172,233,180],{},[172,235,236],{},"project",[218,238,239],{},"ownerType = project","). They post that project's events to the connected channel/group for the whole team.",[202,242,244],{"id":243},"per-event-audience-personal-delivery","Per-event audience (personal delivery)",[162,246,247,248,251,252,256,257,260],{},"Each event is delivered to a specific ",[172,249,250],{},"audience",". A subscriber receives it only if they are in that audience — the subscription checkbox controls ",[253,254,255],"em",{},"whether"," you get an event you're eligible for, not ",[253,258,259],{},"who"," is eligible.",[262,263,264,277],"table",{},[265,266,267],"thead",{},[268,269,270,274],"tr",{},[271,272,273],"th",{},"Event",[271,275,276],{},"Personal recipients",[278,279,280,294,307,318,329],"tbody",{},[268,281,282,288],{},[283,284,285],"td",{},[172,286,287],{},"Task created",[283,289,290,293],{},[172,291,292],{},"all project members"," (a new task usually has no assignees yet)",[268,295,296,301],{},[283,297,298],{},[172,299,300],{},"Task assigned",[283,302,303,304],{},"the task's ",[172,305,306],{},"assignees",[268,308,309,314],{},[283,310,311],{},[172,312,313],{},"Task status changed",[283,315,303,316],{},[172,317,306],{},[268,319,320,325],{},[283,321,322],{},[172,323,324],{},"Task completed / reopened",[283,326,303,327],{},[172,328,306],{},[268,330,331,334],{},[283,332,333],{},"Sprint / member / time-tracking events",[283,335,336],{},"the relevant project members / actors",[162,338,339],{},"Key rules:",[166,341,342,352,374],{},[169,343,344,347,348,351],{},[172,345,346],{},"No initiator exclusion."," Messaging is an explicit opt-in feed, so you receive an event even for ",[172,349,350],{},"your own"," action — e.g. completing a task you're assigned to still DMs you. The initiator is not filtered out; they simply receive the event if they are in the audience.",[169,353,354,357,358,361,362,365,366,369,370,373],{},[172,355,356],{},"Consequence:"," for everything ",[172,359,360],{},"except Task created",", you are notified ",[172,363,364],{},"only if you are an assignee"," of that task. A task with ",[172,367,368],{},"no assignees"," produces ",[172,371,372],{},"no"," personal completed/status notification for anyone (there is no one in the audience). \"Not an assignee → not notified\" applies to the initiator too — they are just one non-assignee among others.",[169,375,376,379,380,383,384,387],{},[172,377,378],{},"Task reopened"," is delivered under the ",[172,381,382],{},"Task completed"," subscription (same checkbox), shown with a ",[218,385,386],{},"[Task reopened]"," title and a ↩️→ Done button.",[202,389,391],{"id":390},"project-delivery-content-gating","Project delivery & content gating",[166,393,394,405],{},[169,395,396,397,400,401,404],{},"Project events go to the connected channel/group when the connection is ",[172,398,399],{},"active"," and ",[172,402,403],{},"subscribed"," to that event.",[169,406,407],{},"Delivery is queued (pg-boss) and retried with backoff (up to 3 attempts).",[409,410,412,413,416],"h4",{"id":411},"the-post-content-toggle","The ",[218,414,415],{},"post content"," toggle",[162,418,419,420,423,424,426,427,430],{},"A shared channel is ",[172,421,422],{},"not"," an RBAC principal — TaskView can't check the permissions of each channel member (they may not even have a TaskView account). So instead of per-viewer gating, a project connection has one explicit switch, ",[172,425,415],{},", set by whoever connected the channel (requires ",[218,428,429],{},"INTEGRATIONS_CAN_MANAGE","). It decides how much of a task event is broadcast:",[262,432,433,450],{},[265,434,435],{},[268,436,437,439,445],{},[271,438],{},[271,440,441,444],{},[218,442,443],{},"post content = true"," (default)",[271,446,447],{},[218,448,449],{},"post content = false",[278,451,452,466,478,494],{},[268,453,454,461,464],{},[283,455,456,457,460],{},"Task ",[172,458,459],{},"title"," + link",[283,462,463],{},"✅ posted",[283,465,463],{},[268,467,468,473,475],{},[283,469,456,470],{},[172,471,472],{},"description",[283,474,463],{},[283,476,477],{},"❌ hidden",[268,479,480,490,492],{},[283,481,482,485,486,489],{},[172,483,484],{},"Assignee"," footer (",[218,487,488],{},"👤 emails",")",[283,491,463],{},[283,493,477],{},[268,495,496,502,505],{},[283,497,498,501],{},[172,499,500],{},"Action buttons"," (Done / Reopen / Assign)",[283,503,504],{},"✅ shown",[283,506,477],{},[162,508,509,510,513,514,517],{},"Turn it ",[172,511,512],{},"off"," for a channel whose audience should see only that ",[253,515,516],{},"something happened"," (title + link) without the task's content, assignee emails, or interactive actions. The event itself is still delivered either way — only the content and buttons are suppressed. Buttons are hidden on a content-hidden channel because the Assign action opens a modal listing project members, which would defeat the point of hiding content.",[519,520,521],"blockquote",{},[162,522,523,525,526,529,530,533],{},[172,524,174],{}," DMs are different: each recipient is a known TaskView user, so the description is gated per recipient by ",[218,527,528],{},"COMPONENT_CAN_WATCH_CONTENT"," and the assignee footer by ",[218,531,532],{},"TASKS_CAN_WATCH_ASSIGNED_USERS"," — and action buttons are always shown (any action still runs under that user's own RBAC).",[190,535,537],{"id":536},"prerequisites","Prerequisites",[166,539,540,551,554],{},[169,541,542,543,546,547,550],{},"TaskView API running and reachable over ",[172,544,545],{},"public HTTPS"," (messengers deliver updates/redirects to your API; ",[218,548,549],{},"localhost"," is not reachable from Telegram/Slack — use your production domain or a tunnel for local testing)",[169,552,553],{},"PostgreSQL database with migrations applied",[169,555,556,559],{},[218,557,558],{},".env.taskview"," file configured",[519,561,562,584],{},[162,563,564,567,568,571,572,575,576,579,580,583],{},[172,565,566],{},"Local testing tunnel:"," prefer ",[218,569,570],{},"cloudflared tunnel --url http://localhost:1401"," — it gives a clean HTTPS URL with no interstitial. ",[218,573,574],{},"ngrok","'s free tier shows a \"You are about to visit…\" warning page that breaks the browser OAuth redirect (you can't inject the ",[218,577,578],{},"ngrok-skip-browser-warning"," header into Slack's redirect); if you must use ngrok free, open the tunnel URL once in your browser and click ",[172,581,582],{},"Visit Site"," first, or upgrade to a paid plan.",[162,585,586,587,590,591,594,595,598,599,602,603,605],{},"For Slack, the API base the ",[172,588,589],{},"frontend"," calls and ",[218,592,593],{},"SLACK_CALLBACK_URL"," must be the ",[172,596,597],{},"same HTTPS origin"," — the anti-CSRF nonce cookie is set on ",[218,600,601],{},"oauth/start"," and read on the callback, so a ",[218,604,549],{}," start + tunnel callback will not work.",[607,608],"hr",{},[190,610,612],{"id":611},"_1-database-migration","1. Database Migration",[162,614,615,616,619,620,619,623,626],{},"The migration creates the required tables (",[218,617,618],{},"tasks.messaging_connections",", ",[218,621,622],{},"tasks.messaging_link_tokens",[218,624,625],{},"tasks.messaging_identity_map",") automatically. The migration container handles this on startup — no manual steps needed.",[607,628],{},[190,630,632],{"id":631},"_2-telegram","2. Telegram",[202,634,636],{"id":635},"_21-create-a-bot","2.1 Create a bot",[638,639,640,651,658],"ol",{},[169,641,642,643,650],{},"Open ",[644,645,649],"a",{"href":646,"rel":647},"https://t.me/BotFather",[648],"nofollow","@BotFather"," in Telegram",[169,652,653,654,657],{},"Send ",[218,655,656],{},"/newbot"," and follow the prompts",[169,659,660,661,664,665,668,669,672,673,489],{},"Copy the ",[172,662,663],{},"bot token"," (looks like ",[218,666,667],{},"123456789:AA...",") and note the ",[172,670,671],{},"bot username"," (without ",[218,674,675],{},"@",[202,677,679],{"id":678},"_22-configure-the-bot-for-group-commands","2.2 Configure the bot for group commands",[162,681,682,683,686],{},"Project connections are completed by typing a command ",[172,684,685],{},"inside a group",", so the bot must be allowed to read group messages:",[638,688,689,703],{},[169,690,691,692,695,696,699,700,489],{},"In @BotFather, send ",[218,693,694],{},"/setprivacy"," → choose your bot → ",[172,697,698],{},"Disable","\n(with privacy enabled, members must instead address the bot explicitly: ",[218,701,702],{},"/connect@your_bot \u003Ctoken>",[169,704,653,705,695,708],{},[218,706,707],{},"/setjoingroups",[172,709,710],{},"Enable",[202,712,714],{"id":713},"_23-environment-variables","2.3 Environment variables",[162,716,717,718,720],{},"Add to ",[218,719,558],{},":",[722,723,728],"pre",{"className":724,"code":726,"language":727},[725],"language-text","TELEGRAM_BOT_TOKEN=123456789:AA-your-bot-token\nTELEGRAM_BOT_USERNAME=your_bot            # without the leading @\nTELEGRAM_WEBHOOK_SECRET=\u003Crandom-secret>   # any long random string\n","text",[218,729,726],{"__ignoreMap":730},"",[162,732,733],{},"Generate a webhook secret:",[722,735,739],{"className":736,"code":737,"language":738,"meta":730,"style":730},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"\n","bash",[218,740,741],{"__ignoreMap":730},[742,743,746,750,754,758,761],"span",{"class":744,"line":745},"line",1,[742,747,749],{"class":748},"sBMFI","node",[742,751,753],{"class":752},"sfazB"," -e",[742,755,757],{"class":756},"sMK4o"," \"",[742,759,760],{"class":752},"console.log(require('crypto').randomBytes(32).toString('hex'))",[742,762,763],{"class":756},"\"\n",[519,765,766],{},[162,767,768,769,772],{},"The webhook secret is compared (in constant time) against the ",[218,770,771],{},"X-Telegram-Bot-Api-Secret-Token"," header on every inbound update, so only Telegram can reach the endpoint.",[202,774,776],{"id":775},"_24-register-the-webhook-with-telegram","2.4 Register the webhook with Telegram",[162,778,779],{},"Tell Telegram where to deliver updates. Run once (replace the placeholders):",[722,781,783],{"className":736,"code":782,"language":738,"meta":730,"style":730},"curl \"https://api.telegram.org/bot\u003CTELEGRAM_BOT_TOKEN>/setWebhook\" \\\n  -d \"url=https://\u003Cyour-api-domain>/module/messaging/telegram/webhook\" \\\n  -d \"secret_token=\u003CTELEGRAM_WEBHOOK_SECRET>\"\n",[218,784,785,802,817],{"__ignoreMap":730},[742,786,787,790,792,795,798],{"class":744,"line":745},[742,788,789],{"class":748},"curl",[742,791,757],{"class":756},[742,793,794],{"class":752},"https://api.telegram.org/bot\u003CTELEGRAM_BOT_TOKEN>/setWebhook",[742,796,797],{"class":756},"\"",[742,799,801],{"class":800},"sTEyZ"," \\\n",[742,803,805,808,810,813,815],{"class":744,"line":804},2,[742,806,807],{"class":752},"  -d",[742,809,757],{"class":756},[742,811,812],{"class":752},"url=https://\u003Cyour-api-domain>/module/messaging/telegram/webhook",[742,814,797],{"class":756},[742,816,801],{"class":800},[742,818,820,822,824,827],{"class":744,"line":819},3,[742,821,807],{"class":752},[742,823,757],{"class":756},[742,825,826],{"class":752},"secret_token=\u003CTELEGRAM_WEBHOOK_SECRET>",[742,828,763],{"class":756},[162,830,831],{},"Verify it took effect:",[722,833,835],{"className":736,"code":834,"language":738,"meta":730,"style":730},"curl \"https://api.telegram.org/bot\u003CTELEGRAM_BOT_TOKEN>/getWebhookInfo\"\n",[218,836,837],{"__ignoreMap":730},[742,838,839,841,843,846],{"class":744,"line":745},[742,840,789],{"class":748},[742,842,757],{"class":756},[742,844,845],{"class":752},"https://api.telegram.org/bot\u003CTELEGRAM_BOT_TOKEN>/getWebhookInfo",[742,847,763],{"class":756},[162,849,850,853,854,400,857,860,861,864],{},[218,851,852],{},"url"," should point at your ",[218,855,856],{},"/module/messaging/telegram/webhook",[218,858,859],{},"pending_update_count"," should be ",[218,862,863],{},"0",".",[202,866,868],{"id":867},"_25-usage","2.5 Usage",[162,870,871,872,864],{},"Restart the API after setting the environment variables, then in TaskView open a project → right-click it in the sidebar → ",[172,873,874],{},"\"Messengers\"",[162,876,877],{},[172,878,879],{},"Personal (Direct messages)",[638,881,882,898,909],{},[169,883,884,885,887,888,891,892,891,895],{},"On the ",[172,886,174],{}," tab, click ",[172,889,890],{},"Connect"," → ",[172,893,894],{},"Telegram",[172,896,897],{},"Get link",[169,899,900,901,904,905,908],{},"Click ",[172,902,903],{},"Open in Telegram"," and press ",[172,906,907],{},"Start"," in the bot chat",[169,910,911],{},"Your account is linked — you'll now get DMs about your tasks",[162,913,914],{},[172,915,916],{},"Project (Group channel)",[638,918,919,933,939,945],{},[169,920,884,921,923,924,926,927,891,929,891,931],{},[172,922,180],{}," tab (requires the ",[218,925,429],{}," permission), click ",[172,928,890],{},[172,930,894],{},[172,932,897],{},[169,934,900,935,938],{},[172,936,937],{},"Add bot to group"," and pick the group, or add the bot manually",[169,940,941,942],{},"In that group, send the shown command: ",[218,943,944],{},"/connect \u003Ctoken>",[169,946,947],{},"The group is connected — project events are now posted there",[162,949,950],{},"You can toggle any connection on/off or disconnect it from the same page.",[162,952,953],{},[172,954,955,956,489],{},"Creating tasks from a group (",[218,957,958],{},"/task",[162,960,961,962,965],{},"In a group linked to ",[172,963,964],{},"exactly one"," project, any member can create a task:",[722,967,970],{"className":968,"code":969,"language":727},[725],"/task Buy the domain\n",[218,971,969],{"__ignoreMap":730},[166,973,974,985,988],{},[169,975,976,977,980,981,984],{},"Requires the sender to have ",[172,978,979],{},"linked their personal Telegram account"," (Personal tab → Connect → Telegram) — the command runs under their TaskView permissions (",[218,982,983],{},"COMPONENT_CAN_ADD_TASKS",").",[169,986,987],{},"The bot replies with the created task and a link. In an unlinked chat, or one linked to several projects, it says so and creates nothing.",[169,989,990,993,994,997,998,1001,1002,1005,1006,864],{},[172,991,992],{},"Privacy mode note:"," with the bot's group privacy ",[172,995,996],{},"enabled",", Telegram only delivers commands addressed to the bot — use ",[218,999,1000],{},"/task@your_bot \u003Cdescription>"," (same as ",[218,1003,1004],{},"/connect","). Disable privacy (and re-add the bot) to allow a bare ",[218,1007,958],{},[607,1009],{},[190,1011,1013],{"id":1012},"_3-slack","3. Slack",[162,1015,1016,1017,1020],{},"Slack uses per-instance OAuth credentials, mirroring the GitHub/GitLab integration model. Requires ",[218,1018,1019],{},"ENCRYPTION_KEY"," (tokens are stored AES-256-GCM encrypted).",[519,1022,1023],{},[162,1024,1025,1028,1029,1031,1032,1034],{},[172,1026,1027],{},"Public HTTPS is required."," Slack must reach your API over HTTPS for OAuth, slash\ncommands and interactivity. In local dev use a tunnel (ngrok / cloudflared); note that a\nfree ngrok URL changes on every restart — if it changes you must update the Redirect URL,\nthe Slash Command / Interactivity Request URLs ",[172,1030,228],{}," ",[218,1033,593],{},", then restart.",[202,1036,1038],{"id":1037},"_31-create-the-app","3.1 Create the app",[638,1040,1041,1060],{},[169,1042,1043,1044,891,1049,891,1052,1055,1056,1059],{},"Go to ",[644,1045,1048],{"href":1046,"rel":1047},"https://api.slack.com/apps",[648],"api.slack.com/apps",[172,1050,1051],{},"Create New App",[172,1053,1054],{},"From scratch"," → name it (e.g. ",[218,1057,1058],{},"TaskView",") and pick your workspace.",[169,1061,1062,1065,1066],{},[172,1063,1064],{},"App name vs bot name"," (these are two different fields — a common point of confusion):",[166,1067,1068,1074],{},[169,1069,1070,1073],{},[172,1071,1072],{},"Basic Information → Display Information → App name"," — the app's name in the api.slack.com console (the dropdown at the top-left). Purely cosmetic for the console.",[169,1075,1076,1079,1080,1083,1084,1086,1087,1090],{},[172,1077,1078],{},"App Home → App Display Name → Edit → Display Name (Bot Name)"," — how the ",[172,1081,1082],{},"bot"," signs its messages in Slack (e.g. ",[218,1085,1058],{},"). Changing it only takes effect after a ",[172,1088,1089],{},"Reinstall"," (step 5).",[202,1092,1094],{"id":1093},"_32-oauth-scopes","3.2 OAuth & scopes",[638,1096,1097,1123],{},[169,1098,1099,1102,1103,1105,1106,720,1109,1115,1118,1119,1122],{},[172,1100,1101],{},"OAuth & Permissions → Redirect URLs → Add New Redirect URL"," — enter exactly your ",[218,1104,593],{},", then ",[172,1107,1108],{},"Save URLs",[722,1110,1113],{"className":1111,"code":1112,"language":727},[725],"https://\u003Cyour-api-domain>/module/messaging/slack/oauth/callback\n",[218,1114,1112],{"__ignoreMap":730},[1116,1117],"br",{},"Slack rejects plain ",[218,1120,1121],{},"http://localhost"," — HTTPS only.",[169,1124,1125,1128,1129,1176],{},[172,1126,1127],{},"OAuth & Permissions → Scopes → Bot Token Scopes"," — add:",[166,1130,1131,1140,1149,1167],{},[169,1132,1133,1136,1137],{},[218,1134,1135],{},"chat:write"," — post messages (personal DMs + project channels). ",[172,1138,1139],{},"Required.",[169,1141,1142,1145,1146,1148],{},[218,1143,1144],{},"commands"," — needed for the ",[218,1147,958],{}," slash command (inbound).",[169,1150,1151,1154,1155,1158,1159,1162,1163,1166],{},[218,1152,1153],{},"chat:write.public"," — ",[253,1156,1157],{},"optional but recommended",": lets the bot post to ",[172,1160,1161],{},"any public channel without being invited",". Without it you must ",[218,1164,1165],{},"/invite"," the bot into each channel (see step 5).",[169,1168,1169,1154,1172,1175],{},[218,1170,1171],{},"im:write",[253,1173,1174],{},"optional",", only if personal DMs fail to open.",[519,1177,1178],{},[162,1179,1180,1183],{},[218,1181,1182],{},"incoming-webhook"," is added automatically when you enable Incoming Webhooks (step 3.3) and is what makes the project-connect channel picker appear.",[202,1185,1187],{"id":1186},"_33-features","3.3 Features",[638,1189,1190,1200,1224],{},[169,1191,1192,1195,1196,1199],{},[172,1193,1194],{},"Incoming Webhooks → Activate Incoming Webhooks: On."," Required for the ",[172,1197,1198],{},"project (channel)"," connect flow — it makes Slack show a channel picker during install.",[169,1201,1202,1205],{},[172,1203,1204],{},"Slash Commands → Create New Command:",[166,1206,1207,1212,1218],{},[169,1208,1209,1210],{},"Command: ",[218,1211,958],{},[169,1213,1214,1215],{},"Request URL: ",[218,1216,1217],{},"https://\u003Cyour-api-domain>/module/messaging/slack/commands",[169,1219,1220,1221],{},"Short description: ",[218,1222,1223],{},"Create a TaskView task",[169,1225,1226,1229,1230,1236,1238,1239,1242],{},[172,1227,1228],{},"Interactivity & Shortcuts → On",", Request URL:",[722,1231,1234],{"className":1232,"code":1233,"language":727},[725],"https://\u003Cyour-api-domain>/module/messaging/slack/interactivity\n",[218,1235,1233],{"__ignoreMap":730},[1116,1237],{},"Powers the ",[172,1240,1241],{},"Done / Reopen / Assign"," buttons and the assignee modal.",[202,1244,1246],{"id":1245},"_34-credentials-environment-variables","3.4 Credentials → environment variables",[162,1248,1249,1252],{},[172,1250,1251],{},"Basic Information → App Credentials"," — copy the three values:",[722,1254,1257],{"className":1255,"code":1256,"language":727},[725],"SLACK_CLIENT_ID=\u003Cyour-client-id>\nSLACK_CLIENT_SECRET=\u003Cyour-client-secret>\nSLACK_CALLBACK_URL=https://\u003Cyour-api-domain>/module/messaging/slack/oauth/callback\nSLACK_SIGNING_SECRET=\u003Cyour-signing-secret>\n",[218,1258,1256],{"__ignoreMap":730},[166,1260,1261,1270,1276,1282],{},[169,1262,1263,1265,1266,1269],{},[218,1264,593],{}," must ",[172,1267,1268],{},"exactly"," match the Redirect URL from step 3.2.",[169,1271,1272,1275],{},[218,1273,1274],{},"SLACK_SIGNING_SECRET"," verifies inbound slash commands / interactivity (without it those endpoints return 503).",[169,1277,1278,1279,1281],{},"Tokens are stored AES-256-GCM encrypted — ",[218,1280,1019],{}," is required.",[169,1283,1284,1285,619,1288,619,1291,1294],{},"Optional endpoint overrides (tests / enterprise proxy): ",[218,1286,1287],{},"SLACK_API_BASE_URL",[218,1289,1290],{},"SLACK_AUTHORIZE_URL",[218,1292,1293],{},"SLACK_WEBHOOK_PREFIX"," (see the environment-variables reference).",[162,1296,1297,1298],{},"Then ",[172,1299,1300],{},"restart the API.",[202,1302,1304],{"id":1303},"_35-install-and-reinstall","3.5 Install (and reinstall)",[638,1306,1307,1316,1322],{},[169,1308,1309,1312,1313,984],{},[172,1310,1311],{},"OAuth & Permissions → Install to Workspace"," (or ",[253,1314,1315],{},"Reinstall to Workspace",[169,1317,1318,1321],{},[172,1319,1320],{},"Reinstall whenever you change scopes"," or the bot display name — otherwise changes don't take effect.",[169,1323,1324,1327,1328,1334,1337,1338,1341,1342,1344,1345,1348],{},[172,1325,1326],{},"Invite the bot into the channel(s)"," it should post to:\n",[722,1329,1332],{"className":1330,"code":1331,"language":727},[725],"/invite @TaskView\n",[218,1333,1331],{"__ignoreMap":730},[218,1335,1336],{},"chat.postMessage"," fails with ",[218,1339,1340],{},"not_in_channel"," if the bot isn't a member. Either invite it per channel, or add the ",[218,1343,1153],{}," scope (step 3.2) so it can post to any ",[172,1346,1347],{},"public"," channel without an invite (private channels always need an invite).",[202,1350,1352],{"id":1351},"_36-connect-and-use","3.6 Connect and use",[162,1354,642,1355,1358],{},[172,1356,1357],{},"Messengers"," in a project:",[166,1360,1361,1380],{},[169,1362,1363,1366,1367,891,1369,891,1372,1375,1376,1379],{},[172,1364,1365],{},"Personal:"," Personal tab → ",[172,1368,890],{},[172,1370,1371],{},"Slack",[172,1373,1374],{},"Continue with Slack"," → authorize. The bot DMs you about your tasks. This also ",[172,1377,1378],{},"links your Slack account"," (required for inbound actions to run as you).",[169,1381,1382,1385,1386,1388,1389,891,1391,891,1393,1395,1396,1399],{},[172,1383,1384],{},"Project:"," Project tab (needs ",[218,1387,429],{},") → ",[172,1390,890],{},[172,1392,1371],{},[172,1394,1374],{}," → pick the channel during install. Project events post to that channel. ",[172,1397,1398],{},"Invite the bot into that channel"," (step 3.5).",[519,1401,1402],{},[162,1403,1404,1407,1408,1411,1412,1414,1415,1417,1418,1420,1421,1423,1424,1429,1430,1432,1433,1436],{},[172,1405,1406],{},"Gotcha — \"added an integration to this channel\" is NOT enough."," After you pick a channel during install, Slack posts ",[253,1409,1410],{},"\"added an integration to this channel: TaskView\""," there. That only adds the app's incoming webhook — it does ",[172,1413,422],{}," make the bot ",[172,1416,215],{}," a channel member, and TaskView posts via the bot (",[218,1419,1336],{},", so it can also render buttons). So delivery still fails with ",[218,1422,1340],{}," until you explicitly run ",[172,1425,1426],{},[218,1427,1428],{},"/invite @TaskView"," in that channel (or use ",[218,1431,1153],{},"). Confirm the connection's channel is the one you meant — the ",[218,1434,1435],{},"#channel"," you picked in the webhook dropdown is where events go.",[162,1438,1439],{},[172,1440,1441],{},"Acting from Slack:",[166,1443,1444,1453,1468],{},[169,1445,1446,1449,1450,1452],{},[218,1447,1448],{},"/task \u003Cdescription>"," in a channel linked to ",[172,1451,964],{}," project → creates a task there. (In a DM or an unlinked channel it replies that the channel isn't linked; a channel linked to several projects is ambiguous and is refused — create it in the app.)",[169,1454,1455,1456,1459,1460,1463,1464,1467],{},"Under each task notification: ",[172,1457,1458],{},"✅ Done"," (complete), ",[172,1461,1462],{},"↩️ Reopen"," (on a completed card), ",[172,1465,1466],{},"👤 Assign"," (a modal that pre-selects current assignees; add/remove and Save to set the list).",[169,1469,1470],{},"Every inbound action runs under the invoking user's TaskView RBAC, so they must have linked their Slack account. Requests are verified with the Signing Secret.",[519,1472,1473],{},[162,1474,1475,1478,1479,1482,1483,1485],{},[172,1476,1477],{},"Notification model:"," messengers are a ",[172,1480,1481],{},"feed/history"," — each event posts a new message. TaskView does ",[172,1484,422],{}," edit past cards, so an old card may still show a live button after the task changed elsewhere. Actions are idempotent and always re-check the current state + permissions, so a stale click is safe.",[202,1487,1489],{"id":1488},"_37-distribution","3.7 Distribution",[162,1491,1492,1493,1496,1497,1500,1501,1503],{},"By default a Slack app is ",[172,1494,1495],{},"private to your workspace",". To let other workspaces install it, enable ",[172,1498,1499],{},"Public Distribution"," (free); to list it in the Slack Marketplace, pass Slack's review (also free — a review, not a fee). Self-hosted installs keep the app private; the official SaaS uses one distributed app. Creating and distributing an app does ",[172,1502,422],{}," require a paid Slack plan.",[607,1505],{},[190,1507,1509,1510,1512],{"id":1508},"_4-full-envtaskview-example","4. Full ",[218,1511,558],{}," Example",[722,1514,1518],{"className":1515,"code":1516,"language":1517,"meta":730,"style":730},"language-env shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# ... existing vars ...\n\n# Telegram messaging integration\nTELEGRAM_BOT_TOKEN=123456789:AA-your-bot-token\nTELEGRAM_BOT_USERNAME=your_bot\nTELEGRAM_WEBHOOK_SECRET=a1b2c3d4e5f6...\n\n# Slack messaging integration\nSLACK_CLIENT_ID=\nSLACK_CLIENT_SECRET=\nSLACK_CALLBACK_URL=https://api.taskview.tech/module/messaging/slack/oauth/callback\nSLACK_SIGNING_SECRET=\n\n# Required for Slack (encrypted token storage), shared with SSO/other integrations\nENCRYPTION_KEY=a1b2c3d4e5f6...  # 64 hex characters\n","env",[218,1519,1520,1525,1531,1536,1542,1548,1554,1559,1565,1571,1577,1583,1589,1594,1600],{"__ignoreMap":730},[742,1521,1522],{"class":744,"line":745},[742,1523,1524],{},"# ... existing vars ...\n",[742,1526,1527],{"class":744,"line":804},[742,1528,1530],{"emptyLinePlaceholder":1529},true,"\n",[742,1532,1533],{"class":744,"line":819},[742,1534,1535],{},"# Telegram messaging integration\n",[742,1537,1539],{"class":744,"line":1538},4,[742,1540,1541],{},"TELEGRAM_BOT_TOKEN=123456789:AA-your-bot-token\n",[742,1543,1545],{"class":744,"line":1544},5,[742,1546,1547],{},"TELEGRAM_BOT_USERNAME=your_bot\n",[742,1549,1551],{"class":744,"line":1550},6,[742,1552,1553],{},"TELEGRAM_WEBHOOK_SECRET=a1b2c3d4e5f6...\n",[742,1555,1557],{"class":744,"line":1556},7,[742,1558,1530],{"emptyLinePlaceholder":1529},[742,1560,1562],{"class":744,"line":1561},8,[742,1563,1564],{},"# Slack messaging integration\n",[742,1566,1568],{"class":744,"line":1567},9,[742,1569,1570],{},"SLACK_CLIENT_ID=\n",[742,1572,1574],{"class":744,"line":1573},10,[742,1575,1576],{},"SLACK_CLIENT_SECRET=\n",[742,1578,1580],{"class":744,"line":1579},11,[742,1581,1582],{},"SLACK_CALLBACK_URL=https://api.taskview.tech/module/messaging/slack/oauth/callback\n",[742,1584,1586],{"class":744,"line":1585},12,[742,1587,1588],{},"SLACK_SIGNING_SECRET=\n",[742,1590,1592],{"class":744,"line":1591},13,[742,1593,1530],{"emptyLinePlaceholder":1529},[742,1595,1597],{"class":744,"line":1596},14,[742,1598,1599],{},"# Required for Slack (encrypted token storage), shared with SSO/other integrations\n",[742,1601,1603],{"class":744,"line":1602},15,[742,1604,1605],{},"ENCRYPTION_KEY=a1b2c3d4e5f6...  # 64 hex characters\n",[607,1607],{},[190,1609,1611],{"id":1610},"_5-saas-vs-self-hosted","5. SaaS vs Self-Hosted",[262,1613,1614,1626],{},[265,1615,1616],{},[268,1617,1618,1620,1623],{},[271,1619],{},[271,1621,1622],{},"Official SaaS",[271,1624,1625],{},"Self-hosted",[278,1627,1628,1643],{},[268,1629,1630,1634,1637],{},[283,1631,1632],{},[172,1633,894],{},[283,1635,1636],{},"One official bot, credentials shipped with the instance",[283,1638,1639,1640],{},"Create your own bot via @BotFather and set ",[218,1641,1642],{},"TELEGRAM_*",[268,1644,1645,1649,1652],{},[283,1646,1647],{},[172,1648,1371],{},[283,1650,1651],{},"One official distributed app",[283,1653,1654,1655],{},"Create your own Slack app and set ",[218,1656,1657],{},"SLACK_*",[162,1659,1660],{},"There is no code fork between the two — only the values of the environment variables differ. If the variables are unset, the integration simply does not appear until an admin configures it.",[607,1662],{},[190,1664,1666],{"id":1665},"_6-production-notes","6. Production Notes",[166,1668,1669,1679,1687,1695],{},[169,1670,1671,1674,1675,1678],{},[172,1672,1673],{},"Public HTTPS is mandatory"," for the inbound webhook. Update the ",[218,1676,1677],{},"setWebhook"," URL to your production domain.",[169,1680,1681,1686],{},[172,1682,1683,1684],{},"Re-run ",[218,1685,1677],{}," whenever the API domain changes.",[169,1688,1689,1694],{},[172,1690,1691],{},[218,1692,1693],{},"TELEGRAM_WEBHOOK_SECRET",": store securely, never commit to git.",[169,1696,1697,1700,1701,1703],{},[172,1698,1699],{},"Group privacy",": if ",[218,1702,1004],{}," in a group does nothing, the bot's privacy mode is likely still enabled (see step 2.2).",[607,1705],{},[190,1707,1709],{"id":1708},"_7-troubleshooting","7. Troubleshooting",[162,1711,1712,1715,1716,1719,1720,1723],{},[172,1713,1714],{},"The \"Messengers\" page shows a 503 / \"This messenger is not configured on the server\"","\n→ ",[218,1717,1718],{},"TELEGRAM_BOT_TOKEN"," or ",[218,1721,1722],{},"TELEGRAM_BOT_USERNAME"," is missing; the API was not restarted after setting them.",[162,1725,1726,1732,1733,1736],{},[172,1727,1728,1729,1731],{},"Pressing Start / sending ",[218,1730,1004],{}," does nothing","\n→ The webhook is not registered or points at the wrong URL. Check ",[218,1734,1735],{},"getWebhookInfo"," (step 2.4) and confirm the API is reachable over public HTTPS.",[162,1738,1739,1744,1745,864],{},[172,1740,1741,1743],{},[218,1742,944],{}," in a group is ignored","\n→ The bot's privacy mode is enabled. Disable it (step 2.2), or use ",[218,1746,702],{},[162,1748,1749,1752,1753,1755],{},[172,1750,1751],{},"\"Link is invalid or expired\"","\n→ Binding tokens are single-use and expire after 15 minutes. Generate a new one from the ",[172,1754,890],{}," dialog.",[162,1757,1758,1761,1762,1765],{},[172,1759,1760],{},"Notifications don't arrive after connecting","\n→ Confirm the connection is toggled ",[172,1763,1764],{},"on",", and that you are actually assigned to the task. Project posts require an active project connection.",[202,1767,1371],{"id":1768},"slack",[162,1770,1771,1774,1775,1778,1779,1781,1782,1784,1785,1787,1788,1792,1793,1795,1796,1799],{},[172,1772,1773],{},"Nothing posts to a linked channel (personal DM works)"," — the log shows ",[218,1776,1777],{},"chat.postMessage failed: not_in_channel","\n→ The bot ",[172,1780,215],{}," isn't a member of that channel. Seeing ",[253,1783,1410],{}," is ",[172,1786,422],{}," the same as the bot joining — that message is just the incoming webhook. Run ",[172,1789,1790],{},[218,1791,1428],{}," (the bot user) in the channel, or add the ",[218,1794,1153],{}," scope and reinstall. The log line names the channel id (",[218,1797,1798],{},"channel=…",") — make sure it's the one you intended.",[162,1801,1802,1807,1808,1811,1812,1815],{},[172,1803,1804,1806],{},[218,1805,958],{}," replies \"This channel is not linked to a TaskView project\"","\n→ Run it in a ",[172,1809,1810],{},"project channel"," (connected via Messengers → Project), not in a DM. A channel linked to ",[172,1813,1814],{},"several"," projects is refused as ambiguous — create the task in the app.",[162,1817,1818,1715,1821,1823,1824,1826,1827,864],{},[172,1819,1820],{},"Slash command / buttons return an error or nothing",[218,1822,1274],{}," is missing (endpoints return 503), the Request URLs don't match your current public host, or the app wasn't reinstalled after enabling ",[218,1825,1144],{}," / ",[218,1828,1135],{},[162,1830,1831,1834],{},[172,1832,1833],{},"\"You need to link your Slack account\"","\n→ Inbound actions run under your TaskView permissions. Link it: Personal tab → Connect → Slack.",[162,1836,1837,1840,1841,1844,1845,1847],{},[172,1838,1839],{},"Bot still shows the old name in messages","\n→ Changing ",[172,1842,1843],{},"Display Name (Bot Name)"," requires ",[172,1846,1315],{},". The console dropdown name is a separate field (Basic Information → App name).",[1849,1850,1851],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}",{"title":730,"searchDepth":804,"depth":804,"links":1853},[1854,1859,1860,1861,1868,1877,1879,1880,1881],{"id":192,"depth":804,"text":193,"children":1855},[1856,1857,1858],{"id":204,"depth":819,"text":205},{"id":243,"depth":819,"text":244},{"id":390,"depth":819,"text":391},{"id":536,"depth":804,"text":537},{"id":611,"depth":804,"text":612},{"id":631,"depth":804,"text":632,"children":1862},[1863,1864,1865,1866,1867],{"id":635,"depth":819,"text":636},{"id":678,"depth":819,"text":679},{"id":713,"depth":819,"text":714},{"id":775,"depth":819,"text":776},{"id":867,"depth":819,"text":868},{"id":1012,"depth":804,"text":1013,"children":1869},[1870,1871,1872,1873,1874,1875,1876],{"id":1037,"depth":819,"text":1038},{"id":1093,"depth":819,"text":1094},{"id":1186,"depth":819,"text":1187},{"id":1245,"depth":819,"text":1246},{"id":1303,"depth":819,"text":1304},{"id":1351,"depth":819,"text":1352},{"id":1488,"depth":819,"text":1489},{"id":1508,"depth":804,"text":1878},"4. Full .env.taskview Example",{"id":1610,"depth":804,"text":1611},{"id":1665,"depth":804,"text":1666},{"id":1708,"depth":804,"text":1709,"children":1882},[1883],{"id":1768,"depth":819,"text":1371},"Connect Telegram and Slack to TaskView to receive task notifications. Personal direct messages and project-wide group channels, with instance-level bot credentials, OAuth, signed inbound webhooks, and SHA-256 hashed binding tokens.","md",{},{"icon":102},{"title":99,"description":1884},"U7cJL6-k7O0Bw9nrvJmxZ8ZxXQXekLwgO7VAUAyXyhg",[1891,1893],{"title":94,"path":95,"stem":96,"description":1892,"icon":97,"children":-1},"Connect AI assistants like Claude Code and Claude Desktop to TaskView via the Model Context Protocol (MCP). Manage projects and tasks with a scoped API token.",{"title":109,"path":110,"stem":111,"description":1894,"icon":112,"children":-1},"Complete reference for TaskView environment variables - database connection, JWT authentication, OAuth providers, SMTP email, GitHub/GitLab integration, encryption, and CORS configuration for your self-hosted Docker deployment.",1783289464129]