Meeting data files

Field-by-field reference for meeting.json, transcript.json and telemetry.json, the three data files s1ft writes for every meeting, with their schema versions.

Every meeting that s1ft records gets a folder, and three plain JSON files in it carry the data: meeting.json, transcript.json and telemetry.json. This page lists every field of each file, with its type and its schema version, so you or an AI agent can read them without guessing. For the agent setup, see Connect AI agents.

Before you start

  • The files sit in the meeting folder. To find the folder, see Finding your recordings.
  • meeting.json is the last data file s1ft writes for a meeting. When it exists, the transcript is ready.
  • Every file is written in one step, so a reader sees the old file or the complete new one, never half a file.
  • Times in meeting.json are ISO 8601 date-times, for example 2026-07-16T10:00:00Z. Times in transcript.json and telemetry.json are seconds.
  • The files hold measurements and text. They hold no scores, no labels and no summary. A figure such as Talk ratio or Filler rate is not stored in any file. An agent can work it out from the segments in transcript.json.

Schema versions and the add-only rule

Each file starts with a schemaVersion number. Read it first, then read the fields.

FileCurrent schemaVersion
meeting.json1
transcript.json1
telemetry.json2

A new schema version only adds fields. It never removes a field and never renames one. Code that reads version 1 keeps working when version 2 appears, as long as it ignores fields it does not know. The JSON Schemas below leave extra fields allowed for the same reason.

Meetings from before meeting.json

The command line reads meetings through meeting.json. A folder without that file is not a meeting for the CLI. Meetings recorded before meeting.json existed (before S1ft 1.3) are therefore not visible to the s1ft command, and s1ft does not change, convert or remove the files of those meetings. If a list comes back empty, see Where the agent looks for meetings.

meeting.json

meeting.json says which meeting the folder holds: its id, its name, when it ran and which calendar events overlapped it. Current version: 1.

Fields

FieldTypeMeaning
schemaVersionintegerThe version of this file. Always 1 today.
idstring (UUID)The id of the meeting. The s1ft command takes this id.
namestringThe meeting name as s1ft shows it. If you rename a meeting, s1ft writes the file again with the new name.
platformstringThe bundle identifier of the app that was recorded, for example us.zoom.xos. A manual recording gives manual.
startstring (date-time)When the recording started.
endstring (date-time)When the recording ended.
calendarEventsarray of eventThe calendar events that overlapped the recording. An empty array when no calendar account is turned on or nothing matched.

calendarEvents items

Each entry in calendarEvents has these fields.

FieldTypeMeaning
titlestringThe title of the event.
startstring (date-time)When the event starts.
endstring (date-time)When the event ends.
isAllDaybooleantrue for an all-day event.
attendeesarray of attendeeThe people on the event.
usedForNamebooleantrue only for the event that gave the meeting its name. After you rename a meeting, or when no event gave the name, every event has false.

attendees items

FieldTypeMeaning
namestring or nullThe attendee name. null when the calendar has none.
emailstring or nullThe attendee email. null when the calendar has none, for example for a room.

Always check for null before you use name or email.

Example

{
  "calendarEvents": [
    {
      "attendees": [
        { "email": "[email protected]", "name": "Ana" },
        { "email": null, "name": "Room 4" }
      ],
      "end": "2026-07-16T10:30:00Z",
      "isAllDay": false,
      "start": "2026-07-16T10:00:00Z",
      "title": "Design Review",
      "usedForName": true
    }
  ],
  "end": "2026-07-16T10:30:00Z",
  "id": "AAAAAAAA-0170-4000-8000-000000000001",
  "name": "Design Review",
  "platform": "us.zoom.xos",
  "schemaVersion": 1,
  "start": "2026-07-16T10:00:00Z"
}

JSON Schema

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "meeting.json (schemaVersion 1)",
  "type": "object",
  "required": ["schemaVersion", "id", "name", "platform", "start", "end", "calendarEvents"],
  "properties": {
    "schemaVersion": { "const": 1 },
    "id": { "type": "string", "format": "uuid" },
    "name": { "type": "string" },
    "platform": { "type": "string" },
    "start": { "type": "string", "format": "date-time" },
    "end": { "type": "string", "format": "date-time" },
    "calendarEvents": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["title", "start", "end", "isAllDay", "attendees", "usedForName"],
        "properties": {
          "title": { "type": "string" },
          "start": { "type": "string", "format": "date-time" },
          "end": { "type": "string", "format": "date-time" },
          "isAllDay": { "type": "boolean" },
          "attendees": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": { "type": ["string", "null"] },
                "email": { "type": ["string", "null"] }
              }
            }
          },
          "usedForName": { "type": "boolean" }
        }
      }
    }
  }
}

transcript.json

transcript.json holds the words of the meeting, split by speaker. Current version: 1.

Fields

FieldTypeMeaning
schemaVersionintegerThe version of this file. Always 1 today.
segmentsarray of segmentThe transcript, in order.

Each segment has exactly four fields.

FieldTypeMeaning
speakerstringWho spoke. Me is you, from your microphone. Other speakers are Participant 1, Participant 2 and so on, numbered by when they first speak. When s1ft finds only one other speaker, the label is Participant.
startnumberStart of the segment, in seconds.
endnumberEnd of the segment, in seconds.
textstringThe words of the segment.

Older meetings keep a transcript.md file instead. s1ft does not convert it.

Example

{
  "schemaVersion": 1,
  "segments": [
    { "end": 5.5, "speaker": "Me", "start": 0, "text": "Hello everyone" },
    { "end": 9, "speaker": "Participant 1", "start": 5.5, "text": "Hi" }
  ]
}

JSON Schema

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "transcript.json (schemaVersion 1)",
  "type": "object",
  "required": ["schemaVersion", "segments"],
  "properties": {
    "schemaVersion": { "const": 1 },
    "segments": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["speaker", "start", "end", "text"],
        "properties": {
          "speaker": { "type": "string" },
          "start": { "type": "number" },
          "end": { "type": "number" },
          "text": { "type": "string" }
        }
      }
    }
  }
}

telemetry.json

telemetry.json holds raw measurements of your own microphone, frame by frame. It has no averages, no scores and no data about other speakers. Current version: 2.

Version 2 holds one span for each segment that transcript.json labels Me. Inside a span, the frames come one after the other: frame i starts at start + i * frameMs / 1000 seconds, where frameMs is the value in method.

Fields

FieldTypeMeaning
schemaVersionintegerThe version of this file. 2 for the current format.
sourcestringWhere the audio came from. Always microphone.
methodobjectHow the frames were measured. See below.
sampleRateHzintegerThe sample rate of the microphone audio, in Hz.
spansarray of spanOne entry for each Me segment.

The method object:

FieldTypeMeaning
frameMsintegerThe length of one frame, in milliseconds.
windowSamplesintegerThe number of audio samples that each measurement looks at.
pitchAlgorithmstringThe pitch method. autocorrelation.
pitchRangeHzarray of two integersThe lowest and the highest pitch the method looks for, in Hz.

Each span has four fields, and the three arrays have one value per frame.

FieldTypeMeaning
startnumberWhere the span starts, in seconds.
pitchHzarray of number or nullThe pitch of each frame, in Hz. null for a frame with no pitch, for example a silent frame.
rmsDbfsarray of numberThe average loudness of each frame, in dBFS.
peakDbfsarray of numberThe peak level of each frame, in dBFS.

Example

{
  "method": {
    "frameMs": 100,
    "pitchAlgorithm": "autocorrelation",
    "pitchRangeHz": [50, 1000],
    "windowSamples": 2048
  },
  "sampleRateHz": 48000,
  "schemaVersion": 2,
  "source": "microphone",
  "spans": [
    {
      "peakDbfs": [-6, -6],
      "pitchHz": [220, null],
      "rmsDbfs": [-9, -60],
      "start": 1
    }
  ]
}

JSON Schema

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "telemetry.json (schemaVersion 2)",
  "type": "object",
  "required": ["schemaVersion", "source", "method", "sampleRateHz", "spans"],
  "properties": {
    "schemaVersion": { "const": 2 },
    "source": { "type": "string" },
    "method": {
      "type": "object",
      "required": ["frameMs", "windowSamples", "pitchAlgorithm", "pitchRangeHz"],
      "properties": {
        "frameMs": { "type": "integer" },
        "windowSamples": { "type": "integer" },
        "pitchAlgorithm": { "type": "string" },
        "pitchRangeHz": { "type": "array", "items": { "type": "integer" } }
      }
    },
    "sampleRateHz": { "type": "integer" },
    "spans": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["start", "pitchHz", "rmsDbfs", "peakDbfs"],
        "properties": {
          "start": { "type": "number" },
          "pitchHz": { "type": "array", "items": { "type": ["number", "null"] } },
          "rmsDbfs": { "type": "array", "items": { "type": "number" } },
          "peakDbfs": { "type": "array", "items": { "type": "number" } }
        }
      }
    }
  }
}

Meetings recorded before version 2 hold a version 1 telemetry.json with a different shape. The s1ft command reads version 2 only.

Numbers from the frames: s1ft voice stats

Raw frames are long. The command s1ft voice stats <id> turns the frames of telemetry.json into a short set of numbers for each span. It reads the file and saves nothing. Add --json to get JSON on stdout.

--silence-threshold-dbfs, --min-silence-ms and --percentiles change the thresholds. The defaults are -50, 300 and 10,50,90.

Output fields

FieldTypeMeaning
frameMsintegerThe frame length, copied from method.frameMs.
optionsobjectThe thresholds in use: silenceThresholdDbfs (number), minSilenceMs (integer) and percentiles (array of integers).
spansarray of span statsOne entry for each span of telemetry.json, in the same order.

Each entry in spans has these fields.

FieldTypeMeaning
startnumberWhere the span starts, in seconds.
durationSecnumberThe length of the span, in seconds.
frameCountintegerThe number of frames.
voicedFrameCountintegerThe number of frames that have a pitch.
pitchMinHznumberThe lowest pitch. Absent when no frame has a pitch.
pitchMaxHznumberThe highest pitch. Absent when no frame has a pitch.
pitchRangeHznumberHighest minus lowest pitch. Absent when no frame has a pitch.
pitchPercentilesHzobjectPitch values by percentile, with keys such as p10, p50 and p90. Empty when no frame has a pitch.
rmsMeanDbfsnumberThe mean of rmsDbfs. Absent when the span has no frames.
peakMaxDbfsnumberThe highest value of peakDbfs. Absent when the span has no frames.
silenceCountintegerThe number of silences. A silence is a run of frames below the silence threshold that lasts at least minSilenceMs.
silenceTotalSecnumberThe total length of those silences, in seconds.

cli.json

The s1ft command keeps one small settings file, ~/Library/Application Support/s1ft/cli.json. It has one key.

FieldTypeMeaning
meetingsDirstringThe meetings folder that the command uses. s1ft config dir <path> sets it. The folder must exist.

--dir beats this key, and this key beats the output folder set in the s1ft app. See Where the agent looks for meetings for the full order.

What’s next