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 4)
├── recording.m4a       track 0 mic (raw), track 1 system (raw); AAC 48 kHz mono each
├── transcript.json     schemaVersion 2 - the public API
├── transcript.md       rendered, chronological
├── pipeline.log        per-run timings, model ids, parameters, warnings
├── summary.md          written by an automation; shown in the app when present
├── exports/            <slug>.md / .srt / …
└── automations/        <name>-<yyyyMMdd-HHmmss>.log + .result.json 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 (v4)

{
  "schemaVersion": 4,
  "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": { "label": "Alice Jones", "email": "alice@example.com", "speakerId": "uuid" }, "S2": { } } },
  "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" } ],
                  "match": { "subject": "Weekly sync", "start": "…", "end": "…",
                             "reason": "subjectAndTime | timeOverlap | subject | manual" } },
  "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. attendance.match (schemaVersion 4, additive) records which Teams meeting the attendees came from and why it was picked - subjectAndTime, timeOverlap, subject, or manual when you chose it from the attendees menu. Files written before v4 have no match.

speakers.map values are objects since schemaVersion 3: label is the speaker's full name as entered (absent while unnamed), email is optional, and speakerId links to the global speaker record once assigned. Keeping name and email here means the speaker database can be rebuilt from the meeting folders. Rex still reads the v2 shape ("S1": "Alice", "S2": null).

recording stays even after retention trashed the audio; the app checks whether the file exists.

transcript.json (v2)

{
  "schemaVersion": 2,
  "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", "speechSec": 412.5,
                  "voiceprint": "<base64 float32[256]>",
                  "speakerId": "uuid", "assignment": { "source": "manual | automatic | voice", "confidence": 0.91, "at": "…" } } ],
  "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.

Since schemaVersion 2 each system-track speaker carries its voiceprint (the mean diarizer embedding for that voice in this meeting, base64 little-endian float32) and speechSec; speakerId and assignment appear once the voice is linked to a speaker record. The voiceprint is biometric data: it stays in this file and never reaches transcript.md or any export, including the JSON export.