Response actions
Send an alert with buttons when your automation needs a decision. The first recipient to choose a button acknowledges the alert and stops escalation. Resolution stays separate. Response buttons are available in the dashboard, iPhone, Android, Apple Watch, and Wear OS apps. Open the notification to choose.
json
{
"message": "Deploy failed",
"response_options": [
{ "id": "retry", "label": "Retry" },
{ "id": "rollback", "label": "Roll back" }
],
"response_webhook_id": "wh_your_endpoint"
}POST this body to /api/v1/alerts using an integration token. The optional webhook ID must identify an active endpoint in the same workspace. There may be 1–5 options, with distinct IDs of 1–64 ASCII letters, digits, _ or -, and nonblank labels up to 80 Unicode characters. Informational alerts cannot have response options. An Idempotency-Key preserves the original alert and options.
A recipient posts {"option_id":"retry"} to /api/v1/alerts/ALERT_ID/respond, authenticated with their device key or session. The response includes the alert, response, can_respond, and badge count. A repeated identical response by the winner returns the saved result. A competing choice returns 409 response_conflict with the current alert. Ordinary acknowledge endpoints return 409 response_required; old apps must use the dashboard or update. Admins may resolve without choosing; waiting clients then receive alert.closed.
CLI
bash
anypager send --option retry=Retry --option rollback="Roll back" --wait "Deploy failed"
anypager listen --alert ALERT_ID --timeout 300The abbreviated anypager --option retry=Retry --wait "Deploy failed" also works. Place options before the message. Both waiting commands print one JSON event to stdout; progress and the created alert ID go to stderr. --quiet suppresses progress, but preserves the result. Exit codes: 0 response, 3 closed without a response, 124 timeout, 1 other error. The default timeout is unlimited; timing out does not close the alert. Reconnect with listen using the printed alert ID.
bash
if result=$(anypager send --option retry=Retry --option rollback="Roll back" --wait "Deploy failed"); then
case "$(printf '%s' "$result" | jq -r .option_id)" in
retry) ./retry-deploy.sh ;;
rollback) ./rollback.sh ;;
esac
fiCLI does not execute commands from alerts. Your script controls the mapping. For actions that must run only once, persist processed event_id values in your application: restarting a listener returns the saved response again.
Result and live subscription
GET /api/v1/alerts/ALERT_ID/response returns the saved event, or a state event with type alert.pending / alert.closed. Connect a WebSocket to /api/v1/alerts/ALERT_ID/response/stream with the same authentication. It sends current state immediately and publishes changes. Send text refresh to refresh state and check access (the CLI does this on idle connections). Reconnects read persisted state, so a response made while disconnected remains available.
Recipients, active workspace owners/admins, and the active sender can read the result. Integration tokens can read only alerts created with that token. Access is checked on connection and before each state delivery. Alerts and responses follow the existing 90-day alert retention policy.
json
{
"version": 1,
"event_id": "unique_event_id",
"type": "alert.responded",
"workspace_id": "workspace_id",
"alert_id": "alert_id",
"option_id": "retry",
"option_label": "Retry",
"responded_by": "user_id",
"responded_at": 1788912000
}Signed webhook
An owner/admin registers an endpoint once:
bash
anypager webhook add --name Deploy --url https://example.com/anypager
anypager webhook list
anypager send --option retry=Retry --webhook wh_your_endpoint "Deploy failed"
anypager webhook deliveries
anypager webhook remove wh_your_endpointUse --workspace ID on webhook commands to override the selected workspace. Registration prints the signing_secret once; store it on your receiver. The management API is /api/response-webhooks (POST/GET), /api/response-webhooks/ID (DELETE), and /api/response-webhooks/deliveries (GET, latest 100 deliveries). Authenticate with an owner/admin device key or session and select the workspace with X-Workspace-ID. Removing an endpoint cancels pending attempts; a request already in flight may finish.
The POST body is the same alert.responded event as CLI. AnyPager-Event-ID identifies it. AnyPager-Signature contains t=TIMESTAMP,v1=HEX_HMAC: HMAC-SHA256 of TIMESTAMP + "." + exact_raw_body using your signing secret. Verify the signature before parsing, compare in constant time, and reject signatures older than five minutes. Example receiver verification in Python:
python
import hashlib
import hmac
import time
def verify(raw_body: bytes, signature: str, secret: str) -> bool:
try:
fields = dict(part.split("=", 1) for part in signature.split(","))
timestamp = fields["t"]
if abs(time.time() - int(timestamp)) > 300:
return False
expected = hmac.new(
secret.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, fields["v1"])
except (KeyError, ValueError):
return FalseReturn 2xx after durably accepting the event. Deduplicate by event_id before executing actions. A callback can be delivered more than once, including when its successful response was lost. Delivery timeout is 10 seconds. Network errors, 408, 429, and 5xx are retried after 1 minute, 5 minutes, 15 minutes, 1 hour, and 6 hours (six total attempts). Redirects are not followed; other statuses are terminal failures. Inspect failures with webhook deliveries.
