Back up and batch-process meetings
Export every meeting to Markdown or JSON on a schedule, and run diarization across many meetings.
This reads and moves meeting content
Backing up reads full meeting content and writes it elsewhere (disk, Notion, Obsidian). You are the data controller for that transfer: get consent, scope the token to read for backups (diarization needs write), 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.
Two batch jobs: back up every meeting to files, and re-diarize a set of meetings. Turn on the Automation API under Settings > Integrations first (see Enable & connect).
Nightly backup
List meetings with pagination (limit clamps to 1..200; page with offset), and export each with GET /v1/meetings/{id}/export?format=json|md|txt. The CLI covers both, so a backup needs no code:
#!/usr/bin/env bash
set -euo pipefail
export MEETILY_PRO_TOKEN="..." # a read-scoped token
mkdir -p meetily-backup
offset=0
while :; do
ids=$(meetily-pro meetings list --limit 200 --offset "$offset" --json | jq -r '.meetings[].id')
[ -z "$ids" ] && break
for id in $ids; do
meetily-pro meetings export "$id" --format md > "meetily-backup/$id.md"
done
offset=$(( offset + $(printf '%s\n' "$ids" | grep -c .) ))
doneThe same over the HTTP API with standard-library Python:
import json, pathlib, urllib.request
MEETILY, TOKEN = "http://127.0.0.1:8420", "..."
out = pathlib.Path("meetily-backup"); out.mkdir(exist_ok=True)
def api(path):
req = urllib.request.Request(f"{MEETILY}{path}", headers={"Authorization": f"Bearer {TOKEN}"})
return urllib.request.urlopen(req).read()
offset = 0
while True:
page = json.loads(api(f"/v1/meetings?limit=200&offset={offset}"))
meetings = page.get("meetings", [])
if not meetings:
break
for m in meetings:
(out / f"{m['id']}.md").write_bytes(api(f"/v1/meetings/{m['id']}/export?format=md"))
offset += len(meetings)MeetingListResponse also carries total, so you can show progress against it.
Batch re-diarize
Submit a diarization job per meeting and wait for each. A rerun that conflicts with one already running returns 409, so handle that. Diarization needs a write token.
for mid in MEETING_A MEETING_B; do
job=$(meetily-pro jobs diarization --meeting-id "$mid" --json 2>/dev/null) || { echo "skip $mid (409 or error)"; continue; }
jid=$(printf '%s' "$job" | jq -r '.job_id')
# No CLI wait stream - poll the SSE wait endpoint with curl:
curl -N -s "http://127.0.0.1:8420/v1/jobs/$jid/wait?timeout=1800" \
-H "Authorization: Bearer $MEETILY_PRO_TOKEN" >/dev/null
echo "done $mid"
doneOmit --speaker-count for auto mode. A job's terminal states are succeeded, failed, or cancelled - there is no completed state.
Detecting new meetings
You can't subscribe to meeting.* events
meeting.created, meeting.updated, and meeting.deleted are not subscribable webhook events - registering for one is rejected at registration with 400 (there's no emitter behind them). To pick up new meetings for a backup job, poll GET /v1/meetings and diff against what you've already saved, rather than waiting for an event. To react to a summary being ready, subscribe to summary.completed instead - see Post summaries to Slack.
See Meetily APIs for the full meetings, export, and jobs route surface.
Last updated on
