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

# Moniteurs

> Surveillez les pages selon un calendrier et envoyez des alertes de changement

Grâce à l’endpoint Olostep `/v1/monitors`, tu peux créer des moniteurs persistants qui s'exécutent selon un calendrier fixe, détectent les changements de page, et te notifient par email, Slack, SMS, ou via un webhook dédié.

* Crée un moniteur à partir d’une `query` en langage naturel
* Définis les sources avec `source_policy`
* Exécute des vérifications selon des calendriers en langage naturel (minimum toutes les 10 minutes, UTC)
* Configure `notification.channels` et la livraison optionnelle par `webhook`
* Diffuse le progrès de provisionnement avec les événements envoyés par le serveur (`?stream=1`)
* Liste, inspecte, mets à jour, mets en pause, reprends, et supprime les moniteurs
* Lis les événements de snapshot, les artefacts de planification, les journaux d'exécution, et les journaux d'agents en direct

Par défaut, chaque exécution de moniteur capture un **instantané complet** de la page surveillée — une image complète de son état actuel à ce moment-là. Si tu veux que le moniteur ne fasse remonter que ce qui est nouveau ou modifié entre les exécutions (deltas) au lieu de l'état complet, exprime cette intention dans la `query`.

## Installation

<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 : le curl intégré est suffisant
  ```
</CodeGroup>

## Créer un moniteur

Crée un moniteur avec `POST /v1/monitors`. L'API valide ton entrée, réserve un enregistrement de moniteur, provisionne un agent fantôme, génère une spécification de workflow, met en file d'attente la planification DAG, et crée un calendrier récurrent.

* `query` est requis — décris ce que tu veux surveiller en langage naturel.
* `frequency` est optionnel et par défaut est `every hour`. Utilise des phrases de planification telles que `every day at 9am` (les calendriers s'exécutent en **UTC** ; l'intervalle minimum est de **10 minutes**).
* `source_policy` contraint éventuellement `include_urls`, `exclude_urls`, `include_domains`, et `exclude_domains`.
* `notification` configure quand et comment alerter (`events` + `channels`). La livraison par canal est résolue à l'exécution par le pipeline du moniteur — tu ne passes pas les destinataires dans le DAG.
* `webhook` est un objet séparé (`{ "url": "https://…" }`) pour les callbacks HTTP en plus de `notification.channels`.
* `output_schema` impose éventuellement une extraction structurée (JSON Schema valide).

La réponse de création est HTTP `202` avec `status: provisioning`. Le moniteur passe à `active` après que la planification ait résolu les cibles `tracked`. Interroge `GET /v1/monitors/:monitor_id` ou passe `?stream=1` (ou `Accept: text/event-stream`) pour suivre les phases de provisionnement et les tokens de raisonnement de spécification via SSE.

### Exemple de requête

Tu n'as besoin que de `query` et `frequency`. Les canaux de notification et les webhooks peuvent être ajoutés plus tard avec `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": "Notify me when a new startup launches on 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: 'Notify me when a new startup launches on 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": "Notify me when a new startup launches on Y Combinator Launches",
      "frequency": "every 20 minutes"
    }'
  ```
</CodeGroup>

### Réponse

Une création réussie (non en streaming) renvoie HTTP `202` avec un objet moniteur. `tracked` est vide jusqu'à ce que la planification se termine ; interroge `GET /v1/monitors/:monitor_id` jusqu'à ce que `status` soit `active` et que `tracked.urls` soit rempli.

```json theme={null}
{
  "id": "monitor_biglavgvq3",
  "object": "monitor",
  "query": "Notify me when a new startup launches on 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
}
```

### Sortie structurée du moniteur

Définis `output_schema` lorsque tu veux que les résultats d'extraction suivent une structure JSON spécifique. Le schéma doit être un JSON Schema valide.

### Flux de provisionnement

Ajoute `?stream=1` ou envoie `Accept: text/event-stream` pour recevoir des événements SSE pendant que le moniteur est créé :

| Événement         | Description                                                                   |
| ----------------- | ----------------------------------------------------------------------------- |
| `phase`           | Étape de provisionnement (`running`, `done`, ou `failed`)                     |
| `reasoning_token` | Texte de conception de spécification incrémental                              |
| `reasoning_reset` | Tronque le raisonnement mis en mémoire tampon après une tentative LLM échouée |
| `complete`        | Objet moniteur final (même forme que la réponse `202`)                        |
| `error`           | Échec terminal                                                                |

## Notifications et webhooks

Les alertes sont configurées sur l'enregistrement du moniteur et résolues à l'exécution — n'intègre pas de cibles de canal dans la `query` de surveillance.

### `notification`

| Champ      | Description                                                                                                                                                                                                                             |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `events`   | Quels résultats d'exécution doivent déclencher la livraison. Voir [événements de notification](#notification-events) ci-dessous. Si tu définis `channels` et omets `events`, par défaut les valeurs sont `changed` et `first_snapshot`. |
| `channels` | Liste d'objets `{ "type", "target", "events"? }`                                                                                                                                                                                        |

#### Événements de notification

Utilise `events` pour indiquer **quand** tu veux être notifié. Valeurs autorisées :

| Événement        | Signification                                                                                                                                                                                    |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `changed`        | Notifie lorsque le moniteur détecte un **changement** par rapport au snapshot précédent (par exemple, nouveau contenu, prix mis à jour, ou une différence que le pipeline classe comme changée). |
| `first_snapshot` | Notifie lorsque le moniteur prend son **premier snapshot** — l'exécution de base initiale qui stocke le contenu actuel avant les comparaisons ultérieures.                                       |

Tu peux inclure l'un ou les deux. Par exemple, `["changed"]` alerte uniquement sur les mises à jour après la base ; `["first_snapshot"]` confirme la configuration sans attendre une différence ; `["changed", "first_snapshot"]` couvre les deux.

Les `events` par canal sur un objet canal utilisent les mêmes valeurs et remplacent la liste de niveau supérieur uniquement pour ce canal.

Types de canaux pris en charge :

| `type`  | Format `target`                                        |
| ------- | ------------------------------------------------------ |
| `email` | Adresse email valide                                   |
| `slack` | URL de webhook entrant Slack                           |
| `sms`   | Numéro de téléphone E.164 (par exemple `+14155552671`) |

### `webhook`

Séparé de `notification.channels`, `webhook.url` reçoit des charges utiles HTTP POST lorsque le moniteur déclenche ton URL de callback. Tu peux utiliser à la fois un webhook et des notifications de canal sur le même moniteur.

### Exemples

Email uniquement :

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

Callback webhook :

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

SMS :

```json theme={null}
{
  "query": "Alert me when https://status.example.com shows an incident",
  "frequency": "every hour",
  "notification": {
    "channels": [
      { "type": "sms", "target": "+14155552671" }
    ]
  }
}
```

## Politique de source

Utilise `source_policy` pour contraindre les URLs et domaines que le planificateur peut utiliser.

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

## Fréquences

Définis `frequency` en langage naturel, par exemple :

* `every hour` (par défaut si omis)
* `every day at 9am`
* `every weekday at 14:30`

Règles :

* Doit être lisible comme un langage de planification (pas une question de moniteur arbitraire).
* Intervalle minimum : **toutes les 10 minutes**.
* Les calendriers sont stockés et exécutés en **UTC** (`schedule.timezone` est `UTC`).
* Longueur maximale : 50 caractères.

L'API dérive une expression cron de ton texte `frequency` et l'expose sur `schedule.cron`. Lorsque le moniteur est `active`, `schedule.next_run_at` montre la prochaine exécution en ISO 8601.

## Lister les moniteurs

Récupère tous les moniteurs pour ton équipe avec `GET /v1/monitors`.

Par défaut, les moniteurs supprimés sont filtrés. Utilise `?include_deleted=true` pour les inclure.

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

### Forme de la réponse

```json theme={null}
{
  "monitors": [
    {
      "id": "monitor_0wj35czpn7",
      "object": "monitor",
      "query": "Watch AirOps blog for new blog 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": "Notify me when a new startup launches on 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
}
```

## Obtenir un moniteur

Récupère un moniteur unique avec `GET /v1/monitors/:monitor_id`.

La réponse inclut `last_run` (résumé du dernier snapshot) et `total_count` (nombre de snapshots) sauf si tu passes `include_total_count=false`. Ajoute `include-diagram=true` pour inclure un `mermaid_diagram` du DAG du moniteur.

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

### Forme de la réponse

```json theme={null}
{
  "id": "monitor_biglavgvq3",
  "object": "monitor",
  "query": "Notify me when a new startup launches on 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
}
```

## Lister les événements du moniteur

Utilise `GET /v1/monitors/:monitor_id/events` pour lister les événements de snapshot pour un moniteur.

Pagination :

* `limit` (par défaut `25`, max `100`)
* `cursor` (jeton opaque de `next_cursor`)
* `count_only=true` retourne seulement `{ "total_count": N }`

Les événements sont retournés du plus récent au plus ancien. Chaque élément inclut une `snapshot_url` pré-signée à durée limitée.

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

### Forme de la réponse

```json theme={null}
{
  "data": [
    {
      "id": "run_iwsoafcpyx",
      "run_id": "run_iwsoafcpyx",
      "created": 1780063395,
      "changed": false,
      "summary": "First snapshot of this monitor has been taken. Stored current content as baseline.",
      "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
}
```

## Obtenir la planification du moniteur

Utilise `GET /v1/monitors/:monitor_id/planning` pour inspecter la spécification de workflow FDA et le DAG du planificateur après provisionnement.

```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": [] }
  }
}
```

## Obtenir une exécution de moniteur

Utilise `GET /v1/monitors/:monitor_id/runs/:run_id` pour les métadonnées de snapshot et les événements de journal d'agent analysés pour une exécution (`run_id` doit commencer par `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 run_v7k2p9m3 completed. Files uploaded: 2",
      "event": { "type": "run_complete", "run_id": "run_v7k2p9m3", "files_uploaded": 2 }
    }
  ]
}
```

## Diffuser les journaux d'agent

Utilise `GET /v1/monitors/:monitor_id/agent-logs?stream=1` (ou `Accept: text/event-stream`) pour suivre les journaux CloudWatch de l'agent du moniteur, filtrés par cet `monitor_id`.

Le paramètre de requête optionnel `since` est un timestamp en millisecondes (par défaut : il y a 30 minutes).

Types d'événements SSE : `ready`, `log`, `heartbeat`, `error`.

## Mettre à jour un moniteur

Mets à jour un moniteur avec `POST /v1/monitors/:monitor_id`.

Champs pris en charge (inclure uniquement ce que tu veux changer) :

* `metadata` — fusionné avec les clés existantes ; les valeurs de chaîne vides suppriment les clés
* `frequency` — recrée le calendrier interne et remet `status` à `active`
* `notification` — remplace l'ensemble de l'objet de notification
* `webhook` — passe `null` pour supprimer

Renvoie `409` pendant que `status` est `provisioning`.

Lorsque tu ajoutes `notification.channels` sans `events`, l'API par défaut `events` à `["changed", "first_snapshot"]`.

### Ajouter une notification par email

<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": "Notify me when a new startup launches on 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
}
```

### Ajouter un 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": "Notify me when a new startup launches on 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
}
```

## Mettre en pause un moniteur

Mets en pause un moniteur avec `POST /v1/monitors/:monitor_id/pause`.

La mise en pause désactive le calendrier sous-jacent et met `status` à `paused`. Seuls les moniteurs avec `status: active` peuvent être mis en pause. Le corps de la requête est vide.

<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 cas de succès, renvoie `200` avec le moniteur et `status: paused`. `schedule.next_run_at` est `null` pendant la pause.

## Reprendre un moniteur

Reprends un moniteur en pause avec `POST /v1/monitors/:monitor_id/resume`.

La reprise réactive le calendrier et remet `status` à `active`. Seuls les moniteurs `paused` peuvent être repris.

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

## Supprimer un moniteur

Supprime un moniteur avec `DELETE /v1/monitors/:monitor_id`.

La suppression supprime de manière logique la ligne du moniteur (`status: deleted`) et retire ses ressources de calendrier et d'agent fantôme.

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

## Statut du moniteur

| Statut         | Signification                                                                     |
| -------------- | --------------------------------------------------------------------------------- |
| `provisioning` | Agent, spécification, planificateur, et calendrier sont en cours de configuration |
| `active`       | Calendrier activé ; les exécutions s'exécutent sur `schedule.frequency`           |
| `paused`       | Calendrier désactivé via `/pause`                                                 |
| `failed`       | Échec de provisionnement ou de mise à jour du calendrier (`error_message` défini) |
| `deleted`      | Supprimé de manière logique via `DELETE`                                          |

## Exemples d'utilisation

Voici des modèles de moniteurs courants. Chaque exemple nécessite seulement `query` et `frequency` au moment de la création ; ajoute `notification` et `webhook` plus tard si tu veux des alertes lors de la première exécution ou lors de changements.

### Nouveaux lancements Y Combinator

Surveille [Y Combinator Launches](https://www.ycombinator.com/launches/) pour les startups nouvellement publiées. Après planification, `tracked.type` est `urls` et `tracked.urls` pointe vers la page des lancements.

<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": "Notify me when a new startup launches on 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: 'Notify me when a new startup launches on 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": "Notify me when a new startup launches on Y Combinator Launches",
      "frequency": "every 20 minutes"
    }'
  ```
</CodeGroup>

Ajoute la livraison par email et webhook après que le moniteur soit `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" }
  }'
```

Avec `channels` défini et `events` omis, l'API par défaut à `["changed", "first_snapshot"]` pour que tu sois notifié lors de l'exécution de base et chaque fois qu'un changement est détecté.

### Articles de blog de concurrents (AirOps, Profound)

Surveille l'index de blog d'un concurrent pour de nouveaux articles. Le planificateur résout `tracked.urls` à l'URL du blog (par exemple `https://www.airops.com/blog` ou `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": "Watch AirOps blog for new blog 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": "Watch Profound blog for new blog posts",
      "frequency": "every 20 minutes"
    }'
  ```
</CodeGroup>

Après création, un moniteur de cette famille ressemble à :

```json theme={null}
{
  "id": "monitor_588ck513zd",
  "object": "monitor",
  "query": "Watch Profound blog for new blog 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"
}
```

Utilise `events: ["changed"]` sur `notification` si tu veux seulement des alertes lorsque de nouveaux articles apparaissent, pas lors du premier snapshot de base.

### Seuil de prix d'action (Tesla)

Surveille une source de données structurée lorsque la condition est numérique plutôt qu'une différence de page. Le planificateur définit `tracked.type` à `data_api` et laisse `tracked.urls` vide.

```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": "Notify me when Tesla stock price drops below 436$",
    "frequency": "every 12 minutes"
  }'
```

```json theme={null}
{
  "id": "monitor_7609p3191t",
  "object": "monitor",
  "query": "Notify me when Tesla stock price drops below 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"
}
```

### Changelog de l'API OpenAI

Sois notifié lorsque le [changelog de l’API OpenAI](https://developers.openai.com/api/docs/changelog) liste de nouvelles fonctionnalités, des sorties de modèles, ou des dépréciations. Mentionne l'URL du changelog dans `query` ou fixe-la avec `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": "Notify me when the OpenAI API changelog has new updates or features https://developers.openai.com/api/docs/changelog",
    "frequency": "every hour",
    "notification": {
      "events": ["changed"],
      "channels": [{ "type": "email", "target": "you@example.com" }]
    }
  }'
```

Définis `events` à `["changed"]` pour être alerté lorsque le contenu du changelog change, pas seulement lorsque le premier snapshot est stocké.

### Gérer plusieurs moniteurs

Liste tous les moniteurs pour ton équipe pour voir les statuts, les calendriers, et les cibles résolues en un seul endroit :

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

Une équipe exécutant les exemples ci-dessus pourrait voir plusieurs moniteurs côte à côte—des surveillances de blog à différents rythmes, une surveillance de prix `data_api`, et un moniteur de lancements YC avec email et webhook configurés—avec `"count": 4` (ou plus) dans la réponse.

## Erreurs de validation courantes

Les endpoints de moniteur renvoient des erreurs de validation claires pour les requêtes invalides courantes :

* `query` manquante ou vide
* `frequency` qui n'est pas un langage de planification, résout trop souvent (moins de 10 minutes), ou dépasse 50 caractères
* Entrées `source_policy` invalides (les tableaux d'URL doivent contenir des chaînes `http`/`https` valides)
* Forme `notification` invalide, `events` inconnus, ou `type` / `target` de canal invalide
* `webhook.url` invalide (doit être `http` ou `https`)
* `output_schema` invalide (doit être un JSON Schema valide)
* Format `monitor_id` ou `run_id` invalide
* Mise à jour pendant que `status` est `provisioning` (`409`)
* Pause/reprise lorsque le statut n'est pas `active` / `paused`

Exemple d'erreur :

```json theme={null}
{
  "error": "Could not interpret 'frequency': \"check every second\". Use scheduling language such as \"every hour\" or \"every day at 9am\"."
}
```
