> ## Documentation Index
> Fetch the complete documentation index at: https://docs.olostep.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 监控器

> 定期监控页面并发送变更提醒

通过 Olostep 的 `/v1/monitors` 端点，你可以创建在固定时间表上运行的持久监控器，检测页面变化，并通过电子邮件、Slack、短信或专用的 webhook 通知你。

* 从自然语言 `query` 创建监控器
* 使用 `source_policy` 限制来源
* 在自然语言时间表上运行检查（最小每 10 分钟一次，UTC）
* 配置 `notification.channels` 和可选的 `webhook` 传递
* 使用服务器发送事件（`?stream=1`）流式传输配置进度
* 列出、检查、更新、暂停、恢复和删除监控器
* 读取快照事件、计划工件、运行日志和实时代理日志

默认情况下，每次监控器运行都会捕获被监控页面的**完整快照**——即该时刻其当前状态的完整图景。如果你希望监控器仅显示运行之间的新内容或变化（增量）而不是完整状态，请在 `query` 中表达这一意图。

## 安装

<CodeGroup>
  ```python Python theme={null}
  # pip install requests

  import requests
  ```

  ```js Node theme={null}
  // npm install node-fetch

  // ESM
  import fetch from 'node-fetch'

  // CommonJS
  const fetch = require('node-fetch')
  ```

  ```bash cURL theme={null}
  # macOS: 内置 curl 即可
  ```
</CodeGroup>

## 创建监控器

使用 `POST /v1/monitors` 创建监控器。API 验证你的输入，保留一个监控器记录，配置一个影子代理，生成一个工作流规范，排队 DAG 计划，并创建一个重复的时间表。

* `query` 是必需的——用自然语言描述要监控的内容。
* `frequency` 是可选的，默认为 `every hour`。使用诸如 `every day at 9am` 的调度短语（调度在 **UTC** 中运行；最小间隔为 **10 分钟**）。
* `source_policy` 可选地限制 `include_urls`、`exclude_urls`、`include_domains` 和 `exclude_domains`。
* `notification` 配置何时以及如何提醒（`events` + `channels`）。频道传递在运行时由监控器管道解析——你无需将接收者传递到 DAG。
* `webhook` 是一个单独的对象（`{ "url": "https://…" }`），用于 HTTP 回调，除了 `notification.channels` 之外。
* `output_schema` 可选地强制执行结构化提取（有效的 JSON Schema）。

创建响应为 HTTP `202`，状态为 `provisioning`。监控器在计划解决 `tracked` 目标后转为 `active`。轮询 `GET /v1/monitors/:monitor_id` 或传递 `?stream=1`（或 `Accept: text/event-stream`）以通过 SSE 跟踪配置阶段和规范推理令牌。

### 示例请求

你只需要 `query` 和 `frequency`。通知频道和 webhooks 可以稍后通过 `POST /v1/monitors/:monitor_id` 添加。

<CodeGroup>
  ```python Python theme={null}
  import requests
  import json

  API_KEY = "<YOUR_API_KEY>"
  API_URL = "https://api.olostep.com/v1"

  payload = {
      "query": "当 Y Combinator Launches 上有新创业公司发布时通知我",
      "frequency": "every 20 minutes",
  }

  headers = {
      "Authorization": f"Bearer {API_KEY}",
      "Content-Type": "application/json",
  }

  response = requests.post(f"{API_URL}/monitors", headers=headers, json=payload)
  print(response.status_code)
  print(json.dumps(response.json(), indent=2))
  ```

  ```js Node theme={null}
  const API_URL = 'https://api.olostep.com/v1'

  const res = await fetch(`${API_URL}/monitors`, {
    method: 'POST',
    headers: { 'Authorization': 'Bearer <YOUR_API_KEY>', 'Content-Type': 'application/json' },
    body: JSON.stringify({
      query: '当 Y Combinator Launches 上有新创业公司发布时通知我',
      frequency: 'every 20 minutes',
    }),
  })

  console.log(res.status)
  console.log(await res.json())
  ```

  ```bash cURL theme={null}
  curl -sS -X POST "https://api.olostep.com/v1/monitors" \
    -H "Authorization: Bearer $OLOSTEP_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "query": "当 Y Combinator Launches 上有新创业公司发布时通知我",
      "frequency": "every 20 minutes"
    }'
  ```
</CodeGroup>

### 响应

成功创建（非流式）返回 HTTP `202` 和一个监控器对象。在计划完成之前，`tracked` 是空的；轮询 `GET /v1/monitors/:monitor_id` 直到 `status` 是 `active` 并且 `tracked.urls` 被填充。

```json theme={null}
{
  "id": "monitor_biglavgvq3",
  "object": "monitor",
  "query": "当 Y Combinator Launches 上有新创业公司发布时通知我",
  "tracked": {
    "type": null,
    "urls": [],
    "web_query": null
  },
  "source_policy": {},
  "schedule": {
    "frequency": "every 20 minutes",
    "cron": "7/20 * * * ? *",
    "timezone": "UTC",
    "next_run_at": null
  },
  "notification": {
    "events": [],
    "channels": []
  },
  "webhook": null,
  "output_schema": {},
  "status": "provisioning",
  "error_message": null,
  "last_run": null,
  "agent": {
    "id": "agent_forward_deployed_0_fda_nlkxhr5kto"
  },
  "metadata": {},
  "created": 1780063068,
  "updated": 1780063071
}
```

### 结构化监控器输出

当你希望提取结果遵循特定的 JSON 结构时，设置 `output_schema`。该模式必须是有效的 JSON Schema。

### 配置流

添加 `?stream=1` 或发送 `Accept: text/event-stream` 以在创建监控器时接收 SSE 事件：

| 事件                | 描述                                |
| ----------------- | --------------------------------- |
| `phase`           | 配置步骤（`running`、`done` 或 `failed`） |
| `reasoning_token` | 增量规范设计文本                          |
| `reasoning_reset` | 在 LLM 尝试失败后截断缓冲的推理                |
| `complete`        | 最终监控器对象（与 `202` 响应相同的形状）          |
| `error`           | 终端失败                              |

## 通知和 webhooks

警报在监控器记录上配置，并在运行时解析——不要在监控 `query` 中嵌入频道目标。

### `notification`

| 字段         | 描述                                                                                                                |
| ---------- | ----------------------------------------------------------------------------------------------------------------- |
| `events`   | 哪些运行结果应触发传递。请参阅下面的[通知事件](#notification-events)。如果你设置了 `channels` 并省略了 `events`，则默认为 `changed` 和 `first_snapshot`。 |
| `channels` | `{ "type", "target", "events"? }` 对象列表                                                                            |

#### 通知事件

使用 `events` 来说明你希望何时收到通知。允许的值：

| 事件               | 含义                                                 |
| ---------------- | -------------------------------------------------- |
| `changed`        | 当监控器检测到与前一个快照相比的**变化**时通知（例如新内容、更新价格或管道分类为已更改的差异）。 |
| `first_snapshot` | 当监控器拍摄其**第一次快照**时通知——存储当前内容的初始基线运行，以便以后进行比较。       |

你可以包含一个或两个。例如，`["changed"]` 仅在基线之后的更新时提醒；`["first_snapshot"]` 确认设置而无需等待差异；`["changed", "first_snapshot"]` 覆盖两者。

频道对象上的每个频道 `events` 使用相同的值，并仅为该频道覆盖顶级列表。

支持的频道类型：

| `type`  | `target` 格式                   |
| ------- | ----------------------------- |
| `email` | 有效的电子邮件地址                     |
| `slack` | Slack 入站 webhook URL          |
| `sms`   | E.164 电话号码（例如 `+14155552671`） |

### `webhook`

与 `notification.channels` 分开，`webhook.url` 在监控器触发你的回调 URL 时接收 HTTP POST 负载。你可以在同一个监控器上同时使用 webhook 和频道通知。

### 示例

仅电子邮件：

```json theme={null}
{
  "query": "监控 https://example.com/terms 上的变化",
  "frequency": "every day at 10am",
  "notification": {
    "events": ["changed"],
    "channels": [
      { "type": "email", "target": "legal@example.com" }
    ]
  }
}
```

Webhook 回调：

```json theme={null}
{
  "query": "监控 https://example.com/terms 上的变化",
  "frequency": "every day at 10am",
  "webhook": {
    "url": "https://hooks.example.com/olostep-monitor"
  }
}
```

短信：

```json theme={null}
{
  "query": "当 https://status.example.com 显示事件时提醒我",
  "frequency": "every hour",
  "notification": {
    "channels": [
      { "type": "sms", "target": "+14155552671" }
    ]
  }
}
```

## 来源策略

使用 `source_policy` 来限制计划者可以使用的 URL 和域。

```json theme={null}
{
  "source_policy": {
    "include_urls": ["https://example.com/pricing"],
    "exclude_domains": ["ads.example.com"]
  }
}
```

## 频率

以自然语言设置 `frequency`，例如：

* `every hour`（省略时的默认值）
* `every day at 9am`
* `every weekday at 14:30`

规则：

* 必须读作调度语言（而不是任意的监控问题）。
* 最小间隔：**每 10 分钟**。
* 调度存储并在 **UTC** 中执行（`schedule.timezone` 是 `UTC`）。
* 最大长度：50 个字符。

API 从你的 `frequency` 文本中派生出一个 cron 表达式，并在 `schedule.cron` 上暴露出来。当监控器为 `active` 时，`schedule.next_run_at` 显示 ISO 8601 格式的下一次运行。

## 列出监控器

使用 `GET /v1/monitors` 检索你的团队的所有监控器。

默认情况下，已删除的监控器被过滤掉。使用 `?include_deleted=true` 来包含它们。

<CodeGroup>
  ```python Python theme={null}
  import requests
  import json

  API_KEY = "<YOUR_API_KEY>"
  API_URL = "https://api.olostep.com/v1"

  headers = { "Authorization": f"Bearer {API_KEY}" }

  response = requests.get(f"{API_URL}/monitors", headers=headers)
  result = response.json()
  print(f"Total monitors: {result['count']}")
  print(json.dumps(result, indent=2))
  ```

  ```js Node theme={null}
  const API_URL = 'https://api.olostep.com/v1'

  const res = await fetch(`${API_URL}/monitors`, {
    headers: { 'Authorization': 'Bearer <YOUR_API_KEY>' }
  })
  const result = await res.json()

  console.log(`Total monitors: ${result.count}`)
  console.log(result.monitors)
  ```

  ```bash cURL theme={null}
  curl -s -X GET "https://api.olostep.com/v1/monitors" \
    -H "Authorization: Bearer $OLOSTEP_API_KEY"
  ```
</CodeGroup>

### 响应形状

```json theme={null}
{
  "monitors": [
    {
      "id": "monitor_0wj35czpn7",
      "object": "monitor",
      "query": "监控 AirOps 博客的新博文",
      "tracked": {
        "type": "urls",
        "urls": ["https://www.airops.com/blog"],
        "web_query": null
      },
      "source_policy": {},
      "schedule": {
        "frequency": "every hour",
        "cron": "2 * * * ? *",
        "timezone": "UTC",
        "next_run_at": null
      },
      "notification": {
        "channels": [],
        "events": []
      },
      "webhook": null,
      "output_schema": {},
      "status": "paused",
      "error_message": null,
      "last_run": null,
      "agent": { "id": "agent_forward_deployed_0_fda_x4822l9h3i" },
      "metadata": {},
      "created": 1780062756,
      "updated": 1780063025
    },
    {
      "id": "monitor_biglavgvq3",
      "object": "monitor",
      "query": "当 Y Combinator Launches 上有新创业公司发布时通知我",
      "tracked": {
        "type": "urls",
        "urls": ["https://www.ycombinator.com/launches/"],
        "web_query": null
      },
      "source_policy": {},
      "schedule": {
        "frequency": "every 20 minutes",
        "cron": "7/20 * * * ? *",
        "timezone": "UTC",
        "next_run_at": "2026-05-29T14:27:00.000Z"
      },
      "notification": {
        "channels": [],
        "events": []
      },
      "webhook": null,
      "output_schema": {},
      "status": "active",
      "error_message": null,
      "last_run": null,
      "agent": { "id": "agent_forward_deployed_0_fda_nlkxhr5kto" },
      "metadata": {},
      "created": 1780063068,
      "updated": 1780063141
    }
  ],
  "count": 5
}
```

## 获取监控器

使用 `GET /v1/monitors/:monitor_id` 检索单个监控器。

响应包括 `last_run`（最新快照摘要）和 `total_count`（快照计数），除非你传递 `include_total_count=false`。添加 `include-diagram=true` 以包含监控器 DAG 的 `mermaid_diagram`。

<CodeGroup>
  ```python Python theme={null}
  import requests
  import json

  API_KEY = "<YOUR_API_KEY>"
  API_URL = "https://api.olostep.com/v1"
  MONITOR_ID = "monitor_biglavgvq3"

  headers = { "Authorization": f"Bearer {API_KEY}" }

  response = requests.get(f"{API_URL}/monitors/{MONITOR_ID}", headers=headers)
  print(json.dumps(response.json(), indent=2))
  ```

  ```js Node theme={null}
  const API_URL = 'https://api.olostep.com/v1'
  const monitorId = 'monitor_biglavgvq3'

  const res = await fetch(`${API_URL}/monitors/${monitorId}`, {
    headers: { 'Authorization': 'Bearer <YOUR_API_KEY>' }
  })
  console.log(await res.json())
  ```

  ```bash cURL theme={null}
  curl -s -X GET "https://api.olostep.com/v1/monitors/monitor_biglavgvq3" \
    -H "Authorization: Bearer $OLOSTEP_API_KEY"
  ```
</CodeGroup>

### 响应形状

```json theme={null}
{
  "id": "monitor_biglavgvq3",
  "object": "monitor",
  "query": "当 Y Combinator Launches 上有新创业公司发布时通知我",
  "tracked": {
    "type": "urls",
    "urls": ["https://www.ycombinator.com/launches/"],
    "web_query": null
  },
  "source_policy": {},
  "schedule": {
    "frequency": "every 20 minutes",
    "cron": "7/20 * * * ? *",
    "timezone": "UTC",
    "next_run_at": "2026-05-29T14:27:00.000Z"
  },
  "notification": {
    "channels": [],
    "events": []
  },
  "webhook": null,
  "output_schema": {},
  "status": "active",
  "error_message": null,
  "last_run": {
    "id": "run_iwsoafcpyx",
    "status": "completed",
    "change_detected": false,
    "ran_at": "2026-05-29T14:03:15.963Z"
  },
  "agent": {
    "id": "agent_forward_deployed_0_fda_nlkxhr5kto"
  },
  "metadata": {},
  "created": 1780063068,
  "updated": 1780063141,
  "total_count": 1
}
```

## 列出监控器事件

使用 `GET /v1/monitors/:monitor_id/events` 列出监控器的快照事件。

分页：

* `limit`（默认 `25`，最大 `100`）
* `cursor`（来自 `next_cursor` 的不透明令牌）
* `count_only=true` 仅返回 `{ "total_count": N }`

事件按最新优先返回。每个项目包括一个短期有效的预签名 `snapshot_url`。

<CodeGroup>
  ```python Python theme={null}
  import requests
  import json

  API_KEY = "<YOUR_API_KEY>"
  API_URL = "https://api.olostep.com/v1"
  MONITOR_ID = "monitor_biglavgvq3"

  headers = { "Authorization": f"Bearer {API_KEY}" }

  response = requests.get(
      f"{API_URL}/monitors/{MONITOR_ID}/events?limit=10",
      headers=headers,
  )
  print(json.dumps(response.json(), indent=2))
  ```

  ```js Node theme={null}
  const API_URL = 'https://api.olostep.com/v1'
  const monitorId = 'monitor_biglavgvq3'

  const res = await fetch(`${API_URL}/monitors/${monitorId}/events?limit=10`, {
    headers: { 'Authorization': 'Bearer <YOUR_API_KEY>' }
  })
  console.log(await res.json())
  ```

  ```bash cURL theme={null}
  curl -s -X GET "https://api.olostep.com/v1/monitors/monitor_biglavgvq3/events?limit=10" \
    -H "Authorization: Bearer $OLOSTEP_API_KEY"
  ```
</CodeGroup>

### 响应形状

```json theme={null}
{
  "data": [
    {
      "id": "run_iwsoafcpyx",
      "run_id": "run_iwsoafcpyx",
      "created": 1780063395,
      "changed": false,
      "summary": "已拍摄此监控器的第一次快照。将当前内容存储为基线。",
      "snapshot_url": "https://olostep-monitor-snapshots.s3.amazonaws.com/monitor_biglavgvq3/run_iwsoafcpyx_snapshot.json?X-Amz-Expires=600&..."
    }
  ],
  "has_more": false,
  "next_cursor": null,
  "total_count": 1
}
```

## 获取监控器计划

使用 `GET /v1/monitors/:monitor_id/planning` 检查 FDA 工作流规范和计划 DAG 在配置后的状态。

```json theme={null}
{
  "spec": {
    "saved_at": "2026-05-29T12:00:00.000000+00:00",
    "status": "complete",
    "goal": "跟踪 example.com 上的定价",
    "reasoning": "...",
    "constraints": "...",
    "assumptions": "...",
    "input": { "query": "...", "urls": ["https://www.ycombinator.com/launches/"] },
    "output": { "type": "free_text" },
    "chat_history": []
  },
  "dag": {
    "user_query": "...",
    "graph": { "nodes": [], "edges": [] },
    "has_unresolved": false,
    "unresolved": [],
    "validation": { "is_valid": true, "attempts": 1, "history": [] }
  }
}
```

## 获取监控器运行

使用 `GET /v1/monitors/:monitor_id/runs/:run_id` 获取一次执行的快照元数据和解析的代理日志事件（`run_id` 必须以 `run_` 开头）。

```json theme={null}
{
  "monitor_id": "monitor_biglavgvq3",
  "run_id": "run_v7k2p9m3",
  "snapshot": { "changed": true, "summary": "..." },
  "log_group": "/aws/ecs/olostep-agents/...",
  "events": [
    {
      "id": "...",
      "ts": 1777960800123,
      "message": "运行 run_v7k2p9m3 完成。上传文件：2",
      "event": { "type": "run_complete", "run_id": "run_v7k2p9m3", "files_uploaded": 2 }
    }
  ]
}
```

## 流式代理日志

使用 `GET /v1/monitors/:monitor_id/agent-logs?stream=1`（或 `Accept: text/event-stream`）来跟踪监控器代理的 CloudWatch 日志，过滤到此 `monitor_id`。

可选查询参数 `since` 是一个毫秒时间戳（默认：30 分钟前）。

SSE 事件类型：`ready`、`log`、`heartbeat`、`error`。

## 更新监控器

使用 `POST /v1/monitors/:monitor_id` 更新监控器。

支持的字段（仅包含你想更改的内容）：

* `metadata` — 与现有键合并；空字符串值删除键
* `frequency` — 重新创建内部时间表并将 `status` 设置回 `active`
* `notification` — 替换整个通知对象
* `webhook` — 传递 `null` 以移除

当 `status` 为 `provisioning` 时返回 `409`。

当你添加 `notification.channels` 而没有 `events` 时，API 默认 `events` 为 `["changed", "first_snapshot"]`。

### 添加电子邮件通知

<CodeGroup>
  ```python Python theme={null}
  import requests
  import json

  API_KEY = "<YOUR_API_KEY>"
  API_URL = "https://api.olostep.com/v1"
  MONITOR_ID = "monitor_biglavgvq3"

  payload = {
      "notification": {
          "channels": [
              {"type": "email", "target": "you@example.com"}
          ]
      }
  }

  headers = {
      "Authorization": f"Bearer {API_KEY}",
      "Content-Type": "application/json",
  }

  response = requests.post(
      f"{API_URL}/monitors/{MONITOR_ID}",
      headers=headers,
      json=payload,
  )
  print(json.dumps(response.json(), indent=2))
  ```

  ```js Node theme={null}
  const API_URL = 'https://api.olostep.com/v1'
  const monitorId = 'monitor_biglavgvq3'

  const res = await fetch(`${API_URL}/monitors/${monitorId}`, {
    method: 'POST',
    headers: { 'Authorization': 'Bearer <YOUR_API_KEY>', 'Content-Type': 'application/json' },
    body: JSON.stringify({
      notification: {
        channels: [{ type: 'email', target: 'you@example.com' }]
      }
    })
  })
  console.log(await res.json())
  ```

  ```bash cURL theme={null}
  curl -s -X POST "https://api.olostep.com/v1/monitors/monitor_biglavgvq3" \
    -H "Authorization: Bearer $OLOSTEP_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "notification": {
        "channels": [
          { "type": "email", "target": "you@example.com" }
        ]
      }
    }'
  ```
</CodeGroup>

```json theme={null}
{
  "id": "monitor_biglavgvq3",
  "object": "monitor",
  "query": "当 Y Combinator Launches 上有新创业公司发布时通知我",
  "tracked": {
    "type": "urls",
    "urls": ["https://www.ycombinator.com/launches/"],
    "web_query": null
  },
  "source_policy": {},
  "schedule": {
    "frequency": "every 20 minutes",
    "cron": "7/20 * * * ? *",
    "timezone": "UTC",
    "next_run_at": "2026-05-29T14:27:00.000Z"
  },
  "notification": {
    "events": ["changed", "first_snapshot"],
    "channels": [
      { "type": "email", "target": "you@example.com" }
    ]
  },
  "webhook": null,
  "output_schema": {},
  "status": "active",
  "error_message": null,
  "last_run": null,
  "agent": { "id": "agent_forward_deployed_0_fda_nlkxhr5kto" },
  "metadata": {},
  "created": 1780063068,
  "updated": 1780064634
}
```

### 添加 webhook

<CodeGroup>
  ```python Python theme={null}
  import requests
  import json

  API_KEY = "<YOUR_API_KEY>"
  API_URL = "https://api.olostep.com/v1"
  MONITOR_ID = "monitor_biglavgvq3"

  payload = {
      "webhook": { "url": "https://webhook.site/your-unique-id" }
  }

  headers = {
      "Authorization": f"Bearer {API_KEY}",
      "Content-Type": "application/json",
  }

  response = requests.post(
      f"{API_URL}/monitors/{MONITOR_ID}",
      headers=headers,
      json=payload,
  )
  print(json.dumps(response.json(), indent=2))
  ```

  ```js Node theme={null}
  const API_URL = 'https://api.olostep.com/v1'
  const monitorId = 'monitor_biglavgvq3'

  const res = await fetch(`${API_URL}/monitors/${monitorId}`, {
    method: 'POST',
    headers: { 'Authorization': 'Bearer <YOUR_API_KEY>', 'Content-Type': 'application/json' },
    body: JSON.stringify({
      webhook: { url: 'https://webhook.site/your-unique-id' }
    })
  })
  console.log(await res.json())
  ```

  ```bash cURL theme={null}
  curl -s -X POST "https://api.olostep.com/v1/monitors/monitor_biglavgvq3" \
    -H "Authorization: Bearer $OLOSTEP_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "webhook": { "url": "https://webhook.site/your-unique-id" }
    }'
  ```
</CodeGroup>

```json theme={null}
{
  "id": "monitor_biglavgvq3",
  "object": "monitor",
  "query": "当 Y Combinator Launches 上有新创业公司发布时通知我",
  "tracked": {
    "type": "urls",
    "urls": ["https://www.ycombinator.com/launches/"],
    "web_query": null
  },
  "source_policy": {},
  "schedule": {
    "frequency": "every 20 minutes",
    "cron": "7/20 * * * ? *",
    "timezone": "UTC",
    "next_run_at": "2026-05-29T14:47:00.000Z"
  },
  "notification": {
    "channels": [
      { "type": "email", "target": "you@example.com" }
    ],
    "events": ["changed", "first_snapshot"]
  },
  "webhook": {
    "url": "https://webhook.site/your-unique-id"
  },
  "output_schema": {},
  "status": "active",
  "error_message": null,
  "last_run": null,
  "agent": { "id": "agent_forward_deployed_0_fda_nlkxhr5kto" },
  "metadata": {},
  "created": 1780063068,
  "updated": 1780065538
}
```

## 暂停监控器

使用 `POST /v1/monitors/:monitor_id/pause` 暂停监控器。

暂停会禁用底层时间表并将 `status` 设置为 `paused`。只有 `status: active` 的监控器可以暂停。请求体为空。

<CodeGroup>
  ```python Python theme={null}
  import requests
  import json

  API_KEY = "<YOUR_API_KEY>"
  API_URL = "https://api.olostep.com/v1"
  MONITOR_ID = "monitor_biglavgvq3"

  response = requests.post(
      f"{API_URL}/monitors/{MONITOR_ID}/pause",
      headers={ "Authorization": f"Bearer {API_KEY}" },
  )
  print(response.status_code)
  print(json.dumps(response.json(), indent=2))
  ```

  ```js Node theme={null}
  const API_URL = 'https://api.olostep.com/v1'
  const monitorId = 'monitor_biglavgvq3'

  const res = await fetch(`${API_URL}/monitors/${monitorId}/pause`, {
    method: 'POST',
    headers: { 'Authorization': 'Bearer <YOUR_API_KEY>' }
  })
  console.log(res.status)
  console.log(await res.json())
  ```

  ```bash cURL theme={null}
  curl -s -X POST "https://api.olostep.com/v1/monitors/monitor_biglavgvq3/pause" \
    -H "Authorization: Bearer $OLOSTEP_API_KEY"
  ```
</CodeGroup>

成功时，返回 `200` 和监控器及 `status: paused`。暂停时，`schedule.next_run_at` 为 `null`。

## 恢复监控器

使用 `POST /v1/monitors/:monitor_id/resume` 恢复暂停的监控器。

恢复会重新启用时间表并将 `status` 设置回 `active`。只有 `paused` 的监控器可以恢复。

<CodeGroup>
  ```python Python theme={null}
  import requests
  import json

  API_KEY = "<YOUR_API_KEY>"
  API_URL = "https://api.olostep.com/v1"
  MONITOR_ID = "monitor_biglavgvq3"

  response = requests.post(
      f"{API_URL}/monitors/{MONITOR_ID}/resume",
      headers={ "Authorization": f"Bearer {API_KEY}" },
  )
  print(response.status_code)
  print(json.dumps(response.json(), indent=2))
  ```

  ```js Node theme={null}
  const API_URL = 'https://api.olostep.com/v1'
  const monitorId = 'monitor_biglavgvq3'

  const res = await fetch(`${API_URL}/monitors/${monitorId}/resume`, {
    method: 'POST',
    headers: { 'Authorization': 'Bearer <YOUR_API_KEY>' }
  })
  console.log(res.status)
  console.log(await res.json())
  ```

  ```bash cURL theme={null}
  curl -s -X POST "https://api.olostep.com/v1/monitors/monitor_biglavgvq3/resume" \
    -H "Authorization: Bearer $OLOSTEP_API_KEY"
  ```
</CodeGroup>

## 删除监控器

使用 `DELETE /v1/monitors/:monitor_id` 删除监控器。

删除会软删除监控器行（`status: deleted`）并移除其时间表和影子代理资源。

<CodeGroup>
  ```python Python theme={null}
  import requests
  import json

  API_KEY = "<YOUR_API_KEY>"
  API_URL = "https://api.olostep.com/v1"
  MONITOR_ID = "monitor_biglavgvq3"

  response = requests.delete(
      f"{API_URL}/monitors/{MONITOR_ID}",
      headers={ "Authorization": f"Bearer {API_KEY}" },
  )
  print(json.dumps(response.json(), indent=2))
  ```

  ```js Node theme={null}
  const API_URL = 'https://api.olostep.com/v1'
  const monitorId = 'monitor_biglavgvq3'

  const res = await fetch(`${API_URL}/monitors/${monitorId}`, {
    method: 'DELETE',
    headers: { 'Authorization': 'Bearer <YOUR_API_KEY>' }
  })
  console.log(await res.json())
  ```

  ```bash cURL theme={null}
  curl -s -X DELETE "https://api.olostep.com/v1/monitors/monitor_biglavgvq3" \
    -H "Authorization: Bearer $OLOSTEP_API_KEY"
  ```
</CodeGroup>

## 监控器状态

| 状态             | 含义                                 |
| -------------- | ---------------------------------- |
| `provisioning` | 代理、规范、计划者和时间表正在设置中                 |
| `active`       | 时间表启用；运行在 `schedule.frequency` 上执行 |
| `paused`       | 通过 `/pause` 禁用时间表                  |
| `failed`       | 配置或时间表更新失败（`error_message` 已设置）    |
| `deleted`      | 通过 `DELETE` 软删除                    |

## 示例用例

以下是常见的监控器模式。每个示例在创建时只需要 `query` 和 `frequency`；如果你想在第一次运行或变化时收到警报，可以稍后添加 `notification` 和 `webhook`。

### Y Combinator 新发布

监控 [Y Combinator Launches](https://www.ycombinator.com/launches/) 上新发布的创业公司。在计划之后，`tracked.type` 是 `urls`，`tracked.urls` 指向发布页面。

<CodeGroup>
  ```python Python theme={null}
  import requests

  API_URL = "https://api.olostep.com/v1"
  headers = {
      "Authorization": "Bearer <YOUR_API_KEY>",
      "Content-Type": "application/json",
  }

  requests.post(
      f"{API_URL}/monitors",
      headers=headers,
      json={
          "query": "当 Y Combinator Launches 上有新创业公司发布时通知我",
          "frequency": "every 20 minutes",
      },
  )
  ```

  ```js Node theme={null}
  const API_URL = 'https://api.olostep.com/v1'

  await fetch(`${API_URL}/monitors`, {
    method: 'POST',
    headers: { 'Authorization': 'Bearer <YOUR_API_KEY>', 'Content-Type': 'application/json' },
    body: JSON.stringify({
      query: '当 Y Combinator Launches 上有新创业公司发布时通知我',
      frequency: 'every 20 minutes',
    }),
  })
  ```

  ```bash cURL theme={null}
  curl -sS -X POST "https://api.olostep.com/v1/monitors" \
    -H "Authorization: Bearer $OLOSTEP_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "query": "当 Y Combinator Launches 上有新创业公司发布时通知我",
      "frequency": "every 20 minutes"
    }'
  ```
</CodeGroup>

在监控器 `active` 后添加电子邮件和 webhook 传递：

```bash theme={null}
curl -s -X POST "https://api.olostep.com/v1/monitors/monitor_biglavgvq3" \
  -H "Authorization: Bearer $OLOSTEP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "notification": {
      "channels": [{ "type": "email", "target": "you@example.com" }]
    },
    "webhook": { "url": "https://webhook.site/your-unique-id" }
  }'
```

设置了 `channels` 并省略了 `events`，API 默认为 `["changed", "first_snapshot"]`，因此你会在基线运行和检测到变化时收到通知。

### 竞争对手博客文章（AirOps，Profound）

监控竞争对手博客索引以获取新文章。计划者将 `tracked.urls` 解析为博客 URL（例如 `https://www.airops.com/blog` 或 `https://www.tryprofound.com/blog`）。

<CodeGroup>
  ```bash cURL AirOps theme={null}
  curl -sS -X POST "https://api.olostep.com/v1/monitors" \
    -H "Authorization: Bearer $OLOSTEP_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "query": "监控 AirOps 博客的新博文",
      "frequency": "every hour"
    }'
  ```

  ```bash cURL Profound theme={null}
  curl -sS -X POST "https://api.olostep.com/v1/monitors" \
    -H "Authorization: Bearer $OLOSTEP_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "query": "监控 Profound 博客的新博文",
      "frequency": "every 20 minutes"
    }'
  ```
</CodeGroup>

创建后，此系列中的监控器看起来像：

```json theme={null}
{
  "id": "monitor_588ck513zd",
  "object": "monitor",
  "query": "监控 Profound 博客的新博文",
  "tracked": {
    "type": "urls",
    "urls": ["https://www.tryprofound.com/blog"],
    "web_query": null
  },
  "schedule": {
    "frequency": "every 20 minutes",
    "cron": "15/20 * * * ? *",
    "timezone": "UTC",
    "next_run_at": "2026-05-29T15:15:00.000Z"
  },
  "status": "active"
}
```

如果你只想在新文章出现时收到警报，而不是在第一次基线快照时，请在 `notification` 上使用 `events: ["changed"]`。

### 股票价格阈值（特斯拉）

当条件是数字而不是页面差异时，监控结构化数据源。计划者将 `tracked.type` 设置为 `data_api` 并将 `tracked.urls` 留空。

```bash theme={null}
curl -sS -X POST "https://api.olostep.com/v1/monitors" \
  -H "Authorization: Bearer $OLOSTEP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "当特斯拉股价跌破 436$ 时通知我",
    "frequency": "every 12 minutes"
  }'
```

```json theme={null}
{
  "id": "monitor_7609p3191t",
  "object": "monitor",
  "query": "当特斯拉股价跌破 436$ 时通知我",
  "tracked": {
    "type": "data_api",
    "urls": [],
    "web_query": null
  },
  "schedule": {
    "frequency": "every 12 minutes",
    "cron": "2/12 * * * ? *",
    "timezone": "UTC",
    "next_run_at": "2026-05-29T15:02:00.000Z"
  },
  "status": "active"
}
```

### OpenAI API 更新日志

当 [OpenAI 的 API 更新日志](https://developers.openai.com/api/docs/changelog) 列出新功能、模型发布或弃用时收到通知。在 `query` 中提到更新日志 URL 或使用 `source_policy.include_urls` 固定它。

```bash theme={null}
curl -sS -X POST "https://api.olostep.com/v1/monitors" \
  -H "Authorization: Bearer $OLOSTEP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "当 OpenAI API 更新日志有新更新或功能时通知我 https://developers.openai.com/api/docs/changelog",
    "frequency": "every hour",
    "notification": {
      "events": ["changed"],
      "channels": [{ "type": "email", "target": "you@example.com" }]
    }
  }'
```

将 `events` 设置为 `["changed"]`，这样当更新日志内容发生变化时你会收到警报，而不仅仅是在存储第一次快照时。

### 管理多个监控器

列出你的团队的所有监控器，以便在一个地方查看状态、时间表和已解析的目标：

```bash theme={null}
curl -s -X GET "https://api.olostep.com/v1/monitors" \
  -H "Authorization: Bearer $OLOSTEP_API_KEY"
```

运行上述示例的团队可能会看到几个监控器并排显示——不同节奏的博客监控、`data_api` 价格监控，以及配置了电子邮件和 webhook 的 YC 发布监控——响应中 `"count": 4`（或更多）。

## 常见验证错误

监控器端点为常见的无效请求返回明确的验证错误：

* 缺少或空的 `query`
* `frequency` 不是调度语言，解析过于频繁（低于 10 分钟）或超过 50 个字符
* 无效的 `source_policy` 条目（URL 数组必须包含有效的 `http`/`https` 字符串）
* 无效的 `notification` 形状，未知的 `events`，或无效的频道 `type` / `target`
* 无效的 `webhook.url`（必须是 `http` 或 `https`）
* 无效的 `output_schema`（必须是有效的 JSON Schema）
* 无效的 `monitor_id` 或 `run_id` 格式
* 当 `status` 为 `provisioning` 时更新（`409`）
* 当状态不是 `active` / `paused` 时暂停/恢复

示例错误：

```json theme={null}
{
  "error": "无法解释 'frequency': \"每秒检查一次\"。请使用调度语言，例如 \"every hour\" 或 \"every day at 9am\"。"
}
```
