---
name: oncall-api
description: |
  Drive the oncall service's REST API directly (instead of clicking through the
  web UI) to read and manage on-call duty. Use this whenever the user wants to know
  who is on call, view or edit an on-call rotation/schedule, swap or override a
  shift ("cover X's shift", "put me on call next week", "I'm out Friday"), add or
  remove team members or managers, set up a backup lane or a reference layer, find
  the system caller-ID phone numbers, or send a Feishu handover/privilege
  notification — even when they don't mention "the API" by name.
  Triggers on "who's on call for payments", "change the rotation", "page the
  on-call engineer", "oncall schedule", "on-call team". This is the company's
  internal oncall system at oncall.pingcap.net, not a generic paging tool and
  not the Jira/Pingkai oncall-toolkit ticket workflow.
---

# Oncall API

Use the oncall service's REST API to read and manage on-call schedules
programmatically. The full endpoint reference — every verb, path, parameter, and
response field — lives in [`references/api-catalog.md`](references/api-catalog.md).
This file is the mental model and the playbook: read it, then open the catalog
when you need the exact shape of a specific call.

## Base URL

Production is served at **`https://oncall.pingcap.net`**, so every endpoint below
is reached at `https://oncall.pingcap.net/api/v1/...` (the same origin hosts both
the web UI and the API). All paths in this skill and the catalog are written
relative to that host. For local/dev work the host differs (e.g.
`http://localhost:8080`) — only the host changes, the `/api/v1/...` paths don't.

## Authentication

Every request needs an **API key** (an `oc_v2_...` bearer token). A human creates
it in the oncall web UI (sign in → create key); there is no programmatic login.
Without a valid token every endpoint returns 401. Auth0 / browser-session tokens
are for the human frontend only and are deliberately not usable here — if you
don't have a key, ask the user for one rather than authenticating another way.

**Keep the key out of the transcript.** The token is a live credential, so never
paste the literal `oc_v2_...` value into a command, and never `echo`/`cat` it —
anything you type is recorded in the conversation/context and would leak it. Keep
it in the `ONCALL_API_KEY` environment variable (or a gitignored `.env` file the
human fills in) and reference it by name only.

Use the bundled helper, which reads `ONCALL_API_KEY` and assembles the
`Authorization` header internally so the secret never reaches the command line:

```bash
# host defaults to https://oncall.pingcap.net; override with ONCALL_BASE_URL
scripts/oncall_http.sh --path /api/v1/me
scripts/oncall_http.sh --method POST --path /api/v1/overlays --body '{"type":"segment", ...}'
```

If you call `curl` directly instead, reference the variable — `-H "Authorization:
Bearer ${ONCALL_API_KEY}"` — never the expanded value.

Two key types matter:
- **service key** — least-privilege machine key. Needs scope `oncall:read` to read
  and additionally `oncall:write` for any non-GET write. If a write 403s on a
  service key, it's probably missing `oncall:write`.
- **user key** — inherits the owning human's permissions; not scope-limited.

## Roles

Permission rises in three steps. Pick reads freely; check before writes.

- **authenticated** — any valid token. Enough for *every* read.
- **team_writer** — a manager of the target team, or a super admin. Gates every
  team/schedule write. Don't guess whether you have it: `GET /api/v1/teams/{ref}`
  returns `is_manager` and `can_write` **for the caller** — trust those.
- **super_admin** — create teams, issue service keys, send the private
  (privilege-grant) batch.

## The schedule model — read this before editing anything

Getting edits right depends on this one idea. The schedule a user sees is computed
in two layers:

1. **Baseline.** Past shifts are frozen as immutable rows. Future shifts are
   *computed* from the team's rotation **layers** (who rotates, how often, when the
   layer is active). You change the baseline by editing layers, never by writing
   individual future shift rows.
2. **Overlays.** Manual overrides stacked on top of the baseline; a higher
   `overlay` value wins. "Cover Friday for me", "swap this week" → an overlay.
   Deleting the overlay reverts that range to the baseline.

So the decision when someone wants a change is:
- **One-off / temporary** (a swap, a day off, "X covers for Y next week") →
  **overlay** (`POST /api/v1/overlays`). Reversible by deleting the overlay.
- **Permanent / structural** (the rotation cadence, who's in the rotation, a new
  business-hours layer) → **layers** (`PUT /api/v1/teams/{ref}/layers`).

Other model facts:
- Each team has a **primary lane** and an optional **backup lane** (`is_backup`).
- A layer's `combine_mode` controls stacking: `override` = when active it's the
  only layer on call; `append` = it also keeps lower-priority layers on call.
- A **reference layer** takes its on-call from another team via `source_team_id`
  (no `people`/rotation of its own) — handy when one team should mirror another.
- An **empty** assignee list in a segment overlay is a deliberate "**no on-call**"
  for that range, not a no-op. Don't send it by accident.
- Times are unix epoch seconds; schedule windows render in UTC+8.

## Workflow

Work read → preview → write → verify. Previews exist so you never surprise a real
rotation; use them.

1. **Identify.** `GET /api/v1/me` — confirm who you are and which teams you may write.
2. **Discover.** `GET /api/v1/teams` and `GET /api/v1/schedule/overview` to find
   the team and see the current board.
3. **Inspect.** `GET /api/v1/teams/{ref}` plus its `current-oncall` / `layers` /
   `managers` to understand the team before touching it.
4. **Preview.** For a one-off override: `POST /api/v1/teams/{ref}/schedule/preview`.
   For a rotation change: `POST /api/v1/teams/{ref}/layers/preview`. Show the user
   the diff and check `warnings`.
5. **Write.** Overlay → `POST /api/v1/overlays`. Rotation → `PUT /api/v1/teams/{ref}/layers`
   (this **replaces the whole layer set** — include every layer you want to keep).
6. **Verify.** Re-read `current-oncall` or `GET /api/v1/schedule` and confirm the
   result matches what the user asked for.

## Safety

- **Never expose the API key.** Pass it via `ONCALL_API_KEY` / the helper script;
  don't paste the literal token, echo it, or print response bodies that might
  contain it. See Authentication.
- **Preview before any schedule write.** It's free and it catches mistakes a live
  rotation would otherwise inherit.
- **`send_*` notifications have external side effects** (they message real people).
  Confirm with the user first, and prefer the matching `preview` endpoint to show
  exactly what will go out.
- **Don't remove a team's last manager, and don't disable a team, unless the user
  explicitly asks** — both are destructive and easy to do by accident.
- **`PUT .../layers` replaces the entire set.** Omitting a layer deletes it. Read
  the current layers first and resend the ones you intend to keep.
