ntfy and webhook
ntfy
coachd sends an ntfy notice when a workout is ready to review (“Workout for Tue 28 Oct ready to review”), with a Review action that opens it on the page. The notice carries only the date, never workout or health data. In built-in mode it also tells you when an app connects, or is revoked for reusing a token.
Create a topic with default access
deny-alland an access token for coachd, and subscribe your phone.Set the topic URL and store the token:
.envCOACHD_NTFY_URL=https://ntfy.sh/<secret-topic>put_secret ntfy_token 65532:65532 # the helper from Secrets docker compose up -d --force-recreate coachd
ntfy needs the public lane and COACHD_PUBLIC_URL. coachd reads the token only at start, so recreate it after changing the token.
Post-run webhook
With COACHD_WEBHOOK_URL set, coachd POSTs an event to your own automation when a recent activity (started in the last 48 hours) is stored, with its FIT file and per-step numbers in. It carries IDs, a date and a sport only:
{"event":"activity.stored","activity_id":24745625144,"date":"2026-10-26","sport":"running","proposal_id":"cc7b5c9c..."}proposal_id is the delivered workout the activity carried out, or null. Point it at n8n, Home Assistant or anything that can start your agent: the agent then calls get_activity with detail laps to review the run, and can send_message to the watch.
- Retries 5xx and network errors twice (after 5 s and 25 s); gives up on other 4xx.
- It doesn’t make coachd poll Garmin more often: the activity arrives on the next hourly sync, or sooner when the agent calls
request_sync. - Treat the URL as a secret (an unguessable path). Plain
http://works for a LAN receiver, with a warning in the log.
For a bearer token as well, store it with put_secret webhook_token 65532:65532 and mount it with a compose override:
services:
coachd:
environment:
COACHD_WEBHOOK_TOKEN_FILE: /run/secrets/webhook_token
secrets: [webhook_token]
secrets:
webhook_token:
file: ./secrets/webhook_token