Monitors schedule any registered Upscrape capability and compare selected JSON output over time. The same API works for platform capabilities and Universal Web capabilities.

## Endpoints

```text
GET    /monitors/waitlist
POST   /monitors/waitlist
GET    /monitors
POST   /monitors
GET    /monitors/:id
PATCH  /monitors/:id
DELETE /monitors/:id
POST   /monitors/:id/run
POST   /monitors/:id/pause
POST   /monitors/:id/resume
GET    /monitors/:id/runs
GET    /monitors/:id/runs/:run_id
GET    /monitors/:id/changes
GET    /monitors/:id/changes/:change_id
```

Any active account can use the waitlist endpoints with an API key. Approved beta accounts use `monitors:read` for reads and `monitors:write` for mutations. Creating or changing a monitor's execution template also requires `execute` access to the selected platform and capability. New API keys receive monitor scopes automatically. Existing API keys are not backfilled.

```http
POST https://data.upscrape.com/monitors
Authorization: Bearer UPSCRAPE_API_KEY
Content-Type: application/json
Idempotency-Key: UNIQUE_LOGICAL_MONITOR
```

## Create a platform monitor

```json
{
  "name": "Public profile",
  "capability": "linkedin.profile.get",
  "input": {"url": "https://www.linkedin.com/in/example"},
  "interval_seconds": 21600,
  "comparator": {
    "paths": ["/description", "/current_company"]
  },
  "monthly_credit_limit": 120
}
```

Send an `Idempotency-Key` header for every mutation: create, update, delete, pause, resume, and manual run. Repeating the same logical mutation returns the original resource and cannot queue or charge a second baseline.

## Create a Universal Web monitor

Universal Web uses the same resource. `web.page.extract` defaults to the `web.extract.data` comparison preset, which selects `/data`:

```json
{
  "name": "Product price",
  "capability": "web.page.extract",
  "input": {
    "url": "https://example.com/product",
    "schema": {
      "type": "object",
      "properties": {"price": {"type": "number"}},
      "required": ["price"]
    }
  },
  "interval_seconds": 3600
}
```

`web.page.capture` defaults to `web.capture.content`, which selects stable parsed text and excludes traces, statistics, and timings. Supply explicit `paths` to override a preset.

## Comparison semantics

- The first successful result becomes the baseline and does not create a change.
- Objects ignore key order. JSON types remain significant.
- Arrays remain ordered unless `array_keys` gives a stable key pointer for that selected path.
- A missing required path is `not_comparable`; it is never reported as a deletion.
- Failed, missing, invalid, or oversized results never advance the baseline.
- Capability module or output-schema changes create a safe rebaseline instead of a content-change event.
- Diffs and saved projections are bounded. Large operation values are represented by a hash rather than copied without limit.

Example stable array configuration:

```json
{
  "paths": ["/data/products"],
  "array_keys": {"/data/products": "/id"}
}
```

## Scheduling and charging

The private-beta scheduler supports intervals from 3,600 seconds to 2,592,000 seconds. Creation queues an immediate baseline. The initial account limit is two active monitors and is operator-configurable. Only one run per monitor executes at a time; an overlapping occurrence is recorded as skipped.

Every run is a normal Upscrape job. The scheduler resolves the currently published capability price when it accepts each occurrence. Successful runs consume normal priced usage exactly once. Failed jobs, skipped occurrences, retries, idempotent replays, comparisons, polling, and notification delivery do not add a second charge.

Responses include a conservative 30-day usage estimate. `monthly_credit_limit` counts completed billable usage plus pending reservations at the current price and pauses the monitor before the hard cap could be exceeded. An account with unavailable execution credits is paused separately and can be resumed after the account is funded.

## Credentials and security

Scheduled monitors are account-owned and constrained to the exact saved platform and capability. They survive rotation or revocation of the API key that created them, but each run still rechecks account state, module visibility, current input validation, network policy, stored credentials, capacity, pricing, and credits.

Monitors use account-stored credentials. Inline cookies and tunnel authentication are rejected because short-lived secrets and sessions must not be persisted in monitor configuration.

## Notifications, polling, and retention

Email notifications are enabled automatically and go to the user who created the monitor. Upscrape sends durable, retryable notifications for detected changes, a failure-threshold crossing, recovery, and pauses caused by budget, credits, or credentials. Repeated failing checks do not send repeated incident emails; recovery is sent only after a notified failure.

The run and change endpoints remain the authoritative record and support cursor pagination. Compact run history is retained for 90 days by default. Snapshots and diffs are retained for 30 days. An active monitor's current baseline remains protected; deleting a monitor releases that protection and all monitor data expires under the normal policy.

Customer webhook delivery is not part of this release. The separate internal platform-monitoring webhooks are not a customer API.
