Public install, private API key

Quave Pager API

Agents should install the Quave Pager skill instead of scraping this HTML page. This page is mainly a human-readable API guide and fallback reference.

Download The Apps

Install Quave Pager on Android and/or macOS, then create or verify your account and keep your API key in an approved secret store. Release assets are hosted in the public agent repository.

Latest Android APKQuavePager.apk
macOS automatic installcurl -fsSL https://pager.quave.ai/install-macos.sh | bash
macOS manual DMGQuavePager-macOS.dmg
macOS terminal zipQuavePager-macOS.zip
Latest releasequavedev/pager-agent/releases/latest
Android packageai.quave.alarm
macOS bundle IDai.quave.alarm.mac

The download links always point to the latest public release assets. Android may ask you to allow installs from your browser or file manager. macOS users have two polished paths: run the terminal installer for an automatic install into ~/Applications, or open the DMG and drag the Quave Pager icon into Applications. The public macOS DMG and zip are Developer ID signed, notarized, and stapled for direct download outside the Mac App Store.

  1. Android: download and install QuavePager.apk, then grant notifications, full-screen alerts, exact alarms, Do Not Disturb bypass, and unrestricted battery/background usage when prompted.
  2. macOS automatic: run curl -fsSL https://pager.quave.ai/install-macos.sh | bash, paste an API key, and enable launch-at-login in Preferences.
  3. macOS manual: download QuavePager-macOS.dmg, open it, drag Quave Pager into Applications, then open the app.
  4. Copy the generated API key only into QUAVE_PAGER_API_KEY for your agent environment or approved secret store.
  5. Run the agent dry-run before the first real page.

macOS Desktop Receiver

The macOS receiver is a native Swift/AppKit client that aims for Android-power parity: Keychain API-key storage, long-poll delivery, cached future alarms, local timers, listener sleep prevention while running, launch-at-login, configurable ringtone/snooze durations, and a full-screen always-on-top alarm with looping sound until dismissed or snoozed.

Automatic installcurl -fsSL https://pager.quave.ai/install-macos.sh | bash
Manual DMGQuavePager-macOS.dmg
Terminal zipQuavePager-macOS.zip
Sourcemacos/ in the Quave Pager repo
Local buildcd macos && swift build
Local app bundlecd macos && ./scripts/build-app.sh
DistributionDirect download after Developer ID signing and Apple notarization; no Mac App Store release is required.

Device priority metadata is platform-aware: macOS defaults to 10, future iOS to 20, and Android to 30. Android and macOS both expose delivery controls: ring all devices with alarms on at once, or use ordered escalation every 30 seconds so lower-priority numbers ring first and later devices join until someone dismisses or snoozes.

For AI Agents: Install The Skill First

If you are an AI agent reading this page, install the public skill package and follow its SKILL.md. The package CLI is the preferred interface for creating, listing, editing, snoozing, canceling, dismissing, and removing alarms.

Choose an Alarm Type intentionally for every new caller. Use critical for blockers that need the user now, regular for work done / review / normal follow-up, and info for FYI-only pages. Google Calendar sync uses calendar automatically, with source-level reminders defaulting to 10 minutes and 1 minute before each event and editable from 0 to 200 minutes. Long-lived clients should call alarm-types list or GET /api/alarm-types and use returned ids for custom user types. Do not create, edit, or remove types automatically unless the user asks or the client is in an onboarding/admin flow.

Use --link for the result/action URL from the conversation. It is intentionally http:// or https:// only. Use the AI conversation resume flags when the alarm should bring the user back to Codex, Claude Code, Cursor, or another agent. Native clients render that as a separate Resume AI conversation action.

Codex deep links: Codex Desktop exposes the current thread id as CODEX_THREAD_ID. Send it with --codex-thread-id "${CODEX_THREAD_ID:-<thread-id>}" so macOS receives codex://threads/<thread-id> as AI resume metadata. Do not send codex://... as link; that field is for web URLs like PRs and documents.

Skill repoquavedev/pager-agent
Install commandnpx skills add quavedev/pager-agent --skill quave-pager -g -a '*'
API key env varQUAVE_PAGER_API_KEY
Machine metadata/.well-known/quave-pager.json

Do not paste API keys in chat, URLs, docs, logs, command arguments, or committed files. Keep them in an environment variable, secret manager, or the current process environment.

npx -y github:quavedev/pager-agent trigger --alarm-type critical --message "Look at Codex: I need your decision." --codex-thread-id "${CODEX_THREAD_ID:-<thread-id>}"
npx -y github:quavedev/pager-agent trigger --alarm-type critical --message "Claude Code is blocked." --claude-session "<session-id>" --ai-cwd "$PWD"
npx -y github:quavedev/pager-agent trigger --alarm-type regular --message "Review this PR." --link "https://github.com/example/repo/pull/123"
npx -y github:quavedev/pager-agent trigger --alarm-type regular --message "Review this when you can."
npx -y github:quavedev/pager-agent alarm-types list
npx -y github:quavedev/pager-agent list
npx -y github:quavedev/pager-agent edit <alarm-id> --scheduled-at "2026-06-13 16:19:00" --time-zone "America/Campo_Grande" --status pending
npx -y github:quavedev/pager-agent remove <alarm-id>

1. Create an API Key

Install the Android app, create or verify your account, then copy the first API key shown by the app. Existing users can create, rotate, and revoke keys through /api/api-keys. The plaintext key is returned only once.

API key env varQUAVE_PAGER_API_KEY
Agent packagequavedev/pager-agent
OpenAPI/openapi.json

2. Choose Create Mode

POST /api/alarms supports three scheduling modes. Use exactly one mode per request.

ModeHow to send itWhen it rings
ASAPOmit scheduledAt and delaySeconds.Immediately, then expires after ttlSeconds.
RelativeSend delaySeconds, for example 300 for five minutes.Now plus the delay.
ScheduledSend scheduledAt plus timeZone for local time, or an ISO timestamp with an offset/Z.At the concrete timestamp.

Do not combine scheduledAt and delaySeconds; the API returns schedule_conflict.

3. Trigger ASAP

For an immediate alarm, omit scheduledAt and delaySeconds. The server records the alarm immediately and expires it after ttlSeconds, defaulting to 900 seconds.

export QUAVE_PAGER_API_KEY="<user-owned API key>"

curl -fsS -X POST https://pager.quave.ai/api/alarms \
  -H "Authorization: Bearer $QUAVE_PAGER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Codex needs you",
    "body": "Look at Codex: I need your decision to continue.",
    "alarmType": "critical",
    "ttlSeconds": 900,
    "aiConversationResume": {
      "provider": "codex",
      "conversationId": "<thread-id>",
      "targets": [
        { "platforms": ["android", "ios", "web"], "kind": "url", "url": "https://chatgpt.com/codex", "label": "Open Codex" },
        { "platforms": ["macos"], "kind": "deeplink", "url": "codex://threads/<thread-id>", "label": "Open Codex app" }
      ],
      "fallbackInstructions": "Open Codex and resume this task."
    }
  }'

4. Relative Delay

For a relative alarm, send delaySeconds. Five minutes is 300. The API stores the computed scheduledAt timestamp in the response.

curl -fsS -X POST https://pager.quave.ai/api/alarms \
  -H "Authorization: Bearer $QUAVE_PAGER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Five-minute follow-up",
    "body": "Look at Codex: five-minute follow-up.",
    "alarmType": "critical",
    "delaySeconds": 300,
    "ttlSeconds": 900,
    "aiConversationResume": {
      "provider": "codex",
      "conversationId": "<thread-id>",
      "targets": [
        { "platforms": ["android", "ios", "web"], "kind": "url", "url": "https://chatgpt.com/codex" },
        { "platforms": ["macos"], "kind": "deeplink", "url": "codex://threads/<thread-id>" }
      ]
    }
  }'

5. Schedule With Time Zone

For local wall-clock scheduling, send scheduledAt as YYYY-MM-DDTHH:mm:ss plus an IANA timeZone. If timeZone is omitted for a local timestamp, the server defaults to America/Campo_Grande.

Best practice for agents: always include the user's intended IANA time zone when scheduling by local time. Use an explicit offset or Z when scheduling in absolute UTC time.

curl -fsS -X POST https://pager.quave.ai/api/alarms \
  -H "Authorization: Bearer $QUAVE_PAGER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Follow-up",
    "body": "Look at Codex: scheduled review time.",
    "alarmType": "critical",
    "scheduledAt": "YYYY-MM-DDT09:00:00",
    "timeZone": "America/Campo_Grande",
    "ttlSeconds": 900,
    "aiConversationResume": {
      "provider": "codex",
      "conversationId": "<thread-id>",
      "targets": [
        { "platforms": ["android", "ios", "web"], "kind": "url", "url": "https://chatgpt.com/codex" },
        { "platforms": ["macos"], "kind": "deeplink", "url": "codex://threads/<thread-id>" }
      ]
    }
  }'

Absolute timestamp alternative

{
  "scheduledAt": "YYYY-MM-DDT13:00:00Z",
  "ttlSeconds": 900
}

6. Payload Contract

FieldRequiredNotes
titleNoDefaults to Quave Pager.
bodyNoUse one concise sentence with the destination and blocker.
alarmType / alarmTypeIdNoChoose a user-controlled Alarm Type by id, key, or name. Prefer built-in keys critical, regular, and info for agent-created pages because display names can be renamed; calendar is reserved for synced calendar event reminders; use custom type ids when they exist.
linkNoOptional http:// or https:// destination/result/action URL. The native app shows an open action. url is accepted as an alias. Do not use this field merely to resume the AI conversation, and do not put codex://... here.
aiConversationResumeNoOptional object that tells compatible native clients how to return to the agent/chat. It renders separately from link as Resume AI conversation.
scheduledAtNoISO timestamp. Local values use timeZone or the default America/Campo_Grande.
timeZoneNoValid IANA time zone. Recommended for local scheduling.
delaySecondsNoRelative scheduling. Do not combine with scheduledAt.
ttlSecondsNoAlarm delivery window after the scheduled time. Clamped from 30 seconds to 24 hours.
deviceIdNoOmit to page every registered device.

7. Alarm Types

Alarm Types are user-controlled categories that express how the user wants a page to behave. New and existing users are lazily seeded with stable built-in keys critical, regular, info, and calendar, shown by default as Critical, Regular, Info, and Calendar. Agents should pass alarmType or alarmTypeId when triggering an alarm.

Use critical for blockers that need the user now, regular for work done / review / normal follow-up, and info for FYI-only pages. Google Calendar sync uses calendar automatically, with source-level reminders defaulting to 10 minutes and 1 minute before each event and editable from 0 to 200 minutes. For custom types such as Time sensitive, Deploys, Compliance, Customer incident, or Family, list types first and use the returned id; do not mutate the user's type list automatically unless asked.

Native devices keep the regular whole-device pause, and also expose per-type pause/resume. Pausing one type on one receiver never suppresses other alarm types or other devices.

npx -y github:quavedev/pager-agent alarm-types list
npx -y github:quavedev/pager-agent alarm-types create --name "Time sensitive" --description "Cooking timers and other time-sensitive real-world reminders"
npx -y github:quavedev/pager-agent trigger --alarm-type regular --message "Review the PR when you can."
curl -fsS https://pager.quave.ai/api/alarm-types \
  -H "Authorization: Bearer $QUAVE_PAGER_API_KEY"

curl -fsS -X POST https://pager.quave.ai/api/alarm-types \
  -H "Authorization: Bearer $QUAVE_PAGER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Time sensitive", "description": "Cooking timers and other time-sensitive real-world reminders" }'

curl -fsS -X POST https://pager.quave.ai/api/alarms \
  -H "Authorization: Bearer $QUAVE_PAGER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "body": "Review the PR when you can.", "alarmType": "regular" }'

8. AI Conversation Resume

aiConversationResume is for the action that returns the user to the AI. It is intentionally separate from link, which is the useful result of the conversation. Each resume target declares the devices it supports so Android, iOS, macOS, web, or future clients can choose the safest native action.

For Codex, prefer the CLI flag --codex-thread-id "${CODEX_THREAD_ID:-<thread-id>}". The package builds a generic web target plus the macOS app target codex://threads/<thread-id>. If the thread id is not available, do not invent one; use fallback instructions or a generic Codex URL.

Claude Code and Cursor currently use copyable resume commands (claude --resume and cursor-agent --resume) rather than a verified stable conversation deeplink in this package.

FieldNotes
providercodex, claude-code, cursor, or other.
conversationIdOptional thread/session/chat identifier when the agent knows one.
targets[].platformsRequired list such as ["android"], ["macos"], or ["android","ios","web"].
targets[].kindurl, deeplink, copyCommand, or instructions.
fallbackInstructionsShort human fallback when no device-specific target is usable.

Android supports URL and instruction resume targets. macOS supports URL, approved deep links, copyable commands, and instructions. Registered devices also advertise capabilities.aiConversationResume plus capabilities.aiConversationResumeKinds.

{
  "aiConversationResume": {
    "provider": "claude-code",
    "conversationId": "<session-id>",
    "targets": [
      {
        "platforms": ["macos"],
        "kind": "copyCommand",
        "command": "claude --resume <session-id>",
        "cwd": "/path/to/repo",
        "label": "Copy Claude resume command"
      }
    ],
    "fallbackInstructions": "Open Claude Code in the repository and resume session <session-id>."
  }
}

9. Register And Manage Devices

Native clients register themselves with platform metadata. Use GET /api/delivery or GET /api/devices to inspect the account delivery rule, Alarm Types, which receivers have alarms on, and which specific device is currently ringing. Use POST /api/delivery to switch between all and ordered, PATCH /api/devices/:deviceId to rename a device, change priority, or pause alarm delivery for a receiver, and DELETE /api/devices/:deviceId to logically remove a device without deleting history.

Every device registration and long-poll sync updates lastSeenAt, even when alarm delivery is paused for that device. The long-poll response also includes stoppedAlarmIds so a dismiss, acknowledge, cancel, remove, snooze, or per-device remote dismiss action can stop a locally ringing alarm. Use POST /api/devices/:deviceId/alarms/:alarmId/dismiss to stop one alarm on one remote device without dismissing it from history or stopping other receivers. Use POST /api/devices/:deviceId/skipped-during-pause/ack with optional alarmIds to mark skipped-during-pause alarms seen and move them to history.

FieldNotes
platformandroid, macos, future ios, or another client type.
priorityLower numbers are intended to ring earlier. Defaults: macOS 10, iOS 20, Android 30.
enabledfalse pauses alarm delivery only: the device stays registered, continues syncing, and can still receive stop updates for already-ringing alarms. Remove a device when it should no longer be used.
alarmTypeStatesPer-device per-type pause state. Use POST /api/devices/:deviceId/alarm-types/:typeId with { "enabled": false } to pause only that type on that receiver.
lastSeenAtServer timestamp for the receiver's last registration or long-poll sync. Native clients show it as last sync time.
ringingAlarmPresent when that receiver reported it is ringing. Includes alarmId, title, alarm type, and timestamps so another device can stop that exact receiver.
deliveryModeAccount setting. all rings every device with alarms on at once; ordered rings by priority and adds the next device after escalationIntervalSeconds.
capabilitiesClient-reported support such as longPoll, intrusiveAlarm, fullScreen, localSound, deliveryControls, aiConversationResume, aiConversationResumeKinds, and launchAtLogin.
curl -fsS https://pager.quave.ai/api/devices \
  -H "Authorization: Bearer $QUAVE_PAGER_API_KEY"

curl -fsS -X PATCH https://pager.quave.ai/api/devices/<device-id> \
  -H "Authorization: Bearer $QUAVE_PAGER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "priority": 10, "enabled": true }'

curl -fsS -X POST https://pager.quave.ai/api/devices/<device-id>/alarm-types/<alarm-type-id> \
  -H "Authorization: Bearer $QUAVE_PAGER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": false }'

curl -fsS -X DELETE https://pager.quave.ai/api/devices/<device-id> \
  -H "Authorization: Bearer $QUAVE_PAGER_API_KEY"

curl -fsS -X POST https://pager.quave.ai/api/devices/<device-id>/alarms/<alarm-id>/dismiss \
  -H "Authorization: Bearer $QUAVE_PAGER_API_KEY"

10. List Alarm History

Use GET /api/alarms with the same bearer token to inspect scheduled and past alarms before creating a duplicate. Regular reads hide alarms with removedAt; use ?includeRemoved=true only when auditing logical deletes. Production state is durable in MongoDB.

curl -fsS https://pager.quave.ai/api/alarms \
  -H "Authorization: Bearer $QUAVE_PAGER_API_KEY"

The native Android and macOS apps cache this list locally and schedule synced future alarms as a best-effort offline fallback. Online delivery is authoritative for ordered escalation windows.

11. Manage Existing Alarms

The package CLI supports the common management operations directly. Prefer it for agent work; the HTTP endpoints below are the underlying contract. Removed alarms stay in MongoDB with removedAt, but regular list, update, and delivery paths ignore them.

npx -y github:quavedev/pager-agent list
npx -y github:quavedev/pager-agent edit <alarm-id> --scheduled-at "2026-06-13 16:19:00" --time-zone "America/Campo_Grande" --status pending
npx -y github:quavedev/pager-agent remove <alarm-id>
npx -y github:quavedev/pager-agent snooze <alarm-id> --delay-seconds 600
ActionEndpointNotes
EditPATCH /api/alarms/:id or POST /api/alarms/:idEdit title, body, alarmType/alarmTypeId, link, aiConversationResume, deviceId, scheduledAt, timeZone, delaySeconds, ttlSeconds, expiresAt, and status. Use clearAiConversationResume: true to remove resume metadata. Use status: "pending" when rescheduling a past alarm.
SnoozePOST /api/alarms/:id/snoozeSend delaySeconds or a concrete scheduledAt plus timeZone. Sets status: "snoozed".
DismissPOST /api/alarms/:id/dismissKeeps the alarm in history and records dismissedAt.
CancelPOST /api/alarms/:id/cancelKeeps the alarm in history and records canceledAt.
RemoveDELETE /api/alarms/:id or POST /api/alarms/:id/removeLogical delete through removedAt.
curl -fsS -X PATCH https://pager.quave.ai/api/alarms/<alarm-id> \
  -H "Authorization: Bearer $QUAVE_PAGER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "Look at Codex: updated schedule.",
    "scheduledAt": "YYYY-MM-DDT09:30:00",
    "timeZone": "America/Campo_Grande",
    "status": "pending"
  }'