Meetily

Post meeting summaries to Slack

When a summary finishes, verify the webhook signature, fetch the summary, and post it to Slack.

This sends meeting content off your device

Meetily is privacy-first and local by default. This automation reads summary text and sends it to a third party (Slack). You are the data controller for that transfer: get the participants' consent, scope the token to read only, and treat the destination as untrusted. Never hard-code a token in a script you share; read it from the environment or the loopback token file.

This recipe posts a meeting summary to Slack the moment it's ready. It subscribes to the summary.completed webhook event, verifies the HMAC signature, fetches the summary with a scoped token, and posts to a Slack incoming webhook.

First, turn on the Automation API and webhook delivery under Settings > Integrations - webhook delivery is off by default, and the webhook routes are gated on it (see Enable & connect).

1. Register the webhook

Register on summary.completed. Webhooks can't be delivered to a loopback or private address unless you've explicitly added that host:port to Local webhook targets, so for Slack run a tunnel (ngrok, cloudflared) in front of your receiver and register the tunnel's public HTTPS URL. See Webhooks & SSE for the full registration, SSRF, and HMAC details.

curl -s -X POST http://127.0.0.1:8420/v1/webhooks \
  -H "Authorization: Bearer $MEETILY_PRO_TOKEN" \
  -H "content-type: application/json" \
  -d '{"url":"https://YOUR-TUNNEL.example/hook","events":["summary.completed"]}'

The 201 response carries an hmac_secret shown only once - store it immediately. There's no endpoint to reveal it again.

A new webhook is `pending` and delivers nothing until you approve it

Registering a destination does not start delivery. It lands in approval_state: pending and sends nothing - silently, with no failed-delivery log entry - until you approve it under Settings > Integrations > Destinations, which moves it to allowed. If your webhook seems dead, check its approval_state with GET /v1/webhooks/{id} before debugging your receiver.

2. Receive, verify, fetch, post

Standard-library Python only - no SDK. Verify the signature over {timestamp}.{body}, then fetch the summary and post it:

import hmac, hashlib, json, urllib.request

HMAC_SECRET = "whsec_..."                      # returned once at registration
SLACK_WEBHOOK = "https://hooks.slack.com/services/XXX"
MEETILY = "http://127.0.0.1:8420"
TOKEN = "..."                                  # a read-scoped token, from env in real code

def verify(secret, timestamp, raw_body, header_sig):
    mac = hmac.new(secret.encode(), f"{timestamp}.{raw_body}".encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(f"sha256={mac}", header_sig)

def handle(headers, raw_body):
    ts = headers["X-Meetily-Timestamp"]
    sig = headers["X-Meetily-Signature"]
    if not verify(HMAC_SECRET, ts, raw_body, sig):
        return                                 # reject unsigned or tampered deliveries

    event = json.loads(raw_body)
    if event["event"] != "summary.completed":
        return
    meeting_id = event["resource"]["id"]       # the resource id for this event is the meeting id

    # Fetch the actual content with your own token - the webhook payload never carries it.
    req = urllib.request.Request(
        f"{MEETILY}/v1/meetings/{meeting_id}/summary",
        headers={"Authorization": f"Bearer {TOKEN}"},
    )
    summary = json.loads(urllib.request.urlopen(req).read())
    text = json.dumps(summary.get("result", summary))[:3000]

    urllib.request.urlopen(urllib.request.Request(
        SLACK_WEBHOOK,
        data=json.dumps({"text": f"Summary for {meeting_id}:\n{text}"}).encode(),
        headers={"content-type": "application/json"},
    ))

Make your handler idempotent, keyed on event_id

Delivery is at-least-once by default, so the same event can arrive more than once. Treat a repeat event_id as a no-op, not a second post. Always compare the signature with hmac.compare_digest, never ==.

Troubleshooting

  • Nothing arrives. Check approval_state (must be allowed) and that webhook delivery is enabled in Settings. Both fail silently.
  • Inspect attempts. GET /v1/webhooks/{id}/deliveries lists each attempt with a reason code.
  • Test end to end. POST /v1/webhooks/{id}/test sends a synthetic delivery so you can confirm your receiver and signature check work before a real summary fires.

The exact resource shape per event, the full 17-event catalogue, and delivery retry mechanics are in the Event catalogue and Webhooks & SSE.

Last updated on

On this page