> ## 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.

# Monitores

> Monitorea páginas en un horario y envía alertas de cambios

A través del endpoint de Olostep `/v1/monitors` puedes crear monitores persistentes que se ejecutan en un horario fijo, detectan cambios en la página y te notifican por correo electrónico, Slack, SMS o un webhook dedicado.

* Crea un monitor a partir de una `query` en lenguaje natural
* Delimita fuentes con `source_policy`
* Ejecuta verificaciones en horarios en lenguaje natural (mínimo cada 10 minutos, UTC)
* Configura `notification.channels` y entrega opcional de `webhook`
* Transmite el progreso de aprovisionamiento con eventos enviados por el servidor (`?stream=1`)
* Lista, inspecciona, actualiza, pausa, reanuda y elimina monitores
* Lee eventos de instantáneas, artefactos de planificación, registros de ejecución y registros de agentes en vivo

Por defecto, cada ejecución del monitor captura una **instantánea completa** de la página monitoreada: una imagen completa de su estado actual en ese momento. Si deseas que el monitor muestre solo lo nuevo o cambiado entre ejecuciones (deltas) en lugar del estado completo, expresa esa intención en la `query`.

## Instalación

<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: el curl integrado está bien
  ```
</CodeGroup>

## Crear un monitor

Crea un monitor con `POST /v1/monitors`. La API valida tu entrada, reserva un registro de monitor, aprovisiona un agente sombra, genera una especificación de flujo de trabajo, pone en cola la planificación de DAG y crea un horario recurrente.

* `query` es obligatorio: describe qué observar en lenguaje natural.
* `frequency` es opcional y por defecto es `every hour`. Usa frases de programación como `every day at 9am` (los horarios se ejecutan en **UTC**; el intervalo mínimo es **10 minutos**).
* `source_policy` opcionalmente restringe `include_urls`, `exclude_urls`, `include_domains` y `exclude_domains`.
* `notification` configura cuándo y cómo alertar (`events` + `channels`). La entrega de canales se resuelve en tiempo de ejecución por la tubería del monitor: no pasas destinatarios al DAG.
* `webhook` es un objeto separado (`{ "url": "https://…" }`) para devoluciones de llamada HTTP además de `notification.channels`.
* `output_schema` opcionalmente impone una extracción estructurada (JSON Schema válido).

La respuesta de creación es HTTP `202` con `status: provisioning`. El monitor pasa a `active` después de que la planificación resuelve los objetivos `tracked`. Haz polling a `GET /v1/monitors/:monitor_id` o pasa `?stream=1` (o `Accept: text/event-stream`) para seguir las fases de aprovisionamiento y los tokens de razonamiento de especificaciones a través de SSE.

### Ejemplo de solicitud

Solo necesitas `query` y `frequency`. Los canales de notificación y webhooks se pueden agregar más tarde con `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": "Notifícame cuando una nueva startup se lance en 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: 'Notifícame cuando una nueva startup se lance en 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": "Notifícame cuando una nueva startup se lance en Y Combinator Launches",
      "frequency": "every 20 minutes"
    }'
  ```
</CodeGroup>

### Respuesta

La creación exitosa (no en streaming) devuelve HTTP `202` con un objeto monitor. `tracked` está vacío hasta que la planificación termina; realiza polling a `GET /v1/monitors/:monitor_id` hasta que `status` sea `active` y `tracked.urls` esté poblado.

```json theme={null}
{
  "id": "monitor_biglavgvq3",
  "object": "monitor",
  "query": "Notifícame cuando una nueva startup se lance en 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
}
```

### Salida estructurada del monitor

Configura `output_schema` cuando desees que los resultados de extracción sigan una estructura JSON específica. El esquema debe ser un JSON Schema válido.

### Flujo de aprovisionamiento

Agrega `?stream=1` o envía `Accept: text/event-stream` para recibir eventos SSE mientras se crea el monitor:

| Evento            | Descripción                                                          |
| ----------------- | -------------------------------------------------------------------- |
| `phase`           | Paso de aprovisionamiento (`running`, `done`, o `failed`)            |
| `reasoning_token` | Texto incremental de diseño de especificaciones                      |
| `reasoning_reset` | Trunca el razonamiento en búfer después de un intento fallido de LLM |
| `complete`        | Objeto monitor final (misma forma que la respuesta `202`)            |
| `error`           | Falla terminal                                                       |

## Notificaciones y webhooks

Las alertas se configuran en el registro del monitor y se resuelven en tiempo de ejecución: no incrustes objetivos de canal en la `query` de monitoreo.

### `notification`

| Campo      | Descripción                                                                                                                                                                                                        |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `events`   | Qué resultados de ejecución deben activar la entrega. Ver [eventos de notificación](#notification-events) abajo. Si configuras `channels` y omites `events`, por defecto se incluyen `changed` y `first_snapshot`. |
| `channels` | Lista de objetos `{ "type", "target", "events"? }`                                                                                                                                                                 |

#### Eventos de notificación

Usa `events` para indicar **cuándo** deseas ser notificado. Valores permitidos:

| Evento           | Significado                                                                                                                                                                                              |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `changed`        | Notifica cuando el monitor detecta un **cambio** en comparación con la instantánea anterior (por ejemplo, nuevo contenido, precio actualizado, o una diferencia que la tubería clasifica como cambiada). |
| `first_snapshot` | Notifica cuando el monitor toma su **primera instantánea**: la ejecución inicial de referencia que almacena el contenido actual antes de comparaciones posteriores.                                      |

Puedes incluir uno o ambos. Por ejemplo, `["changed"]` alerta solo sobre actualizaciones después de la referencia; `["first_snapshot"]` confirma la configuración sin esperar una diferencia; `["changed", "first_snapshot"]` cubre ambos.

Los `events` por canal en un objeto de canal usan los mismos valores y anulan la lista de nivel superior solo para ese canal.

Tipos de canal soportados:

| `type`  | Formato de `target`                                    |
| ------- | ------------------------------------------------------ |
| `email` | Dirección de correo electrónico válida                 |
| `slack` | URL de webhook entrante de Slack                       |
| `sms`   | Número de teléfono E.164 (por ejemplo, `+14155552671`) |

### `webhook`

Separado de `notification.channels`, `webhook.url` recibe cargas útiles HTTP POST cuando el monitor activa tu URL de devolución de llamada. Puedes usar tanto un webhook como notificaciones de canal en el mismo monitor.

### Ejemplos

Solo correo electrónico:

```json theme={null}
{
  "query": "Observa cambios en https://example.com/terms",
  "frequency": "every day at 10am",
  "notification": {
    "events": ["changed"],
    "channels": [
      { "type": "email", "target": "legal@example.com" }
    ]
  }
}
```

Devolución de llamada de webhook:

```json theme={null}
{
  "query": "Observa cambios en https://example.com/terms",
  "frequency": "every day at 10am",
  "webhook": {
    "url": "https://hooks.example.com/olostep-monitor"
  }
}
```

SMS:

```json theme={null}
{
  "query": "Alértame cuando https://status.example.com muestre un incidente",
  "frequency": "every hour",
  "notification": {
    "channels": [
      { "type": "sms", "target": "+14155552671" }
    ]
  }
}
```

## Política de fuente

Usa `source_policy` para restringir qué URLs y dominios puede usar el planificador.

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

## Frecuencias

Configura `frequency` en lenguaje natural, por ejemplo:

* `every hour` (por defecto si se omite)
* `every day at 9am`
* `every weekday at 14:30`

Reglas:

* Debe leerse como lenguaje de programación de horarios (no una pregunta arbitraria del monitor).
* Intervalo mínimo: **cada 10 minutos**.
* Los horarios se almacenan y ejecutan en **UTC** (`schedule.timezone` es `UTC`).
* Longitud máxima: 50 caracteres.

La API deriva una expresión cron de tu texto `frequency` y la expone en `schedule.cron`. Cuando el monitor está `active`, `schedule.next_run_at` muestra la siguiente ejecución en ISO 8601.

## Listar monitores

Recupera todos los monitores para tu equipo con `GET /v1/monitors`.

Por defecto, los monitores eliminados se filtran. Usa `?include_deleted=true` para incluirlos.

<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 de monitores: {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 de monitores: ${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>

### Forma de la respuesta

```json theme={null}
{
  "monitors": [
    {
      "id": "monitor_0wj35czpn7",
      "object": "monitor",
      "query": "Observa el blog de AirOps para nuevos posts",
      "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": "Notifícame cuando una nueva startup se lance en 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
}
```

## Obtener un monitor

Recupera un único monitor con `GET /v1/monitors/:monitor_id`.

La respuesta incluye `last_run` (resumen de la última instantánea) y `total_count` (conteo de instantáneas) a menos que pases `include_total_count=false`. Agrega `include-diagram=true` para incluir un `mermaid_diagram` del DAG del monitor.

<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>

### Forma de la respuesta

```json theme={null}
{
  "id": "monitor_biglavgvq3",
  "object": "monitor",
  "query": "Notifícame cuando una nueva startup se lance en 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
}
```

## Listar eventos del monitor

Usa `GET /v1/monitors/:monitor_id/events` para listar eventos de instantáneas para un monitor.

Paginación:

* `limit` (por defecto `25`, máximo `100`)
* `cursor` (token opaco de `next_cursor`)
* `count_only=true` devuelve solo `{ "total_count": N }`

Los eventos se devuelven del más nuevo al más antiguo. Cada elemento incluye una `snapshot_url` pre-firmada de corta duración.

<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>

### Forma de la respuesta

```json theme={null}
{
  "data": [
    {
      "id": "run_iwsoafcpyx",
      "run_id": "run_iwsoafcpyx",
      "created": 1780063395,
      "changed": false,
      "summary": "Se ha tomado la primera instantánea de este monitor. Se almacenó el contenido actual como referencia.",
      "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
}
```

## Obtener planificación del monitor

Usa `GET /v1/monitors/:monitor_id/planning` para inspeccionar la especificación del flujo de trabajo FDA y el DAG del planificador después del aprovisionamiento.

```json theme={null}
{
  "spec": {
    "saved_at": "2026-05-29T12:00:00.000000+00:00",
    "status": "complete",
    "goal": "Track pricing on 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": [] }
  }
}
```

## Obtener una ejecución del monitor

Usa `GET /v1/monitors/:monitor_id/runs/:run_id` para obtener metadatos de instantáneas y eventos de registro de agentes analizados para una ejecución (`run_id` debe comenzar con `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": "Ejecución run_v7k2p9m3 completada. Archivos subidos: 2",
      "event": { "type": "run_complete", "run_id": "run_v7k2p9m3", "files_uploaded": 2 }
    }
  ]
}
```

## Transmitir registros de agentes

Usa `GET /v1/monitors/:monitor_id/agent-logs?stream=1` (o `Accept: text/event-stream`) para seguir los registros de CloudWatch para el agente del monitor, filtrados a este `monitor_id`.

El parámetro de consulta opcional `since` es una marca de tiempo en milisegundos (por defecto: hace 30 minutos).

Tipos de eventos SSE: `ready`, `log`, `heartbeat`, `error`.

## Actualizar un monitor

Actualiza un monitor con `POST /v1/monitors/:monitor_id`.

Campos soportados (incluye solo lo que deseas cambiar):

* `metadata` — se fusiona con las claves existentes; los valores de cadena vacíos eliminan claves
* `frequency` — recrea el horario interno y establece `status` de nuevo a `active`
* `notification` — reemplaza todo el objeto de notificación
* `webhook` — pasa `null` para eliminar

Devuelve `409` mientras `status` es `provisioning`.

Cuando agregas `notification.channels` sin `events`, la API establece `events` por defecto a `["changed", "first_snapshot"]`.

### Agregar notificación por correo electrónico

<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": "Notifícame cuando una nueva startup se lance en 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
}
```

### Agregar 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": "Notifícame cuando una nueva startup se lance en 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
}
```

## Pausar un monitor

Pausa un monitor con `POST /v1/monitors/:monitor_id/pause`.

Pausar desactiva el horario subyacente y establece `status` en `paused`. Solo los monitores con `status: active` pueden ser pausados. El cuerpo de la solicitud está vacío.

<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>

En caso de éxito, devuelve `200` con el monitor y `status: paused`. `schedule.next_run_at` es `null` mientras está pausado.

## Reanudar un monitor

Reanuda un monitor pausado con `POST /v1/monitors/:monitor_id/resume`.

Reanudar vuelve a habilitar el horario y establece `status` de nuevo a `active`. Solo los monitores `paused` pueden ser reanudados.

<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>

## Eliminar un monitor

Elimina un monitor con `DELETE /v1/monitors/:monitor_id`.

La eliminación elimina suavemente la fila del monitor (`status: deleted`) y elimina sus recursos de horario y agente sombra.

<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>

## Estado del monitor

| Estado         | Significado                                                                             |
| -------------- | --------------------------------------------------------------------------------------- |
| `provisioning` | Se están configurando el agente, la especificación, el planificador y el horario        |
| `active`       | Horario habilitado; las ejecuciones se realizan en `schedule.frequency`                 |
| `paused`       | Horario desactivado vía `/pause`                                                        |
| `failed`       | Falló el aprovisionamiento o la actualización del horario (`error_message` establecido) |
| `deleted`      | Eliminado suavemente vía `DELETE`                                                       |

## Ejemplos de casos de uso

A continuación se presentan patrones comunes de monitores. Cada ejemplo solo necesita `query` y `frequency` en el momento de la creación; agrega `notification` y `webhook` más tarde si deseas alertas en la primera ejecución o en cambios.

### Nuevos lanzamientos de Y Combinator

Observa [Y Combinator Launches](https://www.ycombinator.com/launches/) para startups recién publicadas. Después de la planificación, `tracked.type` es `urls` y `tracked.urls` apunta a la página de lanzamientos.

<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": "Notifícame cuando una nueva startup se lance en 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: 'Notifícame cuando una nueva startup se lance en 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": "Notifícame cuando una nueva startup se lance en Y Combinator Launches",
      "frequency": "every 20 minutes"
    }'
  ```
</CodeGroup>

Agrega entrega por correo electrónico y webhook después de que el monitor esté `active`:

```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" }
  }'
```

Con `channels` configurado y `events` omitido, la API establece por defecto `["changed", "first_snapshot"]` para que seas notificado en la ejecución de referencia y cada vez que se detecte un cambio.

### Publicaciones de blog de competidores (AirOps, Profound)

Monitorea el índice de blogs de un competidor para nuevas publicaciones. El planificador resuelve `tracked.urls` a la URL del blog (por ejemplo, `https://www.airops.com/blog` o `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": "Observa el blog de AirOps para nuevos posts",
      "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": "Observa el blog de Profound para nuevos posts",
      "frequency": "every 20 minutes"
    }'
  ```
</CodeGroup>

Después de la creación, un monitor de esta familia se ve así:

```json theme={null}
{
  "id": "monitor_588ck513zd",
  "object": "monitor",
  "query": "Observa el blog de Profound para nuevos posts",
  "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"
}
```

Usa `events: ["changed"]` en `notification` si solo deseas alertas cuando aparezcan nuevas publicaciones, no en la primera instantánea de referencia.

### Umbral de precio de acciones (Tesla)

Monitorea una fuente de datos estructurada cuando la condición es numérica en lugar de una diferencia de página. El planificador establece `tracked.type` en `data_api` y deja `tracked.urls` vacío.

```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": "Notifícame cuando el precio de las acciones de Tesla caiga por debajo de 436$",
    "frequency": "every 12 minutes"
  }'
```

```json theme={null}
{
  "id": "monitor_7609p3191t",
  "object": "monitor",
  "query": "Notifícame cuando el precio de las acciones de Tesla caiga por debajo de 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"
}
```

### Registro de cambios de la API de OpenAI

Recibe notificaciones cuando el [registro de cambios de la API de OpenAI](https://developers.openai.com/api/docs/changelog) lista nuevas características, lanzamientos de modelos o deprecaciones. Menciona la URL del registro de cambios en `query` o fíjala con `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": "Notifícame cuando el registro de cambios de la API de OpenAI tenga nuevas actualizaciones o características https://developers.openai.com/api/docs/changelog",
    "frequency": "every hour",
    "notification": {
      "events": ["changed"],
      "channels": [{ "type": "email", "target": "you@example.com" }]
    }
  }'
```

Configura `events` a `["changed"]` para que te alerten cuando el contenido del registro de cambios cambie, no solo cuando se almacene la primera instantánea.

### Gestión de múltiples monitores

Lista cada monitor para tu equipo para ver el estado, los horarios y los objetivos resueltos en un solo lugar:

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

Un equipo que ejecute los ejemplos anteriores podría ver varios monitores uno al lado del otro: observaciones de blogs en diferentes cadencias, una observación de precio `data_api`, y un monitor de lanzamientos de YC con correo electrónico y webhook configurados, con `"count": 4` (o más) en la respuesta.

## Errores comunes de validación

Los endpoints del monitor devuelven errores de validación claros para solicitudes inválidas comunes:

* `query` faltante o vacía
* `frequency` que no es lenguaje de programación de horarios, se resuelve demasiado a menudo (menos de 10 minutos) o excede los 50 caracteres
* Entradas inválidas de `source_policy` (las matrices de URL deben contener cadenas válidas `http`/`https`)
* Forma inválida de `notification`, `events` desconocidos o `type` / `target` de canal inválido
* `webhook.url` inválido (debe ser `http` o `https`)
* `output_schema` inválido (debe ser un JSON Schema válido)
* Formato inválido de `monitor_id` o `run_id`
* Actualización mientras `status` es `provisioning` (`409`)
* Pausar/reanudar cuando el estado no es `active` / `paused`

Ejemplo de error:

```json theme={null}
{
  "error": "No se pudo interpretar 'frequency': \"check every second\". Usa lenguaje de programación de horarios como \"every hour\" o \"every day at 9am\"."
}
```
