Pulse
Pulse watches your services two ways, active HTTP checks and passive heartbeats, opens an incident when something changes state, and alerts you across your configured channels. It runs on its own always-on ingest service, so it keeps checking and alerting even when the main app is idle, and it can publish a shareable public status page.
Start using Pulse. No mailbox connection is required.
For agents: Pulse is readable over MCP with
waydock_pulse_status(the "is anything down?" overview) andwaydock_pulse_incidents(incident history), and manageable with thewrite:pulsetools:waydock_pulse_node_createenrols a ping node and returns its URL, and there are tools to pause, archive or delete nodes and to mint, approve or revoke agent keys. Every Pulse tool is effective only for Pulse admins (an owner or admin in the org). No 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 minimum floor configured per deployment, about five minutes by default). 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.
Ping URLs (for cron jobs, scripts and bots)
A ping node needs no agent and no key: Pulse gives you a URL, and the job hits it after every run. The token in the URL is the only credential, stored hashed and shown once. It can do exactly one thing, beat that one node.
| Request | Meaning |
|---|---|
GET or POST https://ingest.waydock.ai/api/pulse/ping/<token> | The run succeeded |
.../ping/<token>/fail | The run failed: alert now |
.../ping/<token>/<exit code> | 0 succeeded, 1 to 255 failed, so cmd; curl -fsS -m 10 ".../ping/<token>/$?" reports either way |
Each ping node has its own period (5 minutes to 1 day) and grace. Pulse alerts when no success ping arrives within period plus grace, and at once on a fail; the next success sends the recovery. A new ping node reads Waiting and never alerts until its first ping. Add one from the Nodes tab (Add a ping URL) or with waydock_pulse_node_create. A lost URL is replaced by rotating it on the node's page; the old one stops working immediately. Ping nodes count against the node allowance.
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), NEW (a ping node waiting for its first ping, shown as Waiting), 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). Each channel has its own on/off switch with its settings underneath, and a channel sends only while its switch is on: switching Webhooks off stops every webhook, Telegram group chats included, and switching Email off stops alert emails even if recipients are listed. Switches save the moment they are flipped. A test-alert path lets an admin verify a channel before relying on it, and it sends even while the channel is off.
| Channel | Status | How it works |
|---|---|---|
| Available | A list of alert email addresses on the org's Pulse settings. A new address gets a confirmation link and receives nothing until its owner clicks it (an admin's own verified sign-in address skips this). Each recipient gets their own copy, so nobody sees who else is on the list, and every alert email has a one-click unsubscribe link. | |
| 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. |
| Telegram group | Behind the same flag | A webhook whose destination is a Telegram chat instead of a URL. Choose Telegram chat when adding a webhook, then connect a group: Waydock opens Telegram to add its bot to the group you pick, and the group is saved to your workspace so the next webhook can reuse it. The Waydock bot posts the alert, so there is no URL or signing secret. |
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.
A group is linked by a one-time token that the bot must see inside that group, never by typing a chat id, so a workspace can only route alerts to groups its admins can add the bot to. Sending the connect command inside a forum topic routes alerts to that topic.
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.
Notes
An owner or admin can add a note to any node or monitor from its detail page: what it is, who owns it, what to check when it alerts. The note shows under the name in the node and monitor lists, and the node filter searches it. Notes stay inside your workspace and never appear on the public status 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 |
Resetting uptime
Downtime while you set something up, such as a ping URL wired to the wrong job, is not real downtime. An owner or admin can reset a node's or monitor's uptime from its detail page (or with resetUptime on waydock_pulse_node_update). Uptime then counts from the moment of the reset.
A reset changes where the figure starts. It does not delete anything:
- Incidents are kept. They stay in the incident history, marked as before the reset, and no longer count toward uptime. An incident still open at the reset keeps counting from the reset on.
- It is audited. Each reset is written to the organization audit log with who did it and when, in the same transaction as the reset itself.
- The public page says so. When a figure covers less than the page's window, because of a reset or because the target is new, the status page shows the date it starts from, so a short clean run never reads as the full window.
A node's reset also restarts the figures for its checks.
Quotas and limits
Pulse limits come with your Waydock plan:
| Free | Pro | |
|---|---|---|
| Heartbeat nodes | 5 | 50 |
| URL monitors | 3 | 25 |
| Fastest check | 5 minutes | 1 minute |
| Enabled alert webhooks | 2 | 10 |
The dashboard and the ingest read the same numbers, and a plan change applies within a couple of minutes. A limit stops you adding or resuming, never what is already running: after a downgrade, every node and monitor keeps reporting and the Pulse pages say you are over, and you can add or resume again once you are under the limit or on Pro. Upgrading is the normal Waydock Pro upgrade under Settings, Organization, Billing.
- Node allowance. Counts nodes that are live, approved and not paused. 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. A heartbeat or ping turned away at the allowance gets a 402, and the node is not watched, but it is not silent: the Pulse overview and Nodes pages list every node turned away in the last day until it is counted, and the org's Pulse alert emails (or its owners and admins, when none are set) get one notice a day naming the node.
- Grace window. How long a node may stay silent past its period before it flips to down. A ping node carries its own grace, set when it is created (default: a quarter of its period, between 1 minute and 1 hour). Every other node uses the org's grace setting on the Alerts tab, or the deployment default (env
PULSE_FREE_GRACE_SECONDS, set on both the app and the ingest) when the org has none. It is never shorter than the one-minute sweep. The dashboard judges a node on the same value the ingest arms it with, so a node reads stale exactly when it is about to page. Jobs that run on a long cadence need a larger grace so they do not false-alarm on run-to-run jitter. - Re-alert cooldown. Optional, off by default. With it set, a new incident on the same node, check or monitor within the cooldown of its last alert is still recorded (incident history and uptime count it) but is not sent, and neither is its recovery.
- Check interval floor. The fastest a URL monitor may check, and the shortest interval a heartbeat node may report, per the table above. A ping node's period is always between 5 minutes and 1 day.
How agents use Pulse over MCP
Three read tools need read:pulse (added in catalog version 7). Six management tools need write:pulse (catalog version 12), which also implies read:pulse. Every one of them is effective only for Pulse admins.
| Tool | What it does |
|---|---|
waydock_pulse_status | The monitoring overview: every heartbeat node and HTTP check with its status, kind (agent or ping), last-seen time, period and grace, 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. |
waydock_pulse_keys_list | Agent ingest keys and pending enrollment requests. Never returns key values. |
waydock_pulse_node_create | Enrol a ping node and return its ping URL and fail URL, once. |
waydock_pulse_node_update | Pause, resume, archive or unarchive a node, or reset its uptime; for ping nodes change the period or grace, or rotate the URL. |
waydock_pulse_node_delete | Delete a node and its incident history. Marked destructive, so MCP clients ask first. |
waydock_pulse_key_create | Mint a key for a host running the Pulse agent, returned once. |
waydock_pulse_key_update | Approve or deny a pending enrollment, or revoke a key. |
The management tools run the same code as the dashboard, so the node allowance, the key cap and the admin check apply exactly as they do in the app. The read tools strip secrets: a monitor's full probe URL never leaves the process, only its hostname does. The management tools return a ping URL or a key only at the moment it is created. These tools are MCP only; in the app, use the Pulse pages. To grant an agent access, mint a key that includes read:pulse or write:pulse in Settings → Account → MCP. The key is only effective if its owner is an owner or admin of the org.
To give an agent Pulse and nothing else over OAuth, either have the client request only read:pulse write:pulse (the consent screen then lists just those), or, for a client that always asks for everything, open Limit to specific areas on the consent screen and leave only Pulse ticked. The grant is bound to the workspace picked on that screen. An app that is already connected skips the screen and keeps what it already holds, so disconnect it first under Settings → Connected agents, or have the client send prompt=consent.
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