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

# Events & Markers

> Timeline event markers for recording analysis

Annotate recordings with timestamped events -- errors, state changes, anomalies, and custom markers. Events appear as colored markers on the MCAP viewer timeline and can be queried across your entire fleet for trend analysis and incident investigation.

<Info>
  Fleet Management is in preview. APIs and features described on this page may change.
</Info>

## Event Types

Every event has a type that determines how it renders on the timeline and how it appears in search results.

| Type           | Description                                            | Timeline Icon   | Default Severity |
| -------------- | ------------------------------------------------------ | --------------- | ---------------- |
| `error`        | System errors, hardware faults, crash reports          | Red circle      | `critical`       |
| `warning`      | Degraded performance, threshold breaches, near-misses  | Yellow triangle | `warning`        |
| `info`         | Informational markers, milestones, mode changes        | Blue circle     | `info`           |
| `state_change` | Robot state transitions (idle, active, charging, etc.) | Purple diamond  | `info`           |
| `anomaly`      | ML-detected anomalies, outlier sensor readings         | Orange star     | `warning`        |
| `custom`       | User-defined event types with custom labels            | Gray square     | `info`           |

<Tip>
  Use `custom` events for domain-specific markers that don't fit the built-in types. Custom events support the same metadata, querying, and visualization features as built-in types.
</Tip>

## Creating Events

### Single Event

Attach an event to a specific timestamp within a recording. The `timestamp` field must fall within the recording's time range.

<CodeGroup>
  ```python Python theme={null}
  from avala import Client

  client = Client()

  event = client.fleet.events.create(
      recording_id="rec_abc123",
      timestamp="2026-01-15T10:30:00Z",
      type="anomaly",
      label="Gripper force spike",
      description="Gripper force exceeded expected maximum during pick operation",
      metadata={"force_n": 45.2, "expected_max": 30.0, "joint": "gripper_left"}
  )

  print(f"Event created: {event.uid} at {event.timestamp}")
  ```

  ```typescript TypeScript theme={null}
  import Avala from "@avala-ai/sdk";

  const avala = new Avala();

  const event = await avala.fleet.events.create({
    recordingId: "rec_abc123",
    timestamp: "2026-01-15T10:30:00Z",
    type: "anomaly",
    label: "Gripper force spike",
    description: "Gripper force exceeded expected maximum during pick operation",
    metadata: { force_n: 45.2, expected_max: 30.0, joint: "gripper_left" },
  });

  console.log(`Event created: ${event.uid} at ${event.timestamp}`);
  ```

  ```bash CLI theme={null}
  avala fleet events create \
    --recording rec_abc123 \
    --timestamp "2026-01-15T10:30:00Z" \
    --type anomaly \
    --label "Gripper force spike" \
    --metadata '{"force_n": 45.2, "expected_max": 30.0}'
  ```
</CodeGroup>

### Event Properties

| Property       | Type       | Required | Description                                                             |
| -------------- | ---------- | -------- | ----------------------------------------------------------------------- |
| `recording_id` | `string`   | Yes      | Recording to attach the event to                                        |
| `timestamp`    | `datetime` | Yes      | ISO 8601 timestamp within the recording's time range                    |
| `type`         | `string`   | Yes      | One of: `error`, `warning`, `info`, `state_change`, `anomaly`, `custom` |
| `label`        | `string`   | Yes      | Short label displayed on the timeline (max 120 characters)              |
| `description`  | `string`   | No       | Longer description shown in the event detail panel                      |
| `metadata`     | `object`   | No       | Arbitrary key-value pairs for structured data                           |
| `severity`     | `string`   | No       | Override default severity: `critical`, `warning`, `info`                |
| `duration_ms`  | `integer`  | No       | Event duration in milliseconds (for range events)                       |
| `tags`         | `string[]` | No       | Tags for filtering and categorization                                   |

### Batch Create

When processing recordings offline, create events in bulk to avoid per-event API overhead.

<CodeGroup>
  ```python Python theme={null}
  events = client.fleet.events.create_batch(
      recording_id="rec_abc123",
      events=[
          {
              "timestamp": "2026-01-15T10:30:00Z",
              "type": "error",
              "label": "Motor overtemp",
              "metadata": {"motor_id": "joint_3", "temp_c": 85.2}
          },
          {
              "timestamp": "2026-01-15T10:30:05Z",
              "type": "state_change",
              "label": "Emergency stop activated",
              "metadata": {"trigger": "thermal_protection"}
          },
          {
              "timestamp": "2026-01-15T10:32:00Z",
              "type": "info",
              "label": "Cooldown complete",
              "metadata": {"motor_id": "joint_3", "temp_c": 55.0}
          }
      ]
  )

  print(f"Created {len(events)} events")
  ```

  ```typescript TypeScript theme={null}
  const events = await avala.fleet.events.createBatch({
    recordingId: "rec_abc123",
    events: [
      {
        timestamp: "2026-01-15T10:30:00Z",
        type: "error",
        label: "Motor overtemp",
        metadata: { motor_id: "joint_3", temp_c: 85.2 },
      },
      {
        timestamp: "2026-01-15T10:30:05Z",
        type: "state_change",
        label: "Emergency stop activated",
        metadata: { trigger: "thermal_protection" },
      },
      {
        timestamp: "2026-01-15T10:32:00Z",
        type: "info",
        label: "Cooldown complete",
        metadata: { motor_id: "joint_3", temp_c: 55.0 },
      },
    ],
  });

  console.log(`Created ${events.length} events`);
  ```
</CodeGroup>

<Note>
  Batch creation accepts up to 1,000 events per request. For larger volumes, split into multiple batch calls. Events within a batch are created atomically -- if any event fails validation, none are created.
</Note>

## Querying Events

### Filter by Device and Type

Search events across recordings and devices to identify patterns and recurring issues.

<CodeGroup>
  ```python Python theme={null}
  # All errors from a specific device in January
  events = client.fleet.events.list(
      device_id="dev_abc123",
      type="error",
      since="2026-01-01T00:00:00Z",
      until="2026-02-01T00:00:00Z"
  )

  for event in events:
      print(f"[{event.timestamp}] {event.label}")
      if event.metadata:
          print(f"  metadata: {event.metadata}")
  ```

  ```typescript TypeScript theme={null}
  const events = await avala.fleet.events.list({
    deviceId: "dev_abc123",
    type: "error",
    since: "2026-01-01T00:00:00Z",
    until: "2026-02-01T00:00:00Z",
  });

  for (const event of events.items) {
    console.log(`[${event.timestamp}] ${event.label}`);
    if (event.metadata) {
      console.log(`  metadata: ${JSON.stringify(event.metadata)}`);
    }
  }
  ```

  ```bash CLI theme={null}
  avala fleet events list \
    --device dev_abc123 \
    --type error \
    --since 2026-01-01 \
    --until 2026-02-01
  ```
</CodeGroup>

### Filter by Recording

List all events within a single recording, ordered by timestamp.

<CodeGroup>
  ```python Python theme={null}
  events = client.fleet.events.list(
      recording_id="rec_abc123",
      order_by="timestamp"
  )

  for event in events:
      print(f"[{event.type}] {event.timestamp} - {event.label}")
  ```

  ```typescript TypeScript theme={null}
  const events = await avala.fleet.events.list({
    recordingId: "rec_abc123",
    orderBy: "timestamp",
  });

  for (const event of events.items) {
    console.log(`[${event.type}] ${event.timestamp} - ${event.label}`);
  }
  ```

  ```bash CLI theme={null}
  avala fleet events list --recording rec_abc123 --order-by timestamp
  ```
</CodeGroup>

### Aggregate Event Counts

Get event counts grouped by type, device, or time interval for trend analysis.

<CodeGroup>
  ```python Python theme={null}
  # Error count by device over the last 7 days
  summary = client.fleet.events.aggregate(
      type="error",
      group_by="device",
      since="2026-01-20T00:00:00Z",
      until="2026-01-27T00:00:00Z"
  )

  for entry in summary:
      print(f"{entry.device_name}: {entry.count} errors")

  # Event count by type per day
  daily = client.fleet.events.aggregate(
      group_by="type",
      interval="day",
      since="2026-01-01T00:00:00Z",
      until="2026-02-01T00:00:00Z"
  )

  for entry in daily:
      print(f"{entry.date} | {entry.type}: {entry.count}")
  ```

  ```typescript TypeScript theme={null}
  // Error count by device over the last 7 days
  const summary = await avala.fleet.events.aggregate({
    type: "error",
    groupBy: "device",
    since: "2026-01-20T00:00:00Z",
    until: "2026-01-27T00:00:00Z",
  });

  for (const entry of summary.items) {
    console.log(`${entry.deviceName}: ${entry.count} errors`);
  }

  // Event count by type per day
  const daily = await avala.fleet.events.aggregate({
    groupBy: "type",
    interval: "day",
    since: "2026-01-01T00:00:00Z",
    until: "2026-02-01T00:00:00Z",
  });

  for (const entry of daily.items) {
    console.log(`${entry.date} | ${entry.type}: ${entry.count}`);
  }
  ```
</CodeGroup>

### Search by Metadata

Query events using metadata field values to find specific incidents.

<CodeGroup>
  ```python Python theme={null}
  # Find all events related to a specific motor
  events = client.fleet.events.list(
      metadata_filter={"motor_id": "joint_3"}
  )

  # Find force spikes above a threshold
  events = client.fleet.events.list(
      type="anomaly",
      metadata_filter={"force_n": {"$gt": 40.0}}
  )
  ```

  ```typescript TypeScript theme={null}
  // Find all events related to a specific motor
  const events = await avala.fleet.events.list({
    metadataFilter: { motor_id: "joint_3" },
  });

  // Find force spikes above a threshold
  const spikes = await avala.fleet.events.list({
    type: "anomaly",
    metadataFilter: { force_n: { $gt: 40.0 } },
  });
  ```
</CodeGroup>

## Timeline Visualization

Events render as colored markers on the MCAP viewer timeline. Each event type has a distinct icon and color that matches the table in [Event Types](#event-types).

### Interaction

* **Hover** over a marker to see the event label and timestamp in a tooltip
* **Click** a marker to jump the playback position to that timestamp and open the event detail panel
* **Right-click** a marker to edit, delete, or copy the event
* **Drag** across the timeline to select a range and filter the event list to that window

### Multi-Panel Sync

When you click an event marker, all viewer panels jump to the event's timestamp simultaneously. Camera panels show the frame at that moment, point cloud panels show the scan at that moment, and plot panels highlight the corresponding data point.

<Tip>
  Use keyboard shortcuts to step between events: press `E` to jump to the next event and `Shift+E` to jump to the previous event. Press `F` to filter the timeline to show only events of the currently selected type.
</Tip>

### Range Events

Events with a `duration_ms` field render as colored spans on the timeline instead of point markers. This is useful for representing conditions that persist over time, such as a period of degraded sensor quality or an extended error state.

<CodeGroup>
  ```python Python theme={null}
  # Create a range event spanning 30 seconds
  event = client.fleet.events.create(
      recording_id="rec_abc123",
      timestamp="2026-01-15T10:30:00Z",
      type="warning",
      label="LiDAR degraded mode",
      description="Point cloud density dropped below 50% of nominal",
      duration_ms=30000,
      metadata={"density_pct": 42.0, "nominal_density_pct": 100.0}
  )
  ```

  ```typescript TypeScript theme={null}
  const event = await avala.fleet.events.create({
    recordingId: "rec_abc123",
    timestamp: "2026-01-15T10:30:00Z",
    type: "warning",
    label: "LiDAR degraded mode",
    description: "Point cloud density dropped below 50% of nominal",
    durationMs: 30000,
    metadata: { density_pct: 42.0, nominal_density_pct: 100.0 },
  });
  ```
</CodeGroup>

## Event Rules

Instead of creating events manually, use [recording rules](/docs/visualization/fleet/recording-rules) to automatically generate events when data patterns match defined conditions. For example, a rule can create an `anomaly` event whenever motor temperature exceeds a threshold, or a `state_change` event whenever the robot transitions between operating modes.

Automatically generated events are tagged with `source: rule` and include the rule ID in their metadata so you can trace them back to the rule that created them.

<CodeGroup>
  ```python Python theme={null}
  # Events created by rules have source metadata
  events = client.fleet.events.list(
      recording_id="rec_abc123",
      tags=["source:rule"]
  )

  for event in events:
      rule_id = event.metadata.get("rule_id")
      print(f"[{event.type}] {event.label} (created by rule {rule_id})")
  ```

  ```typescript TypeScript theme={null}
  const events = await avala.fleet.events.list({
    recordingId: "rec_abc123",
    tags: ["source:rule"],
  });

  for (const event of events.items) {
    const ruleId = event.metadata?.rule_id;
    console.log(`[${event.type}] ${event.label} (created by rule ${ruleId})`);
  }
  ```
</CodeGroup>

## Updating and Deleting Events

### Update an Event

<CodeGroup>
  ```python Python theme={null}
  client.fleet.events.update(
      event_id="evt_abc123",
      label="Gripper force spike (confirmed)",
      severity="critical",
      tags=["reviewed", "confirmed"]
  )
  ```

  ```typescript TypeScript theme={null}
  await avala.fleet.events.update({
    eventId: "evt_abc123",
    label: "Gripper force spike (confirmed)",
    severity: "critical",
    tags: ["reviewed", "confirmed"],
  });
  ```
</CodeGroup>

### Delete an Event

<CodeGroup>
  ```python Python theme={null}
  client.fleet.events.delete(event_id="evt_abc123")
  ```

  ```typescript TypeScript theme={null}
  await avala.fleet.events.delete({ eventId: "evt_abc123" });
  ```

  ```bash CLI theme={null}
  avala fleet events delete evt_abc123
  ```
</CodeGroup>

<Warning>
  Deleting an event removes it permanently from the recording timeline. Events generated by recording rules will not be re-created unless the rule is re-evaluated against the recording.
</Warning>

## Export Events

Export events to CSV or JSON for offline analysis, reporting, or integration with external tools.

<CodeGroup>
  ```python Python theme={null}
  # Export all error events from a device group
  export = client.fleet.events.export(
      group_id="grp_abc123",
      type="error",
      since="2026-01-01T00:00:00Z",
      format="csv"
  )

  with open("errors_jan_2026.csv", "wb") as f:
      f.write(export.content)
  ```

  ```typescript TypeScript theme={null}
  const exportData = await avala.fleet.events.export({
    groupId: "grp_abc123",
    type: "error",
    since: "2026-01-01T00:00:00Z",
    format: "csv",
  });

  await fs.writeFile("errors_jan_2026.csv", exportData.content);
  ```

  ```bash CLI theme={null}
  avala fleet events export \
    --group grp_abc123 \
    --type error \
    --since 2026-01-01 \
    --format csv \
    --output errors_jan_2026.csv
  ```
</CodeGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="" icon="gears" href="/docs/visualization/fleet/recording-rules">
    <p style={{fontWeight: 600, fontSize: '18px', marginBottom: '4px', marginTop: '8px', color: 'inherit'}}>Recording Rules</p>
    <p style={{fontSize: '14px', marginTop: '0px', opacity: 0.6}}>Automatically create events when recording data matches defined conditions.</p>
  </Card>

  <Card title="" icon="gauge-high" href="/docs/visualization/fleet/fleet-dashboard">
    <p style={{fontWeight: 600, fontSize: '18px', marginBottom: '4px', marginTop: '8px', color: 'inherit'}}>Fleet Dashboard</p>
    <p style={{fontSize: '14px', marginTop: '0px', opacity: 0.6}}>Centralized view of all devices, recordings, and fleet health metrics.</p>
  </Card>

  <Card title="" icon="bell" href="/docs/visualization/fleet/alerts-and-notifications">
    <p style={{fontWeight: 600, fontSize: '18px', marginBottom: '4px', marginTop: '8px', color: 'inherit'}}>Alerts & Notifications</p>
    <p style={{fontSize: '14px', marginTop: '0px', opacity: 0.6}}>Route alert notifications to Slack, email, and webhooks.</p>
  </Card>

  <Card title="" icon="timeline" href="/docs/visualization/guides/timeline-navigation">
    <p style={{fontWeight: 600, fontSize: '18px', marginBottom: '4px', marginTop: '8px', color: 'inherit'}}>Timeline Navigation</p>
    <p style={{fontSize: '14px', marginTop: '0px', opacity: 0.6}}>Keyboard shortcuts and controls for navigating the MCAP viewer timeline.</p>
  </Card>
</CardGroup>
