Artifacts¶
Everything about a meeting lives in one folder under [general] meetingsRoot:
~/Meetings/2026-08-18 1030 Weekly sync/
├── meeting.json metadata (schemaVersion 2)
├── recording.m4a track 0 mic (raw), track 1 system (raw); AAC 48 kHz mono each
├── transcript.json schemaVersion 1 - the public API
├── transcript.md rendered, chronological
├── pipeline.log per-run timings, model ids, parameters, warnings
├── summary.md written by a workflow; shown in the app when present
├── exports/ <slug>.md / .srt / …
└── workflows/ <name>-<yyyyMMdd-HHmmss>.log per run
Renaming a meeting renames the folder; meeting.json.id is the stable key. Plain files, no
database required - the SQLite index is a cache (rex reindex rebuilds it).
meeting.json (v2)¶
{
"schemaVersion": 2,
"id": "uuid",
"title": "Weekly sync",
"titleSource": "windowTitle | calendar | manual | default",
"startedAt": "2026-08-18T10:30:02-05:00",
"endedAt": "2026-08-18T11:01:40-05:00",
"durationSec": 1898.0,
"source": { "kind": "detected | manual | import", "app": "com.microsoft.teams2", "appName": "Microsoft Teams" },
"recording": { "file": "recording.m4a", "sampleRate": 48000,
"tracks": [ {"index":0,"role":"mic","device":"Elgato Wave:3"}, {"index":1,"role":"system","device":"MacBook Pro Speakers"} ],
"complete": true, "recovered": false },
"speakers": { "you": "Phill", "map": { "S1": "Alice", "S2": null } },
"transcript": { "file": "transcript.json", "status": "ready | pending | failed", "lastRun": "…", "model": "parakeet-tdt-0.6b-v2", "aec": true },
"attendance": { "source": "teamsSnapshot", "resolvedAt": "…", "iCalUid": "…", "exchangeId": "…",
"organizer": { "name": "Rosaria Bach", "email": "rbach@example.com" },
"invited": [ { "name": "Alice", "email": "alice@example.com" } ] },
"app": { "version": "0.1.0", "build": 1 }
}
attendance is optional (schemaVersion 2, additive): present only when Teams enrichment resolves
organizer + invited emails from the local Teams app. Readers that predate it ignore the field.
recording stays even after retention trashed the audio; the app checks whether the file exists.
transcript.json (v1)¶
{
"schemaVersion": 1,
"meetingId": "uuid",
"generatedAt": "…",
"engine": { "asr": "fluidaudio/parakeet-tdt-0.6b-v2", "vad": "fluidaudio/silero",
"diarization": "fluidaudio/pyannote-community-1", "aec": "webrtc-apm/aec3",
"params": { "micDelayMs": 150, "aec": true, "beam": false, "secondPassRetry": true,
"diarization": true, "maxSpeakers": 8, "speakerThreshold": 70 } },
"speakers": [ { "id": "you", "label": "Phill", "track": "mic" },
{ "id": "S1", "label": "Speaker 1", "track": "system" } ],
"segments": [
{ "id": 0, "speaker": "you", "track": "mic", "start": 12.34, "end": 15.90, "text": "…",
"confidence": 0.93, "words": [ { "w": "Let's", "s": 12.34, "e": 12.51, "c": 0.98 } ] }
]
}
Segments are sorted by start; track is always present; words and confidence depend on the
engine. Exporters consume only this file. Renaming a speaker rewrites speakers[].label (and
meeting.json.speakers.map), never segment text. Changing the shape of either file bumps
schemaVersion.