Microsoft Teams alerts for blocked and finished tasks
By Jake Marsh, who builds Taskhold. Updated 2026-10-26. Facts about other tools checked 2026-10-26. 3 min read.
Teams retired Office 365 connector webhooks in 2026; channel webhooks now come from Workflows. This script follows Taskhold's event stream and posts an Adaptive Card to a channel through one of those when an agent needs a decision or finishes a task, with a link to the live board.
Teams of people running agents together: see several agents on one list. Slack users: Slack alerts.
What you get
One Microsoft Teams message when an agent blocks a task with a question, and one when it finishes a whole task (not each step). Each says which agent, the task title, the question or the summary, and links to the live board, where you reply. Your own changes never trigger a message.
Before you begin
- A Workflows webhook URL: in the channel, open ... > Workflows, choose the template "Send webhook alerts to a channel", finish the steps and copy the URL.
- A Taskhold API key from Settings > API keys, named after this script so you can revoke it on its own.
- bash, curl and jq on the machine that runs the script.
The script
Save this as taskhold-teams.sh and make it executable (chmod +x). The top half is the Microsoft Teams part; the loop below it is the same for every notifier.
#!/bin/bash
# taskhold-teams.sh: post to a Teams channel when an agent blocks or finishes a Taskhold task.
# Needs curl and jq. Run: TASKHOLD_API_KEY=atd_sk_... ./taskhold-teams.sh
key=${TASKHOLD_API_KEY:?set TASKHOLD_API_KEY}
board=https://taskhold.com/dashboard/live
last=""
hook=${TEAMS_WORKFLOW_URL:?set TEAMS_WORKFLOW_URL}
notify() { # kind, title, detail, agent
if [ "$1" = blocked ]; then h="$4 needs you"; else h="$4 finished a task"; fi
jq -n --arg h "$h" --arg t "$2" --arg d "${3:- }" --arg u "$board" '{type: "message", attachments: [{
contentType: "application/vnd.microsoft.card.adaptive",
content: {type: "AdaptiveCard", version: "1.2", body: [
{type: "TextBlock", text: $h, weight: "Bolder", wrap: true},
{type: "TextBlock", text: $t, wrap: true},
{type: "TextBlock", text: $d, wrap: true, isSubtle: true},
{type: "TextBlock", text: ("[Open the live board](" + $u + ")"), wrap: true}]}}]}' \
| curl -s -o /dev/null -H "Content-Type: application/json" -d @- "$hook"
}
while :; do
while IFS= read -r line; do
case "$line" in
"id: "*) last=${line#id: } ;;
"data: "*)
e=${line#data: }
# Agents only: a task (not a step) finished, or any task or step blocked.
jq -e '.type == "task.status" and .actor == "agent" and (.data.to == "blocked" or (.data.to == "done" and .task_id == .root_task_id))' <<<"$e" >/dev/null || continue
kind=$(jq -r .data.to <<<"$e")
title=$(jq -r '.task_title // "a task"' <<<"$e")
agent=$(jq -r '.actor_name // "An agent"' <<<"$e")
detail=$(jq -r 'if .data.to == "blocked" then .data.blocked_reason else .data.completed_summary end // ""' <<<"$e")
notify "$kind" "$title" "$detail" "$agent" ;;
esac
done < <(curl -sN https://taskhold.com/api/events/stream -H "Authorization: Bearer $key" ${last:+-H "Last-Event-ID: $last"})
sleep 3
doneRun it anywhere a process can stay up, such as a spare terminal, a tmux window or a small server:
TEAMS_WORKFLOW_URL='https://...' TASKHOLD_API_KEY=atd_sk_... ./taskhold-teams.shThe server ends each stream after 10 minutes and the script reconnects with the last event id, so events that happen in between arrive once, in order. Details on the stream: events and the live stream.
One limit to know
A workflow belongs to the person who created it. If they leave the organization or lose access, the webhook stops and nothing posts. Create it from a shared or service account if the channel matters.
See how far along your coding agents are.
$12/month or $120/year after the trial. Card up front. Cancel before day 7 and you pay nothing. Every agent you run is included.
Sources
- Microsoft Learn: create incoming webhooks with Workflows, checked 2026-10-26
- Microsoft 365 Developer Blog: retirement of Office 365 connectors, checked 2026-10-26
Related
Updated 2026-10-26. Something wrong or out of date? Email hello@greatwork.company.