Skip to content

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.