Dead-man switch for cron and webhooks: ingest URL, miss detection, alerts and a status feed.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Full index of the HookPulse API.
This tool takes no arguments.
Full index of the HookPulse API.
No arguments.
{
"type": "object",
"properties": {}
}Check a site monitor now. One call every 10 seconds per destination host. One call every 10 seconds per destination host, for everyone: the button cannot hammer a site. The first state of a new site comes from here, without an alert — you are looking at it. After that, the state and the alerts come from the check that runs every minute.
| Argument | Type | Description |
|---|---|---|
| id* | string | ID of the site monitor, from `Monitor.id`. |
| guest_token* | string |
{
"type": "object",
"required": [
"id",
"guest_token"
],
"properties": {
"id": {
"type": "string",
"description": "ID of the site monitor, from `Monitor.id`."
},
"guest_token": {
"type": "string"
}
}
}Write to the people behind the product: a question, or a sponsorship/partnership/advertising proposal. One route for a question and for a sponsorship, partnership or ad proposal (`tipo`, with the placements of `GET /api/partners`). One message every 10 seconds per network: one that arrives sooner waits its turn and then goes out — no error. The message reaches the team by e-mail, with `email` as the reply address. For voice, send multipart/form-data: `dados` is this JSON (up to 32 KiB), and `audio` is one WebM, Ogg or M4A audio file (up to 2 MB). Audio can replace message. The browser records up to two minutes. Audio is attached to the owner's e-mail.
| Argument | Type | Description |
|---|---|---|
| fuso | string | Consulting: the IANA time zone, such as `America/New_York`. |
| name* | string | What to call the person writing. |
| pais | string | Consulting: the country of the person asking. |
| site | string | Website of who is proposing. |
| tipo | string | Proposal: `patrocinio`, `parceria`, `anuncio` or `consultoria`. Turns on the fields below. |
| email* | string | Where to reply. |
| prazo | string | Consulting: when to start (`agora`, `1_mes`, `1_3_meses` or `explorando`). |
Create a monitor for a routine. Use cron, tz and grace_sec for a schedule, or interval_sec for a fixed check-in interval. Set max_duration_sec to detect an open run that exceeds its time limit. With `check` the monitor is a site: HookPulse calls `check.url` every `interval_sec` (default 300) and alerts when it goes down — two failed checks in a row, one minute apart, from a server whose own connection is proven — and when it answers again. `{"check":{"url":"https://example.com/"}}` is enough; the name defaults to the host. A site has no `cron`, no `max_duration_sec` and no ingest URL. This response is the only one that shows the monitor's `token` and the `templates` — keep them. A miss alerts at most once per 24h — or per `alert_repeat_sec`, or per interval, whichever is longer. Send `cron`+`tz`+`grace_sec` instead of `interval_sec` for a real schedule: a 03:00 backup is late at 03:01:30, not 24 hours later. `max_duration_sec` catches the other failure: a run that starts and hangs, which plain silence detection only notices at the next scheduled time.
| Argument | Type | Description |
|---|---|---|
| tz | string | IANA time zone for cron, default UTC |
| cron | string | five-field cron, e.g. 0 3 * * * |
| name* | string | how you will recognise the routine in an alert; required, trimmed, max 80 chars, punctuation alone is rejected |
| tags | array | up to 8 tags to group the monitor; lower-cased, [a-z0-9._-], max 32 chars each |
| alert_to | string | E-mail to alert on a miss; without it, the account is alerted. |
Creates a guest token hp_… Keep the token: without it there is no way back to the monitors, unless you have already tied them to an e-mail.
No arguments.
{
"type": "object",
"properties": {}
}Monitor a website: HookPulse calls check.url every interval_sec (default 300) and alerts when it goes down and when it is back. With `check` the monitor is a site: HookPulse calls `check.url` every `interval_sec` (default 300) and alerts when it goes down — two failed checks in a row, one minute apart, from a server whose own connection is proven — and when it answers again. `{"check":{"url":"https://example.com/"}}` is enough; the name defaults to the host. A site has no `cron`, no `max_duration_sec` and no ingest URL. This response is the only one that shows the monitor's `token` and the `templates` — keep them. A miss alerts at most once per 24h — or per `alert_repeat_sec`, or per interval, whichever is longer. Send `cron`+`tz`+`grace_sec` instead of `interval_sec` for a real schedule: a 03:00 backup is late at 03:01:30, not 24 hours later. `max_duration_sec` catches the other failure: a run that starts and hangs, which plain silence detection only notices at the next scheduled time.
| Argument | Type | Description |
|---|---|---|
| name | string | how you will recognise the site in an alert; default: its host |
| tags | array | Up to 8 tags to group this monitor: lower-case letters, digits, `.`, `_` and `-`, at most 32 characters each. A comma-separated string works too. We lower-case and de-duplicate, so `Prod` and `prod` are the same tag. |
| check* | object | url (http/https, public; required), method (GET or HEAD), expect_status (200-399 by default, a range a-b or one code), keyword (text that must be on the page), timeout_ms (1000..30000) |
| alert_to | string | E-mail to alert on a miss; without it, the account is alerted. |
| alert_url |
Removes the owner's endpoint.
| Argument | Type | Description |
|---|---|---|
| id* | string | ID of the monitor to deactivate. |
| guest_token* | string |
{
"type": "object",
"required": [
"guest_token",
"id"
],
"properties": {
"id": {
"type": "string",
"description": "ID of the monitor to deactivate."
},
"guest_token": {
"type": "string"
}
}
}Status of one endpoint (use the endpoint token if you have it). The monitor token only reads: it lets you put the state on a third-party dashboard without handing over the owner's credential. With a schedule it is here that `next_expected_at` says when the next run is due.
| Argument | Type | Description |
|---|---|---|
| id* | string | Monitor ID, from `Monitor.id`. |
| token | string | endpoint token (query) |
| guest_token | string |
{
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "Monitor ID, from `Monitor.id`."
},
"token": {
"type": "string",
"description": "endpoint token (query)"
},
"guest_token": {
"type": "string"
}
}
}Liveness.
No arguments.
{
"type": "object",
"properties": {}
}Reads a crontab and proposes one monitor per scheduled line. Without apply=1 nothing is written; the proposal says what was ignored and why. Passwords in the command are redacted. The body is the crontab itself, as `text/plain`. Without `?apply=1` **nothing is written**: you get the proposal — what would become a monitor, what was ignored and why, and what still fits in the free tier. With `apply=1` it is all or nothing, under the same rules as `POST /api/endpoints`. Comments, variables and `@reboot` are ignored (there is no way to know when to expect a reboot); `@daily` and friends become the equivalent expression. The command becomes the monitor name, with passwords and tokens redacted first — the name travels in the alert e-mail, in the public dashboard and in the badge.
| Argument | Type | Description |
|---|---|---|
| apply | string | "1" to create the monitors |
| crontab* | string | the crontab text |
| guest_token* | string |
{
"type": "object",
"required": [
"guest_token",
"crontab"
],
"properties": {
"apply": {
"type": "string",
"description": "\"1\" to create the monitors"
},
"crontab": {
"type": "string",
"description": "the crontab text"
},
"guest_token": {
"type": "string"
}
}
}Returns (minting the first time) the key for /in/<key>/<slug>, where a ping creates its own monitor inside the free tier. Instruments a whole fleet with no setup call per job. One key per owner. The key survives signing in: the crontab you deployed keeps working.
| Argument | Type | Description |
|---|---|---|
| guest_token | string |
{
"type": "object",
"properties": {
"guest_token": {
"type": "string"
}
}
}Rotates the ingest key; the previous one stops creating monitors. Existing ones keep working. Monitors already created keep working — rotating only revokes the power to create new ones.
| Argument | Type | Description |
|---|---|---|
| guest_token | string |
{
"type": "object",
"properties": {
"guest_token": {
"type": "string"
}
}
}Lists the guest's/session's endpoints.
| Argument | Type | Description |
|---|---|---|
| guest_token* | string |
{
"type": "object",
"required": [
"guest_token"
],
"properties": {
"guest_token": {
"type": "string"
}
}
}Latest ingest pings.
| Argument | Type | Description |
|---|---|---|
| id* | string | ID of the monitor. |
| token | string | Monitor token, alternative to the `X-Hook-Token` header. |
| guest_token | string |
{
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "ID of the monitor."
},
"token": {
"type": "string",
"description": "Monitor token, alternative to the `X-Hook-Token` header."
},
"guest_token": {
"type": "string"
}
}
}The state of every site you monitor (up, down, waiting, paused), with the last check.
| Argument | Type | Description |
|---|---|---|
| guest_token* | string |
{
"type": "object",
"required": [
"guest_token"
],
"properties": {
"guest_token": {
"type": "string"
}
}
}Ingest snippets and the JSON HookPulse POSTs on a miss. It exists so nobody guesses the alert format: `miss_json` and `recovery_json` here are the same bodies that arrive at your `alert_url`. Slack and Discord receive only the field they read.
No arguments.
{
"type": "object",
"properties": {}
}Simulates the cron: GET on /in/:id. No credential on purpose — the caller is a cron, and the ID is already the secret.
| Argument | Type | Description |
|---|---|---|
| id* | string | Monitor ID, from `Monitor.ingest_url`. |
{
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "Monitor ID, from `Monitor.ingest_url`."
}
}
}Create a monitor on its first check-in. The same slug uses the same monitor. A rejected creation writes no monitor or event. The slug becomes the monitor name and its identity: the same slug always means the same monitor, so a retry never creates a second one. Normalised to lowercase, `[a-z0-9-]`, 40 chars. Works by GET or POST.
| Argument | Type | Description |
|---|---|---|
| slug* | string | Name of the job; it becomes the monitor name. |
| ingestKey* | string | Ingest key, from `GET /api/ingest-key`. |
{
"type": "object",
"required": [
"ingestKey",
"slug"
],
"properties": {
"slug": {
"type": "string",
"description": "Name of the job; it becomes the monitor name."
},
"ingestKey": {
"enum": [
"hpk_exemplo"
],
"type": "string",
"description": "Ingest key, from `GET /api/ingest-key`."
}
}
}The shell exit status: 0 behaves like a ping, 1-255 record the code as a failure. `curl -fsS $ORIGIN/in/ID/$?` at the end of a script is the whole integration. `0` behaves exactly like a plain ping; 1 to 255 record the code and leave the failure for the cron.
| Argument | Type | Description |
|---|---|---|
| id* | string | Monitor ID, from `Monitor.ingest_url`. |
| exit_code* | number | Exit status, 0 to 255. Outside that range it is a 400. |
{
"type": "object",
"required": [
"id",
"exit_code"
],
"properties": {
"id": {
"type": "string",
"description": "Monitor ID, from `Monitor.ingest_url`."
},
"exit_code": {
"type": "number",
"description": "Exit status, 0 to 255. Outside that range it is a 400."
}
}
}Record a failed run. The next five-minute check sends the alert with reason fail. The monitor's alert_repeat_sec controls repeated alerts; the default is 24 hours. Failing is not proof of life either: the clock keeps running. The ingest never sends e-mail and never calls out — it records `alert_pending` and the cron turns it into an alert with `reason: "fail"` within 5 minutes, under the same one-per-24h cap. Works by GET or POST.
| Argument | Type | Description |
|---|---|---|
| id* | string | Monitor ID, from `Monitor.ingest_url`. |
{
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "Monitor ID, from `Monitor.ingest_url`."
}
}
}Send start, fail or an exit code by name, with the ingest key; the first signal of a new name creates its monitor. The same signals as `/in/:id/start`, `/in/:id/fail` and `/in/:id/:exit_code`, for a fleet that only knows the ingest key: the job sends its name, not the monitor id. The first signal of a new name creates the monitor, with the same rules and the same `cron`, `tz` and `grace_sec` as the plain ping by name.
| Argument | Type | Description |
|---|---|---|
| slug* | string | Name of the job; it becomes the monitor name. |
| signal* | string | start, fail, or the exit code 0 to 255 |
| ingestKey* | string | Ingest key, from `GET /api/ingest-key`. |
{
"type": "object",
"required": [
"ingestKey",
"slug",
"signal"
],
"properties": {
"slug": {
"type": "string",
"description": "Name of the job; it becomes the monitor name."
},
"signal": {
"enum": [
"fail"
],
"type": "string",
"description": "start, fail, or the exit code 0 to 255"
},
"ingestKey": {
"enum": [
"hpk_exemplo"
],
"type": "string",
"description": "Ingest key, from `GET /api/ingest-key`."
}
}
}Says the run STARTED. Does not clear the dead-man clock: a routine that starts and hangs still becomes a miss. The next success records the duration. `start` is not proof of life: it does not move `last_event_at` and does not clear the miss counter, so a routine that starts and hangs still becomes a miss at the right time. The next success closes the run and records `last_duration_ms` — pass `?rid=` on both ends when runs can overlap, or the duration is measured against the wrong start. With `max_duration_sec` set on the monitor, a run left open past that ceiling alerts with `reason: "too_long"` on the next 5-minute check. Works by GET or POST.
| Argument | Type | Description |
|---|---|---|
| id* | string | Monitor ID, from `Monitor.ingest_url`. |
{
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "Monitor ID, from `Monitor.ingest_url`."
}
}
}Rotates the status feed token; the previous URL stops working.
| Argument | Type | Description |
|---|---|---|
| guest_token | string |
{
"type": "object",
"properties": {
"guest_token": {
"type": "string"
}
}
}Public URL (JSON and RSS) with the status of all the owner's endpoints. The feed token only reads status: it does not create, change or delete. Whoever has the link sees the dashboard — treat it as a secret.
| Argument | Type | Description |
|---|---|---|
| guest_token | string |
{
"type": "object",
"properties": {
"guest_token": {
"type": "string"
}
}
}| espaco |
| array |
| Placement ids from `GET /api/partners`, up to 6. |
| duracao | string | Exposure in days: `30`, `90` or `365`. |
| empresa | string | Who is proposing, when it is a company. |
| formato | string | Consulting: `projeto` (per scope), `mensal` (monthly) or `assessoria` (advisory). |
| message* | string | What you want to say. |
| projeto | string | Consulting: the kind of project (`llm_privado`, `voz`, `dados_sensiveis`, `gpu`, `integracao` or `outro`). |
| horarios | array | Consulting: up to 3 times for the call, `YYYY-MM-DDTHH:MM` in that time zone. |
| string | Optional reply number with country code, such as +5561999999999. |
| orcamento | string | Advertising: `ate_100`, `100_500`, `500_2000`, `2000_mais` or `a_combinar`. Consulting, in US dollars: `ate_2000`, `2000_5000`, `5000_10000`, `10000_mais` or `nao_sei`. |
| pagamento | string |
| prefer_whatsapp | boolean | Request a WhatsApp reply. Requires whatsapp. |
{
"type": "object",
"required": [
"name",
"email",
"message"
],
"properties": {
"fuso": {
"type": "string",
"description": "Consulting: the IANA time zone, such as `America/New_York`."
},
"name": {
"type": "string",
"description": "What to call the person writing."
},
"pais": {
"type": "string",
"description": "Consulting: the country of the person asking."
},
"site": {
"type": "string",
"description": "Website of who is proposing."
},
"tipo": {
"enum": [
"patrocinio",
"parceria",
"anuncio",
"consultoria"
],
"type": "string",
"description": "Proposal: `patrocinio`, `parceria`, `anuncio` or `consultoria`. Turns on the fields below."
},
"email": {
"type": "string",
"description": "Where to reply."
},
"prazo": {
"enum": [
"agora",
"1_mes",
"1_3_meses",
"explorando"
],
"type": "string",
"description": "Consulting: when to start (`agora`, `1_mes`, `1_3_meses` or `explorando`)."
},
"espaco": {
"type": "array",
"items": {
"type": "string"
},
"description": "Placement ids from `GET /api/partners`, up to 6."
},
"duracao": {
"enum": [
"30",
"90",
"365"
],
"type": "string",
"description": "Exposure in days: `30`, `90` or `365`."
},
"empresa": {
"type": "string",
"description": "Who is proposing, when it is a company."
},
"formato": {
"enum": [
"projeto",
"mensal",
"assessoria"
],
"type": "string",
"description": "Consulting: `projeto` (per scope), `mensal` (monthly) or `assessoria` (advisory)."
},
"message": {
"type": "string",
"description": "What you want to say."
},
"projeto": {
"enum": [
"llm_privado",
"voz",
"dados_sensiveis",
"gpu",
"integracao",
"outro"
],
"type": "string",
"description": "Consulting: the kind of project (`llm_privado`, `voz`, `dados_sensiveis`, `gpu`, `integracao` or `outro`)."
},
"horarios": {
"type": "array",
"items": {
"type": "string"
},
"description": "Consulting: up to 3 times for the call, `YYYY-MM-DDTHH:MM` in that time zone."
},
"whatsapp": {
"type": "string",
"description": "Optional reply number with country code, such as +5561999999999."
},
"orcamento": {
"enum": [
"ate_100",
"100_500",
"500_2000",
"2000_mais",
"a_combinar",
"ate_2000",
"2000_5000",
"5000_10000",
"10000_mais",
"nao_sei"
],
"type": "string",
"description": "Advertising: `ate_100`, `100_500`, `500_2000`, `2000_mais` or `a_combinar`. Consulting, in US dollars: `ate_2000`, `2000_5000`, `5000_10000`, `10000_mais` or `nao_sei`."
},
"pagamento": {
"enum": [
"usdc",
"deposito",
"a_combinar"
],
"type": "string"
},
"prefer_whatsapp": {
"type": "boolean",
"description": "Request a WhatsApp reply. Requires whatsapp."
}
}
}| alert_url |
| string |
| Public HTTPS URL that receives a POST on a miss (Slack Incoming, Discord, n8n). |
| grace_sec | number | tolerance after the scheduled time, default 90, min 30 |
| guest_token* | string |
| interval_sec | number | Tolerated silence, in seconds. |
| paused_until | string | maintenance window end (UTC ISO), max 30 days ahead |
| alert_repeat_sec | number | gap before the same incident alerts again, 3600..2592000; null keeps the 86400 default |
| max_duration_sec | number | ceiling for an open run in seconds, 60..86400; null or 0 turns it off |
{
"type": "object",
"required": [
"guest_token",
"name"
],
"properties": {
"tz": {
"type": "string",
"description": "IANA time zone for cron, default UTC"
},
"cron": {
"type": "string",
"description": "five-field cron, e.g. 0 3 * * *"
},
"name": {
"type": "string",
"description": "how you will recognise the routine in an alert; required, trimmed, max 80 chars, punctuation alone is rejected"
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "up to 8 tags to group the monitor; lower-cased, [a-z0-9._-], max 32 chars each"
},
"alert_to": {
"type": "string",
"description": "E-mail to alert on a miss; without it, the account is alerted."
},
"alert_url": {
"type": "string",
"description": "Public HTTPS URL that receives a POST on a miss (Slack Incoming, Discord, n8n)."
},
"grace_sec": {
"type": "number",
"description": "tolerance after the scheduled time, default 90, min 30"
},
"guest_token": {
"type": "string"
},
"interval_sec": {
"type": "number",
"description": "Tolerated silence, in seconds."
},
"paused_until": {
"type": "string",
"description": "maintenance window end (UTC ISO), max 30 days ahead"
},
"alert_repeat_sec": {
"type": "number",
"description": "gap before the same incident alerts again, 3600..2592000; null keeps the 86400 default"
},
"max_duration_sec": {
"type": "number",
"description": "ceiling for an open run in seconds, 60..86400; null or 0 turns it off"
}
}
}| string |
| Public HTTPS URL that receives a POST on a miss (Slack Incoming, Discord, n8n). |
| guest_token* | string |
| interval_sec | number | seconds between checks, default 300 |
| paused_until | string | maintenance window end (UTC ISO), max 30 days ahead |
{
"type": "object",
"required": [
"guest_token",
"check"
],
"properties": {
"name": {
"type": "string",
"description": "how you will recognise the site in an alert; default: its host"
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Up to 8 tags to group this monitor: lower-case letters, digits, `.`, `_` and `-`, at most 32 characters each. A comma-separated string works too. We lower-case and de-duplicate, so `Prod` and `prod` are the same tag."
},
"check": {
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"type": "string"
},
"method": {
"type": "string"
},
"keyword": {
"type": "string"
},
"timeout_ms": {
"type": "number"
},
"expect_status": {
"type": "string"
}
},
"description": "url (http/https, public; required), method (GET or HEAD), expect_status (200-399 by default, a range a-b or one code), keyword (text that must be on the page), timeout_ms (1000..30000)"
},
"alert_to": {
"type": "string",
"description": "E-mail to alert on a miss; without it, the account is alerted."
},
"alert_url": {
"type": "string",
"description": "Public HTTPS URL that receives a POST on a miss (Slack Incoming, Discord, n8n)."
},
"guest_token": {
"type": "string"
},
"interval_sec": {
"type": "number",
"description": "seconds between checks, default 300"
},
"paused_until": {
"type": "string",
"description": "maintenance window end (UTC ISO), max 30 days ahead"
}
}
}