Card snapshot
hookpulse.net
·
2026-10-16 11:25:12 UTC
·
80c0dab6dce2408e0f6cf90f1247547f6f987823903ac9e884822cc72a401bf2
This is a frozen copy of the agent's agent-card.json as we observed it at the timestamp above. We capture a new snapshot every time the card's content hash changes. Useful for: forensic drift analysis, verifying downstream callers see the right version, reproducing routing decisions made historically.
{
"name": "HookPulse",
"description": "Monitor backups, automations, scheduled tasks and websites. Get an alert when a routine's check-ins stop or a site stops answering, and when it is back. Ten monitors are free.",
"url": "https://hookpulse.net",
"provider": {
"organization": "HookPulse",
"url": "https://hookpulse.net",
"email": "contato@hookpulse.net"
},
"documentationUrl": "https://hookpulse.net/llms.txt",
"protocol": "mcp",
"protocolUrl": "https://modelcontextprotocol.io",
"interfaces": [
{
"transport": "streamable-http",
"url": "https://hookpulse.net/mcp"
}
],
"capabilities": {
"streaming": true,
"pushNotifications": false,
"authentication": "none"
},
"defaultInputModes": [
"application/json"
],
"defaultOutputModes": [
"application/json",
"text/markdown"
],
"skills": [
{
"id": "list_plans",
"name": "list_plans",
"description": "The plans for sale, with price and term, and the account's active plan. Each purchase lasts 30 days, with no automatic renewal. Each purchase lasts 30 days, with no automatic renewal. Without an account or wallet credential, `account` is `null`.",
"endpoint": "GET /api/plans"
},
{
"id": "buy_plan",
"name": "buy_plan",
"description": "Buy a plan: starter (40 more monitors, up to 50), team (90 more monitors, up to 100), business (490 more monitors, up to 500). Each purchase lasts 30 days, with no automatic renewal. Answers 402 with the price; pay with x402 or prepaid credit and repeat. Each purchase lasts 30 days, with no automatic renewal. Without payment, the answer is 402 with the price. Pay with prepaid credit (`X-Credito`) or x402 (`PAYMENT-SIGNATURE` v2 or `X-PAYMENT` v1). Without an account, the verified paying wallet owns the purchase. Use wallet agent authentication to read its plan, or claim it after account sign-in. The `compra` mark prevents repeat credit debits. An x402 nonce can settle only once; keep the receipt after a delivery error.",
"endpoint": "POST /api/plans/:plan"
},
{
"id": "cancel_plan",
"name": "cancel_plan",
"description": "Cancel the active plan now. What it added ends, including a paid period that has not started; that period and a purchase under 7 days are refunded. Requires accept_data_removal: true. Send `{ \"accept_data_removal\": true }`: what the plan added ends now (`account.cancel.losses` in `GET /api/plans` says what), including a period already paid that has not started yet, such as a renewal. A purchase made less than 7 days ago and a paid period that has not started go back the same way they were paid (card or Pix); USDC is refunded by the team.",
"endpoint": "POST /api/plans/cancel"
},
{
"id": "api_index",
"name": "api_index",
"description": "Full index of the HookPulse API.",
"endpoint": "GET /api/"
},
{
"id": "health",
"name": "health",
"description": "Liveness.",
"endpoint": "GET /api/health"
},
{
"id": "create_guest",
"name": "create_guest",
"description": "Creates a guest token hp_\u2026 Keep the token: without it there is no way back to the monitors, unless you have already tied them to an e-mail.",
"endpoint": "POST /api/guest"
},
{
"id": "list_endpoints",
"name": "list_endpoints",
"description": "Lists the guest's/session's endpoints.",
"endpoint": "GET /api/endpoints"
},
{
"id": "create_endpoint",
"name": "create_endpoint",
"description": "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. Read billing before a paid option. A charged request returns HTTP 402. With `check` the monitor is a site: HookPulse calls `check.url` every `interval_sec` (default 300) and alerts when it goes down \u2014 two failed checks in a row, one minute apart, from a server whose own connection is proven \u2014 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` \u2014 keep them. The second monitor, or an interval below the free minimum, answers **402 with `accepts[]`**: pay and repeat. A miss alerts at most once per 24h \u2014 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.",
"endpoint": "POST /api/endpoints"
},
{
"id": "create_site",
"name": "create_site",
"description": "Monitor a website: HookPulse calls check.url every interval_sec (default 300) and alerts when it goes down and when it is back. Read billing before a paid option. A charged request returns HTTP 402. With `check` the monitor is a site: HookPulse calls `check.url` every `interval_sec` (default 300) and alerts when it goes down \u2014 two failed checks in a row, one minute apart, from a server whose own connection is proven \u2014 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` \u2014 keep them. The second monitor, or an interval below the free minimum, answers **402 with `accepts[]`**: pay and repeat. A miss alerts at most once per 24h \u2014 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.",
"endpoint": "POST /api/endpoints"
},
{
"id": "check_site",
"name": "check_site",
"description": "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 \u2014 you are looking at it. After that, the state and the alerts come from the check that runs every minute.",
"endpoint": "POST /api/endpoints/:id/check"
},
{
"id": "list_sites",
"name": "list_sites",
"description": "The state of every site you monitor (up, down, waiting, paused), with the last check. The same answer is the snapshot of the live subscription (`/api/realtime/`): after it, every check arrives as an event, so a screen never has to poll.",
"endpoint": "GET /api/sites/estado"
},
{
"id": "import_crontab",
"name": "import_crontab",
"description": "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 \u2014 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 \u2014 the name travels in the alert e-mail, in the public dashboard and in the badge.",
"endpoint": "POST /api/import/crontab"
},
{
"id": "get_endpoint",
"name": "get_endpoint",
"description": "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. Only this endpoint carries `duration`: `p50_ms` (the low median, so always a duration that really happened) and `max_ms` over the last runs we measured, computed at read time over the event window we already keep \u2014 the list does not pay for it.",
"endpoint": "GET /api/endpoints/:id"
},
{
"id": "delete_endpoint",
"name": "delete_endpoint",
"description": "Removes the owner's endpoint.",
"endpoint": "DELETE /api/endpoints/:id"
},
{
"id": "list_events",
"name": "list_events",
"description": "Latest ingest pings.",
"endpoint": "GET /api/endpoints/:id/events"
},
{
"id": "ping_ingest",
"name": "ping_ingest",
"description": "Simulates the cron: GET on /in/:id. No credential on purpose \u2014 the caller is a cron, and the ID is already the secret. It costs nothing and there is no quota per ping.",
"endpoint": "GET /in/:id"
},
{
"id": "signal_start",
"name": "signal_start",
"description": "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` \u2014 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.",
"endpoint": "GET /in/:id/start"
},
{
"id": "signal_fail",
"name": "signal_fail",
"description": "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 \u2014 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.",
"endpoint": "GET /in/:id/fail"
},
{
"id": "signal_exit_code",
"name": "signal_exit_code",
"description": "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.",
"endpoint": "GET /in/:id/:exit_code"
},
{
"id": "ingest_key",
"name": "ingest_key",
"description": "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. Point a whole fleet at `/in/<key>/<slug>` and each job creates its monitor on the first check-in, **inside the free tier** \u2014 beyond it the ping answers 402 without creating anything. The key survives signing in: the crontab you deployed keeps working.",
"endpoint": "GET /api/ingest-key"
},
{
"id": "ingest_key_rotate",
"name": "ingest_key_rotate",
"description": "Rotates the ingest key; the previous one stops creating monitors. Existing ones keep working. Monitors already created keep working \u2014 rotating only revokes the power to create new ones.",
"endpoint": "DELETE /api/ingest-key"
},
{
"id": "ping_slug",
"name": "ping_slug",
"description": "Create a monitor on its first check-in. The same slug uses the same monitor. Service and billing limits apply. 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. Creation happens **only inside the free tier** (or the trial); past it the ping is a 402 with `accepts[]` and nothing is written \u2014 not even the event. Works by GET or POST.",
"endpoint": "GET /in/:ingestKey/:slug"
},
{
"id": "signal_slug",
"name": "signal_slug",
"description": "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.",
"endpoint": "GET /in/:ingestKey/:slug/:signal"
},
{
"id": "billing",
"name": "billing",
"description": "Read current free allowances, options with billing off, prices and the account's 7-daytrial. E-mail alerts cost $0.10 once per additional monitor, even with a reused address. Check before creating the second monitor or lowering the interval: it is the number IN FORCE, not the documentation's.",
"endpoint": "GET /api/billing"
}
],
"discovery": {
"openapi": "https://hookpulse.net/openapi.json",
"llms": "https://hookpulse.net/llms.txt",
"okf": "https://hookpulse.net/okf/index.md",
"numbers": "https://hookpulse.net/api/vitrine",
"partnerships": "https://hookpulse.net/api/partners",
"api_catalog": "https://hookpulse.net/.well-known/api-catalog",
"agent_access": "https://hookpulse.net/.well-known/agent-access.json",
"x402": "https://hookpulse.net/.well-known/x402",
"pricing": "https://hookpulse.net/pricing",
"docs": "https://hookpulse.net/docs"
},
"updated": "2026-10-16"
}