Microsoft Teams notifications
Post TimeTracking events to Microsoft Teams channels through a signature-verifying relay and a Teams Workflows (Power Automate) webhook.
Last updated: · Markdown version
1. Summary
Status: Recipe (no Teams app).
Direction: TimeTracking to Teams (one-way notifications).
2. Mechanism
TimeTracking webhook → relay (verify signature, enrich, build Adaptive Card) → Teams Workflows webhook → channel post
Microsoft is retiring Office 365 connectors (classic "Incoming Webhook"). Use the Teams Workflows app template "Post to a channel when a webhook request is received", which gives an HTTPS trigger URL that accepts an Adaptive Card payload. Power Automate cannot verify HMAC signatures without code, so keep the verification in the relay (Azure Function, Cloudflare Worker, or an iPaaS code step).
Assumption — verify with your Microsoft 365 admin: Workflows availability and the exact accepted payload depend on tenant policy; the template expects {"type":"message","attachments":[{"contentType":"application/vnd.microsoft.card.adaptive","content":{…}}]}.
3. Prerequisites
- Teams channel and permission to add a workflow; the workflow trigger URL.
- An admin user (dashboard Regels & koppelingen) or an API key with scope
adminto create the webhook; a read key for enrichment.
4. Entity mapping
Same event-to-message mapping as Slack. Each event becomes an Adaptive Card with a title (event type), facts (person, client, date, minutes as h:mm) and a link to the dashboard (https://app.timetracking.tech/). Stateless; nothing stored.
5. Step-by-step setup
- In Teams: channel → Workflows → Post to a channel when a webhook request is received; copy the URL.
- As TimeTracking admin, create a webhook to your relay with the chosen events; save the secret.
- Relay core (reuse
verify()andtext()from the Slack relay) and post:
async function postToTeams(title, facts) {
const card = {
type: "message",
attachments: [{
contentType: "application/vnd.microsoft.card.adaptive",
content: {
$schema: "http://adaptivecards.io/schemas/adaptive-card.json", type: "AdaptiveCard", version: "1.4",
body: [
{ type: "TextBlock", text: title, weight: "Bolder", wrap: true },
{ type: "FactSet", facts: facts.map(([title, value]) => ({ title, value: String(value) })) },
],
actions: [{ type: "Action.OpenUrl", title: "Open TimeTracking", url: "https://app.timetracking.tech/" }],
},
}],
};
const r = await fetch(process.env.TEAMS_WORKFLOW_URL, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(card) });
if (!r.ok) throw new Error(`Teams ${r.status}`);
}
// example for conflict.created after enrichment:
// await postToTeams("Conflict between hour sources", [["Person", "Sanne Janssen"], ["Client", "Bakkerij Ruijters"],
// ["Date", "2026-12-01"], ["Candidates", "manual_office 8:00 · mail 7:30"]]);
6. Sync schedule and triggering events
Event-driven; up to ~5 minutes delivery delay. Suggested: conflict.created, export.completed, period.locked to an office channel; leave.requested to a planning channel.
7. Corrections, conflicts and locked periods
Informational only. Resolve conflicts in TimeTracking. No event fires on resolution.
8. Testing checklist
- A test event appears as a card in the channel.
- Wrong signature returns
401. - Relay responds within 10 s (Workflows calls can be slow: post asynchronously if needed).
9. Troubleshooting
| Problem | Fix |
|---|---|
Workflow returns 400 |
Payload must be the message + Adaptive Card attachment shape. |
Workflow returns 202 but nothing posted |
Check the flow run history in Power Automate; the flow owner needs channel access. |
| No TimeTracking delivery | Public HTTPS only, 2xx within 10 s, no redirects. |
10. Limits and known gaps
Same as Slack: 21 event types, ID-only payloads, no interactive approval.
Use POST /webhooks/{id}/test (admin) to send a webhook.test event, and POST /webhook-deliveries/{id}/replay to resend a failed one.
11. Build it with Claude
Build an Azure Function (Node 20, TypeScript) that receives TimeTracking webhooks, verifies tt-signature
(t=<unix>,v1=hex HMAC-SHA256(secret,"<t>.<raw body>"), 5 min tolerance), enriches names via
GET https://app.timetracking.tech/api/v1/people and /placements (Bearer key, { data }, snake_case), and posts an
Adaptive Card to a Teams Workflows webhook URL ({type:"message", attachments:[{contentType:
"application/vnd.microsoft.card.adaptive", content:{…}}]}). Support all 21 TimeTracking event types, route
them to different channels by config, escape user text, and include tests.