msgraph_webhook gateway platform is an inbound event listener. It’s how Mibyan receives change notifications from Microsoft Graph — “a Teams meeting ended,” “a new message landed in this chat,” “this calendar event was updated.” Different from the teams platform (which is a chat bot users type to) — this one is M365 telling Mibyan something happened, not a person.
Right now the primary consumer is the Teams meeting summary pipeline: Graph notifies when a meeting produces a transcript, the pipeline fetches it, and Mibyan posts a summary back into Teams. Other Graph resources (/chats/.../messages, /users/.../events) use the same listener — the pipeline consumers land with their own PRs.
Prerequisites
- Microsoft Graph application credentials — Register a Microsoft Graph Application
- A public HTTPS URL that Microsoft Graph can reach (Graph does not call private endpoints). A dev tunnel works for testing; production needs a real domain with a valid certificate.
- A strong shared secret to use as the
clientStatevalue. Generate withopenssl rand -hex 32and put it in~/.mibyan/.envasMSGRAPH_WEBHOOK_CLIENT_STATE.
Quick Start
Minimum~/.mibyan/config.yaml:
~/.mibyan/.env (auto-merged on startup):
extra.host in config.yaml (see the example above); there is no MSGRAPH_WEBHOOK_HOST env-var override.
Start the gateway: mibyan gateway run. The listener exposes:
POST /msgraph/webhook— change notifications from GraphGET /msgraph/webhook?validationToken=...— Graph subscription validation handshakeGET /health— readiness probe with accepted/duplicate counters
/msgraph/webhook:
Configuration
All settings go underplatforms.msgraph_webhook.extra:
Most settings also have an equivalent env var (
MSGRAPH_WEBHOOK_*) that merges into the config at gateway startup (the exception is host, which is config-only — see the note above) — see the environment variables reference.
Security Hardening
clientState is the primary auth check
Every Graph notification includes theclientState string your subscription registered with. The listener rejects any notification whose clientState doesn’t match, using timing-safe comparison. This is Microsoft’s documented mechanism — treat the value as a strong shared secret.
If client_state is unset, the listener refuses to start.
Source-IP allowlisting (production deployments)
For production, restrict the listener to Microsoft’s published Graph webhook source IP ranges. Microsoft documents the egress ranges under the Office 365 IP Address and URL Web service. Configure them as:0.0.0.0, ::, or a LAN IP without allowed_source_cidrs is refused at startup. If you’re using a dev tunnel or reverse proxy on the same machine, bind Mibyan to 127.0.0.1 or ::1 and leave the allowlist empty there. Invalid CIDR strings log a warning and are ignored. Review the Microsoft IP list quarterly — it changes.
HTTPS termination
The listener speaks plain HTTP. Terminate TLS at your reverse proxy (Caddy, Nginx, Cloudflare Tunnel, AWS ALB) and proxy to the listener over the local network. Graph refuses to deliver to non-HTTPS endpoints, so there’s no path for unencrypted traffic to reach you from Graph itself.Response hygiene
On success the listener returns202 Accepted with an empty body — internal counters stay out of the wire response. Operators can observe counts via /health, which is guarded by the same source-IP rules as the webhook path.
Status code table:
Troubleshooting
Related Docs
- Register a Microsoft Graph Application — Azure app registration prereq
- Environment Variables → Microsoft Graph — full env var list
- Microsoft Teams bot setup — the different platform that lets users chat with Mibyan in Teams

