REST API

读写你的监控项,读取和更新事件,上传 source map。这里的一切都使用与仪表板相同的权限检查和相同的租户隔离过滤——不存在另一套 API 专用实现会与它发生偏离。

所有套餐都包含,免费套餐也不例外。 没有需要额外购买的附加包,也没有哪一档才解锁它。

基础 URL

https://vitrinaengine.com/api/v1

机器可读的描述

/openapi.json 是一份 OpenAPI 3.1 文档,覆盖本页列出的全部路由。把客户端生成器指向它,或者把它交给一个必须在无人讲解的情况下调用这套 API 的智能体。

它是从服务本身生成的,而不是写在服务旁边:路由来自 API 在 GET / 上公布的接口面,消息结构及其说明文字来自同一批 .proto 文件,也就是 /api/proto 上的那些,速率限制则来自执行这些限制的代码。当提交的文档与 API 不一致时构建会失败,所以它描述的是实际提供的服务,而不是某人上次编辑时的情况。

它描述的是结构。原因写在本页——跨组织的 id 会返回 404、config 永远不会被返回——而这些是任何生成器都写不出来的。

面向智能体:MCP

https://vitrinaengine.com/mcp 是一个 Model Context Protocol 服务器。把它加入某个 MCP 客户端,用 API 密钥作为 bearer token,智能体就能读取你的监控项、事件、错误和分析数据,也能创建、修改和删除监控项,无需任何人向它解释这套 API 如何工作。

{
  "mcpServers": {
    "vitrina-engine": {
      "type": "http",
      "url": "https://vitrinaengine.com/mcp",
      "headers": { "Authorization": "Bearer vte_your_key_here" }
    }
  }
}

它读取一切,只写三样东西。 本页中的每个 GET 操作都是一个工具,此外还有 POST /monitorsPATCH /monitors/{id}DELETE /monitors/{id}。其他任何会写入的东西都够不着——工作区、成员、API 密钥、状态页、你的账户,都不行——因为它们根本没有对应的工具;而允许写入的那份清单,是需要有人特意添上去的三行。

没有写权限的密钥照样会被拒绝。 一次工具调用就是该密钥自己会发出的那个请求,经过同样的权限检查和同样的租户过滤,所以拿着只读密钥的智能体就只能读。除非你确实想让它改动你的监控,否则就给它一把只读密钥——并且要预料到客户端在删除前会先问你一句:删掉一个监控项,它的历史也一并带走,没有撤销。想让一个监控项安静下来又不丢掉它,用 PATCH 把它暂停。

因为是同一个请求,它看到的与密钥看到的完全一致,会写下和仪表盘一样的审计记录,并且计入同一个速率限制——计两次,一次是这次调用,一次是它内部的那次请求。写入计入写入限额。

在让智能体编辑监控项之前,有一点值得知道:config 是整体替换,而不是合并。先读出这个监控项,改掉那一个字段,再把整个对象发回去。残缺的配置会悄悄丢掉检查所依赖的请求头和凭据,而检查仍旧照常运行。

身份验证

设置 → API 密钥中创建一把密钥,然后把它作为 bearer token 发送。密钥只在创建时显示一次,并且只以哈希形式保存——弄丢了就再建一把。

curl https://vitrinaengine.com/api/v1/summary \
  -H "Authorization: Bearer vte_your_key_here"

密钥授予的权限,绝不会超过创建它的人所拥有的。 它绑定在那个人的成员身份上,因此继承其角色:由只读角色的人创建的密钥无法写入,无论你发送什么。密钥还可以被限制在单个工作区内,这种情况下每个响应都会过滤到该工作区,对这把密钥而言,工作区之外的一切都不存在。

把某人移出你的组织,会在同一次操作中吊销他创建的密钥。撤销访问权必须连同凭据一起撤销,否则这个人手里还留着一把能用的。

约定

每个成功的响应都是一个带 data 属性的 JSON 对象。每个失败都是一个带 error 属性的 JSON 对象,其中是一句写给人看的话。

{ "data": { "id": "8f14e45f-…", "name": "Marketing site" } }

{ "error": "This key cannot create monitors." }

响应都带 Cache-Control: private, no-store 发出。这是共享源站上按密钥区分的数据,绝不能被任何代理缓存。

Protobuf 与 gzip

/api/v1 下的每个端点同样支持 Protocol Buffers。发送 Accept: application/x-protobuf,响应就以 protobuf 返回;如果用 Content-Type: application/x-protobuf 发送 protobuf 请求体,响应也会以同一格式返回。两者都不发送,那就还是 JSON,和以前完全一样。

curl https://vitrinaengine.com/api/v1/monitors \
  -H "Authorization: Bearer vte_your_key_here" \
  -H "Accept: application/x-protobuf" \
  --compressed -o monitors.pb

这些消息是公开的:/api/proto 列出了全部文件。下载时保留它们的路径,并把 protoc -I 指向该目录。有三点与 protobuf 用户的预期不同:可以为空的字段在 JSON 中是 null,在 protobuf 中则是缺失;少数结构可变的字段——比如监控项或通知渠道的 config——是装在字符串里的 JSON 文档;还有 StringList,它包装的列表本身可以是 null

Gzip 双向可用:响应用 Accept-Encoding: gzip,请求用 Content-Encoding: gzip。既然两个调用方现在可能从同一个 URL 拿到不同的字节,每个响应都带上了 Vary: Accept, Content-Type

状态码

状态码含义
200一切正常。创建了东西时是 201
400请求体不是 JSON、缺少某个字段,或者什么都没要求改。
401没有密钥,或者密钥无效。刻意从不说明是哪一种——能区分两者的消息就是一种试探密钥的手段。
403密钥有效,但没有执行此操作的权限。消息中会点明是哪一个。
404对你而言没有这条记录。详见下文。
413source map 超过了体积上限。
429超出了某个速率限制。会带 Retry-After。详见下文。

拒绝代码

每个非 2xx 响应都在句子旁带有 code——not_foundlimit_reachedrate_limited——应当据此分支。error 字符串是写给看日志的人的散文,随时可能改写;代码则是稳定的。句子提到某个值时,vars 会以字符串携带这些值,你可以按自己语言的语序放置它们,而不必从英文里解析出来。

{
  "error": "The Pro plan includes 50 monitors, and you have 50.",
  "code": "limit_reached",
  "vars": { "plan": "pro", "limit": "50", "current": "50", "resource": "monitors" }
}

我们自己的应用就是据此用读者的语言呈现拒绝信息的。遇到不认识的代码请照此处理——回退到 error——因为新增代码不会提升版本号。

速率限制

按组织计算,而不是按密钥——再建一把密钥并不会把上限抬高。

限制
读取(GET每分钟 120 次
写入(POSTPATCHDELETE每分钟 30 次
source map 上传每小时 300 次

超限会返回 429,并带一个 Retry-After 头,给出距窗口重置还有多少整秒。请遵守它,不要立刻重试。

这些数值定在正常集成永远碰不到的地方:一块每十秒轮询一次的大屏看板只用掉 120 次读取中的 6 次。所有套餐一视同仁,因为集合类端点会在一个响应里返回全部内容——一个有五百个监控项的账户并不比只有二十个的账户需要更多请求。source map 用的是按小时的桶,因为它们在部署时成批到达,一个前端很容易就有上百个 chunk。

刻意返回 404 而不是 403

属于另一个客户的 id 会得到 「Not found.」,而不是「Forbidden.」。403 会确认这条记录存在,那样端点就成了一种试探 id 是否真实的手段。不要把 404 当作某样东西在任何地方都不存在的证明——它只说明没有任何这把密钥可见的东西存在。

端点

账户中与 ?q= 匹配的一切:监控、事件及其事后分析、主机、状态页、工作区、通知渠道、维护窗口、错误项目与问题、分析站点、成员与邀请、API 密钥、代理和私有探针。一次请求即可,而不是每种类型一次。

?type= 可用逗号分隔的类型列表缩小范围,?limit= 限制每种类型返回的 数量,范围 1 到 20(默认 5)。每种类型所需的权限与其自身端点相同;密钥无权读取的类型不会出现,与没有匹配结果的类型表现一致。任何密封、哈希或机密的内容都不会被检索或返回。

参数与响应
名称位置类型
limitqueryinteger
qquerystring
typequerystring
响应SearchResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

GET /summary

各类计数,供大屏看板或每日摘要使用。需要 monitor:read

{
  "data": {
    "total": 42, "up": 39, "degraded": 1, "down": 1,
    "paused": 1, "pending": 0,
    "openIncidents": 2, "suppressedIncidents": 1
  }
}

suppressedIncidents 统计的是因属于另一起事件的影响范围而被抑制的事件。它们是真实的,但不是各自独立的故障。

参数与响应

未声明参数或请求体字段。

响应GetSummaryResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

GET /setup

新账户还剩下哪些事要做——与仪表板上的初始设置清单所问的是同样四个问题,每次读取时现场推导。需要 monitor:read

{
  "data": {
    "hasMonitor": true,
    "hasConfirmedChannel": false,
    "hasStatusPage": false,
    "hasColleague": false,
    "dismissedAt": null
  }
}

hasConfirmedChannel 问的是告警是否真的能送达,而不是是否存在一个渠道。这两件事曾经严重地脱节过,而一份因为存在某个通知器根本不会投递的渠道就把「告警已配置好」打上勾的清单,等于把那个缺陷以「让人放心」的形式重演一遍。

hasColleague 统计的是成员身份,从不统计邀请:一封发出而未被接受的邀请没有增加任何人。

它没有并进 /summary,因为那个端点只有按状态的计数、别无其他——一块每隔几秒就轮询它的大屏看板,不会为了渲染一条写给几个月前建好账户的人的提示而多做五次查询。

参数与响应

未声明参数或请求体字段。

响应GetSetupResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

DELETE /setup

跳过这份清单。需要 org:update,因为这次忽略是一条组织级的记录,而不是每个人各自的偏好:在它被跳过之后才加入的同事不会再看到它。

账户的其他方面不会有任何变化,也不会写审计日志条目:隐藏一条提示既不改变产品的行为,也不改变它如何告警、如何计费。

参数与响应

未声明参数或请求体字段。

响应DismissedResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

GET /monitors

这把密钥能看到的全部监控项。需要 monitor:read。每一项都带有 idnamekindstatusstatusSinceenabledintervalSecondstagsworkspacelastCheckedAtlastResponseTimeMslastMessageuptime24huptime30dopenIncidentId

?tag=prod,eu 会把列表收窄到同时带有全部所列标签的监控项——是「与」,不是「或」,所以多写一个标签总是返回更少的监控项,而不是更多。控制台自己的筛选也是这么读的。标签按小写存储,这里的值在比较前也会同样转成小写,所以 ?tag=Prod 找得到它们。不可能成为标签的条目——空的,或者比标签允许的还长——会被丢掉,筛选的其余部分照常生效,而不是拒绝这次调用:一条链接不该因为其中一个标签被改名就不能用了。

config 永远不会被返回。 在某些类型中它含有请求头和凭据,而一把只读密钥不应该成为把别人填进表单的机密再读回来的途径。

参数与响应
名称位置类型
tagquerystring
响应ListMonitorsResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

POST /monitors

需要 monitor:create。必填 namekindworkspaceIdconfig。可选:intervalSeconds(默认 300)、confirmationsdependsOn

curl -X POST https://vitrinaengine.com/api/v1/monitors \
  -H "Authorization: Bearer vte_…" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Marketing site",
    "kind": "http",
    "workspaceId": "…",
    "intervalSeconds": 60,
    "config": {
      "url": "https://example.com",
      "assertions": [{ "type": "status_code", "operator": "lt", "value": "400" }]
    }
  }'

返回 201 和新的 id。间隔会被收紧到你所在套餐的下限,而不是被拒绝,所以在下限为 60 的套餐上请求 10 秒得到的是 60——如果这一点对你重要,就把它读回来确认。

参数与响应
名称位置类型
namebodystring
kindbodystring
workspaceIdbodystring
configbodystring
intervalSecondsbodyinteger
confirmationsbodyinteger
dependsOnbodystring[]
probeIdbodystring
tagsbodystring[]
响应CreatedResponse
状态码201 400 401 403 404 429
速率限制每分钟 30 次

GET /monitors/{id}

单个监控项,包含列表给出的全部内容,外加 paused。需要 monitor:read

对于 mail_posturetls_auditsnmp,它还带有 lastCheckDetail,即最近一次检查本身的结果:状态背后的各个发现项,每一项都有一个稳定的 code 和一个 severity,外加一个 TLS 评级或若干 SNMP 读数。在第一次检查运行之前它是 null,其他类型则没有这个字段。

参数与响应
名称位置类型
id 必填pathstring
响应GetMonitorResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

PATCH /monitors/{id}

只发送你想改的内容:nameintervalSecondsconfirmationsenabledconfigpaused

paused 对照 monitor:pause 检查,其余一切对照 monitor:update 检查,两者分开——一把可以暂停但不能编辑的密钥仍然可以暂停。发送一个空的改动会得到 400

监控项会被重新读取后返回,而不是把你的输入原样回显,所以你看到的就是实际存下来的内容。

# Silence a monitor for the length of a deploy.
curl -X PATCH https://vitrinaengine.com/api/v1/monitors/$ID \
  -H "Authorization: Bearer vte_…" \
  -H "Content-Type: application/json" \
  -d '{"paused": true}'
参数与响应
名称位置类型
id 必填pathstring
namebodystring
intervalSecondsbodyinteger
confirmationsbodyinteger
enabledbodyboolean
pausedbodyboolean
configbodystring
tagsbodyStringList
响应UpdatedMonitorResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

DELETE /monitors/{id}

需要 monitor:delete。返回 { "data": { "id": "…", "deleted": true } }

参数与响应
名称位置类型
id 必填pathstring
响应AckResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

GET /hosts · POST /hosts

主机是监控项的第二种分组方式:工作区说明监控项属于谁,主机说明它跑在什么机器上。完全可选——从不创建主机的账户不会失去任何东西,大多数监控项也根本不指定主机。

每台主机都带有 statuspinnedspanningstatus 由仅运行在该主机上的监控项推导而来。横跨多台机器的监控项计入 spanning,并且不会给其中任何一台上色:负载均衡端点失败说明服务坏了,而不是说明哪台机器坏了。 没有专属监控项的主机返回 "status": null 而不是 up

POST 需要 monitor:createname。可选的 provideraddressnotes 只供您自己查看,产品不会读取它们。 与已有主机同名(忽略大小写与结尾的点)会返回 409

参数与响应

GET /hosts

未声明参数或请求体字段。

响应ListHostsResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

POST /hosts

名称位置类型
namebodystring
providerbodystring
addressbodystring
notesbodystring
响应CreatedResponse
状态码201 400 401 403 404 429
速率限制每分钟 30 次

GET /hosts/{id} · PATCH /hosts/{id} · DELETE /hosts/{id}

详情包含该主机及其上的监控项,每项都标有 pinned;若不是专属项,则带上 alsoOn:它还从哪些机器响应——这正是您在重启之前要看的内容。DELETE 会下线该主机,而上面的每个监控项都会继续运行:主机既没有自己的计划任务,也没有自己的历史。

参数与响应

GET /hosts/{id}

名称位置类型
id 必填pathstring
响应GetHostResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

PATCH /hosts/{id}

名称位置类型
id 必填pathstring
namebodystring
providerbodystring
addressbodystring
notesbodystring
响应AckResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

DELETE /hosts/{id}

名称位置类型
id 必填pathstring
响应AckResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

POST /hosts/{id}/monitors · DELETE /hosts/{id}/monitors

一次一个监控项的挂载或摘除,通过请求体中的 monitorId ?monitorId=。刻意不是整份列表:在代理商账户里,一台主机承载着多个工作区的监控项, 替换“整份列表”就会替换掉调用方从未看到过的行。两个方向都是幂等的。

参数与响应

POST /hosts/{id}/monitors

名称位置类型
id 必填pathstring
monitorIdbodystring
响应AckResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

DELETE /hosts/{id}/monitors

名称位置类型
id 必填pathstring
monitorIdquerystring
monitorIdbodystring
响应AckResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

GET /monitors/{id}/hosts · PUT /monitors/{id}/hosts

同一关系的另一侧,这一侧才是整份列表:这些关联属于单个监控项,另一端没有任何别的工作区能看到的东西。 发送 hostIds;空数组是一个真实请求,表示该监控项不属于任何特定主机。不属于您的 id 会被丢弃而不是被拒绝。 每个监控项响应同样带有 hosts,没有主机时为空。

参数与响应

GET /monitors/{id}/hosts

名称位置类型
id 必填pathstring
响应ListMonitorHostsResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

PUT /monitors/{id}/hosts

名称位置类型
id 必填pathstring
hostIdsbodystring[]
响应ListMonitorHostsResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

GET /incidents

需要 incident:read。接受 ?status=——取值为 openacknowledgedresolvedsuppressed 之一——以及 ?open=true 表示那三种尚未解决的状态。无法识别的状态会被忽略而不是被拒绝,正如 ?limit=(默认 50)会被收紧到 200 而不是被拒绝一样。

每一条都带有 idmonitorIdmonitorNametitlecausestatusseveritystartedAtresolvedAtdurationSecondsacknowledgedAtrootIncidentId

如果你在搭建告警信息流,rootIncidentId 就是该看的那个字段。 当它有值时,这起事件属于另一起事件的影响范围——数据库服务器宕了,而这是它背后十二个服务中的一个。跳过这些,你收到的就是一条告警而不是十三条。

参数与响应
名称位置类型
limitqueryinteger
openqueryboolean
statusquerystring
响应ListIncidentsResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

GET /incidents/{id}

单个事件。需要 incident:read。字段与列表相同,外加 workspaceId

不会返回事件的时间线——仪表板里看到的评论和状态变更不在这个端点上。如果你需要它们,说一声就可以加上;在它们还不存在时就把它们写进这里,比留着这个缺口更糟。

参数与响应
名称位置类型
id 必填pathstring
响应GetIncidentResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

PATCH /incidents/{id}

发送值为 "acknowledged""resolved"status,或者一条 comment,或者两者都发。每一项都对照各自的权限检查:incident:acknowledgeincident:resolveincident:comment。给评论加上 "publish": true 会把它发布到状态页,这还额外需要 status_page:manage

# A runbook that fixed the thing itself can close its own incident.
curl -X PATCH https://vitrinaengine.com/api/v1/incidents/$ID \
  -H "Authorization: Bearer vte_…" \
  -H "Content-Type: application/json" \
  -d '{"status": "resolved", "comment": "Restarted by runbook.", "publish": true}'
参数与响应
名称位置类型
id 必填pathstring
statusbodystring
commentbodystring
publishbodyboolean
响应GetIncidentResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

PUT /incidents/{id}/postmortem

撰写或替换事件的事后复盘。需要 incident:resolve,且事件必须已解决——否则返回 409,代码为 incident_not_resolved。以 Markdown 发送 body,去除首尾空白后为 1 到 50,000 个字符(postmortem_requiredpostmortem_too_long)。每个事件只有一份,所以每次调用都会替换上一份。响应是事件本身,与 GET /incidents/{id} 返回的相同。

GET /incidents/{id}postmortem: { body, authorName, updatedAt }null 的形式携带它,列表则携带 hasPostmortem。请将 body 渲染为 Markdown,绝不要作为 HTML:它是一个人输入的文本。事后复盘是内部文档,从不出现在状态页上。

curl -X PUT https://vitrinaengine.com/api/v1/incidents/$ID/postmortem \
  -H "Authorization: Bearer vte_…" \
  -H "Content-Type: application/json" \
  -d '{"body": "## What happened\n\nThe connection pool ran dry after a deploy."}'
参数与响应
名称位置类型
id 必填pathstring
bodybodystring
响应GetIncidentResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

GET /probes

你的组织自己的私有探针:id, name, lastSeenAt, version, hostname, monitorCount, revokedcreatedAt。需要 agent:read。这就是监控的探针选择器所提供的内容。

没有区域字段,将来也不会有。你按名称选择探针;检查在其背后于何处运行,由我们决定和调整。已吊销的探针会带 revoked: true 列出,且不再接收任务,所以不要提供它们。

参数与响应

未声明参数或请求体字段。

响应ListProbesResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

GET /monitors/{id}/push-url

再次显示 heartbeat 监控的 ping URL。需要 monitor:read。对于不接收 ping 的类型,以及无法再解密的令牌,urlnull——监控仍会继续工作,在控制台中轮换令牌即可得到可以显示的 URL。

参数与响应
名称位置类型
id 必填pathstring
响应GetPushUrlResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

GET /organizations · PUT /organizations/active

已登录用户所属的组织,每个都带有其在该组织中的 role,当前使用的那个带 active,以及在它们之间切换。发送 organizationId;响应是现在使用的成员身份。

仅限已登录会话。密钥只属于一个成员身份,会收到 403session_required。切换会移动此人的所有会话,包括控制台;对于其不属于的组织,返回 404

参数与响应

GET /organizations

未声明参数或请求体字段。

响应ListOrganizationsResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

PUT /organizations/active

名称位置类型
organizationIdbodystring
响应ActiveOrganizationResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

POST /sms/enrolment · POST /sms/enrolment/confirm

为短信告警登记你自己的手机号码:发送 phone(可选 name),我们会向其发送六位数验证码;将该 code 发送到第二个路由进行确认。需要 notification_channel:manage 和已登录会话——号码由手持手机的人添加,绝不由密钥添加。

号码以未确认状态保存,在验证码返回之前不会收到任何内容。再次登记会替换你之前的号码。拒绝:sms_not_included(402,套餐不含短信)、not_a_phone_number, sms_country_unsupported, sms_route_not_openverification_code_not_sent(502;再次调用以获取新验证码)、verification_code_malformedverification_code_incorrect。成功开始时会携带 countrycaveatsender-replaced, registration-pending, unverifiednull),值得在有人依赖该号码之前展示。

参数与响应

POST /sms/enrolment

名称位置类型
phonebodystring
namebodystring
响应SmsEnrolmentResponse
状态码201 400 401 403 404 429
速率限制每分钟 30 次

POST /sms/enrolment/confirm

名称位置类型
codebodystring
响应SmsConfirmedResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

POST /channels/{id}/confirmation

重新发送邮件渠道的确认消息。需要 notification_channel:manage。每次调用都会生成新链接,之前的链接随即失效。sent: false 表示没有需要发送的内容(该地址已确认),并且绝不会通过此方式再次给已确认的地址发邮件。如果邮件服务商拒绝发送,返回 502confirmation_not_sent

参数与响应
名称位置类型
id 必填pathstring
响应ChannelConfirmationResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

PATCH /workspaces/{id}

重命名工作区。需要 workspace:updatename 必填(1 到 80 个字符),clientReference 可选——null 会将其清除。slug 永远不会改变,因为它出现在状态页的 URL 中。响应是更新后的工作区。

参数与响应
名称位置类型
id 必填pathstring
namebodystring
clientReferencebodyrequests.NullableString
响应UpdatedWorkspaceResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

POST /sourcemaps

上传一份 source map,让压缩后的堆栈跟踪能够还原。需要 monitor:create。必填 projectRef(一个数字)和 filename,外加 map 本身,形式为 map(JSON)或 mapGzipBase64。可选:debugIdrelease

请提供 debug id 或 release。 两者都没有的 map 无法与堆栈跟踪匹配,只会放在那里什么也不做。匹配先按 debugId,然后按 release 加文件名。

map 是在读取某个问题时解析的,而不是在上传时,所以在错误已经到达之后才上传的 map 依然有用——而这正是通常的先后顺序。

参数与响应
名称位置类型
projectRefbodyinteger
filenamebodystring
debugIdbodystring
releasebodystring
mapbodystring
mapGzipBase64bodystring
响应UploadedSourceMapResponse
状态码201 400 401 403 404 429
速率限制每小时 300 次

POST /symbols

对原生应用来说是同一个思路:上传 iOS 的 .dSYM 或 Android R8 的 mapping.txt,让崩溃堆栈能够还原。需要 monitor:create。每小时二十次上传,每个项目保留二十个构建版本的符号。

/sourcemaps 不同,请求体就是 gzip 压缩后的文件本身,元数据放在请求头里:X-Vitrina-Project-RefX-Vitrina-Platformiosandroid)、X-Vitrina-Symbol-NameX-Vitrina-Release。一份 dSYM 有几十兆,而把它 base64 塞进 JSON 字段会让传输量再多三分之一。

两个平台的匹配方式不同。 一个 iOS 栈帧会指明它所属镜像的 Mach-O UUID,所以 iOS 的上传必须带上 X-Vitrina-Debug-Ids,并按它精确匹配。R8 不产生这样的 id,所以 Android 的 mapping 只按 release 匹配——而它必须与应用上报的完全一致,即 <applicationId>@<versionName>+<versionCode>。差一个数字,栈帧就会还原到看似合理却是错误的行上。

重新上传同一个构建会替换它的符号,而不是失败,所以重试一次发版任务并不是错误。与 source map 一样,还原发生在读取某个问题的时候。

参数与响应

未声明参数或请求体字段。

响应UploadedSymbolsResponse
状态码200 400 401 403 404 429
速率限制每小时 20 次

GET /errors/projects

这个账户下的错误追踪项目。需要 monitor:read。每一项都带有 ref——也就是 DSN 里的那个数字——外加 publicKeyunresolvedeventsLast24h。只读:创建项目时给出的密钥仍然要粘贴进某个应用的配置里,所以这里没有什么是一个端点能替你完成的。

参数与响应

未声明参数或请求体字段。

响应ListErrorProjectsResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

GET /errors/issues

归组后的错误,最近有活动的排在前面。需要 monitor:read。一个问题是一个指纹而不是一个事件,所以同一处抛出发生一千次只是一行,timesSeen 为一千。?status= 默认为 unresolved,也接受 resolvedignoredall?project= 按项目 id 过滤,?q= 搜索类型、值和出错位置,?limit= 上限为 200。spark 是二十四个按小时的计数,最早的在前。

参数与响应
名称位置类型
limitqueryinteger
projectquerystring
qquerystring
statusquerystring
响应ListIssuesResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

GET /errors/issues/{id}

单个问题及其堆栈。需要 monitor:readexceptions 是 SDK 自己的异常链,抛出的错误在最后,它的各级起因在前面,而它的栈帧会用你上传的任何 source map 还原——与仪表板所做的还原是同一套,所以脚本和你正在交谈的那个人永远不会看着两份不同的堆栈。无法应用的 map 会退回到原始栈帧,而不是让请求失败。

lastEvent 特意带了两个时间戳。occurredAt 是 SDK 上报的时间,receivedAt 是接收端写入的时间;一台离线过的设备,或者一条积压的队列,正好就是两者之间的那段差。

参数与响应
名称位置类型
id 必填pathstring
响应GetIssueResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

PATCH /errors/issues/{id}

解决、忽略或重新打开:{ "status": "resolved" }。需要 monitor:updateignoreHours 可以与 ignored 一同发送,以便日后再取消忽略,上限为 90 天。

解决时会记下是在哪个 release 中解决的,这样来自较旧部署的迟到事件不会把问题重新打开。当 SDK 没有发送 release 时,就没有可比较的东西,之后的任何事件都会把它重新打开——在无法区分迟到者和回归的情况下,安全的假设是这个缺陷又回来了。

参数与响应
名称位置类型
id 必填pathstring
statusbodystring
ignoreHoursbodyinteger
响应IssueStatusResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

GET /analytics

这个账户在统计的网站。需要 monitor:read。带有 publicId——放进脚本标签里的那个值,它按设计就是公开的——外加 viewsLast24htrafficAlerting,后者在某个网站的流量低于该时段通常水平期间为真。

参数与响应

未声明参数或请求体字段。

响应ListAnalyticsSitesResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

GET /analytics/{id}

单个网站的数据,供表格或报告使用。需要 monitor:read?days= 默认为 7,上限为 365。?dimensions= 接受一个以逗号分隔的列表,取值来自 pathreferrercountrybrowserosdeviceutm_sourceutm_mediumutm_campaignevent;未知的取值会得到 400 而不是一个空结果,因为一个打错的字若返回空,读起来就像「没有流量」。

访客这个数字叫 dailyUniqueVisitors,而它就是字面意思。 它是每日独立访客的总和,也不可能是别的:访客哈希背后的那个密钥在其 UTC 日结束时就被销毁,所以在两天里都来过的人会被算作两次,也没有任何密钥能把他们关联起来。这是隐私设计在起作用,而不是一个近似值——但一个叫 visitors 的字段摆在 30 天的时间范围旁边,会诱使你去报告一个含义完全不同的数字。

curl "https://vitrinaengine.com/api/v1/analytics/$ID?days=30&dimensions=path,referrer" \
  -H "Authorization: Bearer vte_…"
参数与响应
名称位置类型
id 必填pathstring
daysqueryinteger
响应GetAnalyticsSiteResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

GET /billing

套餐,以及它在哪里计费。需要 billing:read。带有 planstatuscadencecurrentPeriodEndcancelAtaddOnPacks,以及最近十二张收据。

source 的取值是 paddleapplemanualnone。它很重要:在 iOS 应用内购买的订阅归 Apple 管理,套餐、付款方式和取消都在客户的 App Store 设置里,而不在这里。canCheckoutOnWebcanPurchaseInApp 指明哪些购买入口可以安全地展示,这样客户端就不必自己重新推导那条规则,也就不会朝着「向某人重复收费」的方向弄错。

参数与响应

未声明参数或请求体字段。

响应GetBillingResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

GET /monitors/{id}/history

单个监控项的每日可用性。需要 monitor:read。接受 days,最多到你所在套餐的历史保留期。

它是从每日汇总算出来的,而不是从逐条检查结果算的,所以对于原始结果已被保留期删除的时间段,它依然能给出答案。

参数与响应
名称位置类型
id 必填pathstring
daysqueryinteger
响应GetMonitorHistoryResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

GET /monitors/{id}/graph · GET /monitors/graphs

单个监控项最近几个小时的逐小时数据,或者在一个响应里给出此密钥能看到的所有监控项的数据——也就是仪表板在监控项行和监控项页面上绘制的内容。需要 monitor:read。接受 hours,默认 24,最多 168。

uptime 图表给出每个小时通过检查的比例,没有检查的小时为 nullvalue 图表用于定量类型,以带单位的序列给出读数。窗口中的每个小时都会出现,无论是否有记录,这样空缺就明显是空缺。读数按实际测量值发送:raw 序列没有已知刻度,请以它自身的最高读数为基准绘制,并且绝不要把这个比例当作读数显示。

参数与响应

GET /monitors/{id}/graph

名称位置类型
id 必填pathstring
hoursqueryinteger
响应GetMonitorGraphResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

GET /monitors/graphs

名称位置类型
hoursqueryinteger
响应ListMonitorGraphsResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

GET /monitors/{id}/channels · PUT /monitors/{id}/channels

这个监控项向哪些渠道告警,以及如何设置它们。读取需要 notification_channel:read;写入需要 monitor:update

PUT 会替换整个集合——请发送你想要的每一个渠道 id,而不是你要新增的那些。空数组意味着这个监控项不向任何人告警,这是一个可能真心想要的设置,也是一件不该误操作做出来的事。

参数与响应

GET /monitors/{id}/channels

名称位置类型
id 必填pathstring
响应GetMonitorChannelsResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

PUT /monitors/{id}/channels

名称位置类型
id 必填pathstring
channelIdsbodyStringList
响应GetMonitorChannelsResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

GET /incidents/{id}/events

单个事件的时间线:状态变更、确认、评论、告警升级步骤。需要 incident:read。这就是 GET /incidents/{id} 不包含的那条时间线。

参数与响应
名称位置类型
id 必填pathstring
响应ListIncidentEventsResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

GET /channels · POST /channels

读取需要 notification_channel:read;创建需要 notification_channel:manage。每一项都带有 idkindnametargetworkspaceId

secret 只在渠道创建时返回一次,此后再也不会。 它是 webhook 的签名密钥;把它存在某个你能读回来的地方,会让一把只读密钥变成伪造签名请求的途径。

你可以创建那些人在仪表板里也能创建的类型:emailwebhooktelegramslackteamsdiscordpagerdutyjsm。短信、WhatsApp、卫星和推送由接收它们的本人自行登记,无法通过 API 创建——正是这一点让它们成为一份同意,而不是某人填进去的一个字段。

邮件渠道创建时处于未确认状态,并会收到一封询问它是否愿意接收告警的邮件。在它同意之前不会再发送任何东西,所以通过 API 创建一个渠道本身并不会把某个地址放上你的呼叫轮值表。

参数与响应

GET /channels

未声明参数或请求体字段。

响应ListChannelsResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

POST /channels

名称位置类型
kindbodystring
namebodystring
targetbodystring
workspaceIdbodyrequests.NullableString
响应CreatedChannelResponse
状态码201 400 401 403 404 429
速率限制每分钟 30 次

PATCH /channels/{id} · DELETE /channels/{id} · POST /channels/{id}/test

三者都需要 notification_channel:manage。对尚未确认的地址,测试发送会被拒绝——否则它就成了一种无限量地向未同意地址发信的手段,还顶着一个看起来很贴心的名字。

参数与响应

PATCH /channels/{id}

名称位置类型
id 必填pathstring
enabledbodyboolean
响应AckResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

DELETE /channels/{id}

名称位置类型
id 必填pathstring
响应AckResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

POST /channels/{id}/test

名称位置类型
id 必填pathstring
响应TestDeliveryResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

GET /status-pages · POST /status-pages

读取需要 status_page:read;写入需要 status_page:manage。每一项都带有 idnameslugvisibilitycustomDomaindomainVerifiedAtsubscribersEnabledcomponentCountworkspacecreatedAt

参数与响应

GET /status-pages

未声明参数或请求体字段。

响应ListStatusPagesResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

POST /status-pages

名称位置类型
namebodystring
workspaceIdbodystring
响应CreatedResponse
状态码201 400 401 403 404 429
速率限制每分钟 30 次

GET /status-pages/{id} · PATCH /status-pages/{id} · DELETE /status-pages/{id}

读取需要 status_page:read;修改或删除需要 status_page:manage

参数与响应

GET /status-pages/{id}

名称位置类型
id 必填pathstring
响应GetStatusPageResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

PATCH /status-pages/{id}

名称位置类型
id 必填pathstring
namebodystring
headlinebodyrequests.NullableString
descriptionbodyrequests.NullableString
logoUrlbodyrequests.NullableString
themeAccentbodystring
languagebodystring
historyDaysbodyinteger
showResponseTimesbodyboolean
showIncidentHistorybodyboolean
subscribersEnabledbodyboolean
visibilitybodystring
passwordbodyrequests.NullableString
customCssbodyrequests.NullableString
hideVitrinaBrandingbodyboolean
groupByHostbodyboolean
响应UpdatedStatusPageResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

DELETE /status-pages/{id}

名称位置类型
id 必填pathstring
响应AckResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

POST /status-pages/{id}/components · DELETE /status-pages/{id}/components

从一个状态页中添加或移除一个监控项。需要 status_page:manage

该监控项必须与这个状态页处在同一个工作区,而不只是同一个组织。在代理商账户里,正是这一点防止一个客户的监控项被发布到另一个客户的页面上。

参数与响应

POST /status-pages/{id}/components

名称位置类型
id 必填pathstring
monitorIdbodystring
displayNamebodystring
响应ComponentAckResponse
状态码201 400 401 403 404 429
速率限制每分钟 30 次

DELETE /status-pages/{id}/components

名称位置类型
id 必填pathstring
componentquerystring
componentIdbodystring
响应ComponentAckResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

GET /workspaces · POST /workspaces · DELETE /workspaces/{id}

读取需要 workspace:read,其余各自需要对应的权限。每一项都带有 idnameslugclientReferenceisDefault

属于受限成员的密钥只能看到该成员被限定的那些工作区。默认工作区无法删除。

参数与响应

GET /workspaces

未声明参数或请求体字段。

响应ListWorkspacesResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

POST /workspaces

名称位置类型
namebodystring
clientReferencebodystring
响应CreatedResponse
状态码201 400 401 403 404 429
速率限制每分钟 30 次

DELETE /workspaces/{id}

名称位置类型
id 必填pathstring
响应AckResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

GET /members · POST /members

读取需要 member:read;邀请需要 member:invite。响应中 membersinvitations 是分开的——没有人接受的邀请并没有增加任何人,把两者合并会得出一个与你的账单不一致的席位数。

POST 发送的是一封邀请,而不是创建一个账户。它接受一个邮箱地址、一个角色,以及可选的 workspaceIds 来限定其范围。

参数与响应

GET /members

未声明参数或请求体字段。

响应ListMembersResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

POST /members

名称位置类型
emailbodystring
rolebodystring
workspaceIdsbodystring[]
响应AckResponse
状态码201 400 401 403 404 429
速率限制每分钟 30 次

PATCH /members/{id} · DELETE /members/{id} · DELETE /invitations/{id}

修改角色需要 member:update_role;移除成员需要 member:remove;撤销邀请需要 member:invite

移除一名成员会删除他的个人告警渠道——他的手机、他的私人聊天、他的手机号码——连同他的 API 密钥一起。共享渠道不受影响。最后一位所有者既不能被移除,也不能被降级。

参数与响应

PATCH /members/{id}

名称位置类型
id 必填pathstring
rolebodystring
响应AckResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

DELETE /members/{id}

名称位置类型
id 必填pathstring
响应AckResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

DELETE /invitations/{id}

名称位置类型
id 必填pathstring
响应AckResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

GET /maintenance · POST /maintenance · DELETE /maintenance

在这些时间窗内不会呼叫任何人。默认情况下检查照常运行、照常记录,因此这些分钟与其他时间一样计入可用性;把 keepChecking 设为 false 之后,时间窗打开期间不做任何检查,历史记录中留下的是一段空白,而不是一次下滑。读取需要 incident:read;写入需要 maintenance:manage

带有 titlestartsAtendsAttimezonemonitorIdsrecurrenceRulekeepCheckingshowOnStatusPagenotifySubscribers。一个重复的时间窗是一行记录,而不是许多行。

规则会让时间窗在它自己的 timezone 中按同一本地时间重复,因此跨越时钟调整的时间窗仍停留在预定的那个小时。FREQ=DAILYFREQ=WEEKLYFREQ=MONTHLY 会被展开,并可带 INTERVALCOUNTUNTIL,以及指明时间窗自身起始那一天的 BYDAYBYMONTHDAY。其他规则会原样保存并原样返回,且只在第一次发生时拦下告警——我们从不去猜。

参数与响应

GET /maintenance

名称位置类型
pastqueryboolean
响应ListMaintenanceResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

POST /maintenance

名称位置类型
titlebodystring
descriptionbodystring
workspaceIdbodystring
monitorIdsbodystring[]
startsAtbodystring
endsAtbodystring
recurrenceRulebodystring
timezonebodystring
keepCheckingbodyboolean
showOnStatusPagebodyboolean
notifySubscribersbodyboolean
响应CreatedResponse
状态码201 400 401 403 404 429
速率限制每分钟 30 次

DELETE /maintenance

名称位置类型
idquerystring
响应AckResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

GET /on-call

值班表、告警升级策略、临时替班,以及此刻谁在值班。需要 oncall:read

onCallNow 是在你发问的那一刻解析出来的,viaOverride 说明它来自轮班还是来自某人代班。unreachable 列出那些没有任何渠道能真正联系到的参与者,这正是值得在事件发生前而不是发生中发现的问题。

参数与响应

未声明参数或请求体字段。

响应GetOnCallResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

GET /agents · POST /agents · DELETE /agents · GET /agents/{id}/metrics

服务器探针从你自己的机器上报告 CPU、内存和磁盘。登记需要 agent:enroll,读取需要 agent:read,移除需要 agent:delete

登记会返回一个 token,只返回一次。探针用它来进行身份验证,所以此后它无法被读取。

参数与响应

GET /agents

未声明参数或请求体字段。

响应ListAgentsResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

POST /agents

名称位置类型
namebodystring
workspaceIdbodyrequests.NullableString
响应EnrolledAgentResponse
状态码201 400 401 403 404 429
速率限制每分钟 30 次

DELETE /agents

名称位置类型
idquerystring
idbodystring
响应AckResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

GET /agents/{id}/metrics

名称位置类型
id 必填pathstring
hoursqueryinteger
limitqueryinteger
响应GetAgentMetricsResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

GET /api-keys · POST /api-keys · DELETE /api-keys

读取需要 api_key:read;创建和吊销需要 api_key:manage。每一项都带有 idnameprefixscopesworkspaceIdsownercreatedAtlastUsedAtexpiresAtrevokedAt

token 只在创建时返回。prefix 是可显示的那一部分,正是它让你能在列表里区分两把密钥,而无需其中任何一把是可读的。

密钥继承创建它的那个成员身份的角色和工作区限定,所以密钥不会成为绕开其创建者所受限制的途径。

参数与响应

GET /api-keys

未声明参数或请求体字段。

响应ListApiKeysResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

POST /api-keys

名称位置类型
namebodystring
scopesbodystring[]
workspaceIdsbodystring[]
expiresInDaysbodyrequests.NullableInt64
响应CreatedApiKeyResponse
状态码201 400 401 403 404 429
速率限制每分钟 30 次

DELETE /api-keys

名称位置类型
idquerystring
idbodystring
响应AckResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

GET /audit

配置变更,以及是谁做的、改了什么。需要 audit_log:read,也就是 Business 及以上套餐。

它按 nextBefore 分页,而不是按页码,这样分页边界不会在你翻阅时移动。条目在所有套餐上都会记录——套餐限制的是读取,所以升级会打开整段历史,而不是从此刻开始记。

凭据、哈希和机密永远不会写进 changes

参数与响应
名称位置类型
limitqueryinteger
响应ListAuditResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

GET /me

这把密钥是谁、能触及什么,以及套餐允许什么。不需要任何权限——每把密钥都能描述自己。

带有 userorganizationmembership(角色和工作区限定)、entitlements(解析后的套餐限额,其中已经折算进了任何附加包)和 usage。用它在尝试创建之前检查某项限额,而不是在被拒绝之后。

会话还会拿到 organizations,列出这个人所属的每一个组织。API 密钥则不会:密钥是针对一个成员身份签发并被限定在其中的,列出其他组织等于在宣传这份凭据根本触及不到的组织。

参数与响应

未声明参数或请求体字段。

响应GetMeResponse
状态码200 400 401 403 404 429
速率限制每分钟 120 次

DELETE /account

删除这个组织及其中的一切。没有权限常量守卫这一个,因为所有权就是那道检查:任何不是所有者的人都会被拒绝。

这不可逆,也不是暂停。请慎重使用。

参数与响应

未声明参数或请求体字段。

响应AccountDeletedResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

POST /billing/apple/verify

确认一笔在我们自己的移动应用中完成的应用内购买。需要 billing:manage

把它写在这里是为了完整,而不是为了让你调用:只有拿着 Apple 签发给我们应用的收据才调得动,所以第三方集成用它做不了任何事。之所以列出,是因为一个存在却在任何地方都没有描述的路由,与一个被人遗忘的路由无从分辨。

参数与响应
名称位置类型
transactionIdbodystring
响应AppleVerifyResponse
状态码200 400 401 403 404 429
速率限制每分钟 30 次

GET /

索引。列出这次部署所提供的路由,它是本页的机器可读版本,并且永远是最新的——它由实际挂载的内容生成。

事件订阅源(RSS 与 Atom)

把账户中的所有事件做成订阅源,面向阅读器而不是客户端库。在控制台的设置 → 通知中创建该网址:

https://vitrinaengine.com/feeds/incidents/vtf_…/feed.xml
https://vitrinaengine.com/feeds/incidents/vtf_…/atom.xml

这个网址本身就是凭据。不需要发送任何请求头:持有链接的人就能读取账户中的每一个事件,涵盖所有工作区。它会在控制台中随时重新显示,因为它存放在某人早晚要重装的订阅阅读器里——而且可以更换或吊销,自下一次请求起立即生效,而不必等某个缓存过期。

它只承载事件层面的事实:监控项名称、原因、严重程度、状态、开始与结束时间,以及被标记为公开的更新。绝不包含事后复盘,不包含监控项配置,也绝不包含检查从何处发起。多数阅读器会经由第三方服务器同步,因此这份文档是按照“会离开你的场所”来撰写的——事实也正是如此。

每分钟拉取一次属于正常。单个网址每分钟超过六十次请求会返回 429;从未有效、已被更换或已被吊销的网址都返回 404——三种情况给出同一个答复,以免有人借此推断哪些令牌曾经真实存在。

这套 API 刻意不返回的内容

  • 监控项的 config 它可能含有凭据。
  • 某次检查是从哪个区域运行的。 我们从哪里检查由我们决定,也随着探针的部署和迁移由我们更改。那会成为一个关于我们基础设施的承诺,而你对它无从下手,何况产品的其他任何地方也都不展示它。

Exporting your data

Two routes hand back a file rather than a message, so that taking your data out does not mean paging through a collection endpoint a thousand times. Both are on every plan, the free one included — what a plan changes is how much history there is to export, not whether you may.

GET /export/{entity}

某一类行数据,输出为 CSV 或 JSON。entity 可以是 monitorshostsstatus-pagesnotification-channelsmembersincidentsincident-eventspostmortemsaudit-logerror-issuesuptime-dailyuptime-hourlyanalytics-hourly, 其他一律返回 404

formatcsv(默认)或 jsonfrom to 是 RFC 3339 时间戳。bom 会在 CSV 前加上 UTF-8 字节顺序标记: Excel 需要它才能正确显示非 ASCII 名称,而多数脚本语言会把它当作第一个列名的一部分,因此默认关闭。

参数与响应
名称位置类型
entity 必填pathstring
bomqueryboolean
formatquerystring
fromquerystring
toquerystring
状态码200 400 401 403 404 429
速率限制每小时 6 次

GET /export

该密钥可读的全部内容,打包为 zip:每个实体一个文件,外加 manifest.json, 说明压缩包内容、每个文件的行数以及缺少哪些实体。密钥角色无权读取的部分会被省略,而不是让整个 请求失败;清单的作用正是让这种省略不再悄无声息。

参数与响应
名称位置类型
bomqueryboolean
formatquerystring
fromquerystring
toquerystring
状态码200 400 401 403 404 429
速率限制每小时 6 次

What an export does not contain

导出不包含任何可用于认证的内容:没有通道配置,没有 heartbeat 令牌,也没有 密码哈希。监控器的配置会包含在内,但会移除一切形似凭据的字段——请求体、请求头、SNMP community、 邮箱登录。原始检查结果同样不在其中:保留期会将其删除,因此可用率来自汇总数据。

Size, and the one refusal you may meet

小时级序列每次请求最多 92 天;更宽的时间窗会以 export_window_too_wide 拒绝,而不是悄悄截断。它有独立的速率上限:每个组织每小时 6 次导出

版本管理

版本号写在路径里。响应中会新增字段——请把不认识的字段当作可以忽略的东西,而不是错误——但 v1 中不会移除任何东西,也不会有任何东西在它之下改变含义。

还缺什么吗?

这套接口面刻意保持小巧:它覆盖一块大屏看板、一份聊天摘要,以及一个在发版期间让监控项静默的部署脚本。如果你要做的东西超出了它的范围,请写信到 support@vitrinaengine.com——知道大家真正想要什么,正是下一个端点被选中的方式。

价格 · 条款