{
  "name": "HookPulse",
  "description": "Monitor backups, automations and scheduled tasks. Send check-ins and receive an alert when they stop. Manage the same monitors through the interface or API.",
  "build": "425670a3",
  "base_url": "https://hookpulse.net",
  "docs": {
    "llms": "https://hookpulse.net/llms.txt",
    "llms_full": "https://hookpulse.net/llms-full.txt",
    "openapi": "https://hookpulse.net/openapi.json",
    "mcp": "https://hookpulse.net/mcp",
    "pricing": "https://hookpulse.net/api/pricing",
    "billing": "https://hookpulse.net/api/billing",
    "human_ui": "https://hookpulse.net/",
    "data_indexes": [
      {
        "id": "cep",
        "produto": "https://pontofato.com",
        "caminho": "/enderecos",
        "title": "Postal codes and addresses",
        "description": "Find addresses by location, with coordinates and a 2022 reference date. Not a current postal-code certification. Each HTML page opens the product app with its public data already rendered.",
        "hierarchy": "State → municipality → locality → street → addresses",
        "url": "https://pontofato.com/enderecos/",
        "formats": {
          "html": "https://pontofato.com/enderecos/",
          "json": "https://api.pontofato.com/enderecos/index.json",
          "md": "https://api.pontofato.com/enderecos/index.md",
          "okf": "https://api.pontofato.com/enderecos/index.okf.md"
        },
        "llms": "https://api.pontofato.com/enderecos/llms.txt",
        "openapi": "https://api.pontofato.com/enderecos/openapi.json",
        "mcp": "https://api.pontofato.com/enderecos/mcp",
        "okf": "https://api.pontofato.com/enderecos/okf/index.md",
        "access": "public-read-only",
        "pagination": {
          "max_items": 20,
          "next": "links.proximo"
        },
        "updates": "manual"
      },
      {
        "id": "editais",
        "produto": "https://editalmd.com",
        "caminho": "/licitacoes",
        "title": "Public procurement",
        "description": "Find public procurement by location and date. View documents and reading options in EditalMD. Each HTML page opens the product app with its public data already rendered.",
        "hierarchy": "Procedure → state → year → month → day → municipality → purchases",
        "url": "https://editalmd.com/licitacoes/",
        "formats": {
          "html": "https://editalmd.com/licitacoes/",
          "json": "https://api.editalmd.com/licitacoes/index.json",
          "md": "https://api.editalmd.com/licitacoes/index.md",
          "okf": "https://api.editalmd.com/licitacoes/index.okf.md"
        },
        "llms": "https://api.editalmd.com/licitacoes/llms.txt",
        "openapi": "https://api.editalmd.com/licitacoes/openapi.json",
        "mcp": "https://api.editalmd.com/licitacoes/mcp",
        "okf": "https://api.editalmd.com/licitacoes/okf/index.md",
        "access": "public-read-only",
        "pagination": {
          "max_items": 20,
          "next": "links.proximo"
        },
        "updates": "manual"
      }
    ]
  },
  "conventions": {
    "format": "JSON on `/api/*`; errors are `{ error, detail? }`.",
    "cors": "`Access-Control-Allow-Origin: *`.",
    "x402": "Paid actions return HTTP 402 with accepts[]; retry with PAYMENT-SIGNATURE (v2) or X-PAYMENT (v1).",
    "parity": "Touched the UI/API → apidocs + skill + MCP + llms in the same PR."
  },
  "auth": {
    "credito": "Credit token in `Authorization: Bearer cred_…` or `X-Credito`. For recovered x402 funds, use the wallet credential mmw_… in Bearer or X-Wallet-Token, issued through /api/auth/wallet/agent/start and /api/auth/wallet/agent/token.",
    "none": "Public, no credential.",
    "guest": "Guest token returned by monitor creation or `POST /api/guest`, in `X-Guest-Token: hp_…` or `Authorization: Bearer hp_…`. A signed-in account (cookie) also works and takes precedence.",
    "session": "Account session: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page (e-mail code or link, password or Google). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans.",
    "token": "Operator token `METRICS_TOKEN` as Bearer or `?key=`.",
    "github": "GitHub Actions OIDC token in `Authorization: Bearer`, from a job with `permissions: id-token: write`, requested with this site's origin as the audience. It proves the repository and the workflow; no secret. An optional `X-Ingest-Key: hpk_…` (a repository secret) makes the key's owner own the monitor.",
    "github_app": "Only GitHub: the delivery is signed with the HookPulse GitHub App's webhook secret in `X-Hub-Signature-256`.",
    "hook": "The monitor's own token, in `?token=` or in the `X-Hook-Token` header. Read-only: state and pings — it lets you put the monitor on a third-party dashboard without handing over the owner's credential. The owner's token is also accepted on these routes."
  },
  "endpoints": [
    {
      "method": "GET",
      "path": "/agent.json",
      "auth": "none",
      "summary": "Agent card: identity, operator, documentation, the MCP endpoint and the tools it serves. Same document as `/.well-known/agent-card.json`.",
      "grupo": "Discovery",
      "retorno": {
        "_texto": "`application/json`: `name`, `provider`, `protocol` (`mcp`), `interfaces[]` and `skills[]`."
      },
      "exemplo": "curl -s $ORIGIN/agent.json",
      "returns": "`application/json`: `name`, `provider`, `protocol` (`mcp`), `interfaces[]` and `skills[]`.",
      "url": "https://hookpulse.net/agent.json",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/okf/:arquivo",
      "auth": "none",
      "summary": "OKF bundle (Open Knowledge Format v0.1): markdown with frontmatter so an agent reads the whole product without parsing HTML.",
      "grupo": "Discovery",
      "params": {
        "arquivo": {
          "desc": "`index.md`, `sobre.md`, `api.md` or `faq.md`.",
          "exemplo": "index.md"
        }
      },
      "retorno": {
        "_texto": "`text/markdown`. Start at `/okf/index.md`, which lists the bundle."
      },
      "erros": {
        "404": "File outside the bundle."
      },
      "exemplo": "curl -s $ORIGIN/okf/index.md",
      "returns": "`text/markdown`. Start at `/okf/index.md`, which lists the bundle.",
      "url": "https://hookpulse.net/okf/:arquivo",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/.well-known/:arquivo",
      "auth": "none",
      "summary": "Machine discovery before the home page: `api-catalog` (RFC 9727, a linkset with the API and the MCP), `security.txt` (RFC 9116), `x402` (payment manifest: network, wallet and the routes that charge) and `mcp-registry-auth` (the official MCP registry key).",
      "grupo": "Discovery",
      "params": {
        "arquivo": {
          "desc": "`api-catalog`, `security.txt`, `x402`, `mcp-registry-auth` or `apis.json`.",
          "exemplo": "api-catalog"
        }
      },
      "retorno": {
        "_texto": "`application/linkset+json` for the api-catalog; `application/json` for x402 and apis.json; `text/plain` for the other two."
      },
      "erros": {
        "404": "Name outside the five published."
      },
      "exemplo": "curl -s $ORIGIN/.well-known/api-catalog",
      "returns": "`application/linkset+json` for the api-catalog; `application/json` for x402 and apis.json; `text/plain` for the other two.",
      "url": "https://hookpulse.net/.well-known/:arquivo",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/apis.json",
      "auth": "none",
      "summary": "APIs.json (apisjson.org, 0.19): the index APIs.io harvests — the API, the MCP, OpenAPI, guide and OKF bundle in one file. Also at `/.well-known/apis.json`.",
      "grupo": "Discovery",
      "retorno": {
        "_texto": "`application/json` in the APIs.json 0.19 format: `apis[]` with `baseURL`, `humanURL` and `properties[]`."
      },
      "exemplo": "curl -s $ORIGIN/apis.json",
      "returns": "`application/json` in the APIs.json 0.19 format: `apis[]` with `baseURL`, `humanURL` and `properties[]`.",
      "url": "https://hookpulse.net/apis.json",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/",
      "auth": "none",
      "summary": "Self-describing index: the whole API surface, with quota and quickstart.",
      "grupo": "Discovery",
      "retorno": {
        "name": {
          "tipo": "string",
          "desc": "Product name."
        },
        "description": {
          "tipo": "string",
          "desc": "What the product does, in one sentence."
        },
        "build": {
          "tipo": "string",
          "desc": "Deployed commit."
        },
        "base_url": {
          "tipo": "string",
          "desc": "Origin this API is serving from."
        },
        "docs": {
          "tipo": "object",
          "desc": "Links to llms.txt, llms-full.txt, openapi.json, MCP and the UI."
        },
        "conventions": {
          "tipo": "object",
          "desc": "Error format, CORS, x402 and the UI↔API parity rule."
        },
        "auth": {
          "tipo": "object",
          "desc": "Each authentication mode and how to obtain it."
        },
        "endpoints": {
          "tipo": "object[]",
          "desc": "Every endpoint with method, path, auth, absolute URL and what it returns."
        },
        "quota": {
          "tipo": "object",
          "desc": "What is free, what costs and how to pay — before you spend a call."
        },
        "mcp": {
          "tipo": "object",
          "desc": "Address and transport of the MCP server."
        },
        "mcp_tools": {
          "tipo": "string[]",
          "desc": "Name of each MCP tool."
        },
        "quickstart": {
          "tipo": "string[]",
          "desc": "The calls that take you from zero to the first monitor."
        }
      },
      "returns": "{ name, description, build, base_url, docs, conventions, auth, endpoints, quota, mcp, mcp_tools, quickstart }",
      "url": "https://hookpulse.net/api/",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/health",
      "auth": "none",
      "summary": "Liveness and the commit deployed right now — it is how the smoke waits for its own deploy.",
      "grupo": "Discovery",
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always `true` when the Worker answers."
        },
        "app": {
          "tipo": "string",
          "desc": "Product name."
        },
        "build": {
          "tipo": "string",
          "desc": "Deployed commit; CI passes the short SHA on deploy."
        },
        "ts": {
          "tipo": "string",
          "desc": "Time of the response (UTC, ISO-8601)."
        }
      },
      "returns": "{ ok, app, build, ts }",
      "url": "https://hookpulse.net/api/health",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/mcp",
      "auth": "none",
      "summary": "MCP server over HTTP (Streamable HTTP, JSON-RPC 2.0) — plugs into the client with nothing to install.",
      "grupo": "Discovery",
      "desc": "The tools are the operations of this same catalog; the MCP has no backend of its own. `GET /mcp` returns the server card.",
      "retorno": {
        "_texto": "JSON-RPC 2.0 response (`initialize`, `tools/list` or `tools/call`)."
      },
      "notes": [
        "Credentials go in the usual headers (X-Guest-Token, Authorization, X-PAYMENT) and are forwarded to the API.",
        "An exhausted quota comes back as a 402 with accepts[] inside the tool result — pay and repeat."
      ],
      "exemplo": "curl -s -XPOST $ORIGIN/mcp -H 'content-type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\"}'",
      "returns": "JSON-RPC 2.0 response (`initialize`, `tools/list` or `tools/call`).",
      "url": "https://hookpulse.net/mcp",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/guest",
      "auth": "none",
      "summary": "Creates a guest `hp_…` — it is the anonymous owner of your monitors.",
      "grupo": "Identity",
      "desc": "Keep the token: without it there is no way back to the monitors, unless you have already tied them to an e-mail.",
      "retorno": {
        "token": {
          "tipo": "string",
          "desc": "The guest, prefix `hp_`. Send it in `X-Guest-Token` or as Bearer."
        }
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/guest",
      "returns": "{ token }",
      "url": "https://hookpulse.net/api/guest",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/endpoints",
      "auth": "guest",
      "summary": "Lists the owner's monitors, with the state of each one.",
      "grupo": "Monitors",
      "query": {
        "tag": {
          "tipo": "string",
          "desc": "Only monitors carrying this tag. Repeat it to narrow further — `?tag=prod&tag=backup` returns the monitors that have BOTH. Matched after the same normalisation used when saving, so `PROD` finds `prod`; a tag that does not pass the rule matches nothing."
        }
      },
      "retorno": {
        "endpoints": {
          "tipo": "Monitor[]",
          "desc": "The owner's monitors, without the secret fields."
        },
        "tags": {
          "tipo": "string[]",
          "desc": "The tags you filtered by, echoed back. Absent when you did not filter.",
          "opcional": true
        },
        "guest": {
          "tipo": "string",
          "desc": "The guest that owns this list.",
          "nulo": true
        },
        "coverage": {
          "tipo": "object",
          "desc": "Some monitors are beyond what the owner covers: `total`, `covered`, `on_hold`, `reason` and `plan`. `reason` is `trial_ended`, `plan_ended` or `free_limit` (a guest past the free ten). `plan` is the smallest plan that covers all (`{ id, name }`): buy it with `POST /api/plans/:plan`. A guest signs in first. The monitors on hold keep checking, and their alerts go out after the purchase. `null` when every monitor alerts.",
          "nulo": true
        },
        "billing": {
          "tipo": "Billing",
          "desc": "Prices and allowance, to decide before creating the next one.",
          "opcional": true
        }
      },
      "erros": [
        401
      ],
      "exemplo": "curl -s \"$ORIGIN/api/endpoints?tag=prod\" -H \"X-Guest-Token: $HP\"",
      "returns": "{ endpoints[{id,kind,check?,site?,name,interval_sec,cron,tz,grace_sec,next_expected_at,alert_to,alert_url,last_event_at,last_status,last_latency_ms,last_start_at,last_run_id,last_duration_ms,duration,max_duration_sec,alert_pending,miss_count,alerts_on_hold?,alerted_at,alert_repeat_sec,tags,paused_until,state,active,healthy,overdue,waiting_first_ping,created_at,ingest_url?,token?,guest_token?,status_url?,events_url?,curl_example?,templates?}], tags?, guest, coverage, billing?{provider,mode,network,chain_id,pay_to,homolog,dev,dev_gate,alternavel,gratis?,cobrados?,facilitator,asset,asset_address,faucet,wallets,product,free_max_endpoints,free_min_interval_sec,free_email_alerts,prices,usage,trial} }",
      "url": "https://hookpulse.net/api/endpoints",
      "auth_detail": "Guest token returned by monitor creation or `POST /api/guest`, in `X-Guest-Token: hp_…` or `Authorization: Bearer hp_…`. A signed-in account (cookie) also works and takes precedence."
    },
    {
      "method": "POST",
      "path": "/api/import/crontab",
      "auth": "guest",
      "summary": "Paste a `crontab -l` and get one monitor per scheduled line.",
      "grupo": "Monitors",
      "desc": "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.",
      "query": {
        "apply": {
          "tipo": "string",
          "desc": "`1` creates the monitors; anything else only proposes."
        }
      },
      "corpo": {
        "crontab": {
          "tipo": "string",
          "desc": "The crontab itself, sent as the raw `text/plain` body (not JSON) — the output of `crontab -l`.",
          "obrigatorio": true
        }
      },
      "retorno": {
        "applied": {
          "tipo": "bool",
          "desc": "`false` on a proposal, `true` when the monitors were created."
        },
        "recognized": {
          "tipo": "object[]",
          "desc": "Line, cron expression, name and command of each schedule read."
        },
        "ignored": {
          "tipo": "object[]",
          "desc": "Line, reason (`comentario`, `variavel`, `reboot`, `cron_invalido`, `nao_reconhecida`) and the text."
        },
        "existing": {
          "tipo": "object[]",
          "desc": "Lines whose monitor you already have — they are not created again."
        },
        "would_create": {
          "tipo": "int",
          "desc": "How many monitors the import would create."
        },
        "created": {
          "tipo": "object[]",
          "desc": "With `apply=1`: id, name and ingest URL of each monitor created.",
          "opcional": true
        },
        "free_tier": {
          "tipo": "object",
          "desc": "Allowance and prices, to decide before applying."
        }
      },
      "erros": {
        "400": "`crontab_sem_agenda` — no schedule line was recognised.",
        "401": null,
        "402": null
      },
      "exemplo": "crontab -l | curl -s -XPOST \"$ORIGIN/api/import/crontab?apply=1\" -H \"X-Guest-Token: $HP\" -H \"content-type: text/plain\" --data-binary @-",
      "returns": "{ applied, recognized, ignored, existing, would_create, created?, free_tier }",
      "url": "https://hookpulse.net/api/import/crontab",
      "auth_detail": "Guest token returned by monitor creation or `POST /api/guest`, in `X-Guest-Token: hp_…` or `Authorization: Bearer hp_…`. A signed-in account (cookie) also works and takes precedence."
    },
    {
      "method": "GET",
      "path": "/api/endpoints/:id/events",
      "auth": "hook",
      "summary": "The latest pings received at this monitor's ingest.",
      "grupo": "Monitors",
      "params": {
        "id": {
          "desc": "ID of the monitor."
        }
      },
      "query": {
        "token": {
          "tipo": "string",
          "desc": "Monitor token, alternative to the `X-Hook-Token` header."
        }
      },
      "retorno": {
        "events": {
          "tipo": "Ping[]",
          "desc": "The most recent pings, newest first."
        }
      },
      "erros": [
        401,
        404
      ],
      "exemplo": "curl -s \"$ORIGIN/api/endpoints/ID/events?token=$MONITOR_TOKEN\"",
      "returns": "{ events[{kind,rid,duration_ms,exit_code,at,status,latency_ms,source}] }",
      "url": "https://hookpulse.net/api/endpoints/:id/events",
      "auth_detail": "The monitor's own token, in `?token=` or in the `X-Hook-Token` header. Read-only: state and pings — it lets you put the monitor on a third-party dashboard without handing over the owner's credential. The owner's token is also accepted on these routes."
    },
    {
      "method": "POST",
      "path": "/api/endpoints",
      "auth": "none",
      "summary": "Creates a monitor: a routine that pings us (silence beyond the interval becomes an alert), or with `check` a site we call.",
      "grupo": "Monitors",
      "desc": "Reuse your owner credential when available. Without one, this call creates a guest and returns `guest_token`; keep it private for later reads, changes and deletion. No preliminary guest call is needed. 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 monitor beyond the current free coverage, or an interval below the free minimum, can require payment. Read `/api/pricing` and the operation's 402 quote before spending. 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.",
      "corpo": {
        "name": {
          "tipo": "string",
          "desc": "Name to recognise the monitor in the alert — it travels in the e-mail subject, the badge and the public dashboard. Required for a routine: we never invent one for you. A site without it is named after its host. Trimmed, at most 80 characters, any alphabet; punctuation alone and invisible characters are rejected.",
          "obrigatorio": true
        },
        "check": {
          "tipo": "object",
          "desc": "Makes the monitor a site: `url` (http or https, public; required), `method` (`GET` or `HEAD`, default `GET`), `expect_status` (what counts as up: `200-399` by default, a range `a-b` or one code), `keyword` (text that must be in the first 64 KB of the page; it forces `GET`) and `timeout_ms` (1000 to 30000, default 10000). Redirects are followed."
        },
        "interval_sec": {
          "tipo": "int",
          "desc": "Tolerated silence, in seconds. Below the free minimum, it costs."
        },
        "cron": {
          "tipo": "string",
          "desc": "Five-field cron expression (`0 3 * * *`). Mutually exclusive with `interval_sec`: with a schedule, the deadline is the scheduled time plus `grace_sec`."
        },
        "tz": {
          "tipo": "string",
          "desc": "IANA time zone the `cron` is read in. Default `UTC`. Only with `cron`."
        },
        "grace_sec": {
          "tipo": "int",
          "desc": "Seconds of tolerance after the scheduled time before it counts as late. Default 90, minimum 30, maximum 86400. Under the free minimum interval it costs, same rule as `interval_sec`."
        },
        "paused_until": {
          "tipo": "string",
          "desc": "Maintenance window: while this UTC instant is in the future the monitor is not checked. At most 30 days ahead."
        },
        "max_duration_sec": {
          "tipo": "int",
          "desc": "Alerts with `reason: \"too_long\"` when a run opened by `/in/:id/start` has not finished within this many seconds. 60 to 86400; `null` or `0` turns it off. Detection happens on the next 5-minute check."
        },
        "alert_repeat_sec": {
          "tipo": "int",
          "desc": "How long before the SAME incident alerts again. Minimum 3600, maximum 2592000 (30 days, effectively \"tell me once\"); `null` keeps the default of 86400. Your `interval_sec` is the floor: nothing new happened between two checks."
        },
        "tags": {
          "tipo": "string[]",
          "desc": "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."
        },
        "alert_to": {
          "tipo": "string",
          "desc": "Authorized e-mail destination. Without it, the verified account e-mail is used. A guest without alert_to or alert_url has no delivery channel."
        },
        "alert_url": {
          "tipo": "string",
          "desc": "Public HTTPS URL that receives a POST on a miss (Slack Incoming, Discord, n8n)."
        }
      },
      "body": {
        "name": "prod cron",
        "interval_sec": 900
      },
      "retorno": "Monitor",
      "erros": {
        "400": "The name answers with a code: `nome_obrigatorio` (missing, empty or blank) or `nome_invalido` (not a string, punctuation only, or carrying control/format characters). Also an invalid interval or an `alert_url` that is not public HTTPS. The schedule answers with a code: `cron_ou_interval` (both sent), `cron_invalido`, `tz_invalida`, `grace_invalido`, `cron_sem_ocorrencia` (never happens within 366 days). The maintenance window answers `pausa_invalida` (not a future instant) or `pausa_longa` (over 30 days). The `check` answers `check_invalid` (not an object), `check_url_invalid`, `check_url_credentials` (user or password in the URL), `check_url_private` (local name or private address), `check_method_invalid`, `check_expect_status_invalid`, `check_keyword_invalid`, `check_timeout_invalid` or `check_with_cron`.",
        "401": null,
        "402": null
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/endpoints -H 'content-type: application/json' -d '{\"name\":\"prod cron\",\"interval_sec\":900}'",
      "notes": [
        "A site: `curl -s -XPOST $ORIGIN/api/endpoints -H 'content-type: application/json' -d '{\"check\":{\"url\":\"https://example.com/\",\"keyword\":\"Welcome\"}}'`",
        "Set alert_to or alert_url to an authorized destination before relying on notices."
      ],
      "agent_value": {
        "principal": true,
        "idioma": "en",
        "en": {
          "when": "How do I know when a scheduled job stops or a site goes down?",
          "result": "A persistent monitor with schedule, state and configured alerts. Site monitors use active checks; routines send check-ins.",
          "replaces": "We keep the schedule, detect missing runs and send configured alerts. Your agent can stop after setting up the monitor.",
          "free": "Create within free coverage, connect the routine's check-in and an authorized alert destination, then verify state and events. Existing monitoring continues after your agent session ends.",
          "paid": "Compare the action quote with GET /api/plans. Plans add monitor coverage for 30 days and include e-mail alerts and short intervals for those extra monitors, without automatic renewal. Reuse active coverage first.",
          "limits": "A check-in proves the signal arrived, not that a backup can be restored. Alert timing depends on the check cycle. A single paid probe does not create continuous monitoring.",
          "alternative": "An existing monitor already covers the routine, or you need only a one-time HTTP request.",
          "start": {
            "href": "/",
            "label": "Monitor a job or site"
          }
        },
        "pt": {
          "when": "Como saber quando uma rotina parar ou um site cair?",
          "result": "Monitor persistente com agenda, estado e alertas configurados. Sites têm sondas ativas; rotinas enviam sinais.",
          "replaces": "Mantemos a agenda, detectamos ausências e enviamos os alertas configurados. O agente pode encerrar após criar o monitor.",
          "free": "Crie na cobertura gratuita, conecte o sinal da rotina e um destino autorizado de alerta. Confira estado e eventos. O monitoramento continua após a sessão do agente.",
          "paid": "Compare a cotação da ação com GET /api/plans. Os planos ampliam a cobertura por 30 dias e incluem e-mail e intervalo curto nos monitores extras, sem renovação automática. Reutilize a cobertura vigente.",
          "limits": "Um sinal recebido não prova que o backup restaura. O aviso depende do ciclo de checagem. Uma sonda paga não cria monitoramento contínuo.",
          "alternative": "Um monitor existente já cobre a rotina ou basta uma requisição HTTP avulsa.",
          "start": {
            "href": "/",
            "label": "Monitorar rotina ou site"
          }
        }
      },
      "returns": "{ id, kind, check?, site?, name, interval_sec, cron, tz, grace_sec, next_expected_at, alert_to, alert_url, last_event_at, last_status, last_latency_ms, last_start_at, last_run_id, last_duration_ms, duration, max_duration_sec, alert_pending, miss_count, alerts_on_hold?, alerted_at, alert_repeat_sec, tags, paused_until, state, active, healthy, overdue, waiting_first_ping, created_at, ingest_url?, token?, guest_token?, status_url?, events_url?, curl_example?, templates?{ingest_curl,ingest_cron,ingest_n8n,miss_json,recovery_json,miss_url_hint,github_actions} }",
      "url": "https://hookpulse.net/api/endpoints",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/endpoints/:id",
      "auth": "hook",
      "summary": "State of one monitor — accepts the owner's token or the monitor's own token.",
      "grupo": "Monitors",
      "desc": "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 — the list does not pay for it.",
      "params": {
        "id": {
          "desc": "Monitor ID, from `Monitor.id`."
        }
      },
      "query": {
        "token": {
          "tipo": "string",
          "desc": "Monitor token, alternative to the `X-Hook-Token` header."
        }
      },
      "retorno": "Monitor",
      "erros": [
        401,
        404
      ],
      "exemplo": "curl -s \"$ORIGIN/api/endpoints/ID?token=$MONITOR_TOKEN\"",
      "returns": "{ id, kind, check?, site?, name, interval_sec, cron, tz, grace_sec, next_expected_at, alert_to, alert_url, last_event_at, last_status, last_latency_ms, last_start_at, last_run_id, last_duration_ms, duration, max_duration_sec, alert_pending, miss_count, alerts_on_hold?, alerted_at, alert_repeat_sec, tags, paused_until, state, active, healthy, overdue, waiting_first_ping, created_at, ingest_url?, token?, guest_token?, status_url?, events_url?, curl_example?, templates?{ingest_curl,ingest_cron,ingest_n8n,miss_json,recovery_json,miss_url_hint,github_actions} }",
      "url": "https://hookpulse.net/api/endpoints/:id",
      "auth_detail": "The monitor's own token, in `?token=` or in the `X-Hook-Token` header. Read-only: state and pings — it lets you put the monitor on a third-party dashboard without handing over the owner's credential. The owner's token is also accepted on these routes."
    },
    {
      "method": "PATCH",
      "path": "/api/endpoints/:id",
      "auth": "guest",
      "summary": "Changes the monitor's name, interval or alert channels.",
      "grupo": "Monitors",
      "desc": "Lowering the interval below the free minimum costs: the response becomes 402 with `accepts[]` until paid. Editing anything else keeps `next_expected_at` where it was — renaming a late monitor does not make it healthy.",
      "params": {
        "id": {
          "desc": "ID of the monitor to change."
        }
      },
      "corpo": {
        "name": {
          "tipo": "string",
          "desc": "New monitor name, as it shows in the alert. Same rule as on creation — omit it to keep the current one; an empty name is rejected rather than accepted, because a monitor with no name makes its own alert useless."
        },
        "interval_sec": {
          "tipo": "int",
          "desc": "New tolerated silence, in seconds. Rejected while a `cron` is set."
        },
        "cron": {
          "tipo": "string",
          "desc": "New cron expression; `null` drops the schedule and puts the monitor back on `interval_sec`.",
          "nulo": true
        },
        "tz": {
          "tipo": "string",
          "desc": "New IANA time zone; `null` goes back to `UTC`.",
          "nulo": true
        },
        "grace_sec": {
          "tipo": "int",
          "desc": "New tolerance in seconds; `null` goes back to 90.",
          "nulo": true
        },
        "paused_until": {
          "tipo": "string",
          "desc": "Maintenance window: while this UTC instant is in the future the monitor is not checked and never alerts. `null` resumes it. At most 30 days ahead.",
          "nulo": true
        },
        "max_duration_sec": {
          "tipo": "int",
          "desc": "New ceiling for an open run, 60 to 86400 seconds; `null` or `0` turns it off.",
          "nulo": true
        },
        "alert_repeat_sec": {
          "tipo": "int",
          "desc": "New gap between repeats of the same incident, 3600 to 2592000 seconds; `null` goes back to 86400.",
          "nulo": true
        },
        "tags": {
          "tipo": "string[]",
          "desc": "New tags, replacing the old ones. `null` or `[]` removes them all.",
          "nulo": true
        },
        "check": {
          "tipo": "object",
          "desc": "Only on a site: the fields to change (`url`, `method`, `expect_status`, `keyword`, `timeout_ms`); an absent field stays. A new `url` is another site: its state starts again at `waiting`. A routine does not become a site (`check_not_site`)."
        },
        "alert_to": {
          "tipo": "string",
          "desc": "New alert e-mail; `null` turns it off.",
          "nulo": true
        },
        "alert_url": {
          "tipo": "string",
          "desc": "New alert URL; `null` turns it off.",
          "nulo": true
        }
      },
      "body": {
        "name": "…",
        "interval_sec": 300,
        "alert_to": null,
        "alert_url": null
      },
      "retorno": "Monitor",
      "erros": {
        "400": "Invalid field in the body, with the same codes as the creation — `nome_obrigatorio` and `nome_invalido` included.",
        "401": null,
        "402": null,
        "404": null
      },
      "exemplo": "curl -s -XPATCH $ORIGIN/api/endpoints/ID -H \"X-Guest-Token: $HP\" -H 'content-type: application/json' -d '{\"interval_sec\":1800}'",
      "returns": "{ id, kind, check?, site?, name, interval_sec, cron, tz, grace_sec, next_expected_at, alert_to, alert_url, last_event_at, last_status, last_latency_ms, last_start_at, last_run_id, last_duration_ms, duration, max_duration_sec, alert_pending, miss_count, alerts_on_hold?, alerted_at, alert_repeat_sec, tags, paused_until, state, active, healthy, overdue, waiting_first_ping, created_at, ingest_url?, token?, guest_token?, status_url?, events_url?, curl_example?, templates?{ingest_curl,ingest_cron,ingest_n8n,miss_json,recovery_json,miss_url_hint,github_actions} }",
      "url": "https://hookpulse.net/api/endpoints/:id",
      "auth_detail": "Guest token returned by monitor creation or `POST /api/guest`, in `X-Guest-Token: hp_…` or `Authorization: Bearer hp_…`. A signed-in account (cookie) also works and takes precedence."
    },
    {
      "method": "DELETE",
      "path": "/api/endpoints/:id",
      "auth": "guest",
      "summary": "Deactivates the owner's monitor; it stops taking pings and alerting.",
      "grupo": "Monitors",
      "params": {
        "id": {
          "desc": "ID of the monitor to deactivate."
        }
      },
      "retorno": "Ok",
      "erros": [
        401,
        404
      ],
      "exemplo": "curl -s -XDELETE $ORIGIN/api/endpoints/ID -H \"X-Guest-Token: $HP\"",
      "returns": "{ ok }",
      "url": "https://hookpulse.net/api/endpoints/:id",
      "auth_detail": "Guest token returned by monitor creation or `POST /api/guest`, in `X-Guest-Token: hp_…` or `Authorization: Bearer hp_…`. A signed-in account (cookie) also works and takes precedence."
    },
    {
      "method": "POST",
      "path": "/api/endpoints/:id/check",
      "auth": "guest",
      "summary": "Checks a site now and returns what it answered.",
      "grupo": "Sites",
      "desc": "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.",
      "params": {
        "id": {
          "desc": "ID of the site monitor, from `Monitor.id`."
        }
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "`true` when the site answered as expected."
        },
        "status": {
          "tipo": "int",
          "desc": "HTTP status; `null` when nothing answered.",
          "nulo": true
        },
        "ms": {
          "tipo": "int",
          "desc": "How long the check took, in ms."
        },
        "reason": {
          "tipo": "string",
          "desc": "Why it failed: `status`, `keyword`, `timeout`, `dns`, `connection`, `tls`, `private_address` or `network`; `null` when it answered.",
          "nulo": true
        },
        "endpoint": {
          "tipo": "Monitor",
          "desc": "The site monitor after the check."
        }
      },
      "erros": {
        "400": "`check_not_site`: the monitor is a routine.",
        "401": null,
        "404": null,
        "429": "`check_too_soon`, with `retry_after_sec`: this host was checked less than 10 seconds ago."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/endpoints/ID/check -H \"X-Guest-Token: $HP\"",
      "returns": "{ ok, status, ms, reason, endpoint{id,kind,check?,site?,name,interval_sec,cron,tz,grace_sec,next_expected_at,alert_to,alert_url,last_event_at,last_status,last_latency_ms,last_start_at,last_run_id,last_duration_ms,duration,max_duration_sec,alert_pending,miss_count,alerts_on_hold?,alerted_at,alert_repeat_sec,tags,paused_until,state,active,healthy,overdue,waiting_first_ping,created_at,ingest_url?,token?,guest_token?,status_url?,events_url?,curl_example?,templates?} }",
      "url": "https://hookpulse.net/api/endpoints/:id/check",
      "auth_detail": "Guest token returned by monitor creation or `POST /api/guest`, in `X-Guest-Token: hp_…` or `Authorization: Bearer hp_…`. A signed-in account (cookie) also works and takes precedence."
    },
    {
      "method": "GET",
      "path": "/api/sites/estado",
      "auth": "guest",
      "summary": "The state of every site you monitor, with the last check.",
      "grupo": "Sites",
      "desc": "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.",
      "retorno": {
        "sites": {
          "tipo": "object[]",
          "desc": "One per site: `id`, `name`, `state` (`ok`, `down`, `waiting`, `paused`) and `site` (as in `Monitor.site`)."
        }
      },
      "erros": [
        401
      ],
      "exemplo": "curl -s $ORIGIN/api/sites/estado -H \"X-Guest-Token: $HP\"",
      "returns": "{ sites }",
      "url": "https://hookpulse.net/api/sites/estado",
      "auth_detail": "Guest token returned by monitor creation or `POST /api/guest`, in `X-Guest-Token: hp_…` or `Authorization: Bearer hp_…`. A signed-in account (cookie) also works and takes precedence."
    },
    {
      "auth": "credito",
      "method": "GET",
      "path": "/api/x402/check",
      "summary": "Checks a URL once for an agent, paid per call: status, response time, keyword and TLS certificate, with no account and no monitor.",
      "grupo": "Sites",
      "desc": "One reading with the probe of the site monitors, and the certificate of an https address that answered. A request without payment gets the 402 with the price; a paid one gets the reading in the same response, with the payment in `cobranca`. A site that is down is the answer (`ok: false` with `reason`) and is charged: the call was made. When our own probe network fails, the answer is 503 and nothing is charged.",
      "query": {
        "url": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "The address to call: http or https, public, without user or password.",
          "exemplo": "https://example.com/"
        },
        "method": {
          "tipo": "string",
          "desc": "`GET` (default) or `HEAD`."
        },
        "expect_status": {
          "tipo": "string",
          "desc": "What counts as up: `200-399` by default, a range `a-b` or one code."
        },
        "keyword": {
          "tipo": "string",
          "desc": "Text that must be in the first 64 KB of the page; it forces `GET`."
        },
        "timeout_ms": {
          "tipo": "int",
          "desc": "1000 to 30000, default 10000."
        }
      },
      "retorno": "ChecagemAvulsa",
      "exemploRetorno": {
        "url": "https://example.com/",
        "ok": true,
        "status": 200,
        "reason": null,
        "ms": 24,
        "certificate": {
          "endpoint": "https://example.com",
          "fingerprint": "85:CA:6A:B0:68:E9:BC:CE:88:B6:C4:AA:3C:47:F7:D1:72:28:13:4A:45:7F:87:0D:38:00:E6:22:3A:0D:F0:7A",
          "issuer": "SSL Corporation",
          "valid_from": "2026-09-26T22:49:11.000Z",
          "valid_until": "2026-12-25T22:56:35.000Z",
          "authorized": true,
          "errors": [],
          "protocol": "TLSv1.3"
        },
        "certificate_error": null,
        "checked_at": "2026-10-24T11:51:44.520Z"
      },
      "erros": {
        "400": "Paid without `url` (`missing_input`, with `example`), or a field with a code: `check_url_invalid`, `check_url_credentials`, `check_url_private`, `check_method_invalid`, `check_expect_status_invalid`, `check_keyword_invalid` or `check_timeout_invalid`. Not charged.",
        "401": "API key refused (`invalid_api_key`), OAuth token refused (`invalid_token`), credit refused (`credito_invalido`), test credit outside dev (`credito_simulado`), or recovery without wallet authentication (`wallet_proof_required`). The first x402 purchase needs no account.",
        "402": "No payment: `accepts` (x402, USDC on Base) with the price of this call, at least US$ 0.01; `credito.preco_desta_chamada`, the lower price with prepaid credit; and `pacotes` (the credit top-ups and how many calls each one pays). Credit without balance answers `saldo_insuficiente`.",
        "409": "Payment is in progress, the request differs from the original, or the amount was recovered as balance. Do not sign another authorization to retry the purchase.",
        "410": "The 24-hour response recovery period ended. The financial record remains; the same proof cannot cause another charge.",
        "503": "Our probe network did not answer (`collector_unavailable`). Not charged."
      },
      "exemplo": "curl -s -i '$ORIGIN/api/x402/check?url=https://example.com/'",
      "headers": {
        "authorization": {
          "tipo": "string",
          "obrigatorio": false,
          "desc": "`Bearer mmk_…` (API key) or `Bearer mmo_…` (OAuth): the paid call uses the account balance without another header. Without an account, use prepaid credit from `POST /api/credito` as `Bearer cred_…`."
        },
        "x-api-key": {
          "tipo": "string",
          "obrigatorio": false,
          "desc": "The mmk_… API key for this product. It identifies the account and authorizes its balance for the paid call."
        },
        "payment-signature": {
          "tipo": "string",
          "obrigatorio": false,
          "desc": "The signed x402 payment proof (base64): `PAYMENT-SIGNATURE` (v2) or `X-PAYMENT` (v1)."
        },
        "x-wallet-token": {
          "tipo": "string",
          "obrigatorio": false,
          "desc": "To recover an x402 response within 24 hours, repeat the same request and payment proof. Send the payer wallet's `mmw_…` credential here, issued through `/api/auth/wallet/agent/start` and `/api/auth/wallet/agent/token`. There is no new charge."
        }
      },
      "returns": "{ url, ok, status, reason, ms, certificate{endpoint,fingerprint,issuer,valid_from,valid_until,authorized,errors,protocol}, certificate_error, checked_at, cobranca{via,preco_usd,repetido,saldo_usd?} }",
      "url": "https://hookpulse.net/api/x402/check",
      "auth_detail": "Credit token in `Authorization: Bearer cred_…` or `X-Credito`. For recovered x402 funds, use the wallet credential mmw_… in Bearer or X-Wallet-Token, issued through /api/auth/wallet/agent/start and /api/auth/wallet/agent/token."
    },
    {
      "method": "GET",
      "path": "/api/ingest-key",
      "auth": "guest",
      "summary": "Returns (minting it the first time) the key that lets a ping create its own monitor.",
      "grupo": "Ingest",
      "desc": "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** — beyond it the ping answers 402 without creating anything. The key survives signing in: the crontab you deployed keeps working.",
      "retorno": {
        "key": {
          "tipo": "string",
          "desc": "The ingest key, prefix `hpk_`."
        },
        "rotated": {
          "tipo": "bool",
          "desc": "`true` when this response came from a rotation."
        },
        "exemplo": {
          "tipo": "string",
          "desc": "A ready URL: `<origin>/in/<key>/nightly-backup`."
        },
        "note": {
          "tipo": "string",
          "desc": "What the slug means and what rotating does."
        }
      },
      "erros": [
        401
      ],
      "exemplo": "curl -s $ORIGIN/api/ingest-key -H \"X-Guest-Token: $HP\"",
      "returns": "{ key, rotated, exemplo, note }",
      "url": "https://hookpulse.net/api/ingest-key",
      "auth_detail": "Guest token returned by monitor creation or `POST /api/guest`, in `X-Guest-Token: hp_…` or `Authorization: Bearer hp_…`. A signed-in account (cookie) also works and takes precedence."
    },
    {
      "method": "DELETE",
      "path": "/api/ingest-key",
      "auth": "guest",
      "summary": "Rotates the ingest key: the previous one stops creating monitors immediately.",
      "grupo": "Ingest",
      "desc": "Monitors already created keep working — rotating only revokes the power to create new ones.",
      "retorno": {
        "key": {
          "tipo": "string",
          "desc": "The new key."
        },
        "rotated": {
          "tipo": "bool",
          "desc": "Always `true` here."
        },
        "exemplo": {
          "tipo": "string",
          "desc": "A ready URL with the new key."
        },
        "note": {
          "tipo": "string",
          "desc": "What the slug means and what rotating does."
        }
      },
      "erros": [
        401
      ],
      "exemplo": "curl -s -XDELETE $ORIGIN/api/ingest-key -H \"X-Guest-Token: $HP\"",
      "returns": "{ key, rotated, exemplo, note }",
      "url": "https://hookpulse.net/api/ingest-key",
      "auth_detail": "Guest token returned by monitor creation or `POST /api/guest`, in `X-Guest-Token: hp_…` or `Authorization: Bearer hp_…`. A signed-in account (cookie) also works and takes precedence."
    },
    {
      "method": "GET",
      "path": "/in/:ingestKey/:slug",
      "auth": "none",
      "summary": "Ping that CREATES its own monitor on the first hit — a whole fleet with no setup.",
      "grupo": "Ingest",
      "desc": "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 — not even the event. Works by GET or POST.",
      "params": {
        "ingestKey": {
          "desc": "Ingest key, from `GET /api/ingest-key`.",
          "exemplo": "hpk_exemplo"
        },
        "slug": {
          "desc": "Name of the job; it becomes the monitor name."
        }
      },
      "query": {
        "interval": {
          "tipo": "int",
          "desc": "Tolerated silence of the monitor being created, in seconds."
        },
        "cron": {
          "tipo": "string",
          "desc": "Five-field cron of the job, for the monitor being created: late counts from the scheduled time plus `grace_sec`, not from an interval. Takes the place of `interval`."
        },
        "tz": {
          "tipo": "string",
          "desc": "IANA time zone the `cron` is read in. Default `UTC`. Only with `cron`."
        },
        "grace_sec": {
          "tipo": "int",
          "desc": "Tolerance after the scheduled time, 30 to 86400 seconds; default 90. Only with `cron`."
        }
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always `true` when the ping was recorded."
        },
        "endpoint_id": {
          "tipo": "string",
          "desc": "The monitor's id — new or the one that already existed."
        },
        "created": {
          "tipo": "bool",
          "desc": "`true` only on the hit that created it."
        }
      },
      "erros": {
        "400": "`slug_invalido` — nothing usable left after normalising. With `cron`: `cron_invalido`, `tz_invalida`, `grace_invalido` or `cron_sem_ocorrencia`.",
        "402": "The free tier is full: pay and repeat, or delete a monitor.",
        "404": "Unknown or rotated ingest key."
      },
      "exemplo": "curl -fsS \"$ORIGIN/in/$INGEST_KEY/nightly-backup?cron=0%203%20*%20*%20*&tz=UTC&grace_sec=900\"",
      "returns": "{ ok, endpoint_id, created }",
      "url": "https://hookpulse.net/in/:ingestKey/:slug",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/in/:ingestKey/:slug/:signal",
      "auth": "none",
      "summary": "A signal by name: `start`, `fail` or the exit code, for the monitor of that slug.",
      "grupo": "Ingest",
      "desc": "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.",
      "params": {
        "ingestKey": {
          "desc": "Ingest key, from `GET /api/ingest-key`.",
          "exemplo": "hpk_exemplo"
        },
        "slug": {
          "desc": "Name of the job; it becomes the monitor name."
        },
        "signal": {
          "desc": "`start`, `fail`, or the shell exit code 0 to 255 (0 is success).",
          "exemplo": "fail"
        }
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always `true` when the signal was recorded."
        },
        "endpoint_id": {
          "tipo": "string",
          "desc": "The monitor's id — new or the one that already existed."
        },
        "created": {
          "tipo": "bool",
          "desc": "`true` only on the hit that created it."
        }
      },
      "erros": {
        "400": "`slug_invalido`, `exit_code_fora_da_faixa`, or a `cron` code as in the plain ping by name.",
        "402": "The free tier is full: pay and repeat, or delete a monitor.",
        "404": "Unknown or rotated ingest key, or an unknown signal."
      },
      "exemplo": "curl -fsS \"$ORIGIN/in/$INGEST_KEY/nightly-backup/$?\"",
      "returns": "{ ok, endpoint_id, created }",
      "url": "https://hookpulse.net/in/:ingestKey/:slug/:signal",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/in/:id",
      "auth": "none",
      "summary": "The ingest URL: this is where your cron or webhook proves it is alive.",
      "grupo": "Ingest",
      "desc": "No credential on purpose — the caller is a cron, and the ID is already the secret. It costs nothing and there is no quota per ping.",
      "params": {
        "id": {
          "desc": "Monitor ID, from `Monitor.ingest_url`."
        }
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always `true` when the ping was recorded."
        }
      },
      "erros": {
        "404": "The monitor does not exist or is deactivated."
      },
      "exemplo": "curl -fsS $ORIGIN/in/ID",
      "returns": "{ ok }",
      "url": "https://hookpulse.net/in/:id",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/in/:id/start",
      "auth": "none",
      "summary": "Says the routine STARTED — it is what turns silence into duration.",
      "grupo": "Ingest",
      "desc": "`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.",
      "params": {
        "id": {
          "desc": "Monitor ID, from `Monitor.ingest_url`."
        }
      },
      "query": {
        "rid": {
          "tipo": "string",
          "desc": "Run identifier that pairs this start with the check-in that closes it. `[A-Za-z0-9._-]`, up to 64 characters. Send it when two runs of the same routine can overlap — without it the second start replaces the first, and the duration we report belongs to the wrong run. A value that does not pass the rule is ignored, never rejected: refusing a check-in would turn a live routine into a miss."
        }
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always `true` when the signal was recorded."
        },
        "kind": {
          "tipo": "string",
          "desc": "The signal recorded: `start`."
        }
      },
      "erros": {
        "404": "The monitor does not exist or is deactivated."
      },
      "exemplo": "curl -fsS $ORIGIN/in/ID/start",
      "returns": "{ ok, kind }",
      "url": "https://hookpulse.net/in/:id/start",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/in/:id/fail",
      "auth": "none",
      "summary": "Says the routine FAILED — the alert leaves on the next 5-minute check.",
      "grupo": "Ingest",
      "desc": "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.",
      "params": {
        "id": {
          "desc": "Monitor ID, from `Monitor.ingest_url`."
        }
      },
      "query": {
        "rid": {
          "tipo": "string",
          "desc": "The `rid` of the run this closes. It only measures — and only clears the open run — when it matches the `rid` that opened it; a different one leaves the open run alone, so a concurrent run is not cut short. Sending none closes whatever is open."
        }
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always `true` when the signal was recorded."
        },
        "kind": {
          "tipo": "string",
          "desc": "The signal recorded: `fail`."
        }
      },
      "erros": {
        "404": "The monitor does not exist or is deactivated."
      },
      "exemplo": "curl -fsS $ORIGIN/in/ID/fail",
      "returns": "{ ok, kind }",
      "url": "https://hookpulse.net/in/:id/fail",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/in/:id/:exit_code",
      "auth": "none",
      "summary": "The shell's exit status, straight from `$?`: 0 is success, anything else is a failure.",
      "grupo": "Ingest",
      "desc": "`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.",
      "params": {
        "id": {
          "desc": "Monitor ID, from `Monitor.ingest_url`."
        },
        "exit_code": {
          "desc": "Exit status, 0 to 255. Outside that range it is a 400."
        }
      },
      "query": {
        "rid": {
          "tipo": "string",
          "desc": "The `rid` of the run this closes. It only measures — and only clears the open run — when it matches the `rid` that opened it; a different one leaves the open run alone, so a concurrent run is not cut short. Sending none closes whatever is open."
        }
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always `true` when the signal was recorded."
        },
        "kind": {
          "tipo": "string",
          "desc": "`ping` for 0, `fail` for anything else."
        }
      },
      "erros": {
        "400": "`exit_code_fora_da_faixa` — the exit status must be 0 to 255.",
        "404": "The monitor does not exist or is deactivated."
      },
      "exemplo": "ID=... ; ./backup.sh ; curl -fsS \"$ORIGIN/in/$ID/$?\"",
      "returns": "{ ok, kind }",
      "url": "https://hookpulse.net/in/:id/:exit_code",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/in/:id",
      "auth": "none",
      "summary": "The same ingest by POST, for whoever can only send webhooks.",
      "grupo": "Ingest",
      "desc": "The body is optional and records `status` and `latency_ms` along with the ping.",
      "params": {
        "id": {
          "desc": "Monitor ID, from `Monitor.ingest_url`."
        }
      },
      "corpo": {
        "status": {
          "tipo": "int",
          "desc": "Status of the job that is pinging, to show in `Ping.status`."
        },
        "latency_ms": {
          "tipo": "int",
          "desc": "How long the job took, in ms."
        }
      },
      "body": {
        "status": 200,
        "latency_ms": 431
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always `true` when the ping was recorded."
        }
      },
      "erros": {
        "404": "The monitor does not exist or is deactivated."
      },
      "exemplo": "curl -fsS -XPOST $ORIGIN/in/ID -H 'content-type: application/json' -d '{\"status\":200,\"latency_ms\":431}'",
      "returns": "{ ok }",
      "url": "https://hookpulse.net/in/:id",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/github/runs",
      "auth": "github",
      "summary": "The GitHub Action's check-in: a scheduled workflow job reports how it ended, with no secret.",
      "grupo": "GitHub",
      "desc": "The last step of a job with `permissions: id-token: write` calls it (the `wrpbr/hookpulse-action` action does). The GitHub OIDC token proves the repository and the workflow. The first report creates the monitor `owner/repo/file.yml#job` with the cron that fired the run (`github.event.schedule`, UTC) and one hour of grace: GitHub delays scheduled runs under load, and the report arrives at the end of the job. Without `X-Ingest-Key` the repository owns the monitor, inside the free tier per repository, and a miss only goes to `alert_url`. With the key, its owner owns the monitor under the rules of `POST /api/endpoints` (402 past the free tier), gets e-mail alerts, and takes over the monitor the repository had created. The answer never carries the monitor ID: a public repository's log is public.",
      "headers": {
        "Authorization": {
          "tipo": "string",
          "desc": "`Bearer <GitHub OIDC token>`, requested with this site's origin as the audience.",
          "obrigatorio": true
        },
        "X-Ingest-Key": {
          "tipo": "string",
          "desc": "Optional ingest key (`hpk_…`, from `GET /api/ingest-key`), kept as a repository secret."
        }
      },
      "corpo": {
        "status": {
          "tipo": "string",
          "desc": "How the job ended. `cancelled` and `skipped` record nothing: a cancelled run is not an outage.",
          "obrigatorio": true,
          "valores": [
            "success",
            "failure",
            "start",
            "cancelled",
            "skipped"
          ]
        },
        "job": {
          "tipo": "string",
          "desc": "The job id (`GITHUB_JOB`): `[A-Za-z0-9_.-]`, up to 64 characters."
        },
        "schedule": {
          "tipo": "string",
          "desc": "The cron that fired the run (`github.event.schedule`). Needed to create the monitor, unless `interval_sec` comes."
        },
        "grace_sec": {
          "tipo": "int",
          "desc": "Tolerance after each scheduled time, 60 to 86400 seconds.",
          "padrao": 3600
        },
        "interval_sec": {
          "tipo": "int",
          "desc": "Tolerated silence of a job without a schedule, 60 to 86400 seconds."
        },
        "exit_code": {
          "tipo": "int",
          "desc": "Exit status of the failed step, 0 to 255."
        },
        "alert_url": {
          "tipo": "string",
          "desc": "Public HTTPS URL we POST on a miss (Slack, Discord or any webhook)."
        }
      },
      "body": {
        "status": "success",
        "job": "backup",
        "schedule": "17 3 * * *"
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "`true` when the report was recorded or deliberately ignored."
        },
        "kind": {
          "tipo": "string",
          "desc": "The signal recorded: `ping`, `fail` or `start`.",
          "opcional": true
        },
        "ignored": {
          "tipo": "string",
          "desc": "The status that records nothing (`cancelled`, `skipped`).",
          "opcional": true
        },
        "event_id": {
          "tipo": "string",
          "desc": "The event recorded.",
          "opcional": true
        },
        "received_at": {
          "tipo": "string",
          "desc": "When it was recorded (UTC).",
          "opcional": true
        },
        "created": {
          "tipo": "bool",
          "desc": "`true` only on the report that created the monitor."
        },
        "owner": {
          "tipo": "string",
          "desc": "`repository` (no key) or `ingest_key`."
        },
        "monitor": {
          "tipo": "MonitorGithub",
          "desc": "The monitor, without its ID."
        }
      },
      "erros": {
        "400": "Invalid body (`status_invalido`, `cron_invalido`, `schedule_required`…) or `alert_url` that is not public HTTPS.",
        "401": "No GitHub OIDC token, a token for another audience or an expired one (`invalid_github_token`), or an unknown ingest key.",
        "402": "With the key: past the free tier of its owner — pay and repeat.",
        "429": "`monitor_limit` — without a key, the repository's free tier is full."
      },
      "exemplo": "curl -fsS -XPOST $ORIGIN/api/github/runs -H \"Authorization: Bearer $OIDC_TOKEN\" -H 'content-type: application/json' -d '{\"status\":\"success\",\"job\":\"backup\",\"schedule\":\"17 3 * * *\"}'",
      "returns": "{ ok, kind?, ignored?, event_id?, received_at?, created, owner, monitor{name,state,cron,grace_sec,next_expected_at,alert,status_page?,badge?,dashboard?} }",
      "url": "https://hookpulse.net/api/github/runs",
      "auth_detail": "GitHub Actions OIDC token in `Authorization: Bearer`, from a job with `permissions: id-token: write`, requested with this site's origin as the audience. It proves the repository and the workflow; no secret. An optional `X-Ingest-Key: hpk_…` (a repository secret) makes the key's owner own the monitor."
    },
    {
      "method": "POST",
      "path": "/api/github/app/webhook",
      "auth": "github_app",
      "summary": "The HookPulse GitHub App's webhook: scheduled workflow runs of the repositories where it is installed.",
      "grupo": "GitHub",
      "desc": "Only GitHub calls it. A finished `workflow_run` from `schedule` records the run: the first one creates the monitor `owner/repo/file.yml` with the cron read from the workflow file, in UTC with one hour of grace; success proves life and a failure becomes an alert. Each run reads the workflow file again, so an edited cron reaches the monitor. Other events answer 200 and record nothing. A missed or failed run opens an issue labelled `hookpulse` in the repository, and the next success closes it. Before the first missed-run issue, the App reads the file on the default branch: a changed cron only updates the schedule. The repository owns the monitor, inside the free tier per repository.",
      "headers": {
        "X-Hub-Signature-256": {
          "tipo": "string",
          "desc": "`sha256=` HMAC of the raw body with the App's webhook secret.",
          "obrigatorio": true
        },
        "X-GitHub-Event": {
          "tipo": "string",
          "desc": "The event name; only `workflow_run` records.",
          "obrigatorio": true
        }
      },
      "corpo": {
        "workflow_run": {
          "tipo": "object",
          "desc": "GitHub's `workflow_run` payload: `event`, `path`, `conclusion`, `head_sha`."
        }
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "`true` when the delivery was accepted."
        },
        "recorded": {
          "tipo": "string",
          "desc": "The signal recorded: `ping` or `fail`.",
          "opcional": true
        },
        "ignored": {
          "tipo": "string",
          "desc": "Why nothing was recorded.",
          "opcional": true
        }
      },
      "erros": {
        "400": "The body is not JSON.",
        "401": "`invalid_signature`: the HMAC does not match the App's secret.",
        "413": "The body is over 512 KB.",
        "503": "`github_app_not_configured`: this environment has no App credentials."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/github/app/webhook -H 'x-github-event: ping' -H \"x-hub-signature-256: sha256=$HMAC\" -d '{}'   # GitHub sends it; a signed ping answers 200",
      "returns": "{ ok, recorded?, ignored? }",
      "url": "https://hookpulse.net/api/github/app/webhook",
      "auth_detail": "Only GitHub: the delivery is signed with the HookPulse GitHub App's webhook secret in `X-Hub-Signature-256`."
    },
    {
      "method": "GET",
      "path": "/api/status-feed",
      "auth": "guest",
      "summary": "Returns (minting it the first time) the public status URL of all your monitors.",
      "grupo": "Public dashboard",
      "desc": "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.",
      "retorno": "Feed",
      "erros": [
        401
      ],
      "exemplo": "curl -s $ORIGIN/api/status-feed -H \"X-Guest-Token: $HP\"",
      "returns": "{ token, rotated, json, rss, note, api_index }",
      "url": "https://hookpulse.net/api/status-feed",
      "auth_detail": "Guest token returned by monitor creation or `POST /api/guest`, in `X-Guest-Token: hp_…` or `Authorization: Bearer hp_…`. A signed-in account (cookie) also works and takes precedence."
    },
    {
      "method": "DELETE",
      "path": "/api/status-feed",
      "auth": "guest",
      "summary": "Rotates the feed token: the previous URL stops working immediately.",
      "grupo": "Public dashboard",
      "retorno": "Feed",
      "erros": [
        401
      ],
      "exemplo": "curl -s -XDELETE $ORIGIN/api/status-feed -H \"X-Guest-Token: $HP\"",
      "returns": "{ token, rotated, json, rss, note, api_index }",
      "url": "https://hookpulse.net/api/status-feed",
      "auth_detail": "Guest token returned by monitor creation or `POST /api/guest`, in `X-Guest-Token: hp_…` or `Authorization: Bearer hp_…`. A signed-in account (cookie) also works and takes precedence."
    },
    {
      "method": "GET",
      "path": "/s/:token",
      "auth": "none",
      "summary": "The status dashboard as a page — the link you send to the team, no header, no JS needed.",
      "grupo": "Public dashboard",
      "desc": "`/s/:token.html` is the same page. Rendered on the server, so the content is in the body and the link previews anywhere. **`noindex` by default**: the URL is the credential — whoever has the link reads the dashboard. It carries the same data as `.json`, plus each monitor's badge.",
      "params": {
        "token": {
          "desc": "Feed token, from `Feed.token`.",
          "exemplo": "k7m2p9r4t6v8w1y3z5b7c9d1"
        }
      },
      "retorno": {
        "_texto": "`text/html` — the dashboard with the summary, one row per monitor and its badge."
      },
      "erros": {
        "404": "Unknown feed token."
      },
      "exemplo": "curl -s $ORIGIN/s/FEED_TOKEN",
      "returns": "`text/html` — the dashboard with the summary, one row per monitor and its badge.",
      "url": "https://hookpulse.net/s/:token",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/s/:token.json",
      "auth": "none",
      "summary": "The status dashboard as JSON, with no header at all — the token in the path is the credential.",
      "grupo": "Public dashboard",
      "desc": "`summary.all_ok` is the field for an external monitor to check without understanding the rest. The `id` of each row is a label of this dashboard, not the ingest id — rotating the feed changes both.",
      "params": {
        "token": {
          "desc": "Feed token, from `Feed.token`, with `.json` at the end.",
          "exemplo": "k7m2p9r4t6v8w1y3z5b7c9d1"
        }
      },
      "retorno": "FeedStatus",
      "erros": {
        "404": "Unknown feed token."
      },
      "exemplo": "curl -s $ORIGIN/s/FEED_TOKEN.json",
      "returns": "{ generated_at, summary{total,ok,overdue,waiting_first_ping,inactive,all_ok}, endpoints[{badge,id,name,status,healthy,overdue,interval_sec,last_event_at,miss_count,alerts}], _links }",
      "url": "https://hookpulse.net/s/:token.json",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/s/:token.rss",
      "auth": "none",
      "summary": "The same dashboard as RSS 2.0, for a feed reader, a chat bot or a status page.",
      "grupo": "Public dashboard",
      "desc": "The `guid` of each item changes when the STATE changes, not on every generation — with a fixed guid the reader would never re-notify.",
      "params": {
        "token": {
          "desc": "Feed token, from `Feed.token`, with `.rss` at the end.",
          "exemplo": "k7m2p9r4t6v8w1y3z5b7c9d1"
        }
      },
      "retorno": {
        "_texto": "`application/rss+xml` — one item per monitor, with the state in the title."
      },
      "erros": {
        "404": "Unknown feed token."
      },
      "exemplo": "curl -s $ORIGIN/s/FEED_TOKEN.rss",
      "returns": "`application/rss+xml` — one item per monitor, with the state in the title.",
      "url": "https://hookpulse.net/s/:token.rss",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/s/:token/metrics",
      "auth": "none",
      "summary": "The same dashboard as a Prometheus scrape target — point your existing alerting at it.",
      "grupo": "Public dashboard",
      "desc": "Three series labelled per monitor: `hookpulse_last_ping_age_seconds` (gauge; absent for a monitor that never checked in, because a zero there would read as 'just pinged'), `hookpulse_late` (gauge, 1 past the deadline) and `hookpulse_miss_total` (counter). The `id` label is this dashboard's label, not the ingest id. Reading costs no writes, so scrape as often as you like — and the URL is the credential, so treat it like one.",
      "params": {
        "token": {
          "desc": "Feed token, from `Feed.token`, with `/metrics` at the end.",
          "exemplo": "k7m2p9r4t6v8w1y3z5b7c9d1"
        }
      },
      "retorno": {
        "_texto": "`text/plain; version=0.0.4` — the Prometheus exposition format."
      },
      "erros": {
        "404": "Unknown feed token."
      },
      "exemplo": "curl -s $ORIGIN/s/FEED_TOKEN/metrics",
      "returns": "`text/plain; version=0.0.4` — the Prometheus exposition format.",
      "url": "https://hookpulse.net/s/:token/metrics",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/s/:token/:monitor.svg",
      "auth": "none",
      "summary": "A badge of one monitor, as an SVG you paste straight into a README.",
      "grupo": "Public dashboard",
      "desc": "`:monitor` is the **published** id — the one in `FeedStatus.endpoints[].id`, and the same URL comes ready in `FeedStatus.endpoints[].badge`. The ingest id is deliberately not accepted: whoever has it can ping the monitor and silence the alarm. Three colours: green `ok`, red `late`, grey for `paused`, `waiting` and `inactive`. No external font, no request to anyone else, no write on our side; cached for 60 seconds with an `ETag`.",
      "params": {
        "token": {
          "desc": "Feed token, from `Feed.token`.",
          "exemplo": "k7m2p9r4t6v8w1y3z5b7c9d1"
        },
        "monitor": {
          "desc": "Published id of the monitor, from `FeedStatus.endpoints[].id` (24 hex).",
          "exemplo": "9f3c2b1d7a4e58b0c2d4e6f8"
        }
      },
      "retorno": {
        "_texto": "`image/svg+xml` — a 20px-high badge with the monitor name and its state."
      },
      "erros": {
        "404": "Unknown feed token, or a monitor that does not belong to this feed."
      },
      "exemplo": "curl -sI $ORIGIN/s/FEED_TOKEN/PUBLISHED_ID.svg",
      "returns": "`image/svg+xml` — a 20px-high badge with the monitor name and its state.",
      "url": "https://hookpulse.net/s/:token/:monitor.svg",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/templates",
      "auth": "none",
      "summary": "Ingest snippets (curl, cron, n8n) and the exact JSON we POST on a miss and on a recovery.",
      "grupo": "Public dashboard",
      "desc": "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.",
      "retorno": {
        "origin": {
          "tipo": "string",
          "desc": "Origin used to build the snippets."
        },
        "templates": {
          "tipo": "Templates",
          "desc": "The snippets and the alert body."
        }
      },
      "returns": "{ origin, templates{ingest_curl,ingest_cron,ingest_n8n,miss_json,recovery_json,miss_url_hint,github_actions} }",
      "url": "https://hookpulse.net/api/templates",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/billing",
      "auth": "none",
      "summary": "Free allowance, x402 prices in force and the state of the account's trial.",
      "grupo": "Billing",
      "desc": "Check before creating the second monitor or lowering the interval: it is the number IN FORCE, not the documentation's. After the account's trial, alerts come from the ten oldest monitors and from each extra monitor that was paid for; the other monitors need a plan for alerts (`GET /api/plans`).",
      "retorno": "Billing",
      "exemplo": "curl -s $ORIGIN/api/billing -H \"X-Guest-Token: $HP\"",
      "returns": "{ provider, mode, network, chain_id, pay_to, homolog, dev, dev_gate, alternavel, gratis?, cobrados?, facilitator, asset, asset_address, faucet, wallets, product, free_max_endpoints, free_min_interval_sec, free_email_alerts, prices{extra_endpoint_usd,fast_interval_usd,email_alert_usd}, usage, trial{days,active,days_left?,ends_at} }",
      "url": "https://hookpulse.net/api/billing",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/me",
      "auth": "session",
      "summary": "The session's account, its monitors and the state of the trial.",
      "desc": "The account is the house account, served by the account library in this process; `user.id` is the account id itself — the owner of the monitors and devices here.",
      "grupo": "Account",
      "retorno": {
        "user": {
          "tipo": "Conta",
          "desc": "The person who owns the session."
        },
        "profile": {
          "tipo": "object",
          "desc": "Global profile: `name`, `locale`, `timeZone`, `theme`, `revision`."
        },
        "endpoints": {
          "tipo": "Monitor[]",
          "desc": "The account's monitors."
        },
        "trial": {
          "tipo": "Trial",
          "desc": "The trial: how many days, whether active and when it ends (counted from the account's first use of HookPulse)."
        }
      },
      "erros": {
        "401": "invalid_session",
        "503": "auth_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/me\", {credentials: \"same-origin\"}).then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ user{id,email}, profile, endpoints[{id,kind,check?,site?,name,interval_sec,cron,tz,grace_sec,next_expected_at,alert_to,alert_url,last_event_at,last_status,last_latency_ms,last_start_at,last_run_id,last_duration_ms,duration,max_duration_sec,alert_pending,miss_count,alerts_on_hold?,alerted_at,alert_repeat_sec,tags,paused_until,state,active,healthy,overdue,waiting_first_ping,created_at,ingest_url?,token?,guest_token?,status_url?,events_url?,curl_example?,templates?}], trial{days,active,days_left?,ends_at} }",
      "url": "https://hookpulse.net/api/me",
      "auth_detail": "Account session: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page (e-mail code or link, password or Google). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "GET",
      "path": "/api/auth/bootstrap",
      "auth": "none",
      "grupo": "Account",
      "summary": "Prepare the browser for global sign-in.",
      "desc": "Sets a host-only HttpOnly browser cookie. CSRF is bound to the current session. No CORS.",
      "retorno": {
        "csrf": {
          "tipo": "string",
          "desc": "X-CSRF-Token"
        },
        "context": {
          "tipo": "string",
          "desc": "Opaque view context, also in X-MM-Context; not a credential / contexto opaco da vista, não é credencial."
        }
      },
      "erros": {
        "400": "invalid_request",
        "403": "invalid_origin / invalid_csrf",
        "503": "auth_unavailable: a sessão anterior é preservada / the previous session is preserved"
      },
      "returns": "{ csrf, context }",
      "url": "https://hookpulse.net/api/auth/bootstrap",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/account/profile",
      "auth": "session",
      "grupo": "Account",
      "exemplo": "await fetch(\"$ORIGIN/api/account/profile\", {credentials: \"same-origin\"}).then(r => r.json());",
      "exemploLinguagem": "js",
      "summary": "Read your global profile.",
      "desc": "Reads current preferences from the account. Edit them on your account page; products never own a separate profile.",
      "retorno": {
        "_texto": "{profile:{name,locale,timeZone,theme,revision}}"
      },
      "erros": {
        "401": "invalid_session",
        "503": "auth_unavailable"
      },
      "returns": "{profile:{name,locale,timeZone,theme,revision}}",
      "url": "https://hookpulse.net/api/account/profile",
      "auth_detail": "Account session: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page (e-mail code or link, password or Google). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "GET",
      "path": "/api/account/avatar",
      "auth": "session",
      "grupo": "Account",
      "exemplo": "await fetch(\"$ORIGIN/api/account/avatar\", {credentials: \"same-origin\"}).then(r => {if (!r.ok) throw new Error(\"HTTP \" + r.status); return r.blob();});",
      "exemploLinguagem": "js",
      "summary": "Read your global profile photo.",
      "desc": "Private WebP, up to 64 KiB, no cache. Change it on your account. No user ID or object URL accepted.",
      "retorno": {
        "_texto": "image/webp; Cache-Control: no-store"
      },
      "erros": {
        "401": "invalid_session",
        "404": "not_found: no photo / sem foto",
        "503": "auth_unavailable"
      },
      "returns": "image/webp; Cache-Control: no-store",
      "url": "https://hookpulse.net/api/account/avatar",
      "auth_detail": "Account session: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page (e-mail code or link, password or Google). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/auth/logout",
      "auth": "session",
      "grupo": "Account",
      "exemplo": "// Execute no console da página do produto / Run in the product page console.\n(async () => {\n  const origin = \"$ORIGIN\";\n  const {csrf} = await fetch(origin + \"/api/auth/bootstrap\").then(r => r.json());\n  const r = await fetch(origin + \"/api/auth/logout\", {\n    method: \"POST\", credentials: \"same-origin\",\n    headers: {\"Content-Type\": \"application/json\", \"X-CSRF-Token\": csrf},\n    body: JSON.stringify({})\n  });\n  if (!r.ok) throw new Error(\"Auth HTTP \" + r.status);\n  return r.json();\n})();",
      "exemploLinguagem": "js",
      "summary": "Revoke this product session.",
      "desc": "Bootstrap/CSRF must belong to this browser and session. Other product sessions remain active.",
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "true"
        }
      },
      "erros": {
        "400": "invalid_request",
        "403": "invalid_origin / invalid_csrf",
        "503": "auth_unavailable: a sessão anterior é preservada / the previous session is preserved"
      },
      "returns": "{ ok }",
      "url": "https://hookpulse.net/api/auth/logout",
      "auth_detail": "Account session: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page (e-mail code or link, password or Google). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/auth/pwa/install",
      "auth": "session",
      "grupo": "Installed app",
      "summary": "Prepare a personal app installation.",
      "desc": "Requires recent sign-in, exact Origin and browser CSRF. Returns a private signed PIN and a no-store manifest address. At most 10 active installations per account and product.",
      "corpo": {
        "path": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Same-origin task path, up to 1600 characters."
        },
        "state": {
          "tipo": "object",
          "obrigatorio": true,
          "desc": "Up to 30 allowlisted preference keys and string values."
        },
        "manifest": {
          "tipo": "object",
          "obrigatorio": true,
          "desc": "Public app name and same-origin icons."
        }
      },
      "retorno": {
        "_texto": "{pin,path,manifest}"
      },
      "exemplo": "(async () => {\n  const {csrf} = await fetch(\"$ORIGIN/api/auth/bootstrap\").then(r => r.json());\n  const manifest = await fetch(document.querySelector('link[rel=\"manifest\"]').href).then(r => r.json());\n  const r = await fetch(\"$ORIGIN/api/auth/pwa/install\", {method: \"POST\", credentials: \"same-origin\",\n    headers: {\"Content-Type\": \"application/json\", \"X-CSRF-Token\": csrf},\n    body: JSON.stringify({path: location.pathname + location.search, state: {}, manifest})});\n  return r.json();\n})();",
      "exemploLinguagem": "js",
      "erros": {
        "400": "invalid_installation_path / invalid_installation_state / invalid_installation_manifest",
        "401": "invalid_session / reauth_required",
        "409": "installation_not_created",
        "503": "auth_unavailable"
      },
      "returns": "{pin,path,manifest}",
      "url": "https://hookpulse.net/api/auth/pwa/install",
      "auth_detail": "Account session: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page (e-mail code or link, password or Google). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/auth/pwa/restore",
      "auth": "none",
      "grupo": "Installed app",
      "summary": "Restore an installed app.",
      "desc": "Requires the signed PIN, exact Origin and browser CSRF. Verifies host, product, account status, security version and installation revocation. Keeps the original authentication time. Sensitive actions still require recent sign-in.",
      "corpo": {
        "pin": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Private signed installation credential."
        }
      },
      "retorno": {
        "_texto": "{ok:true,state,path,manifest}; HttpOnly session cookies"
      },
      "exemplo": "(async () => {\n  const {csrf} = await fetch(\"$ORIGIN/api/auth/bootstrap\").then(r => r.json());\n  const r = await fetch(\"$ORIGIN/api/auth/pwa/restore\", {method: \"POST\", credentials: \"same-origin\",\n    headers: {\"Content-Type\": \"application/json\", \"X-CSRF-Token\": csrf},\n    body: JSON.stringify({pin: \"…\"})});\n  return r.json();\n})();",
      "exemploLinguagem": "js",
      "erros": {
        "401": "invalid_installation",
        "403": "invalid_origin / invalid_csrf",
        "503": "auth_unavailable"
      },
      "returns": "{ok:true,state,path,manifest}; HttpOnly session cookies",
      "url": "https://hookpulse.net/api/auth/pwa/restore",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/auth/pwa/manifest",
      "auth": "none",
      "grupo": "Installed app",
      "summary": "Read the private installation manifest.",
      "desc": "Requires the installation cookie and its manifest key. Private, no-store. The start URL keeps the PIN in the fragment.",
      "query": {
        "key": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Key in the manifest address that the install or restore call returned."
        }
      },
      "retorno": {
        "_texto": "application/manifest+json"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/auth/pwa/manifest?key=…\", {credentials: \"same-origin\"}).then(r => r.json())",
      "exemploLinguagem": "js",
      "erros": {
        "401": "invalid_installation"
      },
      "returns": "application/manifest+json",
      "url": "https://hookpulse.net/api/auth/pwa/manifest",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/account/keys",
      "auth": "session",
      "grupo": "Account",
      "summary": "List your API keys in this product.",
      "desc": "Never returns the key itself: name, last 4 characters, organization, creation, last use (hourly) and whether it still works.",
      "retorno": {
        "keys": {
          "tipo": "object[]",
          "desc": "`id`, `name`, `organizationId`, `last4`, `createdAt`, `lastUsedAt`, `revokedAt`, `active` (false when revoked or stopped by a password change / ending all sessions)."
        }
      },
      "erros": {
        "401": "invalid_session",
        "503": "auth_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/account/keys\", {credentials: \"same-origin\"}).then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ keys }",
      "url": "https://hookpulse.net/api/account/keys",
      "auth_detail": "Account session: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page (e-mail code or link, password or Google). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/account/keys/create",
      "auth": "session",
      "grupo": "Account",
      "summary": "Create an API key for agents and scripts.",
      "desc": "Needs a sign-in within the last 2 hours; an organization key also needs a second factor in the session and the owner/admin role with this product enabled. At most 10 live keys per account and product. The key (`secret`) is returned ONCE.",
      "corpo": {
        "name": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Up to 60 characters."
        },
        "organizationId": {
          "tipo": "string",
          "nulo": true,
          "obrigatorio": true,
          "desc": "`null` for an account key."
        }
      },
      "body": {
        "name": "agent",
        "organizationId": null
      },
      "retorno": {
        "key": {
          "tipo": "object",
          "desc": "`id`, `name`, `organizationId`, `last4`, `createdAt`."
        },
        "secret": {
          "tipo": "string",
          "desc": "`mmk_…`, shown once."
        }
      },
      "erros": {
        "400": "invalid_key_name / invalid_organization",
        "401": "invalid_session / reauth_required",
        "403": "invalid_origin / invalid_csrf / organization_forbidden / organization_mfa_required",
        "409": "key_limit_reached",
        "503": "auth_unavailable"
      },
      "exemplo": "(async () => {\n  const {csrf} = await fetch(\"$ORIGIN/api/auth/bootstrap\").then(r => r.json());\n  const r = await fetch(\"$ORIGIN/api/account/keys/create\", {method: \"POST\", credentials: \"same-origin\",\n    headers: {\"Content-Type\": \"application/json\", \"X-CSRF-Token\": csrf},\n    body: JSON.stringify({name: \"agent\", organizationId: null})});\n  return r.json();\n})();",
      "exemploLinguagem": "js",
      "returns": "{ key, secret }",
      "url": "https://hookpulse.net/api/account/keys/create",
      "auth_detail": "Account session: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page (e-mail code or link, password or Google). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/account/keys/revoke",
      "auth": "session",
      "grupo": "Account",
      "summary": "Revoke one of your API keys.",
      "desc": "Stops the key at once. Repeating is harmless.",
      "corpo": {
        "id": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "The key `id`."
        }
      },
      "body": {
        "id": "…"
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "true"
        }
      },
      "erros": {
        "400": "invalid_key_id",
        "401": "invalid_session",
        "403": "invalid_origin / invalid_csrf",
        "404": "key_not_found",
        "503": "auth_unavailable"
      },
      "exemplo": "(async () => {\n  const {csrf} = await fetch(\"$ORIGIN/api/auth/bootstrap\").then(r => r.json());\n  const r = await fetch(\"$ORIGIN/api/account/keys/revoke\", {method: \"POST\", credentials: \"same-origin\",\n    headers: {\"Content-Type\": \"application/json\", \"X-CSRF-Token\": csrf},\n    body: JSON.stringify({id: \"…\"})});\n  return r.json();\n})();",
      "exemploLinguagem": "js",
      "returns": "{ ok }",
      "url": "https://hookpulse.net/api/account/keys/revoke",
      "auth_detail": "Account session: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page (e-mail code or link, password or Google). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "GET",
      "path": "/api/account/apps",
      "auth": "session",
      "grupo": "Account",
      "summary": "List the apps connected to your account in this product.",
      "desc": "The MCP clients you signed in with (OAuth), such as claude.ai or ChatGPT. Never returns a token.",
      "retorno": {
        "apps": {
          "tipo": "object[]",
          "desc": "Up to 50: `id`, `nome` (the app; its host when it identifies itself by a metadata document), `criadoEm`, `usadoEm` (hourly, or null)."
        }
      },
      "erros": {
        "401": "invalid_session",
        "503": "auth_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/account/apps\", {credentials: \"same-origin\"}).then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ apps }",
      "url": "https://hookpulse.net/api/account/apps",
      "auth_detail": "Account session: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page (e-mail code or link, password or Google). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/account/apps/revoke",
      "auth": "session",
      "grupo": "Account",
      "summary": "Disconnect an app from your account.",
      "desc": "Stops the app's access at once; it asks you to sign in again. Repeating is harmless.",
      "corpo": {
        "id": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "The app `id`."
        }
      },
      "body": {
        "id": "…"
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "true"
        }
      },
      "erros": {
        "400": "invalid_app_id",
        "401": "invalid_session",
        "403": "invalid_origin / invalid_csrf",
        "404": "app_not_found",
        "503": "auth_unavailable"
      },
      "exemplo": "(async () => {\n  const {csrf} = await fetch(\"$ORIGIN/api/auth/bootstrap\").then(r => r.json());\n  const r = await fetch(\"$ORIGIN/api/account/apps/revoke\", {method: \"POST\", credentials: \"same-origin\",\n    headers: {\"Content-Type\": \"application/json\", \"X-CSRF-Token\": csrf},\n    body: JSON.stringify({id: \"…\"})});\n  return r.json();\n})();",
      "exemploLinguagem": "js",
      "returns": "{ ok }",
      "url": "https://hookpulse.net/api/account/apps/revoke",
      "auth_detail": "Account session: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page (e-mail code or link, password or Google). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "GET",
      "path": "/api/account/callouts",
      "auth": "session",
      "grupo": "Account",
      "summary": "List the notices that you closed in this product.",
      "desc": "The keys of the notices that stay closed for your account, on every device, such as the explanation of an area.",
      "retorno": {
        "callouts": {
          "tipo": "string[]",
          "desc": "Up to 200 keys, in alphabetical order."
        }
      },
      "erros": {
        "401": "invalid_session",
        "403": "product_required",
        "503": "auth_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/account/callouts\", {credentials: \"same-origin\"}).then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ callouts }",
      "url": "https://hookpulse.net/api/account/callouts",
      "auth_detail": "Account session: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page (e-mail code or link, password or Google). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/account/callouts/dismiss",
      "auth": "session",
      "grupo": "Account",
      "summary": "Close a notice for good.",
      "desc": "The notice does not open again for your account in this product. Repeating is harmless.",
      "corpo": {
        "key": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "The notice key: letters, digits and `_.:-`, up to 80."
        }
      },
      "body": {
        "key": "financeiro_expense"
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "true"
        }
      },
      "erros": {
        "400": "invalid_callout",
        "401": "invalid_session",
        "403": "invalid_origin / invalid_csrf",
        "409": "too_many_callouts",
        "503": "auth_unavailable"
      },
      "exemplo": "(async () => {\n  const {csrf} = await fetch(\"$ORIGIN/api/auth/bootstrap\").then(r => r.json());\n  const r = await fetch(\"$ORIGIN/api/account/callouts/dismiss\", {method: \"POST\", credentials: \"same-origin\",\n    headers: {\"Content-Type\": \"application/json\", \"X-CSRF-Token\": csrf},\n    body: JSON.stringify({key: \"financeiro_expense\"})});\n  return r.json();\n})();",
      "exemploLinguagem": "js",
      "returns": "{ ok }",
      "url": "https://hookpulse.net/api/account/callouts/dismiss",
      "auth_detail": "Account session: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page (e-mail code or link, password or Google). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/account/callouts/reopen",
      "auth": "session",
      "grupo": "Account",
      "summary": "Open a closed notice again.",
      "desc": "Undoes the close: the notice shows again for your account in this product. A product also keeps an on/off choice here, as the hidden values of Finance. Repeating is harmless.",
      "corpo": {
        "key": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "The notice key: letters, digits and `_.:-`, up to 80."
        }
      },
      "body": {
        "key": "financeiro_expense"
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "true"
        }
      },
      "erros": {
        "400": "invalid_callout",
        "401": "invalid_session",
        "403": "invalid_origin / invalid_csrf",
        "503": "auth_unavailable"
      },
      "exemplo": "(async () => {\n  const {csrf} = await fetch(\"$ORIGIN/api/auth/bootstrap\").then(r => r.json());\n  const r = await fetch(\"$ORIGIN/api/account/callouts/reopen\", {method: \"POST\", credentials: \"same-origin\",\n    headers: {\"Content-Type\": \"application/json\", \"X-CSRF-Token\": csrf},\n    body: JSON.stringify({key: \"financeiro_expense\"})});\n  return r.json();\n})();",
      "exemploLinguagem": "js",
      "returns": "{ ok }",
      "url": "https://hookpulse.net/api/account/callouts/reopen",
      "auth_detail": "Account session: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page (e-mail code or link, password or Google). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/auth/wallet/agent/start",
      "auth": "none",
      "grupo": "Account",
      "summary": "Start the wallet credential of an agent without an account.",
      "desc": "No account: the wallet proves it is hers by signing the message (EIP-4361). Signing does not pay or transfer anything. The message names this product and lasts 5 minutes. Before you sign, compare the message with the URL that you called. The domain on the first line must be the host of that URL. URI must be its origin. If one is different, do not sign. Sign it with personal_sign and send it to /api/auth/wallet/agent/token.",
      "corpo": {
        "address": {
          "tipo": "string",
          "desc": "0x…, a carteira (EOA ou contrato na Base)",
          "obrigatorio": true
        }
      },
      "body": {
        "address": "0x2c7536E3605D9C16a7a3D7b1898e529396a65c23"
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/auth/wallet/agent/start -H 'content-type: application/json' -d '{\"address\":\"0x2c7536E3605D9C16a7a3D7b1898e529396a65c23\"}'",
      "retorno": {
        "message": {
          "tipo": "string",
          "desc": "EIP-4361"
        },
        "issuedAt": {
          "tipo": "number",
          "desc": "segundos / seconds"
        },
        "expiresIn": {
          "tipo": "number",
          "desc": "300"
        }
      },
      "erros": {
        "400": "invalid_wallet_address",
        "503": "auth_unavailable"
      },
      "returns": "{ message, issuedAt, expiresIn }",
      "url": "https://hookpulse.net/api/auth/wallet/agent/start",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/auth/wallet/agent/token",
      "auth": "none",
      "grupo": "Account",
      "summary": "Get the wallet credential (mmw_…) of this product.",
      "desc": "The wallet's signature of the start message, checked here (own key) or on Base (contract, EIP-1271/6492). The credential lasts 30 days: send `Authorization: Bearer mmw_…` to the routes that accept the wallet as owner (owner `x402:<address>`). Once the wallet is linked to an account, what it did here moves to the account when the person signs in.",
      "corpo": {
        "address": {
          "tipo": "string",
          "desc": "0x…, a mesma carteira do start / the same wallet as in start",
          "obrigatorio": true
        },
        "issuedAt": {
          "tipo": "number",
          "desc": "o issuedAt do start / start's issuedAt",
          "obrigatorio": true
        },
        "signature": {
          "tipo": "string",
          "desc": "personal_sign (0x…)",
          "obrigatorio": true
        }
      },
      "body": {
        "address": "0x2c7536E3605D9C16a7a3D7b1898e529396a65c23",
        "issuedAt": 1791400000,
        "signature": "0x…"
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/auth/wallet/agent/token -H 'content-type: application/json' -d '{\"address\":\"0x2c7536E3605D9C16a7a3D7b1898e529396a65c23\",\"issuedAt\":1791400000,\"signature\":\"0x…\"}'",
      "retorno": {
        "token": {
          "tipo": "string",
          "desc": "mmw_…"
        },
        "owner": {
          "tipo": "string",
          "desc": "x402:0x…"
        },
        "expiresAt": {
          "tipo": "number",
          "desc": "segundos / seconds"
        }
      },
      "erros": {
        "400": "invalid_body",
        "401": "invalid_wallet",
        "429": "wallet_limited",
        "503": "wallet_unavailable / auth_unavailable"
      },
      "returns": "{ token, owner, expiresAt }",
      "url": "https://hookpulse.net/api/auth/wallet/agent/token",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/auth/claim/wallets",
      "auth": "session",
      "grupo": "Account",
      "summary": "Move to the account what its wallets did without an account.",
      "desc": "The sign-in window calls it after every sign-in. For each wallet linked to the account: the rights bought here by x402 (owner x402:<address>) and what this product keeps for that owner. Repeating moves nothing.",
      "exemplo": "await MMConta.fetch(new URL(\"$ORIGIN/api/auth/claim/wallets\").pathname, { method: \"POST\", headers: { \"content-type\": \"application/json\" }, body: \"{}\" })",
      "retorno": {
        "carteiras": {
          "tipo": "number",
          "desc": "wallets"
        },
        "direitos": {
          "tipo": "number",
          "desc": "rights moved"
        }
      },
      "erros": {
        "401": "invalid_session",
        "503": "auth_unavailable"
      },
      "returns": "{ carteiras, direitos }",
      "url": "https://hookpulse.net/api/auth/claim/wallets",
      "auth_detail": "Account session: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page (e-mail code or link, password or Google). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/auth/claim",
      "auth": "session",
      "grupo": "Account",
      "summary": "Moves what the guest created to the signed-in account: monitors, devices and their measurements, the status feed and the ingest key.",
      "desc": "Bootstrap/CSRF of this browser required; the page does it right after sign-in. Only what the guest still owns moves, in one transaction, and anything that collides with what the account already has stays with the guest. What the guest bought moves too. An old unsigned token that owns nothing here is refused. Repeating it is harmless (moves zero).",
      "corpo": {
        "guest_token": {
          "tipo": "string",
          "obrigatorio": false,
          "desc": "Guest `hp_…`; omitted, the `guest` cookie of this browser."
        }
      },
      "body": {},
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Whether the guest was recognized and moved."
        },
        "claimed": {
          "tipo": "object",
          "desc": "`product.movidos` (rows moved, per table), `product.apagados` (guest duplicates dropped) and `product.direitos` (purchases moved)."
        }
      },
      "erros": {
        "400": "invalid_product_claim / invalid_body",
        "401": "invalid_session",
        "403": "invalid_origin / invalid_csrf",
        "409": "unknown_guest (em `claimed.reason` / in `claimed.reason`)",
        "503": "product_claim_pending / auth_unavailable"
      },
      "exemplo": "(async () => {\n  const {csrf} = await fetch(\"$ORIGIN/api/auth/bootstrap\").then(r => r.json());\n  const r = await fetch(\"$ORIGIN/api/auth/claim\", {method: \"POST\", credentials: \"same-origin\",\n    headers: {\"Content-Type\": \"application/json\", \"X-CSRF-Token\": csrf},\n    body: \"{}\"});\n  return r.json();\n})();",
      "exemploLinguagem": "js",
      "returns": "{ ok, claimed }",
      "url": "https://hookpulse.net/api/auth/claim",
      "auth_detail": "Account session: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page (e-mail code or link, password or Google). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/contact",
      "auth": "none",
      "summary": "Write to the people behind the product — free, for people and agents alike.",
      "grupo": "Contact",
      "desc": "One route for a question and for a sponsorship, partnership or ad proposal (`tipo`, with the placements of `GET /api/partners`). No captcha, no account, no payment. 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.",
      "corpo": {
        "name": {
          "tipo": "string",
          "desc": "What to call the person writing.",
          "obrigatorio": true
        },
        "email": {
          "tipo": "string",
          "desc": "Where to reply.",
          "obrigatorio": true
        },
        "message": {
          "tipo": "string",
          "desc": "What you want to say.",
          "obrigatorio": true
        },
        "whatsapp": {
          "tipo": "string",
          "desc": "Optional reply number with country code, such as +5561999999999."
        },
        "prefer_whatsapp": {
          "tipo": "bool",
          "desc": "Request a WhatsApp reply. Requires whatsapp."
        },
        "tipo": {
          "tipo": "string",
          "desc": "Proposal: `patrocinio`, `parceria`, `anuncio` or `consultoria`. Turns on the fields below."
        },
        "empresa": {
          "tipo": "string",
          "desc": "Who is proposing, when it is a company."
        },
        "site": {
          "tipo": "string",
          "desc": "Website of who is proposing."
        },
        "orcamento": {
          "tipo": "string",
          "desc": "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`."
        },
        "espaco": {
          "tipo": "string[]",
          "desc": "Placement ids from `GET /api/partners`, up to 6."
        },
        "duracao": {
          "tipo": "string",
          "desc": "Exposure in days: `30`, `90` or `365`."
        },
        "pagamento": {
          "tipo": "string",
          "desc": "`usdc`, `deposito` or `a_combinar`."
        },
        "projeto": {
          "tipo": "string",
          "desc": "Consulting: the kind of project (`llm_privado`, `voz`, `dados_sensiveis`, `gpu`, `integracao` or `outro`)."
        },
        "prazo": {
          "tipo": "string",
          "desc": "Consulting: when to start (`agora`, `1_mes`, `1_3_meses` or `explorando`)."
        },
        "formato": {
          "tipo": "string",
          "desc": "Consulting: `projeto` (per scope), `mensal` (monthly) or `assessoria` (advisory)."
        },
        "pais": {
          "tipo": "string",
          "desc": "Consulting: the country of the person asking."
        },
        "fuso": {
          "tipo": "string",
          "desc": "Consulting: the IANA time zone, such as `America/New_York`."
        },
        "horarios": {
          "tipo": "string[]",
          "desc": "Consulting: up to 3 times for the call, `YYYY-MM-DDTHH:MM` in that time zone."
        }
      },
      "body": {
        "name": "Agent",
        "email": "agent@example.com",
        "message": "hello from an agent"
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always `true` when the message was accepted."
        }
      },
      "erros": {
        "400": "Validation: `code` names the field.",
        "503": "Contact is not configured on this server."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/contact -H 'content-type: application/json' -d '{\"name\":\"Agent\",\"email\":\"agent@example.com\",\"message\":\"hello from an agent\"}'",
      "returns": "{ ok }",
      "url": "https://hookpulse.net/api/contact",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/visit",
      "auth": "none",
      "summary": "Ping from the interface that increments the day's visits. Agents need not call it.",
      "grupo": "Billing",
      "desc": "Smoke does not count: `X-MM-Smoke`, User-Agent `mm-smoke` or `smoke: true` in the body come in as `counted: false`. Only the page's own ping counts: a call from outside a browser page of the same site (robot User-Agent, or no `Sec-Fetch-Site: same-origin`) comes in as `counted: false` with `reason: \"robo\"`.",
      "corpo": {
        "p": {
          "tipo": "string",
          "desc": "Path of the visited page."
        },
        "smoke": {
          "tipo": "bool",
          "desc": "`true` marks the call as a test and it does not enter the count."
        }
      },
      "body": {
        "p": "/"
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always `true`."
        },
        "counted": {
          "tipo": "bool",
          "desc": "Whether the visit entered the day's count."
        },
        "reason": {
          "tipo": "string",
          "desc": "Why it did not count, when `counted` is `false`.",
          "opcional": true
        }
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/visit -H 'content-type: application/json' -d '{\"p\":\"/\",\"smoke\":true}'",
      "returns": "{ ok, counted, reason? }",
      "url": "https://hookpulse.net/api/visit",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/erro-cliente",
      "auth": "none",
      "summary": "Browser error report, sent by the interface itself. Agents need not call it.",
      "grupo": "Operations",
      "desc": "The interface reports on its own JS errors, unhandled rejections, scripts/CSS that failed to load and CSP blocks — once per session — and the app reports handled failures through `window.mmErro.relata`. The server validates the envelope, redacts credentials, e-mails and phone numbers, merges repeats of the same failure per minute and records an operational event; nothing is written to a database. It keeps no IP, cookie, query string or full User-Agent. Always answers 204, even for an invalid report.",
      "corpo": {
        "code": {
          "tipo": "string",
          "desc": "Failure code, `UI-` + letters/digits (`UI-JS-001` global error, `UI-PROMESSA-001`, `UI-RECURSO-001`, `UI-CSP-001`, `UI-APP-001` app report).",
          "obrigatorio": true
        },
        "phase": {
          "tipo": "string",
          "desc": "Where it broke, lowercase: `global`, `promessa`, `script`, `load_list`…",
          "obrigatorio": true
        },
        "path": {
          "tipo": "string",
          "desc": "Path of the open page, without query; numbers, hashes and tokens in it are kept as `:id`."
        },
        "message": {
          "tipo": "string",
          "desc": "Error message, up to 2000 characters."
        },
        "stack": {
          "tipo": "string",
          "desc": "Stack trace, up to 12000 characters."
        },
        "source": {
          "tipo": "string",
          "desc": "Originating script; only its path is kept."
        },
        "line": {
          "tipo": "int",
          "desc": "Line in the originating script."
        },
        "column": {
          "tipo": "int",
          "desc": "Column in the originating script."
        },
        "visivel": {
          "tipo": "bool",
          "desc": "Whether the tab was visible when it broke."
        },
        "build": {
          "tipo": "string",
          "desc": "Build of the reporting page (its `<meta name=\"build\">`), up to 64 letters, digits, `.`, `_` or `-`; it dates the failure."
        }
      },
      "body": {
        "code": "UI-APP-001",
        "phase": "carregar_lista",
        "path": "/",
        "message": "lista 500"
      },
      "retorno": {
        "_texto": "204 with no body, always — an invalid, repeated or over-cap report also gets 204."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/erro-cliente -H 'content-type: application/json' -d '{\"code\":\"UI-APP-001\",\"phase\":\"carregar_lista\",\"path\":\"/\",\"message\":\"lista 500\"}'",
      "returns": "204 with no body, always — an invalid, repeated or over-cap report also gets 204.",
      "url": "https://hookpulse.net/api/erro-cliente",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/pagamento/aberto",
      "auth": "none",
      "statusOk": 202,
      "summary": "The interface reports a visible payment prompt. Agents must not call this route.",
      "grupo": "Operations",
      "desc": "An empty same-origin report, sent automatically when a payment prompt becomes visible. It starts no payment, grants no access and receives no identity or credentials. It writes no database row per report. Counts events, not unique people. The private operator dashboard separates API payment requests and browser payment views per UTC day; the two counts may overlap.",
      "headers": {
        "Origin": {
          "tipo": "string",
          "desc": "The page origin, identical to this route's origin.",
          "obrigatorio": true
        },
        "Sec-Fetch-Site": {
          "tipo": "string",
          "desc": "`same-origin`, set by the browser.",
          "obrigatorio": true
        },
        "X-MM-Payment-View": {
          "tipo": "string",
          "desc": "`1`, set by the shared component.",
          "obrigatorio": true
        }
      },
      "retorno": {
        "_texto": "202 with no body when accepted; 204 when ignored. Always no-store."
      },
      "returns": "202 with no body when accepted; 204 when ignored. Always no-store.",
      "url": "https://hookpulse.net/api/pagamento/aberto",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/funil",
      "auth": "none",
      "statusOk": 202,
      "summary": "The interface reports the steps of a visit (conversion funnel). Agents must not call this route.",
      "grupo": "Operations",
      "desc": "A same-origin batch sent by the page itself: page views, engagement, offer in view, buy click, payment window, payment sent or accepted. It keeps the browser's random id, the path without query, the referring host and the utm tags; never IP, e-mail or account. It writes no database row: one line per batch in the day's journal, with a cap. Declared robots and smoke tests are left out.",
      "headers": {
        "Origin": {
          "tipo": "string",
          "desc": "The page origin, identical to this route's origin.",
          "obrigatorio": false
        },
        "Content-Type": {
          "tipo": "string",
          "desc": "`application/json` or `text/plain`.",
          "obrigatorio": true
        }
      },
      "corpo": {
        "v": {
          "tipo": "number",
          "desc": "Batch version: `1`.",
          "obrigatorio": true
        },
        "vid": {
          "tipo": "string",
          "desc": "This browser's random id (UUID v4).",
          "obrigatorio": true
        },
        "sid": {
          "tipo": "string",
          "desc": "Session id (30 idle minutes end it).",
          "obrigatorio": true
        },
        "sn": {
          "tipo": "number",
          "desc": "This browser's session number.",
          "obrigatorio": true
        },
        "pv": {
          "tipo": "string",
          "desc": "Page view id.",
          "obrigatorio": true
        },
        "e": {
          "tipo": "object[]",
          "desc": "Up to 40 events `{ t, n, p? }` from the funnel vocabulary.",
          "obrigatorio": true
        }
      },
      "retorno": {
        "_texto": "202 with no body when stored; 204 when ignored. Always no-store."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/funil -H 'content-type: text/plain' -H 'x-mm-smoke: 1' -d '{\"v\":1,\"vid\":\"0f8b9c1e-2a3b-4c5d-8e6f-7a8b9c0d1e2f\",\"sid\":\"s1abcdefgh\",\"sn\":1,\"pv\":\"p1abcdefgh\",\"e\":[{\"t\":0,\"n\":\"pagina\"}]}'",
      "returns": "202 with no body when stored; 204 when ignored. Always no-store.",
      "url": "https://hookpulse.net/api/funil",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/funil/arquivos",
      "auth": "none",
      "summary": "Operator: the funnel journal days kept by this app, with the size of each.",
      "grupo": "Operations",
      "headers": {
        "Authorization": {
          "tipo": "string",
          "desc": "`Bearer <METRICS_TOKEN>`.",
          "obrigatorio": true
        }
      },
      "retorno": {
        "v": {
          "tipo": "number",
          "desc": "Stored batch version."
        },
        "produto": {
          "tipo": "string",
          "desc": "The product."
        },
        "teto": {
          "tipo": "object",
          "desc": "`bytesDia` and `dias` kept."
        },
        "dias": {
          "tipo": "object[]",
          "desc": "`{ dia, bytes }`, oldest to today."
        }
      },
      "erros": {
        "401": "Unauthorized",
        "503": "Not configured"
      },
      "exemplo": "curl -s $ORIGIN/api/funil/arquivos -H \"Authorization: Bearer $METRICS_TOKEN\"",
      "returns": "{ v, produto, teto, dias }",
      "url": "https://hookpulse.net/api/funil/arquivos",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/funil/arquivo",
      "auth": "none",
      "summary": "Operator: a slice of one funnel journal day, as NDJSON, from a byte offset.",
      "grupo": "Operations",
      "desc": "Up to 4 MiB per response, cut at the last whole line. `X-MM-Funil-Proximo` says where to ask for the rest; `X-MM-Funil-Tamanho`, the day's size now.",
      "headers": {
        "Authorization": {
          "tipo": "string",
          "desc": "`Bearer <METRICS_TOKEN>`.",
          "obrigatorio": true
        }
      },
      "query": {
        "dia": {
          "tipo": "string",
          "desc": "The day, `YYYY-MM-DD` (UTC).",
          "obrigatorio": true
        },
        "desde": {
          "tipo": "number",
          "desc": "The byte to read from; `0` at the start.",
          "obrigatorio": false
        }
      },
      "retorno": {
        "_texto": "JSON lines, one per stored batch."
      },
      "erros": {
        "400": "Invalid parameters",
        "401": "Unauthorized",
        "503": "Not configured"
      },
      "exemplo": "curl -s \"$ORIGIN/api/funil/arquivo?dia=2026-09-27&desde=0\" -H \"Authorization: Bearer $METRICS_TOKEN\"",
      "returns": "JSON lines, one per stored batch.",
      "url": "https://hookpulse.net/api/funil/arquivo",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/vitrine",
      "auth": "none",
      "grupo": "Public stats",
      "summary": "The product's public numbers: traffic, agents, usage and reliability, no money.",
      "desc": "Projection published hourly by the house collector, rounded to two significant digits; `null` is a missing measurement, never zero. 15-minute cache with ETag (`If-None-Match` → 304). There is no way to send numbers through this route: publishing belongs to the collector, with its own token.",
      "retorno": {
        "v": {
          "tipo": "int",
          "desc": "Contract version (1)."
        },
        "produto": {
          "tipo": "string",
          "desc": "Product id."
        },
        "publicado": {
          "tipo": "bool",
          "desc": "`false` before the collector's first publication; then only these five keys come."
        },
        "atualizado_em": {
          "tipo": "string",
          "desc": "When the collector published (ISO 8601).",
          "nulo": true
        },
        "stale": {
          "tipo": "bool",
          "desc": "`true` when the projection is older than 26 h."
        },
        "nome": {
          "tipo": "string",
          "desc": "Product name.",
          "opcional": true
        },
        "desde": {
          "tipo": "string",
          "desc": "First day the series covers.",
          "nulo": true,
          "opcional": true
        },
        "fuso": {
          "tipo": "string",
          "desc": "Traffic time zone. Audience periods state their time zone in `pessoas.medicao`.",
          "opcional": true
        },
        "hoje": {
          "tipo": "object",
          "desc": "Today: pages by class (human, AI, bot), API calls by class, machine-surface reads and product usage.",
          "opcional": true
        },
        "dias": {
          "tipo": "object[]",
          "desc": "Up to 31 days, oldest first: `dia`, `paginas`, `api`, `api_ia`, `maquina`, `visitantes`, `uso`.",
          "opcional": true
        },
        "janelas": {
          "tipo": "object",
          "desc": "7- and 30-day sums (`d7`, `d30`).",
          "opcional": true
        },
        "visitantes": {
          "tipo": "object",
          "desc": "Unique browsers with recorded interaction over 7 days.",
          "opcional": true
        },
        "pessoas": {
          "tipo": "object",
          "desc": "First-party measurement of browsers confirmed by interaction: unique browsers, sessions and pages over 7 and 30 days, segments and coverage. `null` means no measurement. Browsers are not verified people.",
          "nulo": true,
          "opcional": true
        },
        "agentes": {
          "tipo": "object",
          "desc": "The AI agents and bots that read the most, 7 days.",
          "opcional": true
        },
        "superficies": {
          "tipo": "object",
          "desc": "Reads of OKF, llms, well-known, OpenAPI and MCP over 7 days.",
          "opcional": true
        },
        "mcp": {
          "tipo": "object",
          "desc": "MCP calls over 7 days.",
          "opcional": true
        },
        "uso": {
          "tipo": "object",
          "desc": "Real product usage per resource: label, today, 7 and 30 days.",
          "opcional": true
        },
        "contas": {
          "tipo": "object",
          "desc": "Users and guests.",
          "nulo": true,
          "opcional": true
        },
        "confiabilidade": {
          "tipo": "object",
          "desc": "Share of requests without 5xx over 7 days, and the live build.",
          "opcional": true
        },
        "catalogo": {
          "tipo": "object",
          "desc": "Size of the catalog, when the product has one.",
          "nulo": true,
          "opcional": true
        },
        "apoio": {
          "tipo": "object",
          "desc": "Measured sponsors: 1-second exposures and native clicks per page in confirmed sessions, 7-day CTR, observed days and coverage. Empty means no history, not zero CTR.",
          "opcional": true
        }
      },
      "exemplo": "curl -s $ORIGIN/api/vitrine",
      "returns": "{ v, produto, publicado, atualizado_em, stale, nome?, desde?, fuso?, hoje?, dias?, janelas?, visitantes?, pessoas?, agentes?, superficies?, mcp?, uso?, contas?, confiabilidade?, catalogo?, apoio? }",
      "url": "https://hookpulse.net/api/vitrine",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/vitrine/operador",
      "auth": "none",
      "grupo": "Public stats",
      "summary": "The product's full document on the operator panel — operator token only.",
      "headers": {
        "Authorization": {
          "tipo": "string",
          "desc": "`Bearer <METRICS_TOKEN>` — the operator class.",
          "obrigatorio": true
        }
      },
      "retorno": {
        "produto": {
          "tipo": "string",
          "desc": "Product id."
        },
        "atualizado_em": {
          "tipo": "string",
          "desc": "When the collector published.",
          "nulo": true
        },
        "operador": {
          "tipo": "object",
          "desc": "The collector's full document, with what the public projection leaves out.",
          "nulo": true
        }
      },
      "erros": {
        "401": "No token, wrong token or a token of another class.",
        "503": "Worker without `METRICS_TOKEN` or without the control plane."
      },
      "exemplo": "curl -s $ORIGIN/api/vitrine/operador -H \"Authorization: Bearer $METRICS_TOKEN\"",
      "returns": "{ produto, atualizado_em, operador }",
      "url": "https://hookpulse.net/api/vitrine/operador",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/vitrine/painel",
      "auth": "none",
      "grupo": "Public stats",
      "summary": "The whole house panel, in the shape the gm reads — operator token only.",
      "headers": {
        "Authorization": {
          "tipo": "string",
          "desc": "`Bearer <METRICS_TOKEN>` — the operator class.",
          "obrigatorio": true
        }
      },
      "retorno": {
        "apps": {
          "tipo": "object[]",
          "desc": "One operator document per product, ordered by id."
        },
        "updated": {
          "tipo": "string",
          "desc": "When the collector closed the round.",
          "opcional": true
        },
        "totals": {
          "tipo": "object",
          "desc": "House totals.",
          "opcional": true
        }
      },
      "erros": {
        "401": "No token, wrong token or a token of another class.",
        "503": "Worker without `METRICS_TOKEN` or without the control plane."
      },
      "exemplo": "curl -s $ORIGIN/api/vitrine/painel -H \"Authorization: Bearer $METRICS_TOKEN\"",
      "returns": "{ apps, updated?, totals? }",
      "url": "https://hookpulse.net/api/vitrine/painel",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/vitrine/cursores",
      "auth": "none",
      "grupo": "Public stats",
      "summary": "Per-product cursors: `borda`, `borda5xx` (5xx only), and `cli`. Operator token only.",
      "headers": {
        "Authorization": {
          "tipo": "string",
          "desc": "`Bearer <METRICS_TOKEN>` — the operator class.",
          "obrigatorio": true
        }
      },
      "retorno": {
        "_texto": "JSON: `{ [product]: { borda?: ISO, borda5xx?: ISO, cli?: ISO } }`; empty is `{}`."
      },
      "erros": {
        "401": "No token, wrong token or a token of another class.",
        "503": "Worker without `METRICS_TOKEN` or without the control plane."
      },
      "exemplo": "curl -s $ORIGIN/api/vitrine/cursores -H \"Authorization: Bearer $METRICS_TOKEN\"",
      "returns": "JSON: `{ [product]: { borda?: ISO, borda5xx?: ISO, cli?: ISO } }`; empty is `{}`.",
      "url": "https://hookpulse.net/api/vitrine/cursores",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/partners",
      "auth": "none",
      "grupo": "Partnership",
      "summary": "Fixed-price advertising: placements, duration, public numbers and checkout. Partnerships and custom campaigns use negotiation.",
      "desc": "Four packages priced in USD per week, as a one-off purchase or weekly card subscription. Lists real channels, current sponsors and public numbers. Purchase through POST /api/partners. Negotiation and partnerships use the contact form. Cached for up to 1 hour; with a linked order, 1 minute.",
      "retorno": {
        "status": {
          "tipo": "string",
          "desc": "`preco_fechado`: priced spaces can be purchased; other offers require negotiation."
        },
        "produto": {
          "tipo": "string",
          "desc": "Product name."
        },
        "idioma": {
          "tipo": "string",
          "desc": "Language of the texts (the product's)."
        },
        "titulo": {
          "tipo": "string",
          "desc": "Title of the offer."
        },
        "descricao": {
          "tipo": "string",
          "desc": "One sentence about the offer."
        },
        "publico": {
          "tipo": "string",
          "desc": "Who uses the product — the audience a sponsor reaches."
        },
        "modalidades": {
          "tipo": "object[]",
          "desc": "`{ id, nome }`: patrocinio, parceria, anuncio."
        },
        "placements": {
          "tipo": "object[]",
          "desc": "Package channels: id, nome, onde, formato, medicao and disponivel. Legacy monthly price is null in the weekly catalogue."
        },
        "packages": {
          "tipo": "object[]",
          "desc": "Four packages: id, nome, assinatura, preco_usd_semana, espacos, canais and allowed periods in exposicao[{dias,preco}]."
        },
        "house_bundle": {
          "tipo": "object",
          "desc": "Unavailable in the weekly catalogue: `{disponivel:false}`."
        },
        "parcerias": {
          "tipo": "string[]",
          "desc": "Partnership ideas the product is open to discuss."
        },
        "current_sponsors": {
          "tipo": "object[]",
          "desc": "Sponsors in effect: `id`, `nome`, `url`, `frase`, `espacos`, `ate`."
        },
        "stats": {
          "tipo": "object",
          "desc": "Excerpt of the public numbers (`hoje`, `janelas`, `agentes`, `confiabilidade`) and the `link` to `/api/vitrine`; `publicado: false` before the first publication."
        },
        "payment": {
          "tipo": "object",
          "desc": "Shared payment through the purchase POST. Wallet fields remain for compatibility; do not send a manual payment before the quote."
        },
        "contact": {
          "tipo": "object",
          "desc": "`email`, `form_url`, `api_url` (`POST /api/contact`, free: one message every 10 s per network), `campos` (required), `campos_proposta` (the optional proposal fields, each with its accepted values), `message_template`, `instructions`."
        },
        "politica": {
          "tipo": "object",
          "desc": "Placement label, refused sectors, prepayment, deadlines."
        },
        "_links": {
          "tipo": "object",
          "desc": "`self`, `stats`, `page` (`null` until the page exists), `contact`, `casa` (the same path on the ten products)."
        }
      },
      "exemplo": "curl -s $ORIGIN/api/partners",
      "returns": "{ status, produto, idioma, titulo, descricao, publico, modalidades, placements, packages, house_bundle, parcerias, current_sponsors, stats, payment, contact, politica, _links }",
      "url": "https://hookpulse.net/api/partners",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/partners",
      "auth": "none",
      "grupo": "Partnership",
      "summary": "Buy a weekly ad package and submit the artwork for review.",
      "desc": "The server prices the selected package and number of weeks. One-off: shared 402 quote and payment, no renewal. modalidade=assinatura: signed-in account, same-origin CSRF, native card payment and explicit weekly renewal acceptance. Read the current BRL offer at /api/credito/assinaturas/ofertas. Cancel future charges in the account. modalidade=recibo returns only that account's paid campaign receipt. Payment precedes content approval; ad display starts on publication. Reuse the id, token and content for retries. The quote does not save artwork or reserve inventory. Custom campaigns and free partnerships use POST /api/contact.",
      "query": [
        {
          "nome": "modalidade",
          "tipo": "string",
          "desc": "Omit for one-off payment; assinatura uses native weekly card billing; recibo reads the account's campaign receipt."
        }
      ],
      "corpo": {
        "id": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Fresh random order id: 32 lowercase hexadecimal characters. Reuse for retries."
        },
        "token": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Private random order capability: 64 lowercase hexadecimal characters. Keep it for recovery."
        },
        "espaco": {
          "tipo": "string[]",
          "desc": "Legacy paid-order retry only. New purchases select a package."
        },
        "pacote": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "One of the four package ids in GET /api/partners packages."
        },
        "duracao": {
          "tipo": "int",
          "obrigatorio": true,
          "desc": "Days from the package exposure list. Weekly subscriptions require 7."
        },
        "assinatura": {
          "tipo": "object",
          "desc": "Only with modalidade=assinatura: the native subscription body with recurso=publicidade:ORDER_ID, simulado, cartao and explicit renewal aceite. The authenticated account must own the campaign."
        },
        "pedido": {
          "tipo": "string",
          "desc": "Only with modalidade=recibo: the campaign id owned by the signed-in account."
        },
        "nome": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Brand or product, up to 80 characters."
        },
        "email": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Contact email."
        },
        "url": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "HTTPS destination URL."
        },
        "frase": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Ad copy, up to 100 characters."
        },
        "arte": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Base64 image bytes, without a data URL prefix. PNG, JPEG or WebP, up to 2 MB."
        },
        "direitos": {
          "tipo": "bool",
          "obrigatorio": true,
          "desc": "true confirms the right to use the image and content."
        }
      },
      "retorno": {
        "id": {
          "tipo": "string",
          "desc": "Order id."
        },
        "estado": {
          "tipo": "string",
          "desc": "revisao: paid and awaiting content approval."
        },
        "recibo": {
          "tipo": "string",
          "desc": "Verified payment receipt."
        }
      },
      "erros": {
        "400": "Invalid order or artwork.",
        "402": "Payment required.",
        "409": "Space occupied or order changed.",
        "503": "Checkout unavailable."
      },
      "exemplo": "curl -X POST '$ORIGIN/api/partners' -H 'content-type: application/json' -H 'Idempotency-Key: ORDER_ID' --data-binary @campaign.json",
      "returns": "{ id, estado, recibo }",
      "url": "https://hookpulse.net/api/partners",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/newsletter",
      "auth": "session",
      "grupo": "Account",
      "summary": "Read email advertising consent.",
      "desc": "Uses the session account. Cannot select another account. Response is not cached.",
      "retorno": {
        "ativo": {
          "tipo": "bool",
          "desc": "Separate email advertising consent."
        },
        "areas": {
          "tipo": "string[]",
          "desc": "[\"publicidade\"]."
        },
        "idioma": {
          "tipo": "string",
          "desc": "pt | en."
        },
        "verificado": {
          "tipo": "bool",
          "desc": "The account has a verified email."
        }
      },
      "erros": {
        "401": "invalid_session",
        "503": "newsletter_indisponivel"
      },
      "exemplo": "curl -s \"$ORIGIN/api/newsletter\" -b \"$COOKIE\"",
      "returns": "{ ativo, areas, idioma, verificado }",
      "url": "https://hookpulse.net/api/newsletter",
      "auth_detail": "Account session: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page (e-mail code or link, password or Google). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "PUT",
      "path": "/api/newsletter",
      "auth": "session",
      "grupo": "Account",
      "summary": "Allow or stop advertising in requested notices.",
      "desc": "Requires a session, Origin and X-CSRF-Token. Enabling requires explicit consent and a verified email. Withdrawal is immediate and keeps essential notices. At most one ad per account in 24 hours.",
      "headers": {
        "X-CSRF-Token": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "CSRF value supplied by the SDK."
        }
      },
      "corpo": {
        "ativo": {
          "tipo": "bool",
          "obrigatorio": true,
          "desc": "true allows; false withdraws."
        },
        "areas": {
          "tipo": "string[]",
          "obrigatorio": true,
          "desc": "[\"publicidade\"]."
        },
        "idioma": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Notice language: pt or en."
        },
        "consentimento": {
          "tipo": "bool",
          "desc": "true when allowing."
        }
      },
      "body": {
        "ativo": false,
        "areas": [
          "publicidade"
        ],
        "idioma": "en"
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "true."
        },
        "ativo": {
          "tipo": "bool",
          "desc": "Separate email advertising consent."
        },
        "areas": {
          "tipo": "string[]",
          "desc": "[\"publicidade\"]."
        },
        "idioma": {
          "tipo": "string",
          "desc": "pt | en."
        }
      },
      "erros": {
        "400": "assinatura_invalida",
        "401": "invalid_session",
        "403": "invalid_csrf | email_verificado_exigido",
        "429": "aguarde_para_alterar | limite_diario",
        "503": "newsletter_indisponivel"
      },
      "exemplo": "curl -X PUT \"$ORIGIN/api/newsletter\" -b \"$COOKIE\" -H \"Origin: $ORIGIN\" -H \"X-CSRF-Token: $CSRF\" -H \"Content-Type: application/json\" -d '{\"ativo\":false,\"areas\":[\"publicidade\"],\"idioma\":\"en\"}'",
      "returns": "{ ok, ativo, areas, idioma }",
      "url": "https://hookpulse.net/api/newsletter",
      "auth_detail": "Account session: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page (e-mail code or link, password or Google). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/newsletter/cancelar",
      "auth": "none",
      "grupo": "Account",
      "summary": "Withdraw consent using the email link.",
      "desc": "The secret link token replaces the session. JSON requires the product Origin. Also accepts RFC 8058: List-Unsubscribe=One-Click form body and token in the query, without login. Essential notices continue.",
      "corpo": {
        "token": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "43-character link token, without the prefix."
        }
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "true."
        }
      },
      "erros": {
        "400": "cancelamento_invalido | confirmacao_exigida",
        "403": "invalid_origin",
        "404": "cancelamento_invalido_ou_vencido"
      },
      "exemplo": "curl \"$ORIGIN/api/newsletter/cancelar\" -H \"Origin: $ORIGIN\" -H \"Content-Type: application/json\" -d '{\"token\":\"<link-token>\"}'",
      "returns": "{ ok }",
      "url": "https://hookpulse.net/api/newsletter/cancelar",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/metrics",
      "auth": "none",
      "summary": "Metrics of the last 7 days. With the operator token, includes payments.",
      "grupo": "Billing",
      "desc": "Without credentials it returns visits and usage; `accounts.people` counts the accounts that have used HookPulse (from the shared account records — guests are not counted). With `METRICS_TOKEN` as Bearer it adds `payments` — and only on Base mainnet, because staging numbers on a finance panel mislead.",
      "headers": {
        "Authorization": {
          "tipo": "string",
          "desc": "`Bearer <METRICS_TOKEN>` to include the finance block.",
          "obrigatorio": false
        }
      },
      "retorno": "Metricas",
      "exemplo": "curl -s $ORIGIN/api/metrics -H \"Authorization: Bearer $METRICS_TOKEN\"",
      "returns": "{ app, today, today_visits, today_contacts?, days, usage, accounts, financeiro?, payments? }",
      "url": "https://hookpulse.net/api/metrics",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/plans",
      "auth": "none",
      "summary": "The plans for sale and the authenticated account or wallet's active plan.",
      "desc": "Each purchase lasts 30 days, with no automatic renewal. Without an account or wallet credential, `account` is `null`.",
      "grupo": "Billing",
      "query": {
        "resumo": {
          "tipo": "string",
          "obrigatorio": false,
          "desc": "Use `1` to read the active plan without cancellation, retention and refund details.",
          "exemplo": "1"
        }
      },
      "retorno": {
        "plans": {
          "tipo": "object[]",
          "desc": "Each plan: `id`, `name`, `extra_monitors`, `days`, `price_usd`, `automatic_renewal`, `charged_now` and the `buy` URL."
        },
        "account": {
          "tipo": "object",
          "desc": "`active` (the plan that ends last, with `valid_until`) and `cancel` (what cancelling removes when the plan ends, the free-days offer, the refund, `keeps_until`, until when what you paid for stays active, `ends_now`, true when cancelling ends part of the plan now, and `renews`, true when a monthly subscription renews it). Without a refund, `ends_now` or `renews`, cancelling changes nothing.",
          "nulo": true
        }
      },
      "erros": {
        "401": "session_ended",
        "503": "auth_unavailable"
      },
      "exemplo": "curl -s $ORIGIN/api/plans",
      "returns": "{ plans, account }",
      "url": "https://hookpulse.net/api/plans",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/plans/:plan",
      "auth": "none",
      "summary": "Buys a plan for the account or paying wallet.",
      "desc": "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.",
      "grupo": "Billing",
      "params": {
        "plan": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "`starter` (40 more monitors, up to 50), `team` (90 more monitors, up to 100), `business` (490 more monitors, up to 500)",
          "exemplo": "starter"
        }
      },
      "query": {
        "compra": {
          "tipo": "string",
          "obrigatorio": false,
          "desc": "Mark of one credit purchase, 8 to 64 letters, digits, `-` or `_`. Repeating it with the same credit token does not debit again.",
          "exemplo": "purchase-2026-10-12"
        }
      },
      "retorno": {
        "plan": {
          "tipo": "string",
          "desc": "The plan bought."
        },
        "extra_monitors_added": {
          "tipo": "number",
          "desc": "Units granted by this purchase."
        },
        "valid_until": {
          "tipo": "string",
          "desc": "When this purchase ends (ISO 8601)."
        },
        "automatic_renewal": {
          "tipo": "bool",
          "desc": "`false` for Pix, USDC and credit."
        },
        "account": {
          "tipo": "object",
          "desc": "The active plan for the owner; `null` if its summary is unavailable.",
          "nulo": true
        },
        "_links": {
          "tipo": "object",
          "desc": "Plans URL and, for a wallet purchase, wallet authentication and claim URLs."
        }
      },
      "erros": {
        "401": "An explicit account, wallet or credit credential was refused.",
        "402": "Payment required: the price and the accepted payments.",
        "403": "Writes with the account cookie need the same Origin and `X-CSRF-Token`.",
        "404": "Unknown plan (`unknown_plan`).",
        "409": "Plans are not on sale now (`sales_closed`) or billing is off (`billing_off`)."
      },
      "exemplo": "curl -s -X POST \"$ORIGIN/api/plans/starter?compra=purchase-2026-10-12\" -H \"X-CSRF-Token: $CSRF\" -H \"X-Credito: $CREDITO\" -b \"$COOKIE\"",
      "returns": "{ plan, extra_monitors_added, valid_until, automatic_renewal, account, _links }",
      "url": "https://hookpulse.net/api/plans/:plan",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/plans/cancel",
      "auth": "session",
      "summary": "Cancels the active plan: nothing else is charged, and what you paid for stays active until the end of its period.",
      "desc": "Send `{ \"accept_data_removal\": true }`. The monthly card subscription stops. A purchase made less than 7 days ago and a paid period that has not started yet, such as a renewal, go back the same way they were paid (card or Pix) and end now. The rest of the paid period stays active until `keeps_until`; then what the plan added ends (`account.cancel.losses` in `GET /api/plans` says what). A purchase paid with USDC or with credit from another purchase stays active until its end; ask for its refund by e-mail, as the cancellation page says.",
      "grupo": "Billing",
      "corpo": {
        "accept_data_removal": {
          "tipo": "bool",
          "obrigatorio": true,
          "desc": "Must be `true`: you accept losing what the plan added."
        }
      },
      "body": {
        "accept_data_removal": true
      },
      "retorno": {
        "canceled": {
          "tipo": "bool",
          "desc": "`true` when a valid plan was canceled: it ended now, or it stays active until `keeps_until`."
        },
        "refund": {
          "tipo": "object",
          "desc": "`method` (`original`), `amount_cents` and its `currency` (`brl` when Asaas refunds, `usd` when Stripe refunds; `null` when both do).",
          "nulo": true
        },
        "keeps_until": {
          "tipo": "string",
          "desc": "When the paid period that stays active ends (ISO 8601); `null` when everything ended now.",
          "nulo": true
        },
        "account": {
          "tipo": "object",
          "desc": "The account state after cancelling, as in `GET /api/plans`."
        }
      },
      "erros": {
        "400": "Missing `accept_data_removal: true` (`confirmation_required`).",
        "401": "Sign in first (`account_required`), or the session ended (`session_ended`).",
        "403": "Writes with the account cookie need the same Origin and `X-CSRF-Token`."
      },
      "exemplo": "curl -s -X POST $ORIGIN/api/plans/cancel -H \"Content-Type: application/json\" -H \"X-CSRF-Token: $CSRF\" -b \"$COOKIE\" -d '{\"accept_data_removal\":true}'",
      "returns": "{ canceled, refund, keeps_until, account }",
      "url": "https://hookpulse.net/api/plans/cancel",
      "auth_detail": "Account session: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page (e-mail code or link, password or Google). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/plans/:plan/grace",
      "auth": "session",
      "summary": "Takes the free-days offer from the cancellation: the offered plan, free for 7 days, once per account.",
      "desc": "`account.cancel.offer` in `GET /api/plans` says which plan and for how long. Nothing renews by itself, and what you paid for stays active until its end; the free days start after it.",
      "grupo": "Billing",
      "params": {
        "plan": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "`starter` (40 more monitors, up to 50), `team` (90 more monitors, up to 100), `business` (490 more monitors, up to 500)",
          "exemplo": "starter"
        }
      },
      "retorno": {
        "plan": {
          "tipo": "string",
          "desc": "The plan granted."
        },
        "valid_until": {
          "tipo": "string",
          "desc": "When the free days end."
        },
        "price_usd": {
          "tipo": "number",
          "desc": "Always `0`."
        },
        "account": {
          "tipo": "object",
          "desc": "The account state after the offer, as in `GET /api/plans`."
        }
      },
      "erros": {
        "401": "Sign in first (`account_required`), or the session ended (`session_ended`).",
        "403": "Writes with the account cookie need the same Origin and `X-CSRF-Token`.",
        "404": "Unknown plan (`unknown_plan`).",
        "409": "No offer for this account or plan (`offer_unavailable`)."
      },
      "exemplo": "curl -s -X POST $ORIGIN/api/plans/starter/grace -H \"X-CSRF-Token: $CSRF\" -b \"$COOKIE\"",
      "returns": "{ plan, valid_until, price_usd, account }",
      "url": "https://hookpulse.net/api/plans/:plan/grace",
      "auth_detail": "Account session: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page (e-mail code or link, password or Google). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "GET",
      "path": "/api/credito/x402/saude",
      "auth": "none",
      "exemplo": "curl -s $ORIGIN/api/credito/x402/saude",
      "grupo": "Credit",
      "summary": "x402 verifier health: pending payments, missing hashes, failed or stalled reconciliation.",
      "retorno": {
        "verificado_em": {
          "tipo": "string",
          "desc": "Reading time in UTC."
        },
        "ativo": {
          "tipo": "boolean",
          "desc": "Verifier configured in this process."
        },
        "tarefas": {
          "tipo": "object[]",
          "desc": "Failures with id and reason, without payer data. Bounded counts show +."
        }
      },
      "erros": {
        "503": "Verifier unavailable in this process."
      },
      "returns": "{ verificado_em, ativo, tarefas }",
      "url": "https://hookpulse.net/api/credito/x402/saude",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/credito/barrados/saude",
      "auth": "none",
      "exemplo": "curl -s $ORIGIN/api/credito/barrados/saude",
      "grupo": "Credit",
      "summary": "People who paid and were blocked on this site in the last 24 hours: the site asked for payment from someone with balance or a plan, or charged and did not deliver.",
      "retorno": {
        "verificado_em": {
          "tipo": "string",
          "desc": "Reading time in UTC."
        },
        "ativo": {
          "tipo": "boolean",
          "desc": "The reading worked."
        },
        "total": {
          "tipo": "int",
          "desc": "Number of different cases."
        },
        "tarefas": {
          "tipo": "object[]",
          "desc": "Up to 8 cases, with class, route, count and last time, without payer data."
        }
      },
      "returns": "{ verificado_em, ativo, total, tarefas }",
      "url": "https://hookpulse.net/api/credito/barrados/saude",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/credito/conta",
      "auth": "credito",
      "exemplo": "curl -s $ORIGIN/api/credito/conta -H 'Cookie: <sessão>'",
      "grupo": "Credit",
      "summary": "The balance of the session account: the sum of its purchases in this environment, in every product, without the code. With movimentos=1, the statement of the account.",
      "query": {
        "movimentos": {
          "tipo": "string",
          "desc": "`1`: reads only the paged statement of the account, without the balance."
        },
        "de": {
          "tipo": "string",
          "desc": "Inclusive start in UTC ISO 8601 with milliseconds. Default: 1970-01-01T00:00:00.000Z."
        },
        "ate": {
          "tipo": "string",
          "desc": "Exclusive end in UTC ISO 8601 with milliseconds. Default: now. Keep the value between pages."
        },
        "antes": {
          "tipo": "string",
          "desc": "The `proximo` cursor of the previous page. Only with movimentos=1."
        },
        "limite": {
          "tipo": "int",
          "desc": "1 to 50 entries per page; default 20. Only with movimentos=1."
        }
      },
      "retorno": {
        "saldo_micros": {
          "tipo": "int",
          "desc": "The balance in millionths of a dollar."
        },
        "saldo_usd": {
          "tipo": "string",
          "desc": "The balance in dollars, formatted."
        },
        "referencia_usd": {
          "tipo": "string",
          "desc": "The balance right after the last top-up, added across the wallets with a balance: the base of what is left."
        },
        "cotacao_brl_por_usd": {
          "tipo": "number",
          "desc": "The BRL rate the screen uses to show the balance."
        },
        "movimentos": {
          "tipo": "object[]",
          "desc": "Only with movimentos=1: id, em, valor_usd, saldo_usd, motivo, produto and recurso, newest first."
        },
        "proximo": {
          "tipo": "string",
          "desc": "Only with movimentos=1: the cursor of the next page or null."
        }
      },
      "erros": {
        "400": "Invalid period, limit or cursor.",
        "401": "No session."
      },
      "returns": "{ saldo_micros, saldo_usd, referencia_usd, cotacao_brl_por_usd, movimentos, proximo }",
      "url": "https://hookpulse.net/api/credito/conta",
      "auth_detail": "Credit token in `Authorization: Bearer cred_…` or `X-Credito`. For recovered x402 funds, use the wallet credential mmw_… in Bearer or X-Wallet-Token, issued through /api/auth/wallet/agent/start and /api/auth/wallet/agent/token."
    },
    {
      "method": "POST",
      "path": "/api/credito/conta",
      "auth": "credito",
      "exemplo": "curl -s -XPOST $ORIGIN/api/credito/conta -H 'Authorization: Bearer cred_…' -H 'Cookie: <sessão>'",
      "grupo": "Credit",
      "summary": "Links credit bought without an account to the account of the session, USDC included. Then the balance works in the products of the shared credit with `X-Credito: conta`, added to the other purchases of the account.",
      "retorno": {
        "ok": {
          "tipo": "boolean",
          "desc": "The link was checked."
        },
        "ligadas": {
          "tipo": "int",
          "desc": "Purchases linked now. Zero when the purchase already has an account."
        },
        "da_conta": {
          "tipo": "boolean",
          "desc": "The balance of this code is already in the account balance."
        }
      },
      "erros": {
        "401": "No session or no credit code."
      },
      "returns": "{ ok, ligadas, da_conta }",
      "url": "https://hookpulse.net/api/credito/conta",
      "auth_detail": "Credit token in `Authorization: Bearer cred_…` or `X-Credito`. For recovered x402 funds, use the wallet credential mmw_… in Bearer or X-Wallet-Token, issued through /api/auth/wallet/agent/start and /api/auth/wallet/agent/token."
    },
    {
      "method": "POST",
      "path": "/api/credito",
      "auth": "none",
      "summary": "Top up prepaid credit: pay once with x402 and get the token that debits on any API of the house.",
      "grupo": "Credit",
      "preco_usd_faixa": [
        0.1,
        25
      ],
      "query": {
        "usd": {
          "tipo": "number",
          "desc": "Package: 0.1, 1, 5, 10 or 25 dollars.",
          "obrigatorio": true,
          "enum": [
            0.1,
            1,
            5,
            10,
            25
          ]
        }
      },
      "retorno": {
        "token": {
          "tipo": "string",
          "desc": "Bearer token for the balance (`cred_…`). Shown ONCE — it cannot be recovered."
        },
        "saldo_usd": {
          "tipo": "string",
          "desc": "Credited balance."
        },
        "guarde": {
          "tipo": "string",
          "desc": "Warning that the token is the bearer of the credit."
        },
        "usar": {
          "tipo": "string",
          "desc": "How to present the token on paid routes."
        },
        "usar_com_outra_credencial": {
          "tipo": "string",
          "desc": "The `X-Credito` header, for when `Authorization` already carries another credential."
        },
        "saldo_em": {
          "tipo": "string",
          "desc": "Where to check balance and statement."
        },
        "onde_gastar": {
          "tipo": "object",
          "desc": "`precos`: the paid actions and prices of this product (`/api/pricing`); `produtos`: the APIs.json with our other products."
        }
      },
      "erros": {
        "400": "Package outside the list (0.1, 1, 5, 10 or 25).",
        "402": "Unpaid — the body carries the x402 `accepts[]`."
      },
      "exemplo": "curl -s -XPOST '$ORIGIN/api/credito?usd=10'",
      "returns": "{ token, saldo_usd, guarde, usar, usar_com_outra_credencial, saldo_em, onde_gastar }",
      "url": "https://hookpulse.net/api/credito",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/credito",
      "auth": "credito",
      "summary": "Balance and statement with cred_…; wallet balance and up to 20 recovery records with mmw_….",
      "desc": "A request without a token receives 401. The Link header points to the x402 manifest. When billing is configured, accepts[] contains the same terms as the smallest POST top-up. GET cannot charge or create credit.",
      "grupo": "Credit",
      "retorno": {
        "saldo_micros": {
          "tipo": "int",
          "desc": "Balance in micro-dollars (1e-6 USD)."
        },
        "saldo_usd": {
          "tipo": "string",
          "desc": "Formatted balance."
        },
        "criado_em": {
          "tipo": "string",
          "desc": "When the credit was opened."
        },
        "movimentos": {
          "tipo": "object[]",
          "desc": "Recent credits and debits, with product and resource."
        },
        "wallet": {
          "tipo": "string",
          "desc": "Only with mmw_…: authenticated wallet address."
        },
        "payments": {
          "tipo": "object[]",
          "desc": "Only with mmw_…: up to 20 payments with id, produto, rede, tx_hash, micros, estado and criado_ms. creditado means recovered balance."
        },
        "onde_gastar": {
          "tipo": "object",
          "desc": "`precos`: the paid actions and prices of this product; `produtos`: the APIs.json with our other products."
        }
      },
      "erros": {
        "401": "No token, or unknown token."
      },
      "exemplo": "curl -s $ORIGIN/api/credito -H 'Authorization: Bearer cred_…'",
      "returns": "{ saldo_micros, saldo_usd, criado_em, movimentos, wallet, payments, onde_gastar }",
      "url": "https://hookpulse.net/api/credito",
      "auth_detail": "Credit token in `Authorization: Bearer cred_…` or `X-Credito`. For recovered x402 funds, use the wallet credential mmw_… in Bearer or X-Wallet-Token, issued through /api/auth/wallet/agent/start and /api/auth/wallet/agent/token."
    },
    {
      "method": "GET",
      "path": "/api/credito/pix",
      "auth": "none",
      "summary": "Credit by Pix (Brazil): the key, the fixed rate, the packs in reais and what the receipt accepts.",
      "grupo": "Credit",
      "retorno": {
        "chave": {
          "tipo": "string",
          "desc": "The Pix key that receives the payment."
        },
        "brl_por_usd": {
          "tipo": "number",
          "desc": "Fixed rate used for the packs."
        },
        "pacotes": {
          "tipo": "object[]",
          "desc": "The packs (3, 5, 10 or 25 dollars), each with `brl_centavos` and `copia_e_cola` (the Pix copy-and-paste code for the amount, the same text as the QR: the bank app comes with the amount filled in)."
        },
        "comprovante": {
          "tipo": "object",
          "desc": "Accepted types (photo or PDF) and the maximum size, in bytes."
        },
        "liberacao": {
          "tipo": "string",
          "desc": "`manual`: the owner checks the Pix and releases it."
        }
      },
      "exemplo": "curl -s $ORIGIN/api/credito/pix",
      "returns": "{ chave, brl_por_usd, pacotes, comprovante, liberacao }",
      "url": "https://hookpulse.net/api/credito/pix",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/credito/pix",
      "auth": "none",
      "summary": "Request credit paid by Pix: multipart with `usd`, `comprovante` (photo or PDF up to 2 MB) and `email`; the rest is optional.",
      "desc": "The credit code comes back in the response and works once the owner checks the Pix and releases it (by hand, usually the same day). `email` is required: it is how the owner reaches the person. Optional: `nome`, `pagina` (the page the request came from, up to 2,000 characters) and what the person was buying when the 402 came — `recurso` (up to 2,000), `descricao` (up to 300) and `preco_usd` (decimal, e.g. `0.50`); and `navegador_id` (a UUID that links the requests from the same browser). Everything that arrives is recorded with the request — the receipt included —, with the signed-in account (checked by the session cookie), the network and browser.",
      "grupo": "Credit",
      "retorno": {
        "id": {
          "tipo": "string",
          "desc": "Request id, to follow it."
        },
        "estado": {
          "tipo": "string",
          "desc": "`pendente` until the decision."
        },
        "credito": {
          "tipo": "string",
          "desc": "The `cred_…` token, shown ONCE; it works after the release."
        },
        "estado_em": {
          "tipo": "string",
          "desc": "Where to follow the request."
        }
      },
      "erros": {
        "400": "Pack outside the list (3, 5, 10 or 25), no receipt or no valid e-mail.",
        "413": "Receipt above 2 MB.",
        "415": "Receipt that is neither a photo (JPEG, PNG, WebP) nor a PDF.",
        "429": "This network already sent today's requests.",
        "502": "The e-mail to the owner did not go out: the request is recorded as `falhou`; send it again.",
        "503": "Review queue full, or Pix not available in this app."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/credito/pix -F usd=5 -F email=you@company.com -F comprovante=@pix.pdf",
      "returns": "{ id, estado, credito, estado_em }",
      "url": "https://hookpulse.net/api/credito/pix",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/credito/pix/:id",
      "auth": "none",
      "summary": "Status of a Pix credit request: `pendente`, `liberado`, `recusado` or `falhou`.",
      "grupo": "Credit",
      "params": {
        "id": {
          "desc": "Request id (32 hex).",
          "exemplo": "0123456789abcdef0123456789abcdef"
        }
      },
      "retorno": {
        "estado": {
          "tipo": "string",
          "desc": "`pendente`, `liberado`, `recusado` or `falhou` (the e-mail to the owner did not go out; send again)."
        },
        "usd": {
          "tipo": "int",
          "desc": "The requested pack."
        },
        "decidido_em": {
          "tipo": "string",
          "desc": "When the owner decided, or `null`."
        }
      },
      "erros": {
        "404": "Unknown request."
      },
      "exemplo": "curl -s $ORIGIN/api/credito/pix/0123456789abcdef0123456789abcdef",
      "returns": "{ estado, usd, decidido_em }",
      "url": "https://hookpulse.net/api/credito/pix/:id",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/credito/asaas/:id/cancelar",
      "auth": "none",
      "grupo": "Crédito",
      "summary": "Cancels a pending charge when the payment dialog closes.",
      "desc": "Requires the `cancelamento` capability returned at creation. An older credit charge accepts its token in `credito`. The product and environment must match. This does not refund payments or remove records. 202 means cancellation is pending; the scheduled worker retries provider failures.",
      "params": {
        "id": {
          "desc": "Charge id (32 hex).",
          "exemplo": "0123456789abcdef0123456789abcdef"
        }
      },
      "retorno": {
        "id": {
          "tipo": "string",
          "desc": "Charge id."
        },
        "estado": {
          "tipo": "string",
          "desc": "`cancelando`, `vencido`, or the confirmed final state."
        }
      },
      "erros": {
        "404": "Invalid charge or capability, or a different product or environment."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/credito/asaas/0123456789abcdef0123456789abcdef/cancelar -H 'content-type: application/json' -d '{\"cancelamento\":\"<bilhete>\"}'",
      "returns": "{ id, estado }",
      "url": "https://hookpulse.net/api/credito/asaas/:id/cancelar",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/credito/asaas",
      "auth": "none",
      "summary": "Automatic Pix: whether it is available in this app, the packs in reais and the balance the product sells.",
      "grupo": "Crédito",
      "retorno": {
        "disponivel": {
          "tipo": "boolean",
          "desc": "`false` when the app has no Asaas configured (use the Pix with receipt)."
        },
        "pacotes": {
          "tipo": "object[]",
          "desc": "The credit packs (3, 5, 10 or 25 dollars), with `brl_centavos`."
        },
        "exatos": {
          "tipo": "object[]",
          "desc": "Prices declared by the app, with `usd` and `brl_centavos`. Amounts start at R$ 5.00 and cannot exceed R$ 10,000."
        },
        "saldo": {
          "tipo": "object",
          "desc": "When the product sells a balance in reais: the range and whether the account can buy."
        },
        "precisa_documento": {
          "tipo": "boolean",
          "desc": "Whether the payer's CPF or CNPJ is still required."
        }
      },
      "exemplo": "curl -s $ORIGIN/api/credito/asaas",
      "returns": "{ disponivel, pacotes, exatos, saldo, precisa_documento }",
      "url": "https://hookpulse.net/api/credito/asaas",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/credito/asaas",
      "auth": "none",
      "summary": "Creates a dynamic Pix: JSON with `oferta` (`pacote` with `usd`, or `saldo` with `centavos`) and the payer.",
      "desc": "The payment releases what was bought by itself: the pack becomes the `cred_…` token already in the response; the balance goes to the logged-in account's organization. The first purchase needs `documento` (CPF or CNPJ, sent to the gateway and not stored here) and `nome`; `email`, `pagina` and `navegador_id` are optional. With `forma: \"cartao_boleto\"` the charge is open: the response brings `pagamento_em`, the gateway page where you pay by card, boleto or Pix (due in three days). With `forma: \"cartao\"` and `cartao` (`titular`, `numero`, `mes`, `ano`, `cvv`, `documento`, `email`, `cep`, `numero_endereco`, `telefone`), the charge is paid by card in the same request: 200 with `estado: \"pago\"`, 202 with `conferir` while the issuer reviews it, 422 when the card is declined; the card is not stored. Set `usd` to an amount from `pacotes` or `exatos` in the GET response. The server sets the amount in cents and rejects amounts that the app has not declared.",
      "grupo": "Crédito",
      "retorno": {
        "id": {
          "tipo": "string",
          "desc": "Charge id, to follow up."
        },
        "credito": {
          "tipo": "string",
          "desc": "For a pack: the `cred_…` token, shown ONCE; valid when the payment arrives."
        },
        "forma": {
          "tipo": "string",
          "desc": "`pix` (the default), `cartao` or `cartao_boleto`."
        },
        "pix": {
          "tipo": "object",
          "desc": "`copia_e_cola`, `imagem` (PNG QR, data URI) and `expira_em`; `pix` form only."
        },
        "pagamento_em": {
          "tipo": "string",
          "desc": "In the `cartao_boleto` form: the gateway payment page."
        },
        "estado": {
          "tipo": "string",
          "desc": "In the `cartao` form: `pago`, or `pendente` with `conferir`."
        },
        "estado_em": {
          "tipo": "string",
          "desc": "Where to follow the payment."
        },
        "cancelamento": {
          "tipo": "string",
          "desc": "Capability to cancel a pending Pix or boleto charge."
        }
      },
      "erros": {
        "400": "Credit amount outside `pacotes` and `exatos`, amount out of range, invalid CPF/CNPJ, name or e-mail, invalid card data (`cartao_dados`, with the `campo`), or the gateway refused the payer (`pagador_recusado`, with the reason in `detalhe`).",
        "422": "Card declined (`cartao_recusado`, with the `campo` when the gateway says which); try another card with `POST /api/credito/asaas/:id/cartao`.",
        "429": "Limit of 3 attempts per minute or 10 per day, per account or network, or the card limit.",
        "502": "The gateway did not create the Pix; try again.",
        "503": "Automatic Pix unavailable in this app, the gateway is busy (`asaas_ocupado`, wait `retry_after_s`), or too many charges from people not signed in waiting for payment."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/credito/asaas -H 'content-type: application/json' -d '{\"oferta\":\"pacote\",\"usd\":5,\"documento\":\"000.000.000-00\",\"nome\":\"Ana\"}'",
      "returns": "{ id, credito, forma, pix, pagamento_em, estado, estado_em, cancelamento }",
      "url": "https://hookpulse.net/api/credito/asaas",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/credito/asaas/:id",
      "auth": "none",
      "summary": "State of an automatic Pix charge: `pendente`, `pago`, `vencido`, `estornado` or `falhou`.",
      "grupo": "Crédito",
      "params": {
        "id": {
          "desc": "Charge id (32 hex).",
          "exemplo": "0123456789abcdef0123456789abcdef"
        }
      },
      "retorno": {
        "estado": {
          "tipo": "string",
          "desc": "`pago` releases the pack token or the balance; a Pix paid after the due date counts."
        },
        "pago_em": {
          "tipo": "string",
          "desc": "When the payment was confirmed, or `null`."
        }
      },
      "erros": {
        "404": "Unknown charge."
      },
      "exemplo": "curl -s $ORIGIN/api/credito/asaas/0123456789abcdef0123456789abcdef",
      "returns": "{ estado, pago_em }",
      "url": "https://hookpulse.net/api/credito/asaas/:id",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/credito/asaas/:id/cartao",
      "auth": "none",
      "summary": "Pays a card charge whose card was declined with another card: the same charge, never charged twice.",
      "desc": "JSON with the `cartao` (the same fields as `POST /api/credito/asaas`) and, without an account, the `navegador_id` that created the charge. Only a card charge waiting for payment, by whoever created it, and up to five attempts per charge.",
      "grupo": "Crédito",
      "params": {
        "id": {
          "desc": "Id da cobrança (32 hex).",
          "exemplo": "0123456789abcdef0123456789abcdef"
        }
      },
      "retorno": {
        "estado": {
          "tipo": "string",
          "desc": "`pago`, or `pendente` with `conferir` while the issuer reviews it."
        },
        "estado_em": {
          "tipo": "string",
          "desc": "Where to follow the payment."
        }
      },
      "erros": {
        "400": "Invalid card data (`cartao_dados`, with the `campo`).",
        "404": "Unknown charge, someone else's charge, or not a card charge.",
        "409": "The charge expired or was closed; start the payment again.",
        "422": "Card declined (`cartao_recusado`).",
        "429": "Used up the card attempts for this charge or for today."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/credito/asaas/0123456789abcdef0123456789abcdef/cartao -H 'content-type: application/json' -d '{\"cartao\":{…}}'",
      "returns": "{ estado, estado_em }",
      "url": "https://hookpulse.net/api/credito/asaas/:id/cartao",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/credito/asaas/saude",
      "auth": "none",
      "summary": "Health of this app's Pix and enabled subscriptions: failures and reasons for the dashboard.",
      "grupo": "Crédito",
      "retorno": {
        "ativo": {
          "tipo": "boolean",
          "desc": "`false` when Asaas and subscriptions are disabled."
        },
        "tarefas": {
          "tipo": "object[]",
          "desc": "Each problem has `id`, `falhando`, and `motivo`. Includes Pix and, when enabled, subscription recovery, queue, and budget. Does not call the provider."
        }
      },
      "exemplo": "curl -s $ORIGIN/api/credito/asaas/saude",
      "returns": "{ ativo, tarefas }",
      "url": "https://hookpulse.net/api/credito/asaas/saude",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/pricing",
      "auth": "none",
      "grupo": "Discovery",
      "summary": "Current prices and free allowances.",
      "retorno": {
        "product": {
          "tipo": "string",
          "desc": "Product name."
        },
        "quota": {
          "tipo": "PaymentQuota",
          "desc": "Public allowances and current list prices; not personal usage."
        },
        "pricing": {
          "tipo": "string",
          "desc": "Absolute URL of the current price list."
        },
        "billing": {
          "tipo": "string",
          "desc": "Absolute URL of payment discovery or the existing billing summary."
        },
        "api_index": {
          "tipo": "string",
          "desc": "Absolute URL of the API catalog."
        }
      },
      "erros": {
        "405": "Use GET or HEAD."
      },
      "exemplo": "curl -s $ORIGIN/api/pricing",
      "returns": "{ product, quota{free,paid,how_to_pay,live,free_now?,charged_now?,trial?}, pricing, billing, api_index }",
      "url": "https://hookpulse.net/api/pricing",
      "auth_detail": "Public, no credential."
    }
  ],
  "quota": {
    "free": [
      {
        "o_que": "dead-man endpoints",
        "limite": "10 per owner",
        "janela": null
      },
      {
        "o_que": "minimum interval at no cost",
        "limite": "90s",
        "janela": null
      },
      {
        "o_que": "ingest (`/in/:id`)",
        "limite": "no charge per ping",
        "janela": null
      },
      {
        "o_que": "webhook alert (`alert_url`)",
        "limite": "no quota — it is a fetch of ours",
        "janela": null
      },
      {
        "o_que": "e-mail alert (`alert_to`)",
        "limite": "1 registration",
        "janela": null
      },
      {
        "o_que": "status dashboard by URL (JSON and RSS)",
        "limite": "no quota",
        "janela": null
      }
    ],
    "paid": [
      {
        "o_que": "Starter: 50 monitors in total, including 40 extra monitors with e-mail alerts and short intervals",
        "price": "$1.19 for 30 days, charged once, no automatic renewal; buy at https://hookpulse.net/api/plans/starter",
        "so_conta": true
      },
      {
        "o_que": "Team: 100 monitors in total, including 90 extra monitors with e-mail alerts and short intervals",
        "price": "$2.49 for 30 days, charged once, no automatic renewal; buy at https://hookpulse.net/api/plans/team",
        "so_conta": true
      },
      {
        "o_que": "Business: 500 monitors in total, including 490 extra monitors with e-mail alerts and short intervals",
        "price": "$12.39 for 30 days, charged once, no automatic renewal; buy at https://hookpulse.net/api/plans/business",
        "so_conta": true
      },
      {
        "o_que": "endpoint beyond the allowance",
        "price_usd": 0.1
      },
      {
        "o_que": "interval below the free minimum",
        "price_usd": 0.05
      },
      {
        "o_que": "e-mail alert registration beyond the 1st",
        "price_usd": 0.1
      },
      {
        "o_que": "GET /api/x402/check — Checks a URL once for an agent, paid per call: status, response time, keyword and TLS certificate, with no account and no monitor.",
        "price_usd": 0.01
      }
    ],
    "how_to_pay": "Read GET /api/billing before a paid operation. If a charge applies, the API returns HTTP 402 with accepts[]. Pay through x402 using USDC on Base, then repeat the same call with PAYMENT-SIGNATURE (x402 v2) or X-PAYMENT (v1); a standard x402 client such as @x402/fetch does it for you. For repeated use, compare GET /api/plans with the action quote. Plans include e-mail and short intervals for their extra-monitor coverage. Outside plan coverage, e-mail registration is charged once per additional monitor, even when the address is reused. Reuse active coverage before paying again.",
    "live": "https://hookpulse.net/api/billing",
    "trial": {
      "days": 7,
      "how": "Sign in at /conta/global for 7 days without usage charges on extra monitors and faster intervals, counted from the account's first use of HookPulse. E-mail alerts on additional monitors remain $0.10 each. Service limits still apply. After the trial, alerts come from the ten oldest monitors and from each extra monitor that was paid for; alerts from the other monitors need a plan (GET /api/plans), and the monitors keep checking. When the trial or a plan ends, the account gets one e-mail with the plan that covers all its monitors, and the alerts on hold go out right after the purchase. Without an account, alerts from the other monitors wait until you sign in. Each monitor says alerts_on_hold, and GET /api/endpoints says coverage."
    }
  },
  "mcp": {
    "endpoint": "https://hookpulse.net/mcp",
    "transport": "streamable-http",
    "tools": 30,
    "note": "Plugs straight into the MCP client; nothing to install. Tools = the operations below."
  },
  "mcp_tools": [
    "list_plans",
    "buy_plan",
    "cancel_plan",
    "api_index",
    "health",
    "create_guest",
    "list_endpoints",
    "create_endpoint",
    "create_site",
    "check_site",
    "list_sites",
    "import_crontab",
    "get_endpoint",
    "delete_endpoint",
    "list_events",
    "ping_ingest",
    "signal_start",
    "signal_fail",
    "signal_exit_code",
    "ingest_key",
    "ingest_key_rotate",
    "ping_slug",
    "signal_slug",
    "billing",
    "status_feed_url",
    "status_feed_rotate",
    "list_templates",
    "contact",
    "pricing",
    "get_api_x402_check"
  ],
  "quickstart": [
    "curl -s -XPOST https://hookpulse.net/api/endpoints -H \"content-type: application/json\" -d '{\"name\":\"demo\",\"interval_sec\":900}'",
    "Keep guest_token from the response private. Reuse it in X-Guest-Token for later owner operations.",
    "Set alert_to or alert_url to an authorized destination. A monitor without a channel does not deliver notices.",
    "curl -s https://hookpulse.net/api/billing"
  ]
}