diff --git a/.changeset/dashboard-replay-search.md b/.changeset/dashboard-replay-search.md
new file mode 100644
index 0000000000..5b6324f2b1
--- /dev/null
+++ b/.changeset/dashboard-replay-search.md
@@ -0,0 +1,5 @@
+---
+"@hyperdx/app": patch
+---
+
+feat(dashboards): add a Replay search action to log and trace dashboard tiles whose event query can be faithfully reconstructed. The action opens a new Search tab with the tile's source, query, filters, and dashboard time range preserved.
diff --git a/packages/app/src/DBDashboardPage.tsx b/packages/app/src/DBDashboardPage.tsx
index bf5ba80740..994301e75a 100644
--- a/packages/app/src/DBDashboardPage.tsx
+++ b/packages/app/src/DBDashboardPage.tsx
@@ -49,6 +49,7 @@ import {
Filter,
getSampleWeightExpression,
isLogSource,
+ isSearchableSource,
isTraceSource,
SearchCondition,
SearchConditionLanguage,
@@ -684,6 +685,40 @@ const Tile = forwardRef(
);
}, [filters, queriedConfig, source]);
+ const replaySearchUrl = useMemo(() => {
+ if (!queriedConfig || !source || !isBuilderChartConfig(queriedConfig)) {
+ return null;
+ }
+
+ if (queriedConfig.metricTables != null || !isSearchableSource(source)) {
+ return null;
+ }
+
+ if (Array.isArray(queriedConfig.select)) {
+ const hasPerSeriesCondition = queriedConfig.select.some(
+ select =>
+ typeof select !== 'string' &&
+ select.aggCondition != null &&
+ select.aggCondition.trim().length > 0,
+ );
+ const canPromoteSingleSeriesCondition =
+ queriedConfig.select.length === 1 && queriedConfig.where.length === 0;
+
+ // buildEventsSearchUrl can promote one per-series condition into the
+ // event query, but cannot faithfully replay multiple conditions or
+ // combine a series condition with a global where clause.
+ if (hasPerSeriesCondition && !canPromoteSingleSeriesCondition) {
+ return null;
+ }
+ }
+
+ return buildEventsSearchUrl({
+ source,
+ config: queriedConfig,
+ dateRange,
+ });
+ }, [dateRange, queriedConfig, source]);
+
const hoverToolbar = useMemo(() => {
const isRawSql = isRawSqlSavedChartConfig(chart.config);
const isPromQL = isPromqlSavedChartConfig(chart.config);
@@ -702,6 +737,25 @@ const Tile = forwardRef(
key="hover-toolbar"
my={2} // Margin to ensure that the Alert Indicator doesn't clip on non-Line/Bar display types
>
+ {replaySearchUrl && (
+