Skip to content

Managing Subscriptions

Once a subscription is created, you can list, inspect, update, pause, resume, and delete it through the wh sub CLI commands or the MCP subscription tools. Subscription management REST endpoints are not currently mounted; the HTTP API surface below is limited to action delivery observability.

Terminal window
wh sub list

Output shows each subscription’s name, kind, active state, and (for cron) the cronspec. For cross-repo subscriptions, a ← org/repo marker indicating the source repo being watched is shown when you have read access to that source repo:

Subscriptions: myorg/myrepo
signal-hook [webhook] active
daily-sync [cron] active 0 8 * * *
upstream-hook [webhook] active ← myorg/otherrepo
{
"name": "warmhub_subscription_list",
"arguments": {
"orgName": "myorg",
"repoName": "myrepo"
}
}
Terminal window
wh sub view signal-hook

Returns the subscription’s kind, active state, webhook URL, and cron configuration (if cron).

For cross-repo subscriptions, a source repo: field is shown when you have read access to the source repo, indicating which repo’s events trigger the subscription:

upstream-hook
kind: webhook
active: true
source repo: myorg/otherrepo
webhookUrl: https://example.com/hook
{
"name": "warmhub_subscription_get",
"arguments": {
"orgName": "myorg",
"repoName": "myrepo",
"name": "signal-hook"
}
}

Pausing a subscription stops all new deliveries. Existing in-flight deliveries complete normally.

Terminal window
# Pause
wh sub pause signal-hook
# Resume
wh sub resume signal-hook

For cron subscriptions, pausing deregisters the cron job entirely — no ticks fire while paused. Resuming re-registers the job with the original cronspec and timezone. Ticks that would have fired during the pause period are not retroactively delivered.

{
"name": "warmhub_subscription_pause",
"arguments": {
"orgName": "myorg",
"repoName": "myrepo",
"name": "signal-hook"
}
}

Use update when the subscription should keep the same name and lifecycle state, but its trigger or webhook configuration needs to change. Provided fields patch the existing subscription, and nested cronConfig keys merge into the current config.

Terminal window
wh sub update signal-hook \
--on Signal \
--filter '{"shape":"Signal","operation":"add"}' \
--webhook-url https://example.com/hook

For cron subscriptions, switch schedules the same way:

Terminal window
wh sub update health-check \
--cronspec "*/30 * * * *" \
--webhook-url https://example.com/health
{
"name": "warmhub_subscription_update",
"arguments": {
"orgName": "myorg",
"repoName": "myrepo",
"name": "signal-hook",
"shapeName": "Signal",
"filterJson": {
"shape": "Signal",
"operation": "add"
},
"webhookUrl": "https://example.com/hook"
}
}

Lifecycle changes still go through pause and resume, and credential bindings remain managed via the bind/unbind commands.

Deleting a subscription permanently removes it along with its credential bindings and cron schedule.

Terminal window
wh sub delete signal-hook
{
"name": "warmhub_subscription_delete",
"arguments": {
"orgName": "myorg",
"repoName": "myrepo",
"name": "signal-hook"
}
}

The delivery feed shows the history of action deliveries for a subscription — what was dispatched, when, and what happened.

Terminal window
wh sub log signal-hook

Output shows each delivery’s run status, attempt count, timing, source label, and matched operation indexes. Failed runs display the error code and message:

Subscription: signal-hook
succeeded 1/1 2m ago run 019d90f0-0000-7000-8000-000000000000 write ops[0,1]
[0] add thing Sensor/temp-1
[1] add assertion Reading/temp-1-v1
dead_letter 3/5 15m ago run 019d90e0-0000-7000-8000-000000000000 write ops[0]
HTTP_502: Webhook responded 502
[0] revise thing Sensor/temp-1

Follow deliveries in real time with --live:

Terminal window
wh sub log signal-hook --live

This opens a WebSocket connection that auto-refreshes as new deliveries arrive.

{
"name": "warmhub_action_livefeed",
"arguments": {
"orgName": "myorg",
"repoName": "myrepo",
"subscriptionName": "signal-hook",
"limit": 20
}
}

The limit parameter controls how many deliveries to return (1–500, default 50). Pagination is supported via the cursor field in the response — when supplying cursor on a follow-up call, you must also pass an explicit limit (the tool rejects cursor alone with "cursor" requires "limit"). See warmhub_action_livefeed for the full parameter contract.

The HTTP API exposes the underlying run list, optionally filtered by subscriptionName. The endpoint requires repo:configure — anonymous calls return an opaque 404:

Terminal window
curl -H "Authorization: Bearer $WH_TOKEN" \
"https://api.warmhub.ai/api/repos/myorg/myrepo/actions/runs?subscriptionName=signal-hook&status=failed_terminal&limit=20"

This returns the run records themselves. For the richer per-delivery feed (matched-operation context, attempt diagnostics, live updates), use the CLI (wh sub log) or MCP (warmhub_action_livefeed). See HTTP API → Subscriptions for the full parameter list.

Each delivery can have multiple attempts if retries are needed. To inspect the attempt history for a specific run:

Terminal window
wh sub attempts 019d90f0-1111-7000-8000-000000000001

The argument is the run ID (UUIDv7) shown in wh sub log and wh sub attempts JSON output. Output shows each attempt’s status, duration, HTTP status, and error details:

Attempts: run 019d90f0-1111-7000-8000-000000000001
#1 failed (300ms) HTTP 502
HTTP_502: Webhook responded 502
#2 failed (450ms) HTTP 502
HTTP_502: Webhook responded 502
#3 failed (280ms) HTTP 502
HTTP_502: Webhook responded 502
{
"name": "warmhub_action_attempts",
"arguments": {
"orgName": "myorg",
"repoName": "myrepo",
"runId": "019d90f0-1111-7000-8000-000000000001"
}
}

Each attempt records:

FieldDescription
attemptAttempt number (1-based)
statusstarted, succeeded, or failed
startedAtTimestamp when the attempt began
finishedAtTimestamp when the attempt completed (if finished)
httpStatusHTTP response status
errorCodeError classification code
errorMessageHuman-readable error description
StatusMeaning
pendingRun created, not yet executed
runningCurrently executing
processingAccepted by the handler and continuing asynchronously while WarmHub waits for a callback
retry_waitFailed, waiting for next retry attempt
succeededCompleted successfully
failed_terminalNon-retryable error (no further attempts scheduled)
dead_letterNo recovery path remains

WarmHub records repo-scoped action notifications for delivery outcomes. dead_letter always records a failure notification. failed_terminal records one immediately unless a fallback URL still has a chance to recover the delivery; in that case, notification is deferred until recovery is no longer possible. Success notifications can also be emitted when the subscription has notifyOnSuccess enabled.

The common dead_letter paths are:

  • retries were exhausted without a successful delivery
  • a previously failed_terminal run’s fallback endpoint was itself rejected by webhook URL validation
  • a previously failed_terminal run’s fallback endpoint exceeded the redirect-follow cap and still could not recover the delivery

wh notifications --repo and the MCP notification tools return repo-scoped delivery notification records for operators, including terminal failures and optional success notifications. Separately, the web app may aggregate related user-facing inbox entries; those inbox entries are not the same API surface as repo-scoped delivery notifications.

Terminal window
wh notifications --repo myorg/myrepo

Filter by time with either epoch milliseconds or an ISO timestamp:

Terminal window
wh notifications --repo myorg/myrepo --since 2026-03-30T12:00:00Z
{
"name": "warmhub_action_notifications",
"arguments": {
"orgName": "myorg",
"repoName": "myrepo",
"limit": 20
}
}

The MCP tool is also repo-scoped and returns delivery notification records rather than the web app’s user feed. When a run reaches failed_terminal or dead_letter status, WarmHub creates a repo-scoped action notification record with the error details. Success notifications can also be enabled per-subscription — see Subscriptions Overview. Those records are visible through the action notification queries:

The endpoint requires repo:configure:

Terminal window
curl -H "Authorization: Bearer $WH_TOKEN" \
"https://api.warmhub.ai/api/repos/myorg/myrepo/actions/notifications"

Common error codes you may see in attempt records:

CodeRetryableDescription
WEBHOOK_NETWORK_ERRORYesNetwork or transport error while connecting to the webhook target
WEBHOOK_TARGET_REJECTEDNoWarmHub rejected the target URL at dispatch time because it was not reachable or not allowed. See Webhook URL Requirements for the allowed scheme, ports, and public-network rules.
WEBHOOK_REDIRECT_LIMITNoThe webhook target exceeded WarmHub’s redirect-follow limit. This is usually a redirect loop or an overly long redirect chain at the partner endpoint. Fix the destination so it returns a terminal response directly or within a small number of redirects.
HTTP_<status>DependsRemote returned an HTTP response. HTTP_429 and HTTP_5xx are retryable; other HTTP_4xx responses are not
WEBHOOK_INPUT_NOT_FOUNDNoCould not load execution input for delivery