---
title: WatchFor A2A agent (Agent2Agent)
description: Let other AI agents delegate monitoring tasks to WatchFor over the Agent2Agent protocol — 18 skills to check status, diagnose incidents, report and act.
canonical: https://watchfor.io/docs/api/a2a
---

# WatchFor A2A agent (Agent2Agent)

Let other AI agents delegate monitoring tasks to WatchFor over the Agent2Agent protocol — 18 skills to check status, diagnose incidents, report and act.

WatchFor is an [A2A](https://a2a-protocol.org) (Agent2Agent) agent, so
**other AI agents can delegate monitoring tasks to it** — check status,
diagnose what is broken and why, report on incidents and reliability,
and take operator actions — without a bespoke integration.

A2A is the horizontal counterpart to the [MCP server](/docs/api/mcp):
MCP connects an AI agent to WatchFor as a *tool*; A2A lets an autonomous AI
agent hand WatchFor a *task* and get a result back with a human summary
and the structured facts behind it.

**Endpoint:** `https://watchfor.io/api/a2a` (JSON-RPC 2.0 over HTTPS, `POST` only)
**Agent Card:** [`/.well-known/agent-card.json`](https://watchfor.io/.well-known/agent-card.json)
(protocol version 0.3.0, also served at `/api/a2a/agent-card`)

## Authentication

Same credentials as the REST API and MCP server
([details](/docs/api/authentication)): a `wf_live_…` API key or an
OAuth 2.1 access token as a Bearer header. Read skills need the `read`
scope; the write skills (`create-monitor`, `manage-monitor`,
`manage-incident`, `manage-maintenance`, `manage-report-emails` in `set`
mode) need `write`. The Agent Card advertises both security schemes
(`apiKey` and `oauth2` with the discovery URL), so OAuth-capable clients
can connect on their own.

Only `agent/getAuthenticatedExtendedCard` and the Agent Card itself are
served without a credential; every other method returns HTTP `401` with
a `WWW-Authenticate` hint pointing at the OAuth protected-resource
metadata.

## Skills

WatchFor is a deterministic skill executor, not an LLM: a peer AI agent
selects a skill **by id** and passes its input; WatchFor runs it against
the REST API with the peer's credential and returns a completed task.
Every skill result carries a one-line `summary` a calling AI agent can
relay as-is plus the structured `data` behind it.

The 18 skills are deliberately **task-shaped, not raw CRUD**: they cover
understanding your monitoring and the operator actions an AI agent
realistically performs mid-task. Full administrative CRUD (alert-rule
thresholds, contacts, status pages, billing) lives on the
[REST API](/docs/api) and the [MCP server](/docs/api/mcp).

### Understand & report (read)

| Skill | Input | What it answers |
| --- | --- | --- |
| `check-status` | `monitor?` (id, or a name/URL to search) | "Is it up right now?" for one monitor or the whole organization (status counts + health score) |
| `list-monitors` | `query?`, `type?`, `status?` (`up`/`degraded`/`down`/`paused`/`unknown`), `limit?` | What is being watched — id, name, type, target, current status |
| `diagnose-incidents` | `monitor?` | What is broken and **why**: every active (firing or acknowledged) incident with the exact rule and trigger value, a plain-language category (TLS certificate, HTTP status, DNS, latency, reachability…), the monitor, how long it has been ongoing and whether it is acknowledged |
| `explain-monitor` | `monitor` | One monitor end to end: what it watches, current status, uptime, alert rules, recent incidents and recent checks |
| `incident-report` | `incident_id` | What happened in one incident: trigger, category and the detected → acknowledged → resolved timeline with durations |
| `list-incidents` | `status?`, `severity?`, `monitor_id?`, `since?`, `until?`, `limit?` | Incident history with filters, newest first |
| `reliability-report` | `period?` (`24h`/`7d`/`30d`/`90d`), `monitor?` | Incident counts by severity and MTTR over a period plus the current snapshot (or one monitor's uptime) |
| `org-report` | `period?` (`7d`/`30d`) | The full organization report vs the previous period — uptime, incidents, MTTR/MTBF, response-time trend, per-monitor rows, type-aware sections, insights |
| `describe-monitor-types` | `type?` | What WatchFor can monitor: every type's target format, config fields and exact alert metrics (or one type's details). Type `playwright` = browser checks (Playwright journeys); type `browser` = Core Web Vitals (Lighthouse) |
| `list-locations` | — | Probe locations and their ids for `create-monitor` |
| `check-hosts` | `status?` | Servers running watchfor-agent: who is online, stale or offline, with current CPU, memory and disk |
| `who-is-on-call` | — | Who is on call now and until when, who is next, layers, overrides, and whether they are reachable |

### Act (write)

| Skill | Input | What it does |
| --- | --- | --- |
| `create-monitor` | `name` (≤ 50 chars), `type`, `target`, `interval`, `locations` (empty only for heartbeat), `config?`, `tags?` (≤ 10) | Start watching a target — call `describe-monitor-types` and `list-locations` first |
| `manage-monitor` | `monitor_id`, `action` (`pause`/`resume`/`run-check`/`update`/`delete`/`accept-baseline`/`test-script`) + update fields, `pages` / `fresh` for `accept-baseline`, or an optional `script` for `test-script` | Operate on an existing monitor; delete is permanent; `accept-baseline` makes the latest check the page integrity baseline (deploy hook; `fresh: true` checks the pages first); `test-script` runs a browser check's saved Playwright script — or a new `script` before you save it — once in a sandboxed browser and returns the result |
| `manage-incident` | `incident_id`, `action` (`acknowledge`/`resolve`) | Take ownership of an incident; resolve is accepted asynchronously and re-fires if the problem persists |
| `manage-maintenance` | `action` (`create`/`list`/`cancel`), `name`, `starts_at`, `duration_minutes`, `monitor_ids?`, `monitor_tags?`, `description?`, `timezone?`, `window_id?` | Silence alerts and exclude downtime during planned work (e.g. a deploy the AI agent is performing); `list` works with `read` scope |
| `manage-report-emails` | `action` (`list`/`set`), `frequency?`, `enabled?`, `recipients?` | Read or change the weekly/monthly report-email cadences (up to 5 recipients each) |
| `diagnose` | `target`, `check?` (one of 24 slugs), `options?`, `location_id?`, `max_locations?` | Investigate any public host or URL **live** from the probe fleet, monitored or not. By default it runs the whole bundle — DNS, propagation, TLS grade, HTTP headers and ping from up to 3 regions — and returns one verdict with a status per check, a machine-readable findings list and the TLS and security headers grades; `check` runs just one, and a graded check returns its WatchFor Grade (`grade`: letter, rules version (`rubric`), the findings that cap it). Each check spends the organization's per-plan allowance and the result says how much is left |

`diagnose` is `write` because a run sends real traffic from WatchFor's
probe IPs and spends a metered allowance — see
[Live diagnostics](/docs/api/diagnostics) for the checks, the budgets and
the response shape. Use it for what is true *right now*; use
`check-status` and `diagnose-incidents` for what WatchFor has recorded.

Monitor references (`monitor`) accept a UUID or a name/URL search; if
the search matches several monitors the skill returns the candidates
instead of guessing.

## JSON-RPC methods

| Method | Behaviour |
| --- | --- |
| `message/send` | Runs one skill and returns a **completed** task in the same response (synchronous) |
| `agent/getAuthenticatedExtendedCard` | Returns the public Agent Card (there are no extra authenticated-only skills); works without credentials |
| `message/stream` | Not supported — error `-32004` (`capabilities.streaming = false`); use `message/send` |
| `tasks/get` | Error `-32001` — tasks complete inside `message/send` and are not stored, so there is nothing to poll |
| `tasks/cancel` | Error `-32002` — nothing to cancel |
| any other | `-32601` Method not found |

Push notifications and state-transition history are advertised as
unsupported in the Agent Card. Notifications (requests without an `id`)
are executed but get no response body.

## Calling a skill

Send `message/send` with a message whose **data part** selects the skill
and carries its input:

```bash
curl https://watchfor.io/api/a2a \
  --header "Authorization: Bearer wf_live_YOUR_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "message/send",
    "params": {
      "message": {
        "role": "user",
        "messageId": "1",
        "parts": [
          { "kind": "data", "data": { "skill": "diagnose-incidents", "input": {} } }
        ]
      }
    }
  }'
```

Accepted ways to select a skill, in order of precedence:

1. a data part `{ "kind": "data", "data": { "skill": "<id>", "input": { … } } }`
   — `skillId` / `skill_id` are accepted aliases, and input fields may
   also be placed directly next to `skill` instead of under `input`;
2. `message.metadata.skill` (or `skillId`), with input from a data part
   or `message.metadata.input`;
3. a text part whose trimmed text is exactly a skill id.

Pass `message.metadata.contextId` to correlate tasks; otherwise a new
`contextId` is generated per task.

The response is a completed task; the artifact's first part is the
plain-language summary and the second the structured data:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "id": "…",
    "contextId": "…",
    "kind": "task",
    "status": { "state": "completed", "timestamp": "2026-08-24T10:20:00.000Z" },
    "artifacts": [
      {
        "artifactId": "…",
        "name": "diagnose-incidents-result",
        "parts": [
          { "kind": "text", "text": "2 active incidents: api.example.com — HTTP status code (…)" },
          { "kind": "data", "data": { "active_count": 2, "incidents": [ { "…": "…" } ] } }
        ]
      }
    ]
  }
}
```

### Task states

| `status.state` | When |
| --- | --- |
| `completed` | The skill ran; `artifacts` holds summary + data |
| `input-required` | No skill could be determined (the status message lists `available_skills` with ids, descriptions and a `write` flag), or the input failed validation (the message names the field) — fix and resend |
| `failed` | The skill ran but the underlying API call failed (for example `403` for a `read` credential on a write skill, or `404`); the status message carries the API error and the skill id |

Because every skill maps to REST calls, scopes, per-plan rate limits and
validation apply identically — see [Errors & rate limits](/docs/api/errors).
Rate-limit headers are returned on every authenticated response (an
unauthenticated `401` carries none).

## Notes

- **Synchronous.** Every skill completes inside `message/send`; there
  are no long-running tasks to poll.
- **Behaviour is the API.** Each skill calls `/api/v1` with your
  credential, so what an AI agent can do through A2A is exactly what its
  key or token allows.
- The Agent Card is generated from the live skill registry, so it can
  never drift from what actually runs.
- Discovery without credentials: the Agent Card, plus
  [`/agents.md`](https://watchfor.io/agents.md) and the
  [sandbox](/docs/api/sandbox).

---

Canonical page: https://watchfor.io/docs/api/a2a · All docs: https://watchfor.io/docs · Site guide: https://watchfor.io/llms.txt
