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.
List Subscriptions
Section titled “List Subscriptions”Via CLI
Section titled “Via CLI”wh sub listOutput 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/otherrepoVia MCP
Section titled “Via MCP”{ "name": "warmhub_subscription_list", "arguments": { "orgName": "myorg", "repoName": "myrepo" }}View Subscription Details
Section titled “View Subscription Details”Via CLI
Section titled “Via CLI”wh sub view signal-hookReturns 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/hookVia MCP
Section titled “Via MCP”{ "name": "warmhub_subscription_get", "arguments": { "orgName": "myorg", "repoName": "myrepo", "name": "signal-hook" }}Pause and Resume
Section titled “Pause and Resume”Pausing a subscription stops all new deliveries. Existing in-flight deliveries complete normally.
Via CLI
Section titled “Via CLI”# Pausewh sub pause signal-hook
# Resumewh sub resume signal-hookFor 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.
Via MCP
Section titled “Via MCP”{ "name": "warmhub_subscription_pause", "arguments": { "orgName": "myorg", "repoName": "myrepo", "name": "signal-hook" }}Update Subscription
Section titled “Update Subscription”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.
Via CLI
Section titled “Via CLI”wh sub update signal-hook \ --on Signal \ --filter '{"shape":"Signal","operation":"add"}' \ --webhook-url https://example.com/hookFor cron subscriptions, switch schedules the same way:
wh sub update health-check \ --cronspec "*/30 * * * *" \ --webhook-url https://example.com/healthVia MCP
Section titled “Via MCP”{ "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.
Delete
Section titled “Delete”Deleting a subscription permanently removes it along with its credential bindings and cron schedule.
Via CLI
Section titled “Via CLI”wh sub delete signal-hookVia MCP
Section titled “Via MCP”{ "name": "warmhub_subscription_delete", "arguments": { "orgName": "myorg", "repoName": "myrepo", "name": "signal-hook" }}Delivery Feed
Section titled “Delivery Feed”The delivery feed shows the history of action deliveries for a subscription — what was dispatched, when, and what happened.
Via CLI
Section titled “Via CLI”wh sub log signal-hookOutput 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-1Follow deliveries in real time with --live:
wh sub log signal-hook --liveThis opens a WebSocket connection that auto-refreshes as new deliveries arrive.
Via MCP
Section titled “Via MCP”{ "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.
Via HTTP API
Section titled “Via HTTP API”The HTTP API exposes the underlying run list, optionally filtered by subscriptionName. The endpoint requires repo:configure — anonymous calls return an opaque 404:
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.
Run Attempts
Section titled “Run Attempts”Each delivery can have multiple attempts if retries are needed. To inspect the attempt history for a specific run:
Via CLI
Section titled “Via CLI”wh sub attempts 019d90f0-1111-7000-8000-000000000001The 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 502Via MCP
Section titled “Via MCP”{ "name": "warmhub_action_attempts", "arguments": { "orgName": "myorg", "repoName": "myrepo", "runId": "019d90f0-1111-7000-8000-000000000001" }}Each attempt records:
| Field | Description |
|---|---|
attempt | Attempt number (1-based) |
status | started, succeeded, or failed |
startedAt | Timestamp when the attempt began |
finishedAt | Timestamp when the attempt completed (if finished) |
httpStatus | HTTP response status |
errorCode | Error classification code |
errorMessage | Human-readable error description |
Run Statuses
Section titled “Run Statuses”| Status | Meaning |
|---|---|
pending | Run created, not yet executed |
running | Currently executing |
processing | Accepted by the handler and continuing asynchronously while WarmHub waits for a callback |
retry_wait | Failed, waiting for next retry attempt |
succeeded | Completed successfully |
failed_terminal | Non-retryable error (no further attempts scheduled) |
dead_letter | No recovery path remains |
Notifications
Section titled “Notifications”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_terminalrun’s fallback endpoint was itself rejected by webhook URL validation - a previously
failed_terminalrun’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.
Via CLI
Section titled “Via CLI”wh notifications --repo myorg/myrepoFilter by time with either epoch milliseconds or an ISO timestamp:
wh notifications --repo myorg/myrepo --since 2026-03-30T12:00:00ZVia MCP
Section titled “Via MCP”{ "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:
Via HTTP API
Section titled “Via HTTP API”The endpoint requires repo:configure:
curl -H "Authorization: Bearer $WH_TOKEN" \ "https://api.warmhub.ai/api/repos/myorg/myrepo/actions/notifications"Error Codes
Section titled “Error Codes”Common error codes you may see in attempt records:
| Code | Retryable | Description |
|---|---|---|
WEBHOOK_NETWORK_ERROR | Yes | Network or transport error while connecting to the webhook target |
WEBHOOK_TARGET_REJECTED | No | WarmHub 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_LIMIT | No | The 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> | Depends | Remote returned an HTTP response. HTTP_429 and HTTP_5xx are retryable; other HTTP_4xx responses are not |
WEBHOOK_INPUT_NOT_FOUND | No | Could not load execution input for delivery |
Hit a problem or have a question? Get in touch.