[{"data":1,"prerenderedAt":1512},["ShallowReactive",2],{"navigation":3,"/docs/features/webhooks":156,"/docs/features/webhooks-surround":1507},[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":69,"body":158,"description":1501,"extension":1502,"meta":1503,"navigation":1504,"path":70,"seo":1505,"stem":71,"__hash__":1506},"docs/docs/2.features/7.webhooks.md",{"type":159,"value":160,"toc":1489},"minimark",[161,165,170,371,375,409,413,420,733,745,768,1019,1057,1061,1068,1076,1079,1155,1163,1167,1170,1200,1207,1211,1218,1221,1225,1228,1260,1263,1267,1270,1308,1312,1315,1329,1332,1336,1339,1475,1485],[162,163,164],"p",{},"Webhooks let you receive HTTP POST requests when events happen in your projects. Use them to integrate TaskView with external systems - CI/CD pipelines, Slack bots, custom dashboards, or any service that can accept HTTP requests.",[166,167,169],"h2",{"id":168},"supported-events","Supported events",[171,172,173,186],"table",{},[174,175,176],"thead",{},[177,178,179,183],"tr",{},[180,181,182],"th",{},"Event",[180,184,185],{},"When it fires",[187,188,189,201,211,221,231,241,251,261,271,281,291,301,311,321,331,341,351,361],"tbody",{},[177,190,191,198],{},[192,193,194],"td",{},[195,196,197],"code",{},"task.created",[192,199,200],{},"A new task is created in the project",[177,202,203,208],{},[192,204,205],{},[195,206,207],{},"task.updated",[192,209,210],{},"A task is updated (description, status, priority, deadline, etc.)",[177,212,213,218],{},[192,214,215],{},[195,216,217],{},"task.deleted",[192,219,220],{},"A task is deleted",[177,222,223,228],{},[192,224,225],{},[195,226,227],{},"task.assigneesChanged",[192,229,230],{},"Task assignees are added or removed",[177,232,233,238],{},[192,234,235],{},[195,236,237],{},"time-entry.started",[192,239,240],{},"A timer is started on a task",[177,242,243,248],{},[192,244,245],{},[195,246,247],{},"time-entry.stopped",[192,249,250],{},"A running timer is stopped",[177,252,253,258],{},[192,254,255],{},[195,256,257],{},"time-entry.created",[192,259,260],{},"A time entry is created manually",[177,262,263,268],{},[192,264,265],{},[195,266,267],{},"time-entry.updated",[192,269,270],{},"A time entry is updated",[177,272,273,278],{},[192,274,275],{},[195,276,277],{},"time-entry.deleted",[192,279,280],{},"A time entry is deleted",[177,282,283,288],{},[192,284,285],{},[195,286,287],{},"sprint.created",[192,289,290],{},"A new sprint is created (draft or planned)",[177,292,293,298],{},[192,294,295],{},[195,296,297],{},"sprint.updated",[192,299,300],{},"Sprint fields are edited (name, dates, capacity, goal)",[177,302,303,308],{},[192,304,305],{},[195,306,307],{},"sprint.activated",[192,309,310],{},"A sprint transitions to the active state",[177,312,313,318],{},[192,314,315],{},[195,316,317],{},"sprint.reviewStarted",[192,319,320],{},"An active sprint enters review",[177,322,323,328],{},[192,324,325],{},[195,326,327],{},"sprint.completed",[192,329,330],{},"A sprint is closed and marked completed",[177,332,333,338],{},[192,334,335],{},[195,336,337],{},"sprint.paused",[192,339,340],{},"An active sprint is paused",[177,342,343,348],{},[192,344,345],{},[195,346,347],{},"sprint.resumed",[192,349,350],{},"A paused sprint is resumed",[177,352,353,358],{},[192,354,355],{},[195,356,357],{},"sprint.deleted",[192,359,360],{},"A sprint is deleted (any status, including completed)",[177,362,363,368],{},[192,364,365],{},[195,366,367],{},"task.assignedToSprint",[192,369,370],{},"A task is added to, moved between, or removed from a sprint",[166,372,374],{"id":373},"setup","Setup",[376,377,378,382,389,395,398,401,406],"ol",{},[379,380,381],"li",{},"Open a project in TaskView",[379,383,384,385],{},"Right-click the project in the sidebar → ",[386,387,388],"strong",{},"\"Webhooks\"",[379,390,391,392],{},"Click ",[386,393,394],{},"\"Add Webhook\"",[379,396,397],{},"Enter the URL where you want to receive events",[379,399,400],{},"Select which events to subscribe to",[379,402,391,403],{},[386,404,405],{},"\"Add\"",[379,407,408],{},"Copy the secret and store it securely - it will not be shown again",[166,410,412],{"id":411},"payload-format","Payload format",[162,414,415,416,419],{},"Every webhook delivery is an HTTP POST with ",[195,417,418],{},"Content-Type: application/json",":",[421,422,427],"pre",{"className":423,"code":424,"language":425,"meta":426,"style":426},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"event\": \"task.updated\",\n  \"timestamp\": \"2026-03-22T12:00:00.000Z\",\n  \"task\": {\n    \"id\": 123,\n    \"goalId\": 774,\n    \"description\": \"Fix login bug\",\n    \"complete\": false,\n    \"statusId\": 5,\n    \"priorityId\": 2,\n    \"tags\": [1, 3],\n    \"assignedUsers\": [10, 22],\n    \"subtasks\": []\n  },\n  \"changes\": {\n    \"statusId\": 5\n  },\n  \"initiatorId\": 1\n}\n","json","",[195,428,429,438,464,485,500,520,537,558,573,590,607,634,658,673,679,693,707,712,727],{"__ignoreMap":426},[430,431,434],"span",{"class":432,"line":433},"line",1,[430,435,437],{"class":436},"sMK4o","{\n",[430,439,441,444,448,451,453,456,459,461],{"class":432,"line":440},2,[430,442,443],{"class":436},"  \"",[430,445,447],{"class":446},"spNyl","event",[430,449,450],{"class":436},"\"",[430,452,419],{"class":436},[430,454,455],{"class":436}," \"",[430,457,207],{"class":458},"sfazB",[430,460,450],{"class":436},[430,462,463],{"class":436},",\n",[430,465,467,469,472,474,476,478,481,483],{"class":432,"line":466},3,[430,468,443],{"class":436},[430,470,471],{"class":446},"timestamp",[430,473,450],{"class":436},[430,475,419],{"class":436},[430,477,455],{"class":436},[430,479,480],{"class":458},"2026-03-22T12:00:00.000Z",[430,482,450],{"class":436},[430,484,463],{"class":436},[430,486,488,490,493,495,497],{"class":432,"line":487},4,[430,489,443],{"class":436},[430,491,492],{"class":446},"task",[430,494,450],{"class":436},[430,496,419],{"class":436},[430,498,499],{"class":436}," {\n",[430,501,503,506,510,512,514,518],{"class":432,"line":502},5,[430,504,505],{"class":436},"    \"",[430,507,509],{"class":508},"sBMFI","id",[430,511,450],{"class":436},[430,513,419],{"class":436},[430,515,517],{"class":516},"sbssI"," 123",[430,519,463],{"class":436},[430,521,523,525,528,530,532,535],{"class":432,"line":522},6,[430,524,505],{"class":436},[430,526,527],{"class":508},"goalId",[430,529,450],{"class":436},[430,531,419],{"class":436},[430,533,534],{"class":516}," 774",[430,536,463],{"class":436},[430,538,540,542,545,547,549,551,554,556],{"class":432,"line":539},7,[430,541,505],{"class":436},[430,543,544],{"class":508},"description",[430,546,450],{"class":436},[430,548,419],{"class":436},[430,550,455],{"class":436},[430,552,553],{"class":458},"Fix login bug",[430,555,450],{"class":436},[430,557,463],{"class":436},[430,559,561,563,566,568,570],{"class":432,"line":560},8,[430,562,505],{"class":436},[430,564,565],{"class":508},"complete",[430,567,450],{"class":436},[430,569,419],{"class":436},[430,571,572],{"class":436}," false,\n",[430,574,576,578,581,583,585,588],{"class":432,"line":575},9,[430,577,505],{"class":436},[430,579,580],{"class":508},"statusId",[430,582,450],{"class":436},[430,584,419],{"class":436},[430,586,587],{"class":516}," 5",[430,589,463],{"class":436},[430,591,593,595,598,600,602,605],{"class":432,"line":592},10,[430,594,505],{"class":436},[430,596,597],{"class":508},"priorityId",[430,599,450],{"class":436},[430,601,419],{"class":436},[430,603,604],{"class":516}," 2",[430,606,463],{"class":436},[430,608,610,612,615,617,619,622,625,628,631],{"class":432,"line":609},11,[430,611,505],{"class":436},[430,613,614],{"class":508},"tags",[430,616,450],{"class":436},[430,618,419],{"class":436},[430,620,621],{"class":436}," [",[430,623,624],{"class":516},"1",[430,626,627],{"class":436},",",[430,629,630],{"class":516}," 3",[430,632,633],{"class":436},"],\n",[430,635,637,639,642,644,646,648,651,653,656],{"class":432,"line":636},12,[430,638,505],{"class":436},[430,640,641],{"class":508},"assignedUsers",[430,643,450],{"class":436},[430,645,419],{"class":436},[430,647,621],{"class":436},[430,649,650],{"class":516},"10",[430,652,627],{"class":436},[430,654,655],{"class":516}," 22",[430,657,633],{"class":436},[430,659,661,663,666,668,670],{"class":432,"line":660},13,[430,662,505],{"class":436},[430,664,665],{"class":508},"subtasks",[430,667,450],{"class":436},[430,669,419],{"class":436},[430,671,672],{"class":436}," []\n",[430,674,676],{"class":432,"line":675},14,[430,677,678],{"class":436},"  },\n",[430,680,682,684,687,689,691],{"class":432,"line":681},15,[430,683,443],{"class":436},[430,685,686],{"class":446},"changes",[430,688,450],{"class":436},[430,690,419],{"class":436},[430,692,499],{"class":436},[430,694,696,698,700,702,704],{"class":432,"line":695},16,[430,697,505],{"class":436},[430,699,580],{"class":508},[430,701,450],{"class":436},[430,703,419],{"class":436},[430,705,706],{"class":516}," 5\n",[430,708,710],{"class":432,"line":709},17,[430,711,678],{"class":436},[430,713,715,717,720,722,724],{"class":432,"line":714},18,[430,716,443],{"class":436},[430,718,719],{"class":446},"initiatorId",[430,721,450],{"class":436},[430,723,419],{"class":436},[430,725,726],{"class":516}," 1\n",[430,728,730],{"class":432,"line":729},19,[430,731,732],{"class":436},"}\n",[162,734,735,736,738,739,741,742,744],{},"The ",[195,737,686],{}," field is only present on ",[195,740,207],{}," and ",[195,743,297],{}," events and contains only the fields that changed.",[162,746,747,748,750,751,750,753,750,755,750,757,759,760,750,763,765,766,419],{},"Sprint events carry the sprint payload. Lifecycle events (",[195,749,307],{},", ",[195,752,317],{},[195,754,327],{},[195,756,337],{},[195,758,347],{},") include the full sprint object reflecting its state at the time the event fired, plus ",[195,761,762],{},"sprintId",[195,764,527],{},", and ",[195,767,719],{},[421,769,771],{"className":423,"code":770,"language":425,"meta":426,"style":426},"{\n  \"event\": \"sprint.completed\",\n  \"timestamp\": \"2026-03-22T12:00:00.000Z\",\n  \"sprintId\": 42,\n  \"goalId\": 774,\n  \"initiatorId\": 1,\n  \"sprint\": {\n    \"id\": 42,\n    \"goalId\": 774,\n    \"name\": \"Sprint 7\",\n    \"status\": \"completed\",\n    \"startDate\": \"2026-03-08\",\n    \"endDate\": \"2026-03-22\",\n    \"goalAchieved\": true,\n    \"completedAt\": \"2026-03-22T12:00:00.000Z\"\n  }\n}\n",[195,772,773,777,795,813,828,842,857,870,884,898,918,938,958,978,992,1010,1015],{"__ignoreMap":426},[430,774,775],{"class":432,"line":433},[430,776,437],{"class":436},[430,778,779,781,783,785,787,789,791,793],{"class":432,"line":440},[430,780,443],{"class":436},[430,782,447],{"class":446},[430,784,450],{"class":436},[430,786,419],{"class":436},[430,788,455],{"class":436},[430,790,327],{"class":458},[430,792,450],{"class":436},[430,794,463],{"class":436},[430,796,797,799,801,803,805,807,809,811],{"class":432,"line":466},[430,798,443],{"class":436},[430,800,471],{"class":446},[430,802,450],{"class":436},[430,804,419],{"class":436},[430,806,455],{"class":436},[430,808,480],{"class":458},[430,810,450],{"class":436},[430,812,463],{"class":436},[430,814,815,817,819,821,823,826],{"class":432,"line":487},[430,816,443],{"class":436},[430,818,762],{"class":446},[430,820,450],{"class":436},[430,822,419],{"class":436},[430,824,825],{"class":516}," 42",[430,827,463],{"class":436},[430,829,830,832,834,836,838,840],{"class":432,"line":502},[430,831,443],{"class":436},[430,833,527],{"class":446},[430,835,450],{"class":436},[430,837,419],{"class":436},[430,839,534],{"class":516},[430,841,463],{"class":436},[430,843,844,846,848,850,852,855],{"class":432,"line":522},[430,845,443],{"class":436},[430,847,719],{"class":446},[430,849,450],{"class":436},[430,851,419],{"class":436},[430,853,854],{"class":516}," 1",[430,856,463],{"class":436},[430,858,859,861,864,866,868],{"class":432,"line":539},[430,860,443],{"class":436},[430,862,863],{"class":446},"sprint",[430,865,450],{"class":436},[430,867,419],{"class":436},[430,869,499],{"class":436},[430,871,872,874,876,878,880,882],{"class":432,"line":560},[430,873,505],{"class":436},[430,875,509],{"class":508},[430,877,450],{"class":436},[430,879,419],{"class":436},[430,881,825],{"class":516},[430,883,463],{"class":436},[430,885,886,888,890,892,894,896],{"class":432,"line":575},[430,887,505],{"class":436},[430,889,527],{"class":508},[430,891,450],{"class":436},[430,893,419],{"class":436},[430,895,534],{"class":516},[430,897,463],{"class":436},[430,899,900,902,905,907,909,911,914,916],{"class":432,"line":592},[430,901,505],{"class":436},[430,903,904],{"class":508},"name",[430,906,450],{"class":436},[430,908,419],{"class":436},[430,910,455],{"class":436},[430,912,913],{"class":458},"Sprint 7",[430,915,450],{"class":436},[430,917,463],{"class":436},[430,919,920,922,925,927,929,931,934,936],{"class":432,"line":609},[430,921,505],{"class":436},[430,923,924],{"class":508},"status",[430,926,450],{"class":436},[430,928,419],{"class":436},[430,930,455],{"class":436},[430,932,933],{"class":458},"completed",[430,935,450],{"class":436},[430,937,463],{"class":436},[430,939,940,942,945,947,949,951,954,956],{"class":432,"line":636},[430,941,505],{"class":436},[430,943,944],{"class":508},"startDate",[430,946,450],{"class":436},[430,948,419],{"class":436},[430,950,455],{"class":436},[430,952,953],{"class":458},"2026-03-08",[430,955,450],{"class":436},[430,957,463],{"class":436},[430,959,960,962,965,967,969,971,974,976],{"class":432,"line":660},[430,961,505],{"class":436},[430,963,964],{"class":508},"endDate",[430,966,450],{"class":436},[430,968,419],{"class":436},[430,970,455],{"class":436},[430,972,973],{"class":458},"2026-03-22",[430,975,450],{"class":436},[430,977,463],{"class":436},[430,979,980,982,985,987,989],{"class":432,"line":675},[430,981,505],{"class":436},[430,983,984],{"class":508},"goalAchieved",[430,986,450],{"class":436},[430,988,419],{"class":436},[430,990,991],{"class":436}," true,\n",[430,993,994,996,999,1001,1003,1005,1007],{"class":432,"line":681},[430,995,505],{"class":436},[430,997,998],{"class":508},"completedAt",[430,1000,450],{"class":436},[430,1002,419],{"class":436},[430,1004,455],{"class":436},[430,1006,480],{"class":458},[430,1008,1009],{"class":436},"\"\n",[430,1011,1012],{"class":432,"line":695},[430,1013,1014],{"class":436},"  }\n",[430,1016,1017],{"class":432,"line":709},[430,1018,732],{"class":436},[162,1020,1021,1023,1024,750,1027,1029,1030,1033,1034,750,1037,765,1039,1041,1042,1023,1044,750,1046,765,1048,1050,1051,1053,1054,1056],{},[195,1022,367],{}," carries ",[195,1025,1026],{},"taskId",[195,1028,762],{}," (the new sprint, or ",[195,1031,1032],{},"null"," when removed), ",[195,1035,1036],{},"prevSprintId",[195,1038,527],{},[195,1040,719],{},". ",[195,1043,357],{},[195,1045,762],{},[195,1047,527],{},[195,1049,719],{}," only, since the sprint no longer exists. ",[195,1052,719],{}," may be ",[195,1055,1032],{}," for sprints generated automatically by a cadence.",[166,1058,1060],{"id":1059},"signature-verification","Signature verification",[162,1062,1063,1064,1067],{},"Every request includes an ",[195,1065,1066],{},"X-Webhook-Signature"," header with an HMAC-SHA256 signature of the request body:",[421,1069,1074],{"className":1070,"code":1072,"language":1073},[1071],"language-text","X-Webhook-Signature: sha256=5d41402abc4b2a76b9719d911017c592...\n","text",[195,1075,1072],{"__ignoreMap":426},[162,1077,1078],{},"Always verify the signature before processing the payload. Example in Node.js:",[421,1080,1084],{"className":1081,"code":1082,"language":1083,"meta":426,"style":426},"language-javascript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const crypto = require('crypto')\n\nfunction verifySignature(body, signature, secret) {\n  const expected = 'sha256=' + crypto\n    .createHmac('sha256', secret)\n    .update(body)\n    .digest('hex')\n  return signature === expected\n}\n\n// In your HTTP handler:\nconst body = req.body // raw string, not parsed JSON\nconst signature = req.headers['x-webhook-signature']\nconst isValid = verifySignature(body, signature, YOUR_SECRET)\n","javascript",[195,1085,1086,1091,1097,1102,1107,1112,1117,1122,1127,1131,1135,1140,1145,1150],{"__ignoreMap":426},[430,1087,1088],{"class":432,"line":433},[430,1089,1090],{},"const crypto = require('crypto')\n",[430,1092,1093],{"class":432,"line":440},[430,1094,1096],{"emptyLinePlaceholder":1095},true,"\n",[430,1098,1099],{"class":432,"line":466},[430,1100,1101],{},"function verifySignature(body, signature, secret) {\n",[430,1103,1104],{"class":432,"line":487},[430,1105,1106],{},"  const expected = 'sha256=' + crypto\n",[430,1108,1109],{"class":432,"line":502},[430,1110,1111],{},"    .createHmac('sha256', secret)\n",[430,1113,1114],{"class":432,"line":522},[430,1115,1116],{},"    .update(body)\n",[430,1118,1119],{"class":432,"line":539},[430,1120,1121],{},"    .digest('hex')\n",[430,1123,1124],{"class":432,"line":560},[430,1125,1126],{},"  return signature === expected\n",[430,1128,1129],{"class":432,"line":575},[430,1130,732],{},[430,1132,1133],{"class":432,"line":592},[430,1134,1096],{"emptyLinePlaceholder":1095},[430,1136,1137],{"class":432,"line":609},[430,1138,1139],{},"// In your HTTP handler:\n",[430,1141,1142],{"class":432,"line":636},[430,1143,1144],{},"const body = req.body // raw string, not parsed JSON\n",[430,1146,1147],{"class":432,"line":660},[430,1148,1149],{},"const signature = req.headers['x-webhook-signature']\n",[430,1151,1152],{"class":432,"line":675},[430,1153,1154],{},"const isValid = verifySignature(body, signature, YOUR_SECRET)\n",[1156,1157,1160],"callout",{"color":1158,"icon":1159},"warning","i-lucide-shield-alert",[162,1161,1162],{},"Never process webhook payloads without verifying the signature. Without verification, anyone who knows your webhook URL can send fake events.",[166,1164,1166],{"id":1165},"retries","Retries",[162,1168,1169],{},"If your server responds with a non-2xx status code or doesn't respond within 10 seconds, TaskView retries the delivery:",[171,1171,1172,1182],{},[174,1173,1174],{},[177,1175,1176,1179],{},[180,1177,1178],{},"Attempt",[180,1180,1181],{},"Delay",[187,1183,1184,1192],{},[177,1185,1186,1189],{},[192,1187,1188],{},"1st retry",[192,1190,1191],{},"~10 seconds",[177,1193,1194,1197],{},[192,1195,1196],{},"2nd retry",[192,1198,1199],{},"~20 seconds",[162,1201,1202,1203,1206],{},"After 3 total attempts (1 original + 2 retries), the delivery is marked as ",[386,1204,1205],{},"failed",".",[166,1208,1210],{"id":1209},"auto-deactivation","Auto-deactivation",[162,1212,1213,1214,1217],{},"If a webhook accumulates ",[386,1215,1216],{},"10 consecutive failed deliveries"," (after all retries are exhausted), it is automatically deactivated. A single successful delivery resets the failure counter.",[162,1219,1220],{},"To reactivate a webhook, toggle it back on from the webhooks page. The failure counter is not reset automatically - the next successful delivery will reset it.",[166,1222,1224],{"id":1223},"delivery-history","Delivery history",[162,1226,1227],{},"The webhooks page shows delivery history for each webhook:",[1229,1230,1231,1236,1242,1248,1254],"ul",{},[379,1232,1233,1235],{},[386,1234,182],{}," - which event was delivered",[379,1237,1238,1241],{},[386,1239,1240],{},"Status"," - success, failed, or pending",[379,1243,1244,1247],{},[386,1245,1246],{},"HTTP code"," - response status code from your server",[379,1249,1250,1253],{},[386,1251,1252],{},"Attempts"," - how many attempts were made",[379,1255,1256,1259],{},[386,1257,1258],{},"Payload"," - click to view the full JSON payload",[162,1261,1262],{},"Failed deliveries can be retried manually from the delivery history.",[166,1264,1266],{"id":1265},"managing-webhooks","Managing webhooks",[162,1268,1269],{},"From the webhooks page you can:",[1229,1271,1272,1278,1284,1290,1296,1302],{},[379,1273,1274,1277],{},[386,1275,1276],{},"Toggle"," webhooks on/off",[379,1279,1280,1283],{},[386,1281,1282],{},"Edit"," the URL and subscribed events",[379,1285,1286,1289],{},[386,1287,1288],{},"Test"," - sends a test payload to verify connectivity",[379,1291,1292,1295],{},[386,1293,1294],{},"Rotate secret"," - generates a new secret (the old one stops working immediately)",[379,1297,1298,1301],{},[386,1299,1300],{},"Delete"," - removes the webhook and all delivery history",[379,1303,1304,1307],{},[386,1305,1306],{},"View deliveries"," - see delivery history with status filter",[166,1309,1311],{"id":1310},"secret-rotation","Secret rotation",[162,1313,1314],{},"If your secret is compromised, rotate it:",[376,1316,1317,1320,1323,1326],{},[379,1318,1319],{},"Click the key icon on the webhook",[379,1321,1322],{},"Confirm that you want to rotate",[379,1324,1325],{},"Copy the new secret",[379,1327,1328],{},"Update the secret in your receiving application",[162,1330,1331],{},"The old secret stops working immediately. Any in-flight deliveries signed with the old secret will fail signature verification on your end.",[166,1333,1335],{"id":1334},"testing-locally","Testing locally",[162,1337,1338],{},"You can use a simple Node.js script to test webhook deliveries:",[421,1340,1342],{"className":1081,"code":1341,"language":1083,"meta":426,"style":426},"const http = require('http')\nconst crypto = require('crypto')\n\nconst PORT = 4545\nconst SECRET = 'your-secret-here'\n\nconst server = http.createServer((req, res) => {\n  const chunks = []\n  req.on('data', (chunk) => chunks.push(chunk))\n  req.on('end', () => {\n    const body = Buffer.concat(chunks).toString()\n    const signature = req.headers['x-webhook-signature'] || ''\n    const expected = 'sha256=' + crypto\n      .createHmac('sha256', SECRET)\n      .update(body)\n      .digest('hex')\n\n    console.log(signature === expected ? 'Valid' : 'INVALID')\n    console.log(JSON.stringify(JSON.parse(body), null, 2))\n\n    res.writeHead(200)\n    res.end('OK')\n  })\n})\n\nserver.listen(PORT, () => console.log(`Listening on :${PORT}`))\n",[195,1343,1344,1349,1353,1357,1362,1367,1371,1376,1381,1386,1391,1396,1401,1406,1411,1416,1421,1425,1430,1435,1440,1446,1452,1458,1464,1469],{"__ignoreMap":426},[430,1345,1346],{"class":432,"line":433},[430,1347,1348],{},"const http = require('http')\n",[430,1350,1351],{"class":432,"line":440},[430,1352,1090],{},[430,1354,1355],{"class":432,"line":466},[430,1356,1096],{"emptyLinePlaceholder":1095},[430,1358,1359],{"class":432,"line":487},[430,1360,1361],{},"const PORT = 4545\n",[430,1363,1364],{"class":432,"line":502},[430,1365,1366],{},"const SECRET = 'your-secret-here'\n",[430,1368,1369],{"class":432,"line":522},[430,1370,1096],{"emptyLinePlaceholder":1095},[430,1372,1373],{"class":432,"line":539},[430,1374,1375],{},"const server = http.createServer((req, res) => {\n",[430,1377,1378],{"class":432,"line":560},[430,1379,1380],{},"  const chunks = []\n",[430,1382,1383],{"class":432,"line":575},[430,1384,1385],{},"  req.on('data', (chunk) => chunks.push(chunk))\n",[430,1387,1388],{"class":432,"line":592},[430,1389,1390],{},"  req.on('end', () => {\n",[430,1392,1393],{"class":432,"line":609},[430,1394,1395],{},"    const body = Buffer.concat(chunks).toString()\n",[430,1397,1398],{"class":432,"line":636},[430,1399,1400],{},"    const signature = req.headers['x-webhook-signature'] || ''\n",[430,1402,1403],{"class":432,"line":660},[430,1404,1405],{},"    const expected = 'sha256=' + crypto\n",[430,1407,1408],{"class":432,"line":675},[430,1409,1410],{},"      .createHmac('sha256', SECRET)\n",[430,1412,1413],{"class":432,"line":681},[430,1414,1415],{},"      .update(body)\n",[430,1417,1418],{"class":432,"line":695},[430,1419,1420],{},"      .digest('hex')\n",[430,1422,1423],{"class":432,"line":709},[430,1424,1096],{"emptyLinePlaceholder":1095},[430,1426,1427],{"class":432,"line":714},[430,1428,1429],{},"    console.log(signature === expected ? 'Valid' : 'INVALID')\n",[430,1431,1432],{"class":432,"line":729},[430,1433,1434],{},"    console.log(JSON.stringify(JSON.parse(body), null, 2))\n",[430,1436,1438],{"class":432,"line":1437},20,[430,1439,1096],{"emptyLinePlaceholder":1095},[430,1441,1443],{"class":432,"line":1442},21,[430,1444,1445],{},"    res.writeHead(200)\n",[430,1447,1449],{"class":432,"line":1448},22,[430,1450,1451],{},"    res.end('OK')\n",[430,1453,1455],{"class":432,"line":1454},23,[430,1456,1457],{},"  })\n",[430,1459,1461],{"class":432,"line":1460},24,[430,1462,1463],{},"})\n",[430,1465,1467],{"class":432,"line":1466},25,[430,1468,1096],{"emptyLinePlaceholder":1095},[430,1470,1472],{"class":432,"line":1471},26,[430,1473,1474],{},"server.listen(PORT, () => console.log(`Listening on :${PORT}`))\n",[162,1476,1477,1478,1481,1482,1206],{},"Run with ",[195,1479,1480],{},"node webhook-receiver.js"," and set the webhook URL to ",[195,1483,1484],{},"http://localhost:4545",[1486,1487,1488],"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 .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":426,"searchDepth":440,"depth":440,"links":1490},[1491,1492,1493,1494,1495,1496,1497,1498,1499,1500],{"id":168,"depth":440,"text":169},{"id":373,"depth":440,"text":374},{"id":411,"depth":440,"text":412},{"id":1059,"depth":440,"text":1060},{"id":1165,"depth":440,"text":1166},{"id":1209,"depth":440,"text":1210},{"id":1223,"depth":440,"text":1224},{"id":1265,"depth":440,"text":1266},{"id":1310,"depth":440,"text":1311},{"id":1334,"depth":440,"text":1335},"Configure webhooks in TaskView to receive real-time HTTP notifications when tasks are created, updated, deleted, or reassigned. Includes HMAC-SHA256 signature verification, automatic retries, and delivery history.","md",{},{"icon":72},{"title":69,"description":1501},"jdZ2A5Bm2GkueXFPXwFbSPM3TItd2YZwj6EJLb2tnD4",[1508,1510],{"title":64,"path":65,"stem":66,"description":1509,"icon":67,"children":-1},"Real-time and push notifications in TaskView - deadline alerts, assignment notifications, per-user preferences, and multi-channel delivery via WebSocket and Firebase Cloud Messaging.",{"title":74,"path":75,"stem":76,"description":1511,"icon":77,"children":-1},"Create API tokens for programmatic access to TaskView. Tokens support permission scoping, project-level restrictions, and optional expiration.",1783289464129]