> ## Documentation Index
> Fetch the complete documentation index at: https://docs.siftstack.com/llms.txt
> Use this file to discover all available pages before exploring further.

# File formats for import and export

> File formats supported for importing and exporting data in the Sift UI.

export const MintTable = ({columns = [], rows = [], columnWidths = []}) => {
  const pushTextWithLineBreaks = (parts, text, keyBase) => {
    const segments = String(text).split(/\\n|\n/);
    segments.forEach((segment, idx) => {
      if (segment) {
        parts.push(<span key={`${keyBase}-text-${idx}`}>{segment}</span>);
      }
      if (idx < segments.length - 1) {
        parts.push(<br key={`${keyBase}-br-${idx}`} />);
      }
    });
  };
  const parseMarkdown = text => {
    if (text === null || text === undefined) return "";
    const str = String(text);
    const parts = [];
    let lastIndex = 0;
    const pattern = /(`[^`]+`|\*\*[^*]+\*\*|\*[^*]+\*|\[([^\]]+)\]\(([^)]+)\))/g;
    let match;
    while (true) {
      match = pattern.exec(str);
      if (match === null) {
        break;
      }
      if (match.index > lastIndex) {
        pushTextWithLineBreaks(parts, str.substring(lastIndex, match.index), `before-${lastIndex}`);
      }
      const fullMatch = match[0];
      if (fullMatch.startsWith("`") && fullMatch.endsWith("`")) {
        parts.push(<code key={match.index}>{fullMatch.slice(1, -1)}</code>);
      } else if (fullMatch.startsWith("**") && fullMatch.endsWith("**")) {
        parts.push(<strong key={match.index}>{fullMatch.slice(2, -2)}</strong>);
      } else if (fullMatch.startsWith("*") && fullMatch.endsWith("*")) {
        parts.push(<em key={match.index}>{fullMatch.slice(1, -1)}</em>);
      } else if (fullMatch.startsWith("[")) {
        const linkText = match[2];
        const linkUrl = match[3];
        parts.push(<a key={match.index} href={linkUrl} className="text-black-600 dark:text-black-400">
            {linkText}
          </a>);
      }
      lastIndex = pattern.lastIndex;
    }
    if (lastIndex < str.length) {
      pushTextWithLineBreaks(parts, str.substring(lastIndex), `tail-${lastIndex}`);
    }
    if (parts.length > 0) {
      return parts;
    }
    const plainParts = [];
    pushTextWithLineBreaks(plainParts, str, "plain");
    return plainParts.length ? plainParts : str;
  };
  const safeColumns = Array.isArray(columns) ? columns : [];
  const safeRows = Array.isArray(rows) ? rows : [];
  const safeColumnWidths = Array.isArray(columnWidths) ? columnWidths : [];
  const hasColumnWidths = safeColumnWidths.some(w => w !== null && w !== undefined && w !== "");
  const toCssWidth = width => typeof width === "number" ? `${width}px` : String(width);
  const getColumnStyle = idx => {
    const rawWidth = safeColumnWidths[idx];
    if (rawWidth === null || rawWidth === undefined || rawWidth === "") {
      return undefined;
    }
    const width = toCssWidth(rawWidth);
    return {
      width,
      minWidth: width
    };
  };
  const containerStyle = hasColumnWidths ? undefined : {
    overflowX: "auto"
  };
  const tableStyle = hasColumnWidths ? {
    tableLayout: "fixed",
    width: "100%"
  } : {
    width: "max-content",
    minWidth: "100%"
  };
  if (!Array.isArray(columns) || !Array.isArray(rows) || !Array.isArray(columnWidths)) {
    console.warn("MintTable received invalid props:", {
      columns,
      rows,
      columnWidths
    });
  }
  if (!safeColumns.length && !safeRows.length) {
    return null;
  }
  return <div className="mint-table-container" style={containerStyle}>
      <table style={tableStyle}>
        {hasColumnWidths && <colgroup>
            {safeColumns.map((_, idx) => {
    const style = getColumnStyle(idx);
    return <col key={idx} style={style} />;
  })}
          </colgroup>}
        <thead>
          <tr>
            {safeColumns.map((col, idx) => <th key={idx} className="text-left" style={getColumnStyle(idx)}>
                <b>{parseMarkdown(col)}</b>
              </th>)}
          </tr>
        </thead>
        <tbody>
          {safeRows.map((row, rIdx) => {
    const safeRow = Array.isArray(row) ? row : [];
    return <tr key={rIdx}>
                {safeRow.map((cell, cIdx) => <td key={cIdx} style={getColumnStyle(cIdx)}>
                    {parseMarkdown(cell)}
                  </td>)}
              </tr>;
  })}
        </tbody>
      </table>
    </div>;
};

## Import file formats

The Sift UI supports the following file formats for import:

<MintTable
  columns={['Format', 'Extension']}
  columnWidths={['50%', '50%']}
  rows={[
['CSV', '`.csv`'],
['Parquet', '`.parquet`'],
['TDMS', '`.tdms`'],
['HDF5', '`.hdf5`, `.h5`'],
['ULog', '`.ulg`'],
]}
/>

## Import file settings

### CSV

Settings and requirements for importing CSV files.

#### File requirements

Your CSV file must meet the following requirements before importing.

<MintTable
  columns={['Requirement', 'Description']}
  columnWidths={['25%', '75%']}
  rows={[
['First row', 'Must contain column headers.'],
['Timestamp column', 'One column must contain timestamps. Recommended name: `timestamp`.'],
['Data columns', 'All other columns are treated as telemetry Channels.'],
]}
/>

#### Dialog settings

Settings in the **Upload CSV** dialog.

<MintTable
  columns={['Setting', 'Description']}
  columnWidths={['30%', '70%']}
  rows={[
['Asset', 'The system that generated the data. Required. Select **Existing** to choose an existing Asset, or **New** to create one.'],
['Run', 'The data collection session. Select **Existing** to append to a session, or **New** to create one. Defaults to the file name.'],
['First Data Row', 'The row number where time-series data begins. Use to skip additional header rows. Defaults to 2.'],
['Timestamp Column', 'The column containing timestamps. Auto-detected. Editable.'],
['Time Format', 'The format of the timestamp column. Required. See [Timestamp formats](#timestamp-formats).'],
['Relative Start time (UTC)', 'The absolute UTC start time used to anchor relative timestamps (such as `seconds` or `milliseconds`). Only applied when Time Format is relative; ignored for absolute formats such as `rfc3339`. Format: `yyyy-MM-dd\'T\'HH:mm:ss.SSS`.'],
['Channel Configuration', 'Per-Channel table for configuring which columns to import. See [Channel configuration table](#csv-and-parquet).'],
]}
/>

### Parquet

Settings and requirements for importing Parquet files.

#### File requirements

Your Parquet file must meet the following requirements before importing.

<MintTable
  columns={['Requirement', 'Description', 'Applies to']}
  columnWidths={['25%', '55%', '20%']}
  rows={[
['Timestamp column', 'One column must contain timestamps.', 'All Parquet files'],
['Channel columns', 'Each additional column is treated as a telemetry Channel. Nested columns are flattened using dot notation (for example, `location.lat`).', 'Flat Dataset only'],
['Row structure', 'Each row is one data point. Must contain a timestamp column, a data column, and optionally a name column.', 'Channel Per Row only'],
]}
/>

#### Flat Dataset dialog settings

Settings in the **Upload Parquet: Flat Dataset** dialog.

<MintTable
  columns={['Setting', 'Description']}
  columnWidths={['30%', '70%']}
  rows={[
['Asset', 'The system that generated the data. Required. Select **Existing** to choose an existing Asset, or **New** to create one.'],
['Run', 'The data collection session. Select **Existing** to append to a session, or **New** to create one. Defaults to the file name.'],
['Timestamp Column', 'The column containing timestamps. Auto-detected. Editable.'],
['Time Format', 'The format of the timestamp column. Required. See [Timestamp formats](#timestamp-formats).'],
['Relative Start time (UTC)', 'The absolute UTC start time used to anchor relative timestamps. Only applied when Time Format is relative. Format: `yyyy-MM-dd\'T\'HH:mm:ss.SSS`.'],
['Complex types import mode', 'How lists and maps are imported. See [Complex types modes](#complex-types-modes).'],
['Channel Configuration', 'Per-Channel table for configuring which columns to import. See [Channel configuration table](#csv-and-parquet).'],
]}
/>

#### Channel Per Row dialog settings

Settings in the **Upload Parquet: Channel Per Row** dialog.

<MintTable
  columns={['Setting', 'Description']}
  columnWidths={['30%', '70%']}
  rows={[
['Asset', 'The system that generated the data. Required. Select **Existing** to choose an existing Asset, or **New** to create one.'],
['Run', 'The data collection session. Select **Existing** to append to a session, or **New** to create one. Defaults to the file name.'],
['Timestamp Column', 'The column containing timestamps. Auto-detected. Editable.'],
['Time Format', 'The format of the timestamp column. Required. See [Timestamp formats](#timestamp-formats).'],
['Relative Start time (UTC)', 'The absolute UTC start time used to anchor relative timestamps. Only applied when Time Format is relative. Format: `yyyy-MM-dd\'T\'HH:mm:ss.SSS`.'],
['Channel Configuration', 'Set to `Single` for a file containing one Channel, or `Multi` for a file with multiple Channels identified by a name column.'],
['Data Column', 'Required. The column containing the Channel values.'],
['Channel Name', 'Required in `Single` mode. The name assigned to the imported Channel.'],
['Name Column', 'Required in `Multi` mode. The column whose values identify which Channel each row belongs to.'],
]}
/>

#### Complex types modes

Applies to Flat Dataset Parquet files.

<MintTable
  columns={['Mode', 'Description']}
  columnWidths={['25%', '75%']}
  rows={[
['Both (default)', 'Imported as Arrow bytes and JSON strings.'],
['Bytes', 'Imported as Arrow bytes only.'],
['String', 'Imported as JSON strings only. `.json` is appended to the Channel name to avoid naming conflicts.'],
['Ignore', 'Skipped.'],
]}
/>

### TDMS

Settings for importing TDMS files.

#### Dialog settings

Settings in the **Upload TDMS** dialog.

<MintTable
  columns={['Setting', 'Description']}
  columnWidths={['30%', '70%']}
  rows={[
['Asset', 'The system that generated the data. Required. Select **Existing** to choose an existing Asset, or **New** to create one.'],
['Run', 'The data collection session. Select **Existing** to append to a session, or **New** to create one. Defaults to the file name.'],
['Fallback method', 'Controls how Channels with missing timing information are handled. `Fail on error` aborts the import if any Channel lacks timing info; `Ignore error` skips those Channels and continues.'],
['Import file properties', 'When enabled, TDMS file-level properties are imported and stored as Run metadata. They appear in the Run details panel after import.'],
['Time format (optional)', 'Use when time Channels are not standard TDMS Timestamp data types and the format needs to be specified explicitly. See [Timestamp formats](#timestamp-formats).'],
['Relative start time (UTC)', 'The absolute UTC start time used to anchor relative timestamps. Format: `yyyy-MM-dd\'T\'HH:mm:ss.SSS`.'],
['Override waveform start time (optional)', 'Use when waveform Channels have `wf_increment` but no `wf_start_time` (for example, Veristand files). The supplied value is used as the start time in place of the missing `wf_start_time`.'],
['Local / UTC', 'Time zone applied to the overridden waveform start time.'],
]}
/>

#### Channel mapping

TDMS imports use the group and Channel hierarchy embedded in the file to derive Channel names. The TDMS dialog does not expose a per-Channel configuration table.

Channel-level description properties are imported and appear in the Channel detail panel in the Run view. Embedded enum configurations are also imported and surfaced as enum types on the Channel.

To select specific Channels and control how each is imported via the REST API or Python client, use `TdmsDataConfig` entries in the `data` field of `TDMSConfig`. This is recommended when your TDMS file structure does not fit one of the predefined import behaviors. When `data` is empty, Sift imports all Channels and applies the fallback method to any with missing timing information. See [TDMSConfig](/api/reference/protocol-buffers/data_imports#tdmsconfig) in the protocol buffers reference.

<Note>
  **Ingested volume**: TDMS and other compact file formats expand when translated into Sift's wire format for ingestion, so the total volume ingested is larger than the source file size on disk. This ingested volume, not the source file size, counts against your ingestion quota.
</Note>

### HDF5

Settings for importing HDF5 files.

#### Schemas

The schema determines how Sift interprets the structure of your HDF5 file.

<MintTable
  columns={['Schema', 'Description']}
  columnWidths={['30%', '70%']}
  rows={[
['HDF5: 1D Datasets', 'Channels live as separate 1D datasets. Within each group, one dataset is the timestamp and the remaining datasets are Channel values.'],
['HDF5: Compound Dataset', 'Channels live in compound datasets. The first compound field is the timestamp; remaining fields are Channel values.'],
['HDF5: 2D Dataset', 'Channels live as 2D datasets. Column 0 is the timestamp and column 1 is the Channel value.'],
]}
/>

#### Dialog settings

These settings appear in all three HDF5 upload dialogs.

<MintTable
  columns={['Setting', 'Description']}
  columnWidths={['30%', '70%']}
  rows={[
['Asset', 'The system that generated the data. Required. Select **Existing** to choose an existing Asset, or **New** to create one.'],
['Run', 'The data collection session. Select **Existing** to append to a session, or **New** to create one. Defaults to the file name.'],
['Time format (optional)', 'Use when the time dataset does not use a standard timestamp type and the format needs to be specified explicitly. See [Timestamp formats](#timestamp-formats).'],
['Relative start time (UTC)', 'The absolute UTC start time used to anchor relative timestamps. Format: `yyyy-MM-dd\'T\'HH:mm:ss.SSS`.'],
['Channel Configuration', 'Per-Channel table for mapping datasets to Channels. See [Channel configuration table: HDF5](#hdf5).'],
]}
/>

#### Programmatic Channel mapping

To map specific datasets to Channels via the REST API or Python client, use `Hdf5DataConfig` entries in the `data` field of `Hdf5Config`. This is recommended when your HDF5 file structure does not fit one of the predefined schemas. When `data` is empty, Sift does not automatically import all datasets; use the predefined schemas instead. See [Hdf5Config](/api/reference/protocol-buffers/data_imports#hdf5config) in the protocol buffers reference.

### ULog

Settings for importing PX4 ULog files.

#### Dialog settings

Settings in the **Upload ULog** dialog.

<MintTable
  columns={['Setting', 'Description']}
  columnWidths={['30%', '70%']}
  rows={[
['Asset', 'The system that generated the data. Required. Select **Existing** to choose an existing Asset, or **New** to create one.'],
['Run', 'The data collection session. Select **Existing** to append to a session, or **New** to create one. Defaults to the file name.'],
['Parse errors', 'Controls how a recoverable parse error (such as a truncated final record) is handled. **Fail on error** aborts the import; **Ignore error** imports the records that parsed and skips the rest.'],
['Log start time (UTC, optional)', 'Anchors the timeline to an absolute time. If not set, Sift anchors the timeline using the log\'s GPS fix. The import fails if neither is available. Use the **Local / UTC** toggle to set the value in local time or UTC.'],
]}
/>

#### Channel naming

ULog files are self-describing. Sift detects Channels in the browser before upload and names each one `<message>_<instance>.<field>`, for example `sensor_accel_0.x`. Logged status text is imported as `log_messages` Channels.

<Note>
  The ULog dialog has no Channel preview or per-Channel configuration. Every Channel is imported automatically. To rename, retype, or select Channels, see **Programmatic Channel selection** below.
</Note>

#### Programmatic Channel selection

To select specific Channels via the REST API or Python client, use `UlogDataConfig` entries in the `data` field of `UlogConfig`. When `data` is empty, Sift imports every detected Channel with its default name and type; when `data` is non-empty, only the listed Channels import, and each requires a full Channel config. Use `info_keys` and `param_keys` to import ULog info and parameter values as Run metadata; both require a Run. See [UlogConfig](/api/reference/protocol-buffers/data_imports#ulogconfig) in the protocol buffers reference.

The Python client requires the `ulog` extra: `pip install sift-stack-py[ulog]`.

### Timestamp formats

Supported timestamp formats for the Time Format setting.

<MintTable
  columns={['Format', 'Description']}
  columnWidths={['30%', '70%']}
  rows={[
['rfc3339', '2023-01-02T15:04:05Z'],
['datetime', '2023-01-02 15:04:05'],
['UNIX', 'Seconds since epoch'],
['unix_millis', 'Milliseconds since epoch'],
['unix_micros', 'Microseconds since epoch'],
['unix_nanos', 'Nanoseconds since epoch'],
['nanoseconds', 'Relative time in nanoseconds'],
['microseconds', 'Relative time in microseconds'],
['milliseconds', 'Relative time in milliseconds'],
['seconds', 'Relative time in seconds'],
['minutes', 'Relative time in minutes'],
['hours', 'Relative time in hours'],
]}
/>

### Channel configuration table

#### CSV and Parquet

Shown for CSV and Parquet (Flat Dataset). Each non-timestamp column appears as one row.

<MintTable
  columns={['Column', 'Description']}
  columnWidths={['25%', '75%']}
  rows={[
['Checkbox', 'Whether the Channel is included in the import.'],
['#', 'Row number. Auto-assigned.'],
['Name', 'Auto-detected Channel name. Editable.'],
['Data Type', 'Auto-detected data type. Editable. See [Data types](#data-types).'],
['Units', 'Optional. Unit of the Channel (for example, °C).'],
['Description', 'Optional. Description of the Channel.'],
]}
/>

#### HDF5

Each row maps one dataset to a Channel. The columns available are the same across all three HDF5 schemas.

<MintTable
  columns={['Column', 'Description']}
  columnWidths={['25%', '75%']}
  rows={[
['Checkbox', 'Whether the Channel is included in the import.'],
['#', 'Row number. Auto-assigned.'],
['Name', 'Channel name. Auto-detected from the dataset path. Editable.'],
['Data Type', 'Data type of the Channel values. Auto-detected. Editable. See [Data types](#data-types).'],
['Time Dataset', 'The dataset containing timestamps for this Channel.'],
['T Idx', 'Column index within the time dataset. Use when the dataset contains multiple columns.'],
['Value Dataset', 'The dataset containing the Channel values.'],
['V Idx', 'Column index within the value dataset. Use when the dataset contains multiple columns.'],
['Time Field', 'Optional. The field within the time dataset to use as the timestamp.'],
['Value Field', 'Optional. The field within the value dataset to use as the Channel value.'],
['Units', 'Optional. Unit of the Channel (for example, °C).'],
['Description', 'Optional. Description of the Channel.'],
]}
/>

### Data types

Supported data types for the Data Type field in the Channel configuration table.

<MintTable
  columns={['Data type', 'Description']}
  columnWidths={['25%', '75%']}
  rows={[
['double', '64-bit floating point number'],
['string', 'Text or alphanumeric values'],
['bool', 'Boolean value (true or false)'],
['float', '32-bit floating point number'],
['int32', '32-bit signed integer'],
['int64', '64-bit signed integer'],
['uint32', '32-bit unsigned integer'],
['uint64', '64-bit unsigned integer'],
]}
/>

## Export file formats

The Sift UI supports the following file formats for export:

<MintTable
  columns={['Format', 'Extension']}
  columnWidths={['50%', '50%']}
  rows={[
['CSV', '`.csv`'],
['Parquet', '`.parquet`'],
['Sun (WinPlot)', '`.sun`'],
]}
/>

## Export file settings

### Channel name display

Controls how Channel names appear in column headers.

<MintTable
  columns={['Option', 'Description', 'Format example']}
  columnWidths={['15%', '35%', '50%']}
  rows={[
['Default', 'Displays data as it appears in Sift, including Run and Asset details.', '`runName|assetName|channel.name`'],
['Simplified', 'Shows only the Channel name if it is unique, removing preceding text for clarity.', '`runName|assetName|name`'],
['Legacy', 'Follows the naming conventions used in previous versions of Sift export.', '`channel.name (assetName=... runName=...)`'],
]}
/>

### File splitting

Controls how exported data is partitioned across multiple files.

<MintTable
  columns={['Option', 'Description']}
  columnWidths={['25%', '75%']}
  rows={[
['Split by Asset', 'Generates a separate file for each Asset. The Asset name is removed from the Channel name display to reduce redundancy.'],
['Split by Run', 'Generates a separate file for each Run. The Run name is removed from the Channel name display for clarity.'],
]}
/>

### Channel data grouping

Determines how Channels are organized within the exported files.

<MintTable
  columns={['Option', 'Description']}
  columnWidths={['25%', '75%']}
  rows={[
['Combine Runs', 'Identical Channels within the same Asset and across multiple Runs are combined into a single column. Makes it easy to compare data directly across different time intervals.'],
]}
/>
