仪表板
仪表板 API 允许您使用已用于摄取和查询操作的相同 API 密钥以编程方式列出、创建、编辑和删除仪表板。
支持的操作:
- 列表.
GET https://api.telemetry.sh/dashboard - 创建.
POST https://api.telemetry.sh/dashboard - 编辑.
PATCH https://api.telemetry.sh/dashboard - 删除.
DELETE https://api.telemetry.sh/dashboard
标题
| 名称 | 类型 | 描述 |
|---|---|---|
Content-Type |
字符串 | 必须是 application/json。 |
Authorization |
字符串 | 您的 API 密钥,作为原始密钥或 Bearer <key>。 |
范围规则:
GET /dashboard接受read、write或read-and-writePOST /dashboard、PATCH /dashboard和DELETE /dashboard需要write或read-and-write
列出回应
GET /dashboard 返回 API 密钥团队的每个仪表板,由 updated_at DESC 订购。
支持的查询参数:
| 领域 | 类型 | 必填 | 精确合约 | 默认 |
|---|---|---|---|---|
page |
整数 | 否 | 正整数页码。小于 1 的值将被拒绝。 |
1 |
pageSize |
整数 | 否 | 正整数页面大小。大于 100 的值将被限制为 100。 API 还接受旧别名 page_size。 |
50 |
每个项目都包含仪表板元数据、url、widget_count 以及 API 形状的完整小部件列表。
高层回应:
| 领域 | 类型 | 精确合约 |
|---|---|---|
status |
字符串 | 总是 "success" 就成功。 |
pagination |
对象 | 当前页面的分页元数据。参见下表。 |
dashboards |
数组 | API 密钥团队的仪表板对象数组。当团队没有仪表板时,数组为空。 |
pagination 包括:
| 领域 | 类型 | 精确合约 | 示例 |
|---|---|---|---|
page |
整数 | 当前从 1 开始的页码。 | 2 |
pageSize |
整数 | 默认和最大尺寸限制后的有效页面尺寸。 | 25 |
total |
整数 | 分页之前团队的仪表板总数。 | 63 |
totalPages |
整数 | 当前总页数为pageSize。 0,当 total 为 0 时。 |
3 |
hasNextPage |
布尔值 | 当当前页面之后存在另一个页面时,true。 |
true |
hasPreviousPage |
布尔值 | true 当当前页面之前存在另一个页面时。 |
true |
dashboards中的每一项包括:
| 领域 | 类型 | 精确合约 | 示例 |
|---|---|---|---|
id |
字符串 | 仪表板 ID。 | "550e8400-e29b-41d4-a716-446655440000" |
team_id |
字符串 | 拥有团队 ID。 | "a7d4..." |
name |
字符串 | 仪表板名称。 | "Operations Overview" |
slug |
字符串 | 仪表板蛞蝓。 | "operations-overview" |
description |
字符串或 null |
保存的仪表板描述。 | "Core service health and latency" |
created_by |
字符串或 null |
UI 创建的仪表板的用户 ID。 null 用于 API 创建的仪表板。 |
null |
created_by_api_key_id |
字符串或 null |
API 创建的仪表板的团队 API 密钥 ID。 null 用于 UI 创建的仪表板。 |
"key_123" |
created_at |
字符串 | 仪表板表中的时间戳字符串。 | "2026-03-27T00:09:03.797Z" |
updated_at |
字符串 | 仪表板表中的时间戳字符串。 | "2026-03-27T00:09:03.797Z" |
url |
字符串 | Telemetry UI 中的相对仪表板 URL。 | "/team/acme/dashboard/operations-overview" |
widget_count |
整数 | 附加到仪表板的小部件数量。 | 2 |
widgets |
数组 | 创建和编辑响应返回的形状相同的小部件数组。 | [] |
创建身体
| 领域 | 类型 | 必填 | 精确合约 | 示例 |
|---|---|---|---|---|
name |
字符串 | 是的 | 修剪后的仪表板名称。修剪后必须非空。 | "HTTP Monitoring" |
slug |
字符串 | 否 | 可选的定制块。 Telemetry 通过修剪、小写、删除除 a-z、0-9、空格和 - 之外的所有字符、将空白运行转换为 -、折叠重复的 - 以及修剪前导/尾随来对其进行规范化-。如果省略,则 Telemetry 源自 name。标准化结果仍必须至少包含一个字母数字字符。 |
"HTTP Monitoring!!!" 变为 "http-monitoring" |
description |
字符串或 null |
否 | 可选描述。空字符串标准化为 null。 |
"Core service health and latency" |
widgets |
数组 | 否 | 要立即创建的可选小部件数组。如果省略,仪表板将创建为空。如果任何小部件无效,则整个请求将被拒绝,并显示 400。 |
[] |
创建响应
POST /dashboard 返回 201 Created 以及保存的仪表板和规范化的小部件列表。
{
"status": "success",
"dashboard": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"team_id": "a7d4...",
"name": "HTTP Monitoring",
"slug": "http-monitoring",
"description": "Core service health and latency",
"created_by": null,
"created_by_api_key_id": "key_123",
"created_at": "2026-03-27T00:09:03.797Z",
"updated_at": "2026-03-27T00:09:03.797Z",
"url": "/team/acme/dashboard/http-monitoring"
},
"widgets": [
{
"id": "0b3cb3f5-1147-4f2f-bf64-5044147c3e9a",
"title": "Errors Per Hour",
"widget_type": "query",
"config": {
"querySql": "SELECT hour, errors FROM error_rollups",
"chartType": "Line Chart",
"xAxis": "hour",
"yAxis": "errors",
"groupBy": null,
"queryId": null,
"sourceUrl": null
},
"layout": {
"x": 0,
"y": 0,
"w": 12,
"h": 4
},
"sort_order": 1
}
]
}
编辑正文
PATCH /dashboard 是部分更新端点。省略的字段保持不变。
您必须使用 dashboardId 或 dashboardSlug 标识目标仪表板。
| 领域 | 类型 | 必填 | 精确合约 | 示例 |
|---|---|---|---|---|
dashboardId |
字符串 | dashboardId 或 dashboardSlug 之一 |
要编辑的现有仪表板 ID。如果发送两个标识符,它们必须引用同一个仪表板。 | "550e8400-e29b-41d4-a716-446655440000" |
dashboardSlug |
字符串 | dashboardId 或 dashboardSlug 之一 |
要编辑的现有仪表板 slug。这是当前的 slug,而不是新的 slug。 | "http-monitoring" |
name |
字符串 | 否 | 新修剪的仪表板名称。提供时必须非空。更新 name 不会自动更改 slug。 |
"HTTP Monitoring v2" |
slug |
字符串 | 否 | 新的段头值。 Telemetry 使用与 create 相同的规则对其进行规范化。如果省略,现有的 slug 保持不变。 | "http-monitoring-v2" |
description |
字符串或 null |
否 | 新的仪表板描述。 null 或 "" 清除描述。如果省略,则现有描述保持不变。 |
"Updated on-call dashboard" |
widgets |
数组 | 否 | 完整的替换小部件列表。如果省略,现有小部件将保持不变。如果提供,Telemetry 将删除当前小部件集并将其替换为该数组中的小部件。 [] 清除所有小部件。 |
[] |
注意事项:
PATCH尚不支持附加或编辑单个小部件。- 当提供
widgets时,会重新生成小部件 ID,因为 API 会替换完整的小部件集。
编辑回复
PATCH /dashboard 返回 200 OK ,其响应形状与创建相同:
dashboard包含保存的仪表板元数据和urlwidgets包含 API 形状的完整持久小部件列表- 如果
widgets被替换,则返回的小部件 id 反映新插入的行
删除正文
DELETE /dashboard 删除单个仪表板及其所有小部件。
您必须使用 dashboardId 或 dashboardSlug 标识目标仪表板。
| 领域 | 类型 | 必填 | 精确合约 | 示例 |
|---|---|---|---|---|
dashboardId |
字符串 | dashboardId 或 dashboardSlug 之一 |
要删除的现有仪表板 ID。如果发送两个标识符,它们必须引用同一个仪表板。 | "550e8400-e29b-41d4-a716-446655440000" |
dashboardSlug |
字符串 | dashboardId 或 dashboardSlug 之一 |
要删除的现有仪表板 slug。 | "http-monitoring" |
注意事项:
- 删除是永久性的。
- 删除仪表板也会删除当前附加到其的所有小部件。
删除回复
DELETE /dashboard 返回 200 OK:
{
"status": "success",
"deleted_dashboard": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "HTTP Monitoring",
"slug": "http-monitoring",
"description": "Core service health and latency",
"widget_count": 2,
"url": "/team/acme/dashboard/http-monitoring"
}
}
常见错误
400 Bad Request如果 JSON 主体无效或请求主体不是 JSON 对象400 Bad Request如果page或pageSize不是GET /dashboard上的正整数400 Bad Request(如果PATCH或DELETE缺少dashboardId或dashboardSlug)400 Bad Request(如果PATCH省略所有可编辑字段)400 Bad Request(如果name、slug、description或widgets验证失败)400 Bad Request如果查询小部件包含非只读 SQL403 Forbidden如果 API 密钥没有请求操作的权限401 Unauthorized(如果 API 密钥丢失或无效)404 Not Found(如果 API 密钥的团队不存在仪表板)409 Conflict如果同一团队中的另一个仪表板已使用请求的 slug
小部件字段
widgets中的每个项目都支持:
| 领域 | 类型 | 必填 | 精确合约 | 示例 |
|---|---|---|---|---|
title |
字符串 | 是的 | 修剪后的小部件标题。修剪后必须非空。 | "Errors Per Hour" |
widget_type |
字符串 | 是的 | 精确枚举:query、explorer、header 或 free_text。 |
"query" |
config |
对象 | 是的 | 小部件配置对象。其必填字段取决于widget_type。 |
{ "querySql": "...", "chartType": "Line Chart" } |
layout |
对象 | 否 | 可选的网格放置。如果省略,Telemetry 使用小部件类型默认值垂直堆叠小部件。 | { "x": 0, "y": 0, "w": 12, "h": 4 } |
了解布局
Telemetry 仪表板使用 12 列网格。
layout 对象使用网格单位,而不是像素:
| 领域 | 类型 | 默认 | 精确合约 | 示例 |
|---|---|---|---|---|
x |
整数 | 0 |
非负整数。 0 是最左边的。无效值回退到 0。 |
0 |
y |
整数 | 下一个空行 | 非负整数。 0是仪表板的顶部。如果省略,Telemetry 会将小部件放置在前一个小部件的 y + h 之后。无效值会回退到该计算行。 |
0 |
w |
整数 | 12 |
网格列中的正整数宽度。 12 表示全角,6 表示半角。无效值回退到 12。 |
12 |
h |
整数 | 小部件类型默认值 | 网格行中的正整数高度。无效值会回退到下面列出的小部件类型默认值。 | 4 |
所以这个:
"layout": { "x": 0, "y": 0, "w": 12, "h": 4 }
意思是:
- 将小部件放置在仪表板的左上角
- 使其跨越所有 12 列
- 使其高为 4 行仪表板网格
这个:
"layout": { "x": 6, "y": 0, "w": 6, "h": 4 }
意思是:
- 将小部件放在顶行
- 从仪表板的一半开始
- 让它占据行的右半部分
例如,这两个小部件并排呈现:
[
{ "layout": { "x": 0, "y": 0, "w": 6, "h": 4 } },
{ "layout": { "x": 6, "y": 0, "w": 6, "h": 4 } }
]
如果省略 layout,Telemetry 将使用以下默认值垂直堆叠小部件:
x = 0w = 12h = 4适用于query和explorerh = 2为headerh = 3为free_texty放置在下一个空行上
Telemetry 不会将 x 和 w 钳位到 12 列网格,并且不会为您解决重叠位置。如果您想要可预测的渲染,请保留 x + w <= 12 并避免重叠 x/y 范围。
查询小部件配置
| 领域 | 类型 | 必填 | 精确值或格式 | 示例 |
|---|---|---|---|---|
querySql |
字符串 | 是的 | 已修剪只读 SQL 字符串。修剪后必须非空。仪表板查询小部件拒绝写入语句,例如 INSERT、UPDATE、DELETE、DROP、ALTER、TRUNCATE、CREATE、GRANT 和REVOKE。 |
"SELECT endpoint, COUNT(*) AS errors FROM http_logs GROUP BY endpoint" |
chartType |
字符串 | 是的 | 由 API 验证的精确枚举:table、Scatter Plot、Bar Chart、Line Chart 或 Stacked Area Chart。 |
"Line Chart" |
xAxis |
字符串 | 非 table 图表需要 |
用于 x 轴的修剪结果列名称。对于 Bar Chart,这可以是文本/分类、数字或类似时间的列。对于 Scatter Plot、Line Chart 和 Stacked Area Chart,请使用数字或类似时间的列。仅当 chartType 为 table 时才可以为空字符串。 |
"hour" |
yAxis |
字符串 | 非 table 图表需要 |
修剪后的数字结果列名称用于 y 轴。仅当 chartType 为 table 时才可以为空字符串。 |
"errors" |
groupBy |
字符串或 null |
否 | 可选的修剪结果列名称,用于将图表拆分为多个系列。使用 null 或省略它以不进行分组。 |
"service" |
queryId |
字符串或 null |
否 | 可选的修剪保存查询 ID。 API 将其存储为字符串,并且不验证其形状。 | "550e8400-e29b-41d4-a716-446655440000" |
sourceUrl |
字符串或 null |
否 | 单击小部件标题时打开的可选修剪 UI URL。建议使用相关应用程序 URL,例如 /team/acme/ops/draft/0?...。 |
"/team/acme/ops/draft/0?tab=chart&chartType=Line+Chart&xAxis=hour&yAxis=errors" |
注意事项:
- 如果您想要简单的结果表而不是图表,请使用
chartType: "table"。 - 仪表板查询小部件会在仪表板加载时自动运行,因此仅接受只读 SQL。
- 对于所有非
table图表,xAxis和yAxis必须与querySql返回的实际列匹配。 - 对于
Bar Chart,xAxis可以是文本/分类、数字或类时间。 - 对于
Scatter Plot、Line Chart和Stacked Area Chart,xAxis必须是数字或类时间。 - 对于所有非
table图表,yAxis必须是数字。 - 对于分类
Bar Chartx 轴,小部件保留查询结果顺序。使用querySql中的ORDER BY来控制条顺序。 - 如果省略
sourceUrl,则仪表板 UI 会在可能的情况下派生后备草稿查询 URL。派生链接保留defaultQuery、tab、chartType、xAxis、yAxis和groupBy。
示例:带有分类条形图 x 轴的查询小部件
{
"title": "Requests by Endpoint",
"widget_type": "query",
"config": {
"querySql": "SELECT endpoint, COUNT(*) AS requests FROM http_logs GROUP BY endpoint ORDER BY requests DESC",
"chartType": "Bar Chart",
"xAxis": "endpoint",
"yAxis": "requests",
"groupBy": null
}
}
在那个例子中:
endpoint是文本 x 轴列requests是 y 轴数字列ORDER BY requests DESC控制从左到右的条形顺序
标题小部件配置
使用 widget_type: "header" 作为对相关小部件进行分组的部分标题。
| 领域 | 类型 | 必填 | 精确值或格式 | 示例 |
|---|---|---|---|---|
description |
字符串或 null |
否 | 可选的降价描述显示在标题标题下。空字符串标准化为 null。 |
"Watch these charts during deploys and incident triage." |
注意事项:
- 小部件
title是可见部分标题。 description支持降价。原始 HTML 在 UI 中呈现之前会被转义。
示例:
{
"title": "Deploy Health",
"widget_type": "header",
"config": {
"description": "Use this section during rollout checks and incident response."
}
}
自由文本小部件配置
使用 widget_type: "free_text" 获取独立的 Markdown 注释、链接或运行手册说明。
| 领域 | 类型 | 必填 | 精确值或格式 | 示例 |
|---|---|---|---|---|
content |
字符串 | 是的 | 修剪后的非空 Markdown 内容。 | "Check [the latency recipe](/sql/api-latency-percentiles) before paging infra." |
注意事项:
content支持降价。原始 HTML 在 UI 中呈现之前会被转义。- 即使 UI 仅呈现此组件类型的 Markdown 主体,API 仍然需要小部件
title。
示例:
{
"title": "API Runbook Note",
"widget_type": "free_text",
"config": {
"content": "Check the on-call runbook first, then compare request volume with deploy timestamps."
}
}
资源管理器小部件配置
| 领域 | 类型 | 必填 | 精确值或格式 | 示例 |
|---|---|---|---|---|
tableName |
字符串 | 是的 | 修剪后的源表名称。修剪后必须非空。 | "queue_metrics" |
explorerState |
对象 | 是的 | 用于呈现小部件的完整探索配置。请参阅下面的详细字段表。 | { "graphType": "line", "aggregation": "p95", ... } |
sourceUrl |
字符串或 null |
否 | 单击小部件标题时打开的可选修剪 UI URL。如果省略,Telemetry 将从 tableName 和 explorerState 派生回退探索 URL。 |
"/team/acme/table/queue_metrics?tab=explore&graphType=line" |
explorerState 字段
| 领域 | 类型 | 必填 | 精确值或格式 | 默认 | 示例 |
|---|---|---|---|---|---|
graphType |
字符串 | 是的 | 由 API 验证的精确枚举:samples、table、line、bar、stacked-area。 |
"samples" |
"line" |
aggregation |
字符串 | 是的 | 由 API 验证的精确枚举:count、sum、avg、min、max、p50、p90、 p95、p99。 |
"count" |
"p95" |
metric |
字符串或 null |
否 | 修剪后的列或字段路径进行聚合。将 null 用于 count。对于非 count 聚合,这是主要度量,并且优先于自动检测到的数字 selectedColumns(如果提供)。 |
null |
"latency_ms" |
timeZone |
字符串或 null |
否 | 使用 UTC 或 IANA 时区标识符,例如 America/Los_Angeles 或 Europe/Berlin。 API 当前会修剪并存储任何非空字符串,但无效的时区名称稍后在小部件查询运行时可能会失败。如果省略,保存的配置将保留 null,并且 UI 根据上下文回退到客户端时区或 UTC。 |
null |
"America/Los_Angeles" |
timePreset |
字符串 | 是的 | 支持的值为 1h、6h、24h、7d、30d、90d、custom。 API 当前接受任何非空字符串,但 UI 和 SQL 生成器仅支持这些值。 |
"7d" |
"7d" |
customStart |
字符串 | 否 | 当 timePreset 为 custom 时使用。推荐格式:YYYY-MM-DDTHH:mm、YYYY-MM-DD HH:mm、YYYY-MM-DDTHH:mm:ss 或 YYYY-MM-DDTHH:mm:ss.sss,可选地后跟 Z 或数字偏移量(如 -07:00)。如果不存在时区后缀,则以 timeZone 解释该值。 |
未设置 | "2026-03-20T00:00:00Z" |
customEnd |
字符串 | 否 | 与 customStart 格式相同。如果自定义范围被省略,则查询没有时间上限。 |
未设置 | "2026-03-26T00:00:00-07:00" |
granularity |
字符串 | 是的 | 由 API 验证的精确枚举:auto、minute、hour、day、week、month。 |
"auto" |
"hour" |
splitBy |
字符串数组 | 是的 | 非空修剪字段名称数组。嵌套 JSON 字段允许使用点路径,例如 attributes.queue_name。 |
[] |
["queue_name"] |
seriesLimit |
整数或 null |
否 | 正整数或 null。 null 表示无明确系列上限。对于分割图最有用。 |
100 |
10 |
filters |
数组 | 是的 | 过滤器组数组。每个组在内部进行“与”运算;多个组通过 OR 组合在一起。在规范化过程中,空的或无效的组将被删除。 | [] |
[] |
selectedColumns |
字符串数组 | 是的 | 要在 samples 或 table 视图中显示的非空修剪字段名称数组。允许使用点状 JSON 路径。使用 [] 让 Telemetry 选择默认值。 |
[] |
["timestamp_utc", "queue_name", "latency_ms"] |
orderBy |
字符串或 null |
否 | 用于对表格结果进行排序的可选字段名称。允许使用点状 JSON 路径。 | null |
"timestamp_utc" |
limit |
整数 | 是的 | 正整数行或组限制。无效值将恢复为默认值。 | 200 |
200 |
orderDirection |
字符串 | 是的 | 由 API 验证的精确枚举:ASC 或 DESC。 |
"DESC" |
"DESC" |
timePreset 值
| 价值 | 含义 |
|---|---|
1h |
最后 1 小时 |
6h |
最后 6 小时 |
24h |
过去 24 小时 |
7d |
过去 7 天 |
30d |
过去 30 天 |
90d |
过去 90 天 |
custom |
使用 customStart 和 customEnd 代替相对范围 |
如果您发送未记录的 timePreset,则当前的 SQL 生成器将回退到 7 天行为。将上述值视为受支持的合约。
granularity: "auto" 行为
当 granularity 为 auto 时,Telemetry 解析如下:
timePreset |
有效粒度 |
|---|---|
1h、6h、24h |
minute |
7d |
hour |
30d、90d、custom |
day |
过滤组和条件
filters 的形状应如下所示:
[
{
"logic": "AND",
"conditions": [
{ "field": "queue_name", "operator": "=", "value": "email" },
{ "field": "latency_ms", "operator": ">", "value": "1000" }
]
}
]
filters中的每一项都是一个过滤器组:
| 领域 | 类型 | 必填 | 精确值或格式 | 示例 |
|---|---|---|---|---|
logic |
字符串 | 是的 | 必须是 AND。仪表板创建 API 将每个组标准化为 AND;没有每组 OR。要表达 OR,请使用多个组,因为组是通过 OR 组合在一起的。 |
"AND" |
conditions |
数组 | 是的 | 组中的过滤条件数组。有效条件为零的组将被删除。 | [{ "field": "queue_name", "operator": "=", "value": "email" }] |
conditions中的每个项目都支持:
| 领域 | 类型 | 必填 | 精确值或格式 | 示例 |
|---|---|---|---|---|
field |
字符串 | 是的 | 非空修剪字段路径。支持点嵌套 JSON 路径,例如 attributes.queue_name。不允许有空路径段。 |
"latency_ms" |
operator |
字符串 | 是的 | 由 API 验证的精确枚举:=、!=、>、>=、<、<=、LIKE、 NOT LIKE、IS NULL、IS NOT NULL。 |
">" |
value |
字符串、数字、布尔值或 null |
通常 | 在标准化期间存储为字符串。对于 IS NULL 和 IS NOT NULL,使用 "" 或省略语义值。 null 变为 ""。 |
"1000" |
生成的SQL运行时:
- 组内条件与
AND相连接 - 组加入
OR - 点状字段路径(例如
attributes.queue_name)被转换为嵌套字段访问
示例:创建一个空仪表板
示例:列出仪表板
curl "https://api.telemetry.sh/dashboard?page=1&pageSize=25" \
-H "Authorization: $API_KEY"
响应示例:
{
"status": "success",
"pagination": {
"page": 1,
"pageSize": 25,
"total": 1,
"totalPages": 1,
"hasNextPage": false,
"hasPreviousPage": false
},
"dashboards": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"team_id": "team_123",
"name": "Operations Overview",
"slug": "operations-overview",
"description": "Core service health and latency",
"created_by": null,
"created_by_api_key_id": "key_123",
"created_at": "2026-03-27T00:09:03.797Z",
"updated_at": "2026-03-27T00:09:03.797Z",
"url": "/team/acme/dashboard/operations-overview",
"widget_count": 1,
"widgets": [
{
"id": "widget_123",
"title": "Requests Per Hour",
"widget_type": "query",
"config": {
"querySql": "SELECT DATE_TRUNC('hour', timestamp_utc) AS hour, COUNT(*) AS requests FROM http_logs GROUP BY 1 ORDER BY 1",
"chartType": "Line Chart",
"xAxis": "hour",
"yAxis": "requests",
"groupBy": null,
"sourceUrl": "/team/acme/ops/draft/0?tab=chart&chartType=Line+Chart&xAxis=hour&yAxis=requests"
},
"layout": { "x": 0, "y": 0, "w": 12, "h": 4 },
"sort_order": 0
}
]
}
]
}
示例:创建一个空仪表板
curl -X POST https://api.telemetry.sh/dashboard \
-H "Content-Type: application/json" \
-H "Authorization: $API_KEY" \
-d '{
"name": "Operations Overview",
"description": "Core service health and latency"
}'
示例:重命名仪表板并更新其描述
curl -X PATCH https://api.telemetry.sh/dashboard \
-H "Content-Type: application/json" \
-H "Authorization: $API_KEY" \
-d '{
"dashboardSlug": "operations-overview",
"name": "Operations Overview v2",
"description": "Updated to focus on API latency and error rates"
}'
示例:替换现有仪表板上的所有小部件
此请求保留相同的仪表板,但用新的 widgets 阵列替换完整的小部件集。
curl -X PATCH https://api.telemetry.sh/dashboard \
-H "Content-Type: application/json" \
-H "Authorization: $API_KEY" \
-d '{
"dashboardSlug": "operations-overview",
"widgets": [
{
"title": "Revenue by City",
"widget_type": "explorer",
"config": {
"tableName": "uber_rides",
"explorerState": {
"graphType": "table",
"aggregation": "sum",
"metric": "price",
"timePreset": "30d",
"granularity": "auto",
"splitBy": ["city"],
"filters": [],
"selectedColumns": [],
"orderBy": "price",
"limit": 10,
"orderDirection": "DESC"
}
},
"layout": { "x": 0, "y": 0, "w": 12, "h": 4 }
}
]
}'
示例:删除仪表板
curl -X DELETE https://api.telemetry.sh/dashboard \
-H "Content-Type: application/json" \
-H "Authorization: $API_KEY" \
-d '{
"dashboardSlug": "operations-overview"
}'
常见小部件示例
下一个示例仅显示您发送到 POST /dashboard 的 JSON 请求正文。
这些示例假设 uber_rides 表包含以下字段:
citypricewait_time_minutesstatususer_idtimestamp_utc
示例:创建一个包含两个常见 Explore 小部件的仪表板
该示例包括:
- 一个表格小部件,按城市汇总乘车收入并按收入降序排序
- 一个时间序列小部件,将按城市的平均等待时间绘制为分组线
{
"name": "Uber Marketplace Overview",
"widgets": [
{
"title": "Revenue by City",
"widget_type": "explorer",
"config": {
"tableName": "uber_rides",
"explorerState": {
"graphType": "table",
"aggregation": "sum",
"metric": "price",
"timePreset": "30d",
"granularity": "auto",
"splitBy": ["city"],
"filters": [
{
"logic": "AND",
"conditions": [
{ "field": "status", "operator": "=", "value": "completed" }
]
}
],
"selectedColumns": ["city", "price"],
"orderBy": "price",
"limit": 20,
"orderDirection": "DESC"
}
},
"layout": { "x": 0, "y": 0, "w": 6, "h": 4 }
},
{
"title": "Average Wait Time by City",
"widget_type": "explorer",
"config": {
"tableName": "uber_rides",
"explorerState": {
"graphType": "line",
"aggregation": "avg",
"metric": "wait_time_minutes",
"timePreset": "7d",
"granularity": "hour",
"splitBy": ["city"],
"seriesLimit": null,
"filters": [
{
"logic": "AND",
"conditions": [
{ "field": "status", "operator": "=", "value": "completed" }
]
}
],
"selectedColumns": ["city", "wait_time_minutes"],
"limit": 200,
"orderDirection": "ASC"
}
},
"layout": { "x": 6, "y": 0, "w": 6, "h": 4 }
}
]
}
在那个例子中:
Revenue by City是一个探索表格小部件selectedColumns: ["city", "price"]加上aggregation: "sum"为每个city生成一个聚合price色谱柱orderBy: "price"按汇总收入列排序Average Wait Time by City是一个探索行小部件splitBy: ["city"]每个城市创建一条线路orderDirection: "ASC"保持时间轴按时间顺序排列
示例:创建队列保留微笑曲线的查询小部件
此示例使用查询小部件,因为群组保留在探索中更难表达。它使用:
- 首次乘坐群体定义
- 独特的每周活动表
- 队列规模和保留乘客之间的联系
可读querySql:
WITH first_rides AS (
SELECT
user_id,
date_trunc('week', MIN(timestamp_utc)) AS cohort_week
FROM
uber_rides
WHERE
status = 'completed'
GROUP BY
user_id
),
weekly_activity AS (
SELECT DISTINCT
user_id,
date_trunc('week', timestamp_utc) AS activity_week
FROM
uber_rides
WHERE
status = 'completed'
),
cohort_activity AS (
SELECT
f.cohort_week,
a.activity_week,
date_part('day', a.activity_week - f.cohort_week) / 7 AS weeks_since_first_ride,
COUNT(DISTINCT a.user_id) AS retained_riders
FROM
first_rides f
JOIN weekly_activity a ON a.user_id = f.user_id
WHERE
a.activity_week >= f.cohort_week
GROUP BY
f.cohort_week,
a.activity_week,
date_part('day', a.activity_week - f.cohort_week) / 7
),
cohort_sizes AS (
SELECT
cohort_week,
COUNT(*) AS cohort_size
FROM
first_rides
GROUP BY
cohort_week
)
SELECT
weeks_since_first_ride,
100.0 * retained_riders / cohort_size AS retention_pct,
CAST(ca.cohort_week AS VARCHAR) AS cohort_week
FROM
cohort_activity ca
JOIN cohort_sizes cs ON ca.cohort_week = cs.cohort_week
WHERE
weeks_since_first_ride BETWEEN 0 AND 12
ORDER BY
weeks_since_first_ride ASC,
cohort_week ASC
{
"name": "Uber Rider Retention",
"widgets": [
{
"title": "Weekly Rider Retention Smile Curves",
"widget_type": "query",
"config": {
"querySql": "WITH first_rides AS ( SELECT user_id, date_trunc('week', MIN(timestamp_utc)) AS cohort_week FROM uber_rides WHERE status = 'completed' GROUP BY user_id ), weekly_activity AS ( SELECT DISTINCT user_id, date_trunc('week', timestamp_utc) AS activity_week FROM uber_rides WHERE status = 'completed' ), cohort_activity AS ( SELECT f.cohort_week, a.activity_week, date_part('day', a.activity_week - f.cohort_week) / 7 AS weeks_since_first_ride, COUNT(DISTINCT a.user_id) AS retained_riders FROM first_rides f JOIN weekly_activity a ON a.user_id = f.user_id WHERE a.activity_week >= f.cohort_week GROUP BY f.cohort_week, a.activity_week, date_part('day', a.activity_week - f.cohort_week) / 7 ), cohort_sizes AS ( SELECT cohort_week, COUNT(*) AS cohort_size FROM first_rides GROUP BY cohort_week ) SELECT weeks_since_first_ride, 100.0 * retained_riders / cohort_size AS retention_pct, CAST(ca.cohort_week AS VARCHAR) AS cohort_week FROM cohort_activity ca JOIN cohort_sizes cs ON ca.cohort_week = cs.cohort_week WHERE weeks_since_first_ride BETWEEN 0 AND 12 ORDER BY weeks_since_first_ride ASC, cohort_week ASC",
"chartType": "Line Chart",
"xAxis": "weeks_since_first_ride",
"yAxis": "retention_pct",
"groupBy": "cohort_week"
},
"layout": { "x": 0, "y": 0, "w": 12, "h": 5 }
}
]
}
在那个例子中:
weeks_since_first_ride是 x 轴retention_pct是 y 轴groupBy: "cohort_week"为每个注册队列创建一条线,从而生成微笑曲线风格比较- 这是一个查询小部件而不是探索小部件,因为队列逻辑依赖于多个 CTE 和连接
回应
成功的 POST 请求返回 201 Created。
成功的 PATCH 请求返回 200 OK。
两者都返回这个形状:
{
"status": "success",
"dashboard": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"team_id": "team_123",
"name": "HTTP Monitoring",
"slug": "http-monitoring",
"description": null,
"created_by": null,
"created_by_api_key_id": "8dff0c1a-8b36-49c3-9e6b-dc0d3ff51d74",
"created_at": "2026-03-26T00:00:00.000Z",
"updated_at": "2026-03-26T00:00:00.000Z",
"url": "/team/acme/dashboard/http-monitoring"
},
"widgets": [
{
"id": "9a6e4f27-d3bf-4a70-bf3a-38fe63211f93",
"title": "Errors Per Hour",
"widget_type": "query",
"config": {
"queryId": null,
"querySql": "SELECT ...",
"chartType": "Line Chart",
"xAxis": "hour",
"yAxis": "errors",
"groupBy": null,
"sourceUrl": null
},
"layout": {
"x": 0,
"y": 0,
"w": 12,
"h": 4
},
"sort_order": 1
}
]
}
成功的 DELETE 请求返回 200 OK 以及删除摘要:
{
"status": "success",
"deleted_dashboard": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "HTTP Monitoring",
"slug": "http-monitoring",
"description": null,
"widget_count": 6,
"url": "/team/acme/dashboard/http-monitoring"
}
}
通过此 API 创建的仪表板是团队范围的,并归因于创建它们的 API 密钥:
- 对于 API 创建的仪表板,
created_by是null,因为 API 密钥不是用户 created_by_api_key_id是创建密钥的内部team_api_keys.id行- 通过 UI 创建的仪表板仍然使用
created_by和用户 ID,并且通常将created_by_api_key_id保留为null
如果您的团队已存在标准化的段,则 API 将返回 409 Conflict。
其他常见错误:
400 Bad Request(如果必填字段缺失或无效)400 Bad Request如果查询小部件包含非只读 SQL404 Not Found(如果您的团队不存在目标仪表板)401 Unauthorized(如果 API 密钥丢失或无效)400 Bad Request如果 API 密钥仅具有read范围