Metadata-Version: 2.4
Name: watchfor
Version: 0.7.2
Summary: Official Python SDK + CLI for WatchFor — uptime & infrastructure monitoring (monitors, alert rules, incidents, maintenance windows) over the REST API.
Author-email: WatchFor <hello@watchfor.io>
License: MIT
Project-URL: Homepage, https://watchfor.io/docs/api
Project-URL: Documentation, https://watchfor.io/docs/api
Project-URL: Source, https://www.npmjs.com/package/watchfor
Project-URL: Bug Reports, https://watchfor.io/docs/api
Keywords: watchfor,monitoring,uptime,sdk,cli,incidents,status-page,mcp
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: System :: Monitoring
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# watchfor (Python)

Official Python SDK + CLI for [WatchFor](https://watchfor.io) — uptime &
infrastructure monitoring (monitors, alert rules, incidents, maintenance
windows) over the REST API. Zero dependencies (standard library only).

There is also a [TypeScript SDK](https://www.npmjs.com/package/watchfor),
an [MCP server](https://watchfor.io/docs/api/mcp) and an
[A2A agent](https://watchfor.io/docs/api/a2a) for AI agents.

## Install

```bash
pip install watchfor
```

## Quick start

```python
from watchfor import WatchFor

wf = WatchFor(api_key="wf_live_...")  # create keys in Settings → API keys

# One-call org snapshot
print(wf.summary())

# List monitors
for m in wf.monitors.list()["data"]:
    print(m["name"], m["status"])

# Create a monitor (idempotency key optional, for safe retries)
mon = wf.monitors.create(
    {
        "name": "example.com",
        "type": "http",
        "target": "https://example.com",
        "interval": 300,
        "locations": ["<location-id>"],  # from wf.locations()
    },
    idempotency_key="create-example-1",
)

# Diagnose: firing incidents right now
for inc in wf.incidents.list(status="firing")["data"]:
    print(inc["message"], inc["severity"])

# False positive? Keep the resolved incident on record but drop it from
# uptime, SLA, reports and the status page (include() undoes it)
wf.incidents.exclude(19351, reason="Probe-side DNS hiccup")

# Irreversible: wipe a monitor's checks and incidents; uptime restarts "since reset"
wf.monitors.reset(mon["id"])

# Older checks: pass next_cursor back as cursor (keep the same hours/success)
page = wf.monitors.checks(mon["id"], hours=24, limit=100)
if page["next_cursor"]:
    wf.monitors.checks(mon["id"], hours=24, limit=100, cursor=page["next_cursor"])

# Delete a contact group that alert rules still use (409 without force)
wf.contact_groups.delete("<group-id>", force=True)
```

### Live diagnostics

Run any of 18 checks from WatchFor's probe fleet against **any public
target**, monitored or not — this measures right now, rather than reading
what WatchFor recorded:

```python
# What can I run, and how much budget is left?
catalog = wf.diagnostics.list()

# One check, optionally from a location you choose (plan-gated)
dns = wf.diagnostics.run(
    "dns-lookup", "example.com", options={"recordType": "A", "resolver": "8.8.8.8"}
)
# A closed port / NXDOMAIN / failed handshake does NOT raise:
if not dns["success"]:
    print("finding:", dns["error"])
print("runs left this hour:", dns["remaining"])

# The whole picture in one call: DNS + propagation + TLS + HTTP + ping
# from up to 3 regions, aggregated into one verdict
report = wf.diagnostics.diagnose_target("example.com")
print(report["verdict"]["status"], report["verdict"]["summary"])
```

Runs need a `write` key (a probe sends real traffic from WatchFor's IPs)
and spend the same per-plan hourly allowance as the dashboard Toolbox.
Details: <https://watchfor.io/docs/api/diagnostics>

Resource namespaces: `wf.monitors`, `wf.alert_rules`, `wf.incidents`,
`wf.maintenance_windows`, `wf.contacts`, `wf.contact_groups`,
`wf.diagnostics`. Top-level:
`wf.summary()`, `wf.plan()`, `wf.me()`, `wf.locations()`, `wf.monitor_types()`,
`wf.incident_stats(period=...)`, `wf.notifications(...)`, `wf.activity(...)`.

Errors raise `WatchForError` with `.status`, `.code` and `.message`.

## CLI

```bash
export WATCHFOR_API_KEY=wf_live_...
watchfor summary
watchfor monitors
watchfor incidents --status firing
watchfor checks <monitor_id> [--cursor <next_cursor>]
watchfor report --period 30d
```

## Auth & scopes

Keys carry a scope: `read` (all GET endpoints) or `write` (read plus
create/update/delete). See
[authentication](https://watchfor.io/docs/api/authentication). The API is also
reachable via OAuth 2.1 for MCP clients.

## Reference

- OpenAPI spec: <https://watchfor.io/openapi.json>
- Guides: <https://watchfor.io/docs/api>

MIT License.

## Releasing

```bash
./publish.sh          # build, check, confirm, upload
./publish.sh --build  # build and check only
```

Debian and Ubuntu mark the system Python "externally managed" (PEP 668), so
`pip install build twine` is refused and `python3 -m build` reports *No module
named build*. The script keeps its own `.venv-publish` instead of touching the
system Python. Credentials are read from `~/.pypirc` as usual.
