Skip to main content
An on-call schedule says who responds to incidents, and when. Use this API when a script or agent needs to read or change responder coverage. The API is project-scoped and derives the owning team from {project_id}, so team-wide schedules and project-scoped schedules can both cover a service.

Operations

onCall.getCurrentEngineer includes people who are on call through layered rotations. onCall.deleteSchedule needs on_call:write. It removes the rotation with its members, layers, and shifts. When an escalation policy still targets the rotation, the API returns 409 and names the policies. Remove the rotation from those policies first, or add ?force=true to delete it anyway; a level whose only target was the rotation then pages nobody.

Escalation, rotation, and paging endpoints

All paths in this table start with /v1/projects/{project_id}/on-call. Common error statuses: 400 for an invalid body or query, 404 when the project, schedule, policy, page, or contact method is not found, and 422 when a target or member is not valid for the organization.

Create schedule

project_scoped defaults to false, which creates a team-wide schedule. Set it to true when a service needs its own rotation. When the organization already has the number of schedules its plan allows, the request returns 403. See plan limits.

Shift windows

onCall.listShifts accepts:
  • from - ISO timestamp. Defaults to seven days before now.
  • to - ISO timestamp. Defaults to 56 days after now.
Use onCall.listShiftsCsv when a human wants a calendar subscription. MCP Code Mode should prefer onCall.listShifts so agents receive structured JSON.

Overrides

Overrides win during their time window. Ask for explicit confirmation before creating one.

Layered rotations

PUT /schedules/{schedule_id}/layers replaces every layer of the schedule. Send an empty layers array to remove all layers.
  • Up to 10 layers. When two layers cover the same time, the later layer in the list wins. Overrides win over all layers.
  • rotation_kind - daily, weekly, or custom. custom requires rotation_length_hours (1–8760).
  • handoff_local_time (HH:MM) and anchor_date (YYYY-MM-DD) use the schedule timezone. The first member’s shift starts then. Daily and weekly handoffs stay at the same local time across daylight saving time changes.
  • member_user_ids - 1–50 organization members in rotation order. Other users return 422.
The response is { "layers": [ ... ] }. Each layer has id, position, name, rotation_kind, rotation_length_hours, handoff_local_time, anchor_date, and member_user_ids. A schedule that is imported from another system returns 409; change it in its source system. See Layered rotations.

Final schedule

GET /schedules/{schedule_id}/final accepts:
  • from - ISO timestamp. Defaults to now.
  • to - ISO timestamp. Defaults to 14 days after from. The window can be at most 92 days.
source is override, layer, or shift (the schedule’s weekly rotation).

On call now

GET /now returns one entry for each schedule that covers the project:
project_id is null for a team-wide schedule. user_id, source, layer_id, and until are null when nobody is on call for that schedule.

Escalation policies

See On-call escalation for the paging behavior.
  • name - required, 1–120 characters. description - optional, up to 2000 characters.
  • loop_count - how many more times to run all levels after the last level. 0–9, default 1.
  • levels - 1–10 levels, in escalation order.
  • targets - 1–10 per level. { "type": "schedule", "schedule_id" } pages the person who is on call for that schedule. { "type": "user", "user_id" } pages that person. Schedules and people must belong to the project’s organization, or the request returns 422.
  • steps - optional, 1–10 per level. When you omit steps, the level uses the default: text, wait 60 seconds, call, wait 60 seconds, call, wait 60 seconds.
    • channel - sms, voice, or teams.
    • destination_id - required for teams steps, not allowed for others. The ID of a Microsoft Teams notification destination of the organization (of this project for a project-scoped policy), or the request returns 422. A teams step posts one page card to that channel.
    • wait_seconds - wait after this step before the next one. 15–3600, default 60.
    • repeat - how many times to run this step in sequence. 1–5, default 1.
  • project_scoped - create only. false (default) makes a team-wide policy.
  • enabled - defaults to true. A disabled policy starts no new pages.
POST returns 201 with { "policy": { ... } }. A policy has id, team_id, project_id (null for team-wide), name, description, enabled, loop_count, levels (every step with its effective channel, wait_seconds, and repeat), created_at, and updated_at. GET returns { "policies": [ ... ] } for the list and { "policy": { ... } } for one policy. PATCH accepts the same fields except project_scoped. When you send levels, it replaces all levels. Active pages keep the policy as it was when they started. To start a policy for incidents, create a notification destination of type oncall_escalation with { "policyId": "<policy id>" } and attach it to a route.

Pages

A page is one escalation run for one incident. GET /pages accepts:
  • incident_id - the incident’s display id (for example inc_42) or UUID.
  • status - triggered, notifying, acknowledged, resolved, or exhausted.
  • limit - 1–200.
The list returns { "pages": [ ... ] }. GET /pages/{page_id} also includes an attempts array with every text and call:
  • incident_id is the incident UUID.
  • levels and loop_count show the policy as it was when the page started. The example shortens levels.
  • loop, level, and step are zero-based. step counts each repeat of a step separately. On an active page, they point to the next step, and next_action_at shows when it runs.
  • ack_source is sms, voice, dashboard, api, or incident (somebody acknowledged the incident somewhere else).
  • Attempt status is one of claimed, queued, sent, delivered, answered, no_answer, busy, machine (voicemail or answering machine), failed, acknowledged, skipped, or blocked. skipped means nobody was reachable, the channel is turned off for the number, or the number replied STOP. blocked means the plan does not allow the channel. error_message explains skipped, blocked, and failed.
  • Phone numbers in to are always masked.
POST /pages/{page_id}/ack acknowledges an active page and its incident, and stops escalation for the incident. It needs incidents:write. It returns 200 with { "page": { ... } }, 404 when the page does not exist, and 409 when the page is already acknowledged, resolved, or exhausted. To acknowledge an incident after its page is exhausted, use the Incidents API.

Usage

GET /usage?month=YYYY-MM returns how many texts and calls the project’s organization sent in a UTC calendar month. month defaults to the current month. Only texts and calls that Squasher sent count; skipped and blocked attempts do not.

Contact methods

Contact methods are the signed-in user’s own phone numbers. They belong to the user and apply to every project. The project in the path sets the organization for rate limits. API keys get 403: only a signed-in user can read or change their own numbers. POST /contact-methods:
  • phone_number - international (E.164) format with a leading +. Spaces, dashes, dots, and parentheses are removed.
  • label - optional, up to 64 characters.
  • consent - must be true. It records that the user agrees to receive automated alert texts and calls.
It returns 201 with { "contact_method": { ... } }. Squasher does not page a number until it is verified. A contact method has id, kind (phone), phone_number, label, country_code, sms_enabled, voice_enabled, verified_at, verification_sent_at, consented_at, sms_opted_out_at (set when the number replied STOP, cleared after START), and created_at. GET returns { "contact_methods": [ ... ] }. PATCH /contact-methods/{contact_method_id} accepts sms_enabled and voice_enabled booleans.

Verification

  1. POST /contact-methods/{contact_method_id}/verification sends a code by text and returns 202 with { "sent": true }. If the number is already verified, it returns 200 with { "contact_method": { ... } }.
  2. POST /contact-methods/{contact_method_id}/verify with { "code": "123456" } returns 200 with the verified contact method.

Auth

Every operation is authenticated and project-scoped. Use on_call:read for list, current, member, shift, layer, final schedule, now, page, and usage operations. Use on_call:write for create, enable/disable, override, layer, and escalation-policy changes. Acknowledging a page needs incidents:write. Contact methods need a signed-in user; API keys get 403. The * wildcard and global write shortcut also include these scopes.

CLI and MCP

The CLI exposes the same operation family through squasher on-call .... MCP remains Code Mode only: search for onCall, inspect operation schemas, and call exposed operations through execute. Related: On-call, On-call escalation, Notifications API, Incidents API.