Docs
Pulse
Pulse is Waydock's built-in uptime and status monitoring. It watches your services two ways, active HTTP checks and passive heartbeats, opens an incident when something changes state, alerts you across your configured channels, and can publish a shareable public status page. Pulse runs on its own always-on ingest service, so it keeps checking and alerting even when the main app is idle.
For agents: Pulse is readable over MCP through two read-only tools,
waydock_pulse_status(the "is anything down?" overview) andwaydock_pulse_incidents(incident history). Both require theread:pulsescope and are effective only for Pulse admins (an owner or admin in the org). Neither tool ever returns a probe URL: monitors are reported by hostname only, because a probe URL can carry a bearer token. This page is also available as Markdown at /docs/pulse.md.
What Pulse is
Pulse answers one question: is anything I run down, and when did it break? You register the things you care about (public endpoints, internal jobs, scheduled crons), Pulse tracks their state on a rolling window, and it pages you when a state change happens. Every incident is recorded so you can see the history and the duration of each outage.
Access is role gated. Reading and configuring Pulse is limited to organization owners and admins. Viewing status in the app, configuring monitors, and reading Pulse over MCP all pass the same owner-or-admin check, so MCP is never a way around the in-app gate.
Monitor types
Pulse has two independent monitoring models. You can use either or both.
HTTP checks (active probes)
An HTTP check is an outbound probe to a target URL on a schedule. You set the method, the expected status, a timeout, and a poll interval (the interval is clamped to a per-plan floor, tighter on Pro). The probe runs from the ingest service with an SSRF guard on the request. A single failure does not page: by default a check must fail a small number of times in a row (a debounce, default two consecutive failures) before its down incident opens, which absorbs transient blips.
Each check can be private or public. Public checks appear on your status page under a label you choose. The target URL is encrypted at rest, because it can embed a token in its path.
The monitor detail view shows the latest check so you can see why something is down: the last status code, the last latency, and the captured response detail (response headers plus a bounded response body, up to roughly 16 KB). Uptime on the detail view is selectable across five windows: 24h, 7d, 30d, 90d, and 180d.
Heartbeats and cron nodes (dead-man's-switch)
A node is a device or job that reports in by POSTing a heartbeat to the ingest endpoint (https://ingest.waydock.ai). Each node has an expected period and a grace window. If a node goes silent for longer than its expected period plus grace, it flips to down. The next heartbeat recovers it. This is a dead-man's-switch: you are alerted by the absence of a signal, which is what you want for a scheduled job that should run on a cadence.
Nodes authenticate with an ingest key (prefixed pk_, stored hashed). A device-approval flow at /pulse/device lets an admin approve a new device before it starts counting.
Waydock's own Railway cron jobs already post a per-job heartbeat to Pulse on each successful run (node id cron:<job>), so every scheduled job gets its own dead-man's-switch with no extra wiring.
Statuses and lifecycle
Heartbeat nodes and HTTP checks derive slightly different statuses.
| Surface | Statuses |
|---|---|
| Heartbeat node | UP, DOWN, STALE (past its expected period but not yet alerting), PENDING (never approved), PAUSED, ARCHIVED |
| HTTP check | UP, DOWN, PAUSED, PENDING |
Nodes support three lifecycle actions beyond normal running:
- Pause suspends monitoring. The node stops alerting until you resume it.
- Archive retires a node: hidden from the list, never alerts, frees its quota slot, and a stray late heartbeat will not revive it. Archiving resolves any open alert and is fully reversible (you can unarchive later). History is kept.
- Delete permanently removes a node and its incident history.
For HTTP checks, pausing suspends probing, and deleting removes the check and its incident and daily-rollup history for good. (Note: deleting a check is a hard delete, not a soft pause.)
Incidents and recovery alerts
Each incident is one episode. When a monitor or node goes down, Pulse opens an episode, records when it opened, and starts delivering the down alert to your enabled channels. When the same target recovers, Pulse closes the episode and sends a recovery notice to the current alert configuration, the same audience that received the down alert. Pulse pages in both directions: down and recovered.
Delivery is resilient. The ingest sweep runs an outbox: it retries a channel that failed transiently on a later tick, and it dedupes per recipient so the same episode never re-pages the same webhook, email, or chat. Recovery notices are a single best-effort pass (a recovery is informational, so it is not retried).
Alert channels
Configure channels in the Pulse alert settings (owner or admin only). A test-alert path lets an admin verify a channel before relying on it.
| Channel | Status | How it works |
|---|---|---|
| Available | A list of alert email addresses on the org's Pulse settings. Delivered through Waydock's mail sender. | |
| Webhook | Available | A signed POST (HMAC-SHA256) to your URL. The payload is shaped for the destination: Slack and Discord are auto-detected from the URL and formatted for each; anything else gets generic JSON. Detection is exact-host, so a lookalike URL falls back to generic. |
| Telegram | Behind a flag | A dedicated channel gated by the EXTERNAL_CHANNELS_TELEGRAM_ENABLED flag. The org enables it, and each member opts in on their linked Telegram identity. Delivery targets the member's own active chat. |
Email and webhook are the always-on channels. Telegram is the newest channel and is gated behind a feature flag plus per-member opt-in, so treat it as opt-in rather than assumed on.
The webhook URL is itself a bearer credential: many providers put a secret token in the URL path. Pulse treats it accordingly. The URL is encrypted at rest, masked in the UI, and revealing it is consent gated. The webhook signing secret is shown once, at creation.
Public status page
Every org can publish a status page at /pulse/status/[slug]. It is unauthenticated and shareable, and it shows only the checks you marked public, under their public labels. It never exposes a probe URL or an internal hostname you did not choose to show. If an org has no public checks, the page returns a 404 rather than an empty page, so there is no accidental disclosure.
The page shows a rollup uptime percentage over a window the org picks: 7, 30, 90, or 180 days. The default window is 90 days. One window applies to every monitor on the page.
Uptime windows
Pulse computes uptime against an observed window: the lookback window is clamped to when the target was first seen, so a monitor that has existed for two days is not scored against the 28 days before it existed. Periods before first-seen render as "no data" rather than as "up".
| Surface | Selectable windows | Default |
|---|---|---|
| Monitor detail (in-app) | 24h, 7d, 30d, 90d, 180d | 24h |
| Public status page | 7d, 30d, 90d, 180d | 90d |
MCP waydock_pulse_status | 30-day rolling uptime per target | n/a |
Quotas and limits
- Node allowance. The number of actively monitored heartbeat nodes an org may run is configurable per deployment (env
PULSE_FREE_NODE_ALLOWANCE), with a conservative default. The allowance is enforced not just at node creation but also on reactivation: resuming or unarchiving a node re-checks the count under an advisory lock and returns a 409 if it would push you over, so a pause/archive then add then resume sequence cannot bypass the cap. - Grace window. How long a node may stay silent before it flips to down is configurable (env
PULSE_FREE_GRACE_SECONDSon the ingest side, plus a per-org grace setting), defaulting to a short window. Jobs that run on a long cadence need a larger grace so they do not false-alarm on run-to-run jitter. - HTTP check interval floor is plan gated (tighter on Pro).
These are org and deployment level settings rather than fixed product numbers, so raise them where your fleet needs it rather than assuming a hard limit.
How agents read Pulse over MCP
Two read-only tools expose Pulse to an MCP client. Both require read:pulse and are effective only for Pulse admins. The scope was added in catalog version 7.
| Tool | Returns |
|---|---|
waydock_pulse_status | The monitoring overview: every heartbeat node and HTTP check with its status, last-seen time, last latency and status code, and 30-day uptime, plus counts, an anyDown flag, and the number of open incidents. Targets are reported by hostname only. |
waydock_pulse_incidents | Incident history for the org, open episodes first then the most recent resolved, each with its subject, kind, open and resolved timestamps, and duration. |
Both tools mirror exactly what a Pulse admin can see in the app, and both strip secrets: a monitor's full probe URL never leaves the process, only its hostname does. To grant an agent read access, mint a key that includes read:pulse in Settings → Account → MCP. The key is only effective if its owner is an owner or admin of the org.
Architecture
Pulse runs across two deployments that share one database:
- App side (
apps/waydock): the dashboard at/pulse, the alert and status-page settings, and the CRUD APIs under/api/pulse/*. All of it is owner-or-admin gated and hidden behind a deployment kill switch. - Ingest side (
apps/pulse-ingest): an always-on service atingest.waydock.aithat receives heartbeats, runs the HTTP probe sweep, detects incidents, rolls up daily uptime, and dispatches alerts. It reads and writes the same database under a bounded connection pool.
See also
- Integrations for connecting the rest of your context
- Tool reference for every MCP tool and scope
- Security for how access and secrets are governed