---
title: Plan, limits & on-call
description: Read your organization's plan, its limits and live usage to pre-flight automation, and see who is on call right now, until when, and who is next.
canonical: https://watchfor.io/docs/api/plan
---

# Plan, limits & on-call

Read your organization's plan, its limits and live usage to pre-flight automation, and see who is on call right now, until when, and who is next.

| Method | Path | Scope | operationId |
| --- | --- | --- | --- |
| GET | `/v1/plan` | read | `getPlan` |
| GET | `/v1/on-call` | read | `listOnCallSchedules` |

## Get your plan

`GET /v1/plan` — the organization's current plan with its limits and
live usage counts. Use it to pre-flight automation ("do we have a
monitor slot left?", "is the API budget large enough?") without
triggering a `403`.

```bash
curl https://watchfor.io/api/v1/plan \
  -H "Authorization: Bearer wf_live_YOUR_KEY"
```

```json
{
  "object": "plan",
  "name": "Pro",
  "limits": {
    "max_monitors": 100,
    "max_monitors_by_type": { "rdap": 50, "browser": 15 },
    "min_interval_seconds": 60,
    "min_interval_seconds_by_type": { "ssl": 3600, "rdap": 86400 },
    "max_members": 50,
    "max_status_pages": 50,
    "max_contacts": 100,
    "max_contact_groups": 25,
    "max_maintenance_windows": 10,
    "max_on_call_schedules": 3,
    "max_regions": 3,
    "max_locations": -1,
    "max_parallel_monitors": 10,
    "allowed_strategies": ["roundrobin", "random", "parallel"],
    "allow_postmortems": true,
    "allow_incident_comments": true,
    "api_requests_per_minute": 60,
    "run_now_per_hour": 1
  },
  "usage": {
    "monitors": 42,
    "status_pages": 2,
    "contacts": 12,
    "contact_groups": 3,
    "maintenance_windows": 1,
    "members": 6
  }
}
```

The numbers above are illustrative — plans and limits are managed
centrally and change; see [/pricing](/pricing) for what each plan
includes today.

### Limit semantics

- **`-1` = unlimited**, **`0` = feature not available on this plan**;
  positive numbers are hard caps. Exceeding a cap returns
  `403 forbidden` with a human-readable message on the endpoint that hit
  it.
- `usage.maintenance_windows` counts windows that are **upcoming or
  active** — ended windows are history and never count against
  `max_maintenance_windows`.
- `min_interval_seconds_by_type` overrides the plan-wide minimum interval
  for specific monitor types (seconds).
- `max_monitors_by_type` caps specific types (for example Core Web
  Vitals pages) inside the overall `max_monitors`.
- `max_regions` / `max_locations` bound how many regions and individual
  locations one monitor may use; `allowed_strategies` and
  `max_parallel_monitors` govern `schedulingStrategy` (see
  [Monitors](/docs/api/monitors#create-a-monitor)).
- `api_requests_per_minute` is the per-key [rate limit](/docs/api/errors#rate-limits);
  `0` means the plan has no API access.
- `run_now_per_hour` is the per-monitor allowance for
  [on-demand checks](/docs/api/checks-uptime#run-a-check-now).
- Only customer-relevant limits are exposed; `limits` is `null` if the
  organization has no plan record.

Also available as the MCP tool `get_plan`.

## Who is on call

`GET /v1/on-call` — read-only: every on-call schedule with the person
currently on call and until when, the next shifts, the rotation layers
and any active or upcoming overrides — what the dashboard's schedule
card shows, resolved server-side (layers, restrictions and overrides
applied, DST-safe). Schedule management and escalation policies stay in
the dashboard — see [On-call](/docs/alerting/on-call).

```bash
curl https://watchfor.io/api/v1/on-call \
  -H "Authorization: Bearer wf_live_YOUR_KEY"
```

```json
{
  "object": "list",
  "data": [
    {
      "id": 3,
      "object": "oncall_schedule",
      "name": "Primary on-call",
      "timezone": "Europe/Vilnius",
      "enabled": true,
      "layer_count": 1,
      "current_on_call": {
        "user_id": "…", "name": "Petras P.", "email": "petras@acme.io",
        "until": "2026-09-23T06:00:00.000Z", "reachable": true
      },
      "upcoming_shifts": [
        { "user": { "id": "…", "name": "Ona K.", "email": "ona@acme.io" },
          "from": "2026-09-23T06:00:00.000Z", "until": "2026-09-24T06:00:00.000Z",
          "layer": { "id": 41, "position": 0 }, "override": false },
        { "user": { "id": "…", "name": "Jonas B.", "email": "jonas@acme.io" },
          "from": "2026-09-24T06:00:00.000Z", "until": "2026-09-24T18:00:00.000Z",
          "layer": null, "override": true }
      ],
      "layers": [
        { "id": 41, "position": 0, "rotation": "daily", "interval_days": 1,
          "handoff_time": "09:00", "anchor_date": "2026-09-21",
          "participants": [
            { "id": "…", "name": "Petras P.", "email": "petras@acme.io", "reachable": true },
            { "id": "…", "name": "Ona K.", "email": "ona@acme.io", "reachable": false }
          ],
          "restrictions": null }
      ],
      "overrides": [
        { "id": 9, "user": { "id": "…", "name": "Jonas B.", "email": "jonas@acme.io" },
          "from": "2026-09-24T06:00:00.000Z", "until": "2026-09-24T18:00:00.000Z",
          "active": false }
      ]
    }
  ],
  "has_more": false
}
```

| Field | Meaning |
| --- | --- |
| `current_on_call` | Who is on call this instant; `null` when the schedule is disabled or nobody is on shift (a restricted layer outside its hours). `until` is the end of their current block — the next handoff, or the end of the override they are covering. `reachable` says whether they have at least one enabled way to be paged; contact details themselves are never exposed |
| `upcoming_shifts` | The next five staffed blocks after the current one, in order — the calendar's next entries. Gaps where nobody is on call are skipped. `layer` names the rotation layer that staffs the block (`null` for an override); `override` marks a block that comes from an override rather than the rotation |
| `layers` | The rotation layers, lowest `position` first; a higher position wins where two layers cover the same time. `rotation` is `daily`, `weekly` or `custom` (every `interval_days` days), handing off at `handoff_time` (schedule time zone) counted from `anchor_date`. `participants` are in rotation order, each with `reachable`. `restrictions` is `{ start, end, days }` (`HH:MM` in the schedule's zone; ISO weekdays 1 = Monday … 7 = Sunday, empty = every day) for a layer that only covers part of the week, else `null` |
| `overrides` | Overrides that are running or still ahead (`active` tells which); past ones are not listed. Who the override replaces is what the rotation would otherwise give for that period |

A person who has left the organization but is still in a rotation
appears with their `id` and `null` name/email — the dashboard shows the
same slot as "Unknown". Personal pages sent to the on-call person show
up in [notifications](/docs/api/notifications) with `oncall_page: true`.

---

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