Skip to content

Working with Boards

Boards are visualization dashboards in Honeycomb. They organize multiple queries and visualizations in one place for monitoring and analysis.

Basic Board Operations

List Boards

async def list_boards(client: HoneycombClient) -> list[Board]:
    """List all boards in the environment.

    Args:
        client: Authenticated HoneycombClient

    Returns:
        List of boards
    """
    boards = await client.boards.list_async()
    for board in boards:
        print(f"{board.name}: {board.type} type")
    return boards

Get a Specific Board

async def get_board(client: HoneycombClient, board_id: str) -> Board:
    """Get a specific board by ID.

    Args:
        client: Authenticated HoneycombClient
        board_id: ID of the board to retrieve

    Returns:
        The board object
    """
    board = await client.boards.get_async(board_id)
    print(f"Name: {board.name}")
    print(f"Description: {board.description}")
    print(f"Type: {board.type}")
    return board

Update a Board

async def update_board(client: HoneycombClient, board_id: str) -> Board:
    """Update a board's name and description.

    Args:
        client: Authenticated HoneycombClient
        board_id: ID of the board to update

    Returns:
        The updated board
    """
    # Get existing board first to preserve values
    existing = await client.boards.get_async(board_id)

    # Update with new values
    updated = await client.boards.update_async(
        board_id,
        BoardCreate(
            name="Updated Service Overview",
            description="Updated: Key metrics for the API service",
            type=existing.type,
        ),
    )
    return updated

Delete a Board

async def delete_board(client: HoneycombClient, board_id: str) -> None:
    """Delete a board.

    Args:
        client: Authenticated HoneycombClient
        board_id: ID of the board to delete
    """
    await client.boards.delete_async(board_id)

Creating Boards with BoardBuilder

BoardBuilder provides a fluent interface for creating boards with inline QueryBuilder instances or existing query IDs. Single fluent call creates queries, annotations, and boards automatically.

Simple Example - Inline QueryBuilder with Auto-Layout

async def create_board_with_builder(client: HoneycombClient, dataset: str = "integration-test") -> str:
    """Create a service dashboard with inline QueryBuilder instances.

    This example demonstrates:
    - Inline QueryBuilder with name in constructor
    - Automatic query + annotation creation via create_from_bundle_async()
    - Multiple query panels with different visualization styles
    - Auto-layout for automatic panel arrangement
    - Dataset scoping variations (explicit vs environment-wide)
    """
    from honeycomb import BoardBuilder, QueryBuilder

    # Single fluent call with inline QueryBuilder instances
    created = await client.boards.create_from_bundle_async(
        BoardBuilder("Service Health Dashboard")
        .description("Request metrics and latency tracking")
        .auto_layout()
        .tag("team", "platform")
        .tag("service", "api")
        # Query 1: Dataset-scoped query
        .query(
            QueryBuilder("Request Count")
            .dataset(dataset)
            .last_24_hours()
            .count()
            .group_by("service")
            .description("Total requests by service over 24 hours"),
            style="graph",
        )
        # Query 2: Environment-wide query (all datasets)
        .query(
            QueryBuilder("Avg Latency")
            .environment_wide()
            .last_1_hour()
            .avg("duration_ms")
            .group_by("endpoint")
            .limit(10)
            .description("Top 10 endpoints by average latency"),
            style="table",
        )
        .build()
    )
    return created.id

Key Points: - Use QueryBuilder instances directly in .query() calls - no need to create queries separately - Pass name in constructor: QueryBuilder("Query Name") (required for board integration) - create_from_bundle_async() handles query creation + annotation + board in one call

This example demonstrates all capabilities: inline QueryBuilder instances, SLO panels, text panels, visualization settings, preset filters, and manual layout with tuple positioning.

async def create_complex_board(client: HoneycombClient, dataset: str = "my-dataset") -> str:
    """Create a comprehensive dashboard with all panel types and advanced features.

    This example demonstrates:
    - Inline QueryBuilder and SLOBuilder instances
    - Automatic creation of queries, SLOs, and board
    - Multiple query panels with different styles (graph, table, combo)
    - Inline SLO panel creation
    - Text panel for notes
    - Advanced visualization settings (hiding markers, UTC time)
    - Preset filters for dynamic filtering
    - Manual layout with precise positioning using PositionInput
    """
    from honeycomb import BoardBuilder, QueryBuilder, SLOBuilder
    from honeycomb.models.tool_inputs import PositionInput

    # Single fluent call with inline builders and manual positioning
    created = await client.boards.create_from_bundle_async(
        BoardBuilder("Production Monitoring Dashboard")
        .description("Complete service health monitoring with queries, SLOs, and notes")
        .manual_layout()
        .tag("team", "platform")
        .tag("environment", "production")
        # Preset filters for dynamic filtering
        .preset_filter("service", "Service")
        .preset_filter("environment", "Environment")
        # Top row: Request count (dataset-scoped, graph with advanced settings)
        .query(
            QueryBuilder("Request Count")
            .dataset(dataset)
            .last_24_hours()
            .count()
            .group_by("service")
            .description("Total requests by service over 24 hours"),
            position=PositionInput(x_coordinate=0, y_coordinate=0, width=9, height=6),
            style="graph",
            visualization={"hide_markers": True, "utc_xaxis": True},
        )
        # Top right: Inline SLO creation with new derived column
        .slo(
            SLOBuilder("API Availability")
            .dataset(dataset)
            .target_percentage(99.9)
            .sli(
                alias="board_sli_success",
                expression="IF(LT($status_code, 400), 1, 0)",
                description="Success rate: 1 if status < 400, 0 otherwise",
            )
            .description("API success rate SLO"),
            position=PositionInput(x_coordinate=9, y_coordinate=0, width=3, height=6),
        )
        # Middle left: Latency table (environment-wide)
        .query(
            QueryBuilder("Avg Latency")
            .environment_wide()
            .last_1_hour()
            .avg("duration_ms")
            .group_by("endpoint")
            .limit(10)
            .description("Top 10 endpoints by average latency"),
            position=PositionInput(x_coordinate=0, y_coordinate=6, width=6, height=5),
            style="table",
        )
        # Middle right: Error rate combo view (dataset-scoped)
        .query(
            QueryBuilder("Error Rate")
            .dataset(dataset)
            .last_2_hours()
            .count()
            .gte("status_code", 400)
            .group_by("status_code")
            .description("HTTP errors by status code"),
            position=PositionInput(x_coordinate=6, y_coordinate=6, width=6, height=5),
            style="combo",
        )
        # Bottom: Notes panel (full width)
        .text(
            "## Monitoring Guidelines\n\n- Watch for latency > 500ms\n- Error rate should stay < 1%\n- Check SLO during peak hours",
            position=PositionInput(x_coordinate=0, y_coordinate=11, width=12, height=3),
        )
        .build()
    )
    return created.id

BoardBuilder Reference

Layout Configuration

Method Description
.auto_layout() Use automatic panel positioning (positions optional)
.manual_layout() Use manual positioning (positions required for all panels)

Panel Methods

Method Description
.query(QueryBuilder, ...) Add query panel with inline QueryBuilder instance
.query(query_id, annotation_id, ...) Add query panel from existing query ID
.slo(slo_id, ...) Add an SLO panel
.text(content, ...) Add a text panel with markdown content

Query Panel Options

Parameter Type Description
query QueryBuilder \| str QueryBuilder instance with name OR existing query ID
annotation_id str \| None Required only when using existing query ID
position PositionInput \| None Optional position for manual layout
style "graph" \| "table" \| "combo" Display style (default: "graph")
dataset str \| None Override QueryBuilder's dataset (rare)
visualization dict \| None Advanced visualization configuration

Positioning for Manual Layout

For manual layout, use PositionInput objects with named fields:

from honeycomb import BoardBuilder, QueryBuilder
from honeycomb.models.tool_inputs import PositionInput

# Position format: PositionInput(x_coordinate, y_coordinate, width, height)
bundle = (
    BoardBuilder("Dashboard")
    .manual_layout()
    .query(
        QueryBuilder("Requests").dataset("api-logs").last_1_hour().count(),
        position=PositionInput(x_coordinate=0, y_coordinate=0, width=9, height=6),  # Top left, large
    )
    .slo("slo-id", position=PositionInput(x_coordinate=9, y_coordinate=0, width=3, height=6))  # Top right, small
    .text("Notes", position=PositionInput(x_coordinate=0, y_coordinate=6, width=12, height=2))  # Bottom, full width
    .build()
)

Understanding PositionInput

PositionInput specifies panel position and size on the board's 24-column grid:

Field Type Range Description
x_coordinate int 0-23 Horizontal position (left edge)
y_coordinate int 0+ Vertical position (top edge)
width int 1-24 Panel width in grid units
height int 1-24 Panel height in grid units

Grid Layout: - Boards use a 24-column grid system - Panels are positioned using coordinates (0-indexed) - Width + x_coordinate should not exceed 24 - Height is unlimited (boards scroll vertically) - Common widths: 6 (quarter), 8 (third), 12 (half), 24 (full)

Example Layouts:

from honeycomb.models.tool_inputs import PositionInput

# Two panels side-by-side (12 columns each)
left = PositionInput(x_coordinate=0, y_coordinate=0, width=12, height=6)
right = PositionInput(x_coordinate=12, y_coordinate=0, width=12, height=6)

# Three panels in a row (8 columns each)
panel1 = PositionInput(x_coordinate=0, y_coordinate=0, width=8, height=6)
panel2 = PositionInput(x_coordinate=8, y_coordinate=0, width=8, height=6)
panel3 = PositionInput(x_coordinate=16, y_coordinate=0, width=8, height=6)

# Full-width panel below
bottom = PositionInput(x_coordinate=0, y_coordinate=6, width=24, height=4)

Preset Filter Methods

Method Description
.preset_filter(column, alias) Add a dynamic filter for a column with display name

Tag Methods (from TagsMixin)

See Triggers documentation for full details on tag methods: - .tag(key, value) - Add a single tag - .tags(dict) - Add multiple tags from dictionary

Creating Boards Manually

For simple cases without panels:

async def create_basic_board(client: HoneycombClient) -> str:
    """Create a basic service overview board.

    Args:
        client: Authenticated HoneycombClient

    Returns:
        The created board ID
    """
    board = await client.boards.create_async(
        BoardCreate(
            name="Service Overview",
            description="Key metrics for the API service",
            type="flexible",
        )
    )
    return board.id

Board Views

Board views are filtered perspectives on a board (max 50 per board). Create them using BoardBuilder.add_view() or the view CRUD methods.

Creating Views with BoardBuilder

async def create_board_with_views(client: HoneycombClient, dataset: str = "integration-test") -> str:
    """Create a board with multiple views for different perspectives."""
    from honeycomb import BoardBuilder, QueryBuilder

    created = await client.boards.create_from_bundle_async(
        BoardBuilder("Service Dashboard")
        .description("Multi-perspective service monitoring")
        .auto_layout()
        .query(
            QueryBuilder("Request Metrics")
            .dataset(dataset)
            .last_1_hour()
            .count()
            .group_by("service"),
            style="graph",
        )
        # View 1: Active services only
        .add_view("Active Services", [{"column": "status", "operation": "=", "value": "active"}])
        # View 2: Production environment
        .add_view("Production", [{"column": "environment", "operation": "=", "value": "production"}])
        # View 3: Errors (multi-filter)
        .add_view(
            "Errors",
            [
                {"column": "status_code", "operation": ">=", "value": 400},
                {"column": "environment", "operation": "=", "value": "production"},
            ],
        )
        .build()
    )
    return created.id

Managing Views

async def manage_board_views(client: HoneycombClient, board_id: str) -> None:
    """List, create, get, update, and delete board views."""
    from honeycomb.models.boards import BoardViewCreate, BoardViewFilter

    # List all views
    views = await client.boards.list_views_async(board_id)
    print(f"Found {len(views)} views")

    # Create a new view (using automatic enum conversion from string)
    new_view = await client.boards.create_view_async(
        board_id,
        BoardViewCreate(
            name="Slow Requests",
            filters=[BoardViewFilter(column="duration_ms", operation=">", value=1000)],
        ),
    )

    # Get the view
    view = await client.boards.get_view_async(board_id, new_view.id)
    print(f"View: {view.name}")

    # Update the view
    await client.boards.update_view_async(
        board_id,
        new_view.id,
        BoardViewCreate(name="Very Slow Requests", filters=view.filters),
    )

    # Delete the view
    await client.boards.delete_view_async(board_id, new_view.id)

Export/Import with Views

async def export_and_import_board(client: HoneycombClient, board_id: str) -> str:
    """Export a board with views and import it."""
    import json

    # Export board with views
    data = await client.boards.export_with_views_async(board_id)

    # Data is portable (no IDs/timestamps)
    assert "id" not in data
    assert "created_at" not in data

    # Save to file
    json_str = json.dumps(data, indent=2)
    print(f"Exported: {json_str[:100]}...")

    return board_id  # Return for cleanup

Filter Operations: Use FilterOp enum (same as QueryBuilder): EQUALS, GREATER_THAN, CONTAINS, STARTS_WITH, ENDS_WITH, EXISTS, IN, etc.

CLI: hny boards export board-123 --views (default) or --no-views

Board Preset Filters

Preset filters allow dynamic filtering of board data. Add using .preset_filter(column, alias) (max 5):

board = await client.boards.create_from_bundle_async(
    BoardBuilder("Dashboard")
        .auto_layout()
        .preset_filter("service_name", "Service")
        .preset_filter("environment", "Environment")
        .build()
)

Sync Usage

All board operations have sync equivalents. Use sync=True when creating the client:

with HoneycombClient(api_key="...", sync=True) as client:
    # List boards
    boards = client.boards.list()

    # Create board
    board = client.boards.create(BoardCreate(name="My Board", type="flexible"))

    # Get board
    board = client.boards.get(board_id)

    # Update board
    updated = client.boards.update(board_id, BoardCreate(name="Updated", type="flexible"))

    # Delete board
    client.boards.delete(board_id)

    # Board views (sync)
    views = client.boards.list_views(board_id)
    data = client.boards.export_with_views(board_id)