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
Complex Example - Full Featured Dashboard¶
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)