Skip to content

Operate the Teams Meeting Pipeline

Use this guide after you have already enabled the feature from Teams Meetings.

This page covers:

  • operator CLI flows
  • routine subscription maintenance
  • failure triage
  • go-live checks
  • rollout worksheet
Окно терминала
hermes teams-pipeline validate

Use this first after any config change.

Окно терминала
hermes teams-pipeline token-health
hermes teams-pipeline token-health --force-refresh

Use --force-refresh when you suspect stale auth state.

Окно терминала
hermes teams-pipeline subscriptions
Окно терминала
hermes teams-pipeline maintain-subscriptions
hermes teams-pipeline maintain-subscriptions --dry-run

Automating subscription renewal (REQUIRED for production)

Section titled “Automating subscription renewal (REQUIRED for production)”

Microsoft Graph subscriptions expire in at most 72 hours. If nothing renews them, meeting notifications silently stop after 3 days and the pipeline looks “broken.” This is the #1 operational failure mode for any Graph-backed integration.

You MUST run maintain-subscriptions on a schedule. Pick one of these three options:

Section titled “Option 1: Hermes cron (recommended if you already run the Hermes gateway)”

Hermes ships a built-in cron scheduler. The --no-agent mode runs a script as the job (rather than using an LLM), and --script must point at a file under ~/.hermes/scripts/. First create the script:

mkdir -p ~/.hermes/scripts
cat > ~/.hermes/scripts/maintain-teams-subscriptions.sh <<'EOF'
#!/usr/bin/env bash
exec hermes teams-pipeline maintain-subscriptions
EOF
chmod +x ~/.hermes/scripts/maintain-teams-subscriptions.sh

Then register a script-only cron job that runs every 12 hours (gives 6x headroom against the 72h expiry window):

Окно терминала
hermes cron create "0 */12 * * *" \
--name "teams-pipeline-maintain-subscriptions" \
--no-agent \
--script maintain-teams-subscriptions.sh \
--deliver local

Verify it was registered and inspect the next run time:

Окно терминала
hermes cron list
hermes cron status # scheduler status
Section titled “Option 2: systemd timer (recommended for Linux production deployments)”

Create /etc/systemd/system/hermes-teams-pipeline-maintain.service:

[Unit]
Description=Hermes Teams pipeline subscription maintenance
After=network-online.target
[Service]
Type=oneshot
User=hermes
EnvironmentFile=/etc/hermes/env
ExecStart=/usr/local/bin/hermes teams-pipeline maintain-subscriptions

And /etc/systemd/system/hermes-teams-pipeline-maintain.timer:

[Unit]
Description=Run Hermes Teams pipeline subscription maintenance every 12 hours
[Timer]
OnBootSec=5min
OnUnitActiveSec=12h
Persistent=true
[Install]
WantedBy=timers.target

Enable:

Окно терминала
sudo systemctl daemon-reload
sudo systemctl enable --now hermes-teams-pipeline-maintain.timer
systemctl list-timers hermes-teams-pipeline-maintain.timer
0 */12 * * * /usr/local/bin/hermes teams-pipeline maintain-subscriptions >> /var/log/hermes/teams-pipeline-maintain.log 2>&1

Make sure the cron environment has the MSGRAPH_* credentials. Simplest fix: source ~/.hermes/.env at the top of a wrapper script that crontab calls.

After you’ve set up the schedule, check renewal activity after the first scheduled run:

Окно терминала
hermes teams-pipeline subscriptions # should show expirationDateTime advanced
hermes teams-pipeline maintain-subscriptions --dry-run # should show "0 expiring soon" most of the time

If you ever see your Graph webhook mysteriously “stop working” after exactly ~72 hours, this is the first thing to check: did the renewal job actually run?

Окно терминала
hermes teams-pipeline list
hermes teams-pipeline list --status failed
hermes teams-pipeline show <job-id>
Окно терминала
hermes teams-pipeline run <job-id>
Окно терминала
hermes teams-pipeline fetch --meeting-id <meeting-id>
hermes teams-pipeline fetch --join-web-url "<join-url>"

Run these in order:

Окно терминала
hermes teams-pipeline validate
hermes teams-pipeline token-health --force-refresh
hermes teams-pipeline subscriptions

Then trigger or wait for a real meeting event and confirm:

Окно терминала
hermes teams-pipeline list
hermes teams-pipeline show <job-id>
  • run hermes teams-pipeline maintain-subscriptions --dry-run
  • inspect hermes teams-pipeline list --status failed
  • verify the Teams delivery target is still the correct chat or channel

Before changing webhook URLs or delivery targets

Section titled “Before changing webhook URLs or delivery targets”
  • update the public notification URL or Teams target config
  • run hermes teams-pipeline validate
  • renew or recreate affected subscriptions
  • confirm new events land in the expected sink

Check:

  • msgraph_webhook is enabled
  • the public notification URL points to /msgraph/webhook
  • the client state in the subscription matches MSGRAPH_WEBHOOK_CLIENT_STATE
  • subscriptions still exist remotely and are not expired

Jobs stay in retry or fail before summarization

Section titled “Jobs stay in retry or fail before summarization”

Check:

  • transcript permissions and availability
  • recording permissions and artifact availability
  • ffmpeg availability if recording fallback is enabled
  • Graph token health

Summaries are produced but not delivered to Teams

Section titled “Summaries are produced but not delivered to Teams”

Check:

  • platforms.teams.enabled: true
  • delivery_mode
  • incoming_webhook_url for webhook mode
  • chat_id or team_id plus channel_id for Graph mode
  • Teams auth config if Graph posting is used

Check:

  • whether you manually replayed a job with hermes teams-pipeline run
  • whether the sink record already exists for that meeting
  • whether you intentionally enabled a resend path in your local config
  • Graph credentials are present and correct
  • msgraph_webhook is enabled and reachable from the public internet
  • MSGRAPH_WEBHOOK_CLIENT_STATE is set and matches subscriptions
  • transcript subscription is created
  • recording subscription is created if STT fallback is required
  • ffmpeg is installed if recording fallback is enabled
  • Teams outbound delivery target is configured and verified
  • Notion and Linear sinks are configured only if actually needed
  • hermes teams-pipeline validate returns an OK snapshot
  • hermes teams-pipeline token-health --force-refresh succeeds
  • maintain-subscriptions is scheduled (Hermes cron, systemd timer, or crontab — see Automating subscription renewal). Without this, Graph subscriptions silently expire within 72 hours.
  • a real end-to-end meeting event has produced a stored job
  • at least one summary has reached the intended delivery sink
ModeUse whenTradeoff
incoming_webhookyou only need simple posting into Teamssimplest setup, less control
graphyou need channel or chat posting through Graphmore control, more auth and target config

Fill this out before rollout:

ItemValue
Public notification URL
Graph tenant ID
Graph client ID
Webhook client state
Transcript resource subscription
Recording resource subscription
Teams delivery mode
Teams chat ID or team/channel
Notion database ID
Linear team ID
Store path override, if any
Owner for daily checks

Use this before changing the deployment:

QuestionAnswer
Are we changing the public webhook URL?
Are we rotating Graph credentials?
Are we changing Teams delivery mode?
Are we moving to a new Teams chat or channel?
Do subscriptions need to be recreated or renewed?
Do we need a fresh end-to-end verification run?