
{
  "openapi": "3.1.0",
  "info": {
    "title": "HookPulse",
    "version": "314dba24",
    "description": "Dead-man switch for webhooks/cron. Index: GET /api/."
  },
  "servers": [
    {
      "url": "https://hookpulse.net"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Guest token (`POST /api/guest`) in `X-Guest-Token: hp_…` or `Authorization: Bearer hp_…`. A `sess_…` session also works."
      }
    },
    "schemas": {
      "Monitor": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Monitor ID; it is the `:id` of the ingest URL."
          },
          "name": {
            "type": "string",
            "description": "Name you gave it, to recognise it in the alert."
          },
          "interval_sec": {
            "type": "integer",
            "description": "Tolerated silence, in seconds. Past that, it is a miss. With `cron` set it is read-only and mirrors the schedule's window."
          },
          "cron": {
            "type": "string",
            "description": "Five-field cron expression, when the monitor runs on a schedule instead of an interval.",
            "nullable": true
          },
          "tz": {
            "type": "string",
            "description": "IANA time zone the `cron` is read in.",
            "nullable": true
          },
          "grace_sec": {
            "type": "integer",
            "description": "Tolerance after the scheduled time before it counts as late.",
            "nullable": true
          },
          "next_expected_at": {
            "type": "string",
            "description": "When the next scheduled run is due (UTC). Computed forward on creation, on patch and on every ping; late is this plus `grace_sec`.",
            "nullable": true
          },
          "alert_to": {
            "type": "string",
            "description": "E-mail alerted on a miss.",
            "nullable": true
          },
          "alert_url": {
            "type": "string",
            "description": "HTTPS URL that receives a POST on a miss (Slack, Discord, n8n).",
            "nullable": true
          },
          "last_event_at": {
            "type": "string",
            "description": "Last ping received (UTC); `null` while it never pinged.",
            "nullable": true
          },
          "last_status": {
            "type": "integer",
            "description": "HTTP status the last ping sent, when it did.",
            "nullable": true
          },
          "last_latency_ms": {
            "type": "integer",
            "description": "Latency reported in the last ping, in ms.",
            "nullable": true
          },
          "last_start_at": {
            "type": "string",
            "description": "When the current run said it started (`/in/:id/start`), while it has not finished.",
            "nullable": true
          },
          "last_run_id": {
            "type": "string",
            "description": "The `rid` of the run that is currently open, when the `start` carried one.",
            "nullable": true
          },
          "last_duration_ms": {
            "type": "integer",
            "description": "How long the last run took, measured between its `start` and its outcome.",
            "nullable": true
          },
          "duration": {
            "type": "object",
            "description": "Duration statistics over the last runs we measured: `samples`, `p50_ms` and `max_ms`. `p50_ms` is the low median, so it is always a duration that actually happened. Only on `GET /api/endpoints/:id` — the list does not pay for it. All null when nothing has been measured."
          },
          "max_duration_sec": {
            "type": "integer",
            "description": "Ceiling for an open run: past it, the next 5-minute check alerts with `reason: \"too_long\"`. `null` when it is off.",
            "nullable": true
          },
          "alert_pending": {
            "type": "string",
            "description": "An outcome waiting for the next 5-minute check to alert: `fail`, `recovery`, or `null` when there is nothing pending.",
            "nullable": true
          },
          "miss_count": {
            "type": "integer",
            "description": "How many times this monitor has gone silent."
          },
          "alerted_at": {
            "type": "string",
            "description": "When the last alert went out — it is what holds the repeat cap.",
            "nullable": true
          },
          "alert_repeat_sec": {
            "type": "integer",
            "description": "EFFECTIVE gap before the same incident alerts again: what you asked for, or your `interval_sec` when that is longer, or the 86400 default."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The monitor's tags, normalised and sorted. Empty when it has none."
          },
          "paused_until": {
            "type": "string",
            "description": "End of the maintenance window (UTC). While it is in the future the monitor is not checked and never alerts.",
            "nullable": true
          },
          "state": {
            "type": "string",
            "description": "The state in one word: `ok`, `late`, `waiting` (never pinged), `paused` or `inactive`. Read this instead of re-deriving precedence from the booleans."
          },
          "active": {
            "type": "boolean",
            "description": "Whether the monitor is on."
          },
          "healthy": {
            "type": "boolean",
            "description": "`true` when it has pinged at least once and is not overdue."
          },
          "overdue": {
            "type": "boolean",
            "description": "`true` when the silence passed `interval_sec`."
          },
          "waiting_first_ping": {
            "type": "boolean",
            "description": "`true` while it never pinged. Neither healthy nor overdue: nobody has wired it yet."
          },
          "created_at": {
            "type": "string",
            "description": "When the monitor was created (UTC)."
          },
          "ingest_url": {
            "type": "string",
            "description": "The URL your cron/webhook calls to prove life."
          },
          "token": {
            "type": "string",
            "description": "Read token of this monitor. Only comes on creation and to the owner."
          },
          "status_url": {
            "type": "string",
            "description": "Status of this monitor with the token already in the query."
          },
          "events_url": {
            "type": "string",
            "description": "Latest pings with the token already in the query."
          },
          "curl_example": {
            "type": "string",
            "description": "The ingest `curl`, ready to paste in the cron."
          },
          "templates": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Templates"
              }
            ],
            "description": "Ingest snippets and the alert body, with this monitor already in them."
          }
        },
        "required": [
          "id",
          "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",
          "alerted_at",
          "alert_repeat_sec",
          "tags",
          "paused_until",
          "state",
          "active",
          "healthy",
          "overdue",
          "waiting_first_ping",
          "created_at",
          "ingest_url"
        ],
        "description": "A dead-man switch: the thing you make ping. If the ping stops for longer than `interval_sec` — or, with a schedule, past `next_expected_at` plus `grace_sec` — it becomes `overdue` and the alert goes out."
      },
      "Templates": {
        "type": "object",
        "properties": {
          "ingest_curl": {
            "type": "string",
            "description": "A `curl` that works as proof of life."
          },
          "ingest_cron": {
            "type": "string",
            "description": "The equivalent crontab line."
          },
          "ingest_n8n": {
            "type": "string",
            "description": "How to call the ingest from n8n."
          },
          "miss_json": {
            "type": "string",
            "description": "The exact JSON we POST to `alert_url` when a monitor goes bad. `reason` says which way: `silence` (it stopped pinging) or `fail` (it reported an explicit failure)."
          },
          "recovery_json": {
            "type": "string",
            "description": "The JSON we POST when the monitor comes back, with `down_since`, `recovered_at` and `downtime_sec`. It has its own `type` (`hookpulse.recovery`) so an integration filtering by type never shows good news as an incident."
          },
          "miss_url_hint": {
            "type": "string",
            "description": "What works as `alert_url` — public HTTPS only."
          }
        },
        "required": [
          "ingest_curl",
          "ingest_cron",
          "ingest_n8n",
          "miss_json",
          "recovery_json",
          "miss_url_hint"
        ],
        "description": "How to ping, what we send when it fails and what we send when it comes back. It is what saves guessing the format."
      },
      "Billing": {
        "type": "object",
        "properties": {
          "provider": {
            "type": "string",
            "description": "Always `x402` — the only billing protocol accepted."
          },
          "mode": {
            "type": "string",
            "description": "Seller mode: `live` charges for real, `dev` lets calls through unpaid."
          },
          "network": {
            "type": "string",
            "description": "USDC network: `base` in production, `base-sepolia` in staging."
          },
          "chain_id": {
            "type": "integer",
            "description": "EVM chain ID of the network above, so the wallet signs on the right chain."
          },
          "pay_to": {
            "type": "string",
            "description": "Address that receives the payment.",
            "nullable": true
          },
          "homolog": {
            "type": "boolean",
            "description": "Staging seam on: the loop can be closed without spending USDC."
          },
          "dev": {
            "type": "boolean",
            "description": "Development mode: the 402 is simulated."
          },
          "dev_gate": {
            "type": "boolean",
            "description": "A homologation credential is configured; this grants no access."
          },
          "gratis": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Temporarily free SKUs."
          },
          "facilitator": {
            "type": "string",
            "description": "URL of the facilitator that verifies and settles the payment."
          },
          "asset": {
            "type": "string",
            "description": "Accepted currency — always `USDC`."
          },
          "asset_address": {
            "type": "string",
            "description": "USDC contract on the network above."
          },
          "faucet": {
            "type": "string",
            "description": "Test-USDC faucet; only on base-sepolia.",
            "nullable": true
          },
          "wallets": {
            "type": "object",
            "description": "Links to wallets that speak x402 (metamask, coinbase, base_app)."
          },
          "product": {
            "type": "string",
            "description": "Name of the product charging."
          },
          "free_max_endpoints": {
            "type": "integer",
            "description": "Free monitors per owner."
          },
          "free_min_interval_sec": {
            "type": "integer",
            "description": "Shortest interval that is still free. Below it, it costs."
          },
          "free_email_alerts": {
            "type": "integer",
            "description": "Free e-mail alert registrations; the rest is paid (it is SES cost per miss)."
          },
          "prices": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Precos"
              }
            ],
            "description": "What each paid action costs, in USD."
          },
          "usage": {
            "type": "object",
            "description": "How much of the allowance the owner has used."
          },
          "trial": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Trial"
              }
            ],
            "description": "The account's trial, when there is a session."
          }
        },
        "required": [
          "provider",
          "mode",
          "network",
          "chain_id",
          "pay_to",
          "homolog",
          "dev",
          "dev_gate",
          "facilitator",
          "asset",
          "asset_address",
          "faucet",
          "wallets",
          "product",
          "free_max_endpoints",
          "free_min_interval_sec",
          "free_email_alerts",
          "prices",
          "usage",
          "trial"
        ],
        "description": "Everything that decides whether the next call will cost: x402 configuration, allowance, prices and trial."
      },
      "Precos": {
        "type": "object",
        "properties": {
          "extra_endpoint_usd": {
            "type": "number",
            "description": "Monitor beyond the allowance."
          },
          "fast_interval_usd": {
            "type": "number",
            "description": "Interval below the free minimum."
          },
          "email_alert_usd": {
            "type": "number",
            "description": "E-mail alert registration beyond the first."
          },
          "contact_agent_usd": {
            "type": "number",
            "description": "Agent contact."
          }
        },
        "required": [
          "extra_endpoint_usd",
          "fast_interval_usd",
          "email_alert_usd",
          "contact_agent_usd"
        ],
        "description": "Prices in force, in dollars. Read them here, not from the documentation."
      },
      "Trial": {
        "type": "object",
        "properties": {
          "days": {
            "type": "integer",
            "description": "Trial length in days."
          },
          "active": {
            "type": "boolean",
            "description": "Whether it is in force now."
          },
          "days_left": {
            "type": "integer",
            "description": "How many days remain."
          },
          "ends_at": {
            "type": "string",
            "description": "When it ends (UTC).",
            "nullable": true
          },
          "granted": {
            "type": "boolean",
            "description": "`true` when THIS call granted the trial."
          }
        },
        "required": [
          "days",
          "active",
          "ends_at"
        ],
        "description": "The period without the usage paywall that confirming the e-mail grants. It is the alternative to paying."
      },
      "Ping": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "description": "`ping` (proof of life), `start` or `fail`."
          },
          "rid": {
            "type": "string",
            "description": "The `rid` this signal carried, when it had one — it is what pairs a start with the check-in that closed it.",
            "nullable": true
          },
          "duration_ms": {
            "type": "integer",
            "description": "How long the run this signal CLOSED took. Only a close measures it: a start has nothing to measure yet, and closing a run we were not tracking would measure against the wrong start.",
            "nullable": true
          },
          "exit_code": {
            "type": "integer",
            "description": "The exit status the caller reported in `/in/:id/<0-255>`, when it did.",
            "nullable": true
          },
          "at": {
            "type": "string",
            "description": "When it arrived (UTC)."
          },
          "status": {
            "type": "integer",
            "description": "Status reported by the caller, when it did.",
            "nullable": true
          },
          "latency_ms": {
            "type": "integer",
            "description": "Latency reported by the caller, in ms.",
            "nullable": true
          },
          "source": {
            "type": "string",
            "description": "Where it came from: `get` or `post`.",
            "nullable": true
          }
        },
        "required": [
          "kind",
          "rid",
          "duration_ms",
          "exit_code",
          "at",
          "status",
          "latency_ms",
          "source"
        ],
        "description": "A ping received at the ingest — the proof of life, or the signal that a run started or failed."
      },
      "Ok": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "Always `true` — failure comes as a 4xx/5xx status, not as `ok:false`."
          }
        },
        "required": [
          "ok"
        ],
        "description": "Write confirmation with no body of its own to return."
      },
      "Feed": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "Feed token. Read-only: it does not create, change or delete."
          },
          "rotated": {
            "type": "boolean",
            "description": "`true` when this call rotated the token and invalidated the previous URL."
          },
          "json": {
            "type": "string",
            "description": "URL of the dashboard as JSON."
          },
          "rss": {
            "type": "string",
            "description": "URL of the same dashboard as RSS 2.0."
          },
          "note": {
            "type": "string",
            "description": "The warning that whoever has the link reads the dashboard."
          },
          "api_index": {
            "type": "string",
            "description": "Self-describing API index."
          }
        },
        "required": [
          "token",
          "rotated",
          "json",
          "rss",
          "note",
          "api_index"
        ],
        "description": "The URLs of the owner's public dashboard, with the token just minted or rotated."
      },
      "FeedStatus": {
        "type": "object",
        "properties": {
          "generated_at": {
            "type": "string",
            "description": "When this dashboard was built (UTC)."
          },
          "summary": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ResumoStatus"
              }
            ],
            "description": "The number you look at first."
          },
          "endpoints": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LinhaStatus"
            },
            "description": "One row per monitor, with no secret at all."
          },
          "_links": {
            "type": "object",
            "description": "This dashboard as JSON, as RSS and the API index."
          }
        },
        "required": [
          "generated_at",
          "summary",
          "endpoints",
          "_links"
        ],
        "description": "The owner's whole status dashboard, addressable by URL. The token in the path IS the credential — whoever has the link, reads."
      },
      "ResumoStatus": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer",
            "description": "Monitors on the dashboard."
          },
          "ok": {
            "type": "integer",
            "description": "Active and healthy."
          },
          "overdue": {
            "type": "integer",
            "description": "Active and silent beyond the interval."
          },
          "waiting_first_ping": {
            "type": "integer",
            "description": "Active ones that never pinged."
          },
          "inactive": {
            "type": "integer",
            "description": "Deactivated."
          },
          "all_ok": {
            "type": "boolean",
            "description": "`true` only with at least one monitor, none overdue and none waiting for the first ping — one that never pinged does not count as healthy."
          }
        },
        "required": [
          "total",
          "ok",
          "overdue",
          "waiting_first_ping",
          "inactive",
          "all_ok"
        ],
        "description": "The set in numbers. `all_ok` is the field for an external monitor to check without understanding the rest."
      },
      "LinhaStatus": {
        "type": "object",
        "properties": {
          "badge": {
            "type": "string",
            "description": "URL of this monitor's SVG badge, ready to paste into a README. It carries the published id, never the ingest one."
          },
          "id": {
            "type": "string",
            "description": "Stable label of THIS dashboard, derived from the pair (feed token, monitor) — it is not the ingest id, and it changes when the owner rotates the token. The real id does not leave here because `GET /in/:id` asks for no credential: publishing it would give whoever only reads the power to reset the silence counter."
          },
          "name": {
            "type": "string",
            "description": "Name given by the owner."
          },
          "status": {
            "type": "string",
            "description": "State in one word."
          },
          "healthy": {
            "type": "boolean",
            "description": "Whether it is healthy now."
          },
          "overdue": {
            "type": "boolean",
            "description": "Whether it is silent beyond the interval."
          },
          "interval_sec": {
            "type": "integer",
            "description": "Tolerated silence, in seconds."
          },
          "last_event_at": {
            "type": "string",
            "description": "Last ping (UTC).",
            "nullable": true
          },
          "miss_count": {
            "type": "integer",
            "description": "How many misses have happened."
          },
          "alerts": {
            "type": "object",
            "description": "Which alert channels are on: `{ email, url }` — booleans, never the address."
          }
        },
        "required": [
          "badge",
          "id",
          "name",
          "status",
          "healthy",
          "overdue",
          "interval_sec",
          "last_event_at",
          "miss_count",
          "alerts"
        ],
        "description": "A monitor on the public dashboard: enough to know the state, with no token, no e-mail and no ingest id."
      },
      "Conta": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "ID of the account."
          },
          "email": {
            "type": "string",
            "description": "E-mail confirmed by code."
          }
        },
        "required": [
          "id",
          "email"
        ],
        "description": "The person behind the session."
      },
      "Metricas": {
        "type": "object",
        "properties": {
          "app": {
            "type": "string",
            "description": "Product name."
          },
          "today": {
            "type": "string",
            "description": "Reference day (UTC, YYYY-MM-DD)."
          },
          "today_visits": {
            "type": "integer",
            "description": "Visits counted today."
          },
          "today_contacts": {
            "type": "integer",
            "description": "Contact messages received today. Only with `METRICS_TOKEN`: contact does not leave without a token."
          },
          "days": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "One record per day of the window, with the counts of each metric."
          },
          "usage": {
            "type": "object",
            "description": "Usage per product resource — here, monitors."
          },
          "accounts": {
            "type": "object",
            "description": "Total guests and accounts."
          },
          "financeiro": {
            "type": "object",
            "description": "Aggregate of the day: `hoje_usd`, `hoje_count`, `rede`. Only with `METRICS_TOKEN`: money does not leave without a token; the full series is `payments`."
          },
          "payments": {
            "type": "object",
            "description": "Finance summary; only with METRICS_TOKEN."
          }
        },
        "required": [
          "app",
          "today",
          "today_visits",
          "days",
          "usage",
          "accounts"
        ],
        "description": "7-day panel. `payments` only shows with the operator token and only on Base mainnet."
      },
      "PaymentQuota": {
        "type": "object",
        "properties": {
          "free": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentFree"
            },
            "description": "Free allowances and their windows."
          },
          "paid": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentPrice"
            },
            "description": "List prices in USD. The operation's 402 is the payable quote."
          },
          "how_to_pay": {
            "type": "string",
            "description": "Payment instructions and availability restrictions."
          },
          "live": {
            "type": "string",
            "description": "Authoritative product quota endpoint.",
            "nullable": true
          },
          "free_now": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "SKUs temporarily free despite their list price."
          },
          "trial": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PaymentTrial"
              }
            ],
            "description": "Registration trial, when offered."
          }
        },
        "required": [
          "free",
          "paid",
          "how_to_pay",
          "live"
        ]
      },
      "PaymentFree": {
        "type": "object",
        "properties": {
          "o_que": {
            "type": "string",
            "description": "Operation or allowance."
          },
          "limite": {
            "type": "string",
            "description": "Allowance and eligibility."
          },
          "janela": {
            "type": "string",
            "description": "Reset window, when applicable.",
            "nullable": true
          }
        },
        "required": [
          "o_que",
          "limite",
          "janela"
        ]
      },
      "PaymentPrice": {
        "type": "object",
        "properties": {
          "o_que": {
            "type": "string",
            "description": "Operation and billing unit."
          },
          "price_usd": {
            "type": "number",
            "description": "Current list price in USD."
          }
        },
        "required": [
          "o_que",
          "price_usd"
        ]
      },
      "PaymentTrial": {
        "type": "object",
        "properties": {
          "days": {
            "type": "integer",
            "description": "Trial duration in days."
          },
          "how": {
            "type": "string",
            "description": "Eligibility and activation steps."
          }
        },
        "required": [
          "days",
          "how"
        ]
      }
    }
  },
  "paths": {
    "/agent.json": {
      "get": {
        "operationId": "get_agent_json",
        "summary": "Agent card: identity, operator, documentation, the MCP endpoint and the tools it serves. Same document as `/.well-known/agent-card.json`.",
        "description": "Returns: `application/json`: `name`, `provider`, `protocol` (`mcp`), `interfaces[]` and `skills[]`.",
        "security": [],
        "responses": {
          "200": {
            "description": "`application/json`: `name`, `provider`, `protocol` (`mcp`), `interfaces[]` and `skills[]`."
          }
        }
      }
    },
    "/okf/{arquivo}": {
      "get": {
        "operationId": "get_okf_by_arquivo",
        "summary": "OKF bundle (Open Knowledge Format v0.1): markdown with frontmatter so an agent reads the whole product without parsing HTML.",
        "description": "Returns: `text/markdown`. Start at `/okf/index.md`, which lists the bundle.",
        "security": [],
        "parameters": [
          {
            "name": "arquivo",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`text/markdown`. Start at `/okf/index.md`, which lists the bundle."
          },
          "404": {
            "description": "File outside the bundle."
          }
        }
      }
    },
    "/.well-known/{arquivo}": {
      "get": {
        "operationId": "get_well_known_by_arquivo",
        "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).",
        "description": "Returns: `application/linkset+json` for the api-catalog; `application/json` for x402 and apis.json; `text/plain` for the other two.",
        "security": [],
        "parameters": [
          {
            "name": "arquivo",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`application/linkset+json` for the api-catalog; `application/json` for x402 and apis.json; `text/plain` for the other two."
          },
          "404": {
            "description": "Name outside the five published."
          }
        }
      }
    },
    "/apis.json": {
      "get": {
        "operationId": "get_apis_json",
        "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`.",
        "description": "Returns: `application/json` in the APIs.json 0.19 format: `apis[]` with `baseURL`, `humanURL` and `properties[]`.",
        "security": [],
        "responses": {
          "200": {
            "description": "`application/json` in the APIs.json 0.19 format: `apis[]` with `baseURL`, `humanURL` and `properties[]`."
          }
        }
      }
    },
    "/api/": {
      "get": {
        "operationId": "api_index",
        "summary": "Self-describing index: the whole API surface, with quota and quickstart.",
        "description": "Returns: { name, description, build, base_url, docs, conventions, auth, endpoints, quota, mcp, mcp_tools, quickstart }",
        "security": [],
        "responses": {
          "200": {
            "description": "{ name, description, build, base_url, docs, conventions, auth, endpoints, quota, mcp, mcp_tools, quickstart }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "Product name."
                    },
                    "description": {
                      "type": "string",
                      "description": "What the product does, in one sentence."
                    },
                    "build": {
                      "type": "string",
                      "description": "Deployed commit."
                    },
                    "base_url": {
                      "type": "string",
                      "description": "Origin this API is serving from."
                    },
                    "docs": {
                      "type": "object",
                      "description": "Links to llms.txt, llms-full.txt, openapi.json, MCP and the UI."
                    },
                    "conventions": {
                      "type": "object",
                      "description": "Error format, CORS, x402 and the UI↔API parity rule."
                    },
                    "auth": {
                      "type": "object",
                      "description": "Each authentication mode and how to obtain it."
                    },
                    "endpoints": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Every endpoint with method, path, auth, absolute URL and what it returns."
                    },
                    "quota": {
                      "type": "object",
                      "description": "What is free, what costs and how to pay — before you spend a call."
                    },
                    "mcp": {
                      "type": "object",
                      "description": "Address and transport of the MCP server."
                    },
                    "mcp_tools": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Name of each MCP tool."
                    },
                    "quickstart": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "The calls that take you from zero to the first monitor."
                    }
                  },
                  "required": [
                    "name",
                    "description",
                    "build",
                    "base_url",
                    "docs",
                    "conventions",
                    "auth",
                    "endpoints",
                    "quota",
                    "mcp",
                    "mcp_tools",
                    "quickstart"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/health": {
      "get": {
        "operationId": "health",
        "summary": "Liveness and the commit deployed right now — it is how the smoke waits for its own deploy.",
        "description": "Returns: { ok, app, build, ts }",
        "security": [],
        "responses": {
          "200": {
            "description": "{ ok, app, build, ts }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always `true` when the Worker answers."
                    },
                    "app": {
                      "type": "string",
                      "description": "Product name."
                    },
                    "build": {
                      "type": "string",
                      "description": "Deployed commit; CI passes the short SHA on deploy."
                    },
                    "ts": {
                      "type": "string",
                      "description": "Time of the response (UTC, ISO-8601)."
                    }
                  },
                  "required": [
                    "ok",
                    "app",
                    "build",
                    "ts"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "operationId": "post_mcp",
        "summary": "MCP server over HTTP (Streamable HTTP, JSON-RPC 2.0) — plugs into the client with nothing to install.",
        "description": "The tools are the operations of this same catalog; the MCP has no backend of its own. `GET /mcp` returns the server card.\nReturns: JSON-RPC 2.0 response (`initialize`, `tools/list` or `tools/call`).\nCredentials go in the usual headers (X-Guest-Token, Authorization, X-PAYMENT) and are forwarded to the API.\nAn exhausted quota comes back as a 402 with accepts[] inside the tool result — pay and repeat.",
        "security": [],
        "responses": {
          "200": {
            "description": "JSON-RPC 2.0 response (`initialize`, `tools/list` or `tools/call`)."
          }
        }
      }
    },
    "/api/guest": {
      "post": {
        "operationId": "create_guest",
        "summary": "Creates a guest `hp_…` — it is the anonymous owner of your monitors.",
        "description": "Keep the token: without it there is no way back to the monitors, unless you have already tied them to an e-mail.\nReturns: { token }",
        "security": [],
        "responses": {
          "200": {
            "description": "{ token }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string",
                      "description": "The guest, prefix `hp_`. Send it in `X-Guest-Token` or as Bearer."
                    }
                  },
                  "required": [
                    "token"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/endpoints": {
      "get": {
        "operationId": "list_endpoints",
        "summary": "Lists the owner's monitors, with the state of each one.",
        "description": "Returns: { endpoints[{id,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,alerted_at,alert_repeat_sec,tags,paused_until,state,active,healthy,overdue,waiting_first_ping,created_at,ingest_url,token?,status_url?,events_url?,curl_example?,templates?}], tags?, guest, billing?{provider,mode,network,chain_id,pay_to,homolog,dev,dev_gate,gratis?,facilitator,asset,asset_address,faucet,wallets,product,free_max_endpoints,free_min_interval_sec,free_email_alerts,prices,usage,trial} }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "tag",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "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."
          }
        ],
        "responses": {
          "200": {
            "description": "{ endpoints[{id,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,alerted_at,alert_repeat_sec,tags,paused_until,state,active,healthy,overdue,waiting_first_ping,created_at,ingest_url,token?,status_url?,events_url?,curl_example?,templates?}], tags?, guest, billing?{provider,mode,network,chain_id,pay_to,homolog,dev,dev_gate,gratis?,facilitator,asset,asset_address,faucet,wallets,product,free_max_endpoints,free_min_interval_sec,free_email_alerts,prices,usage,trial} }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoints": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Monitor"
                      },
                      "description": "The owner's monitors, without the secret fields."
                    },
                    "tags": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "The tags you filtered by, echoed back. Absent when you did not filter."
                    },
                    "guest": {
                      "type": "string",
                      "description": "The guest that owns this list.",
                      "nullable": true
                    },
                    "billing": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Billing"
                        }
                      ],
                      "description": "Prices and allowance, to decide before creating the next one."
                    }
                  },
                  "required": [
                    "endpoints",
                    "guest"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No credential, or an invalid one. See this endpoint's auth."
          }
        }
      },
      "post": {
        "operationId": "create_endpoint",
        "summary": "Creates a dead-man switch: silence beyond the interval becomes an alert.",
        "description": "This response is the only one that shows the monitor's `token` and the `templates` — keep them. The second monitor, or an interval below the free minimum, answers **402 with `accepts[]`**: pay and repeat. A miss alerts at most once per 24h — 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.\nReturns: { id, 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, alerted_at, alert_repeat_sec, tags, paused_until, state, active, healthy, overdue, waiting_first_ping, created_at, ingest_url, token?, status_url?, events_url?, curl_example?, templates?{ingest_curl,ingest_cron,ingest_n8n,miss_json,recovery_json,miss_url_hint} }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Name to recognise the monitor in the alert — it travels in the e-mail subject, the badge and the public dashboard. Required: we never invent one for you. Trimmed, at most 80 characters, any alphabet; punctuation alone and invisible characters are rejected."
                  },
                  "interval_sec": {
                    "type": "integer",
                    "description": "Tolerated silence, in seconds. Below the free minimum, it costs."
                  },
                  "cron": {
                    "type": "string",
                    "description": "Five-field cron expression (`0 3 * * *`). Mutually exclusive with `interval_sec`: with a schedule, the deadline is the scheduled time plus `grace_sec`."
                  },
                  "tz": {
                    "type": "string",
                    "description": "IANA time zone the `cron` is read in. Default `UTC`. Only with `cron`."
                  },
                  "grace_sec": {
                    "type": "integer",
                    "description": "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": {
                    "type": "string",
                    "description": "Maintenance window: while this UTC instant is in the future the monitor is not checked. At most 30 days ahead."
                  },
                  "max_duration_sec": {
                    "type": "integer",
                    "description": "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": {
                    "type": "integer",
                    "description": "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": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Up to 8 tags to group this monitor: lower-case letters, digits, `.`, `_` and `-`, at most 32 characters each. A comma-separated string works too. We lower-case and de-duplicate, so `Prod` and `prod` are the same tag."
                  },
                  "alert_to": {
                    "type": "string",
                    "description": "E-mail to alert on a miss; without it, the account is alerted."
                  },
                  "alert_url": {
                    "type": "string",
                    "description": "Public HTTPS URL that receives a POST on a miss (Slack Incoming, Discord, n8n)."
                  }
                },
                "required": [
                  "name"
                ]
              },
              "example": {
                "name": "prod cron",
                "interval_sec": 900,
                "alert_to": "optional@email.com",
                "alert_url": "https://n8n.example/webhook/hp"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ id, 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, alerted_at, alert_repeat_sec, tags, paused_until, state, active, healthy, overdue, waiting_first_ping, created_at, ingest_url, token?, status_url?, events_url?, curl_example?, templates?{ingest_curl,ingest_cron,ingest_n8n,miss_json,recovery_json,miss_url_hint} }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Monitor"
                }
              }
            }
          },
          "400": {
            "description": "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)."
          },
          "401": {
            "description": "No credential, or an invalid one. See this endpoint's auth."
          },
          "402": {
            "description": "Quota exceeded. The response carries `accepts[]` (x402, USDC on Base): pay and repeat the same call with `X-PAYMENT`."
          }
        }
      }
    },
    "/api/import/crontab": {
      "post": {
        "operationId": "import_crontab",
        "summary": "Paste a `crontab -l` and get one monitor per scheduled line.",
        "description": "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.\nReturns: { applied, recognized, ignored, existing, would_create, created?, free_tier }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "apply",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "`1` creates the monitors; anything else only proposes."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "crontab": {
                    "type": "string",
                    "description": "The crontab itself, sent as the raw `text/plain` body (not JSON) — the output of `crontab -l`."
                  }
                },
                "required": [
                  "crontab"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ applied, recognized, ignored, existing, would_create, created?, free_tier }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "applied": {
                      "type": "boolean",
                      "description": "`false` on a proposal, `true` when the monitors were created."
                    },
                    "recognized": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Line, cron expression, name and command of each schedule read."
                    },
                    "ignored": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Line, reason (`comentario`, `variavel`, `reboot`, `cron_invalido`, `nao_reconhecida`) and the text."
                    },
                    "existing": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Lines whose monitor you already have — they are not created again."
                    },
                    "would_create": {
                      "type": "integer",
                      "description": "How many monitors the import would create."
                    },
                    "created": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "With `apply=1`: id, name and ingest URL of each monitor created."
                    },
                    "free_tier": {
                      "type": "object",
                      "description": "Allowance and prices, to decide before applying."
                    }
                  },
                  "required": [
                    "applied",
                    "recognized",
                    "ignored",
                    "existing",
                    "would_create",
                    "free_tier"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`crontab_sem_agenda` — no schedule line was recognised."
          },
          "401": {
            "description": "No credential, or an invalid one. See this endpoint's auth."
          },
          "402": {
            "description": "Quota exceeded. The response carries `accepts[]` (x402, USDC on Base): pay and repeat the same call with `X-PAYMENT`."
          }
        }
      }
    },
    "/api/endpoints/{id}/events": {
      "get": {
        "operationId": "list_events",
        "summary": "The latest pings received at this monitor's ingest.",
        "description": "Returns: { events[{kind,rid,duration_ms,exit_code,at,status,latency_ms,source}] }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "token",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Monitor token, alternative to the `X-Hook-Token` header."
          }
        ],
        "responses": {
          "200": {
            "description": "{ events[{kind,rid,duration_ms,exit_code,at,status,latency_ms,source}] }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "events": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Ping"
                      },
                      "description": "The most recent pings, newest first."
                    }
                  },
                  "required": [
                    "events"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No credential, or an invalid one. See this endpoint's auth."
          },
          "404": {
            "description": "The resource does not exist (or is not yours — the API does not tell the two apart on purpose)."
          }
        }
      }
    },
    "/api/endpoints/{id}": {
      "get": {
        "operationId": "get_endpoint",
        "summary": "State of one monitor — accepts the owner's token or the monitor's own token.",
        "description": "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.\nReturns: { id, 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, alerted_at, alert_repeat_sec, tags, paused_until, state, active, healthy, overdue, waiting_first_ping, created_at, ingest_url, token?, status_url?, events_url?, curl_example?, templates?{ingest_curl,ingest_cron,ingest_n8n,miss_json,recovery_json,miss_url_hint} }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "token",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Monitor token, alternative to the `X-Hook-Token` header."
          }
        ],
        "responses": {
          "200": {
            "description": "{ id, 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, alerted_at, alert_repeat_sec, tags, paused_until, state, active, healthy, overdue, waiting_first_ping, created_at, ingest_url, token?, status_url?, events_url?, curl_example?, templates?{ingest_curl,ingest_cron,ingest_n8n,miss_json,recovery_json,miss_url_hint} }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Monitor"
                }
              }
            }
          },
          "401": {
            "description": "No credential, or an invalid one. See this endpoint's auth."
          },
          "404": {
            "description": "The resource does not exist (or is not yours — the API does not tell the two apart on purpose)."
          }
        }
      },
      "patch": {
        "operationId": "patch_api_endpoints_by_id",
        "summary": "Changes the monitor's name, interval or alert channels.",
        "description": "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.\nReturns: { id, 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, alerted_at, alert_repeat_sec, tags, paused_until, state, active, healthy, overdue, waiting_first_ping, created_at, ingest_url, token?, status_url?, events_url?, curl_example?, templates?{ingest_curl,ingest_cron,ingest_n8n,miss_json,recovery_json,miss_url_hint} }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "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": {
                    "type": "integer",
                    "description": "New tolerated silence, in seconds. Rejected while a `cron` is set."
                  },
                  "cron": {
                    "type": "string",
                    "description": "New cron expression; `null` drops the schedule and puts the monitor back on `interval_sec`."
                  },
                  "tz": {
                    "type": "string",
                    "description": "New IANA time zone; `null` goes back to `UTC`."
                  },
                  "grace_sec": {
                    "type": "integer",
                    "description": "New tolerance in seconds; `null` goes back to 90."
                  },
                  "paused_until": {
                    "type": "string",
                    "description": "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."
                  },
                  "max_duration_sec": {
                    "type": "integer",
                    "description": "New ceiling for an open run, 60 to 86400 seconds; `null` or `0` turns it off."
                  },
                  "alert_repeat_sec": {
                    "type": "integer",
                    "description": "New gap between repeats of the same incident, 3600 to 2592000 seconds; `null` goes back to 86400."
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "New tags, replacing the old ones. `null` or `[]` removes them all."
                  },
                  "alert_to": {
                    "type": "string",
                    "description": "New alert e-mail; `null` turns it off."
                  },
                  "alert_url": {
                    "type": "string",
                    "description": "New alert URL; `null` turns it off."
                  }
                }
              },
              "example": {
                "name": "…",
                "interval_sec": 300,
                "alert_to": null,
                "alert_url": null
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ id, 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, alerted_at, alert_repeat_sec, tags, paused_until, state, active, healthy, overdue, waiting_first_ping, created_at, ingest_url, token?, status_url?, events_url?, curl_example?, templates?{ingest_curl,ingest_cron,ingest_n8n,miss_json,recovery_json,miss_url_hint} }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Monitor"
                }
              }
            }
          },
          "400": {
            "description": "Invalid field in the body, with the same codes as the creation — `nome_obrigatorio` and `nome_invalido` included."
          },
          "401": {
            "description": "No credential, or an invalid one. See this endpoint's auth."
          },
          "402": {
            "description": "Quota exceeded. The response carries `accepts[]` (x402, USDC on Base): pay and repeat the same call with `X-PAYMENT`."
          },
          "404": {
            "description": "The resource does not exist (or is not yours — the API does not tell the two apart on purpose)."
          }
        }
      },
      "delete": {
        "operationId": "delete_endpoint",
        "summary": "Deactivates the owner's monitor; it stops taking pings and alerting.",
        "description": "Returns: { ok }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "description": "No credential, or an invalid one. See this endpoint's auth."
          },
          "404": {
            "description": "The resource does not exist (or is not yours — the API does not tell the two apart on purpose)."
          }
        }
      }
    },
    "/api/ingest-key": {
      "get": {
        "operationId": "ingest_key",
        "summary": "Returns (minting it the first time) the key that lets a ping create its own monitor.",
        "description": "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.\nReturns: { key, rotated, exemplo, note }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "{ key, rotated, exemplo, note }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "key": {
                      "type": "string",
                      "description": "The ingest key, prefix `hpk_`."
                    },
                    "rotated": {
                      "type": "boolean",
                      "description": "`true` when this response came from a rotation."
                    },
                    "exemplo": {
                      "type": "string",
                      "description": "A ready URL: `<origin>/in/<key>/nightly-backup`."
                    },
                    "note": {
                      "type": "string",
                      "description": "What the slug means and what rotating does."
                    }
                  },
                  "required": [
                    "key",
                    "rotated",
                    "exemplo",
                    "note"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No credential, or an invalid one. See this endpoint's auth."
          }
        }
      },
      "delete": {
        "operationId": "ingest_key_rotate",
        "summary": "Rotates the ingest key: the previous one stops creating monitors immediately.",
        "description": "Monitors already created keep working — rotating only revokes the power to create new ones.\nReturns: { key, rotated, exemplo, note }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "{ key, rotated, exemplo, note }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "key": {
                      "type": "string",
                      "description": "The new key."
                    },
                    "rotated": {
                      "type": "boolean",
                      "description": "Always `true` here."
                    },
                    "exemplo": {
                      "type": "string",
                      "description": "A ready URL with the new key."
                    },
                    "note": {
                      "type": "string",
                      "description": "What the slug means and what rotating does."
                    }
                  },
                  "required": [
                    "key",
                    "rotated",
                    "exemplo",
                    "note"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No credential, or an invalid one. See this endpoint's auth."
          }
        }
      }
    },
    "/in/{ingestKey}/{slug}": {
      "get": {
        "operationId": "ping_slug",
        "summary": "Ping that CREATES its own monitor on the first hit — a whole fleet with no setup.",
        "description": "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.\nReturns: { ok, endpoint_id, created }",
        "security": [],
        "parameters": [
          {
            "name": "ingestKey",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "interval",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Tolerated silence of the monitor being created, in seconds."
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, endpoint_id, created }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always `true` when the ping was recorded."
                    },
                    "endpoint_id": {
                      "type": "string",
                      "description": "The monitor's id — new or the one that already existed."
                    },
                    "created": {
                      "type": "boolean",
                      "description": "`true` only on the hit that created it."
                    }
                  },
                  "required": [
                    "ok",
                    "endpoint_id",
                    "created"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`slug_invalido` — nothing usable left after normalising."
          },
          "402": {
            "description": "The free tier is full: pay and repeat, or delete a monitor."
          },
          "404": {
            "description": "Unknown or rotated ingest key."
          }
        }
      }
    },
    "/in/{id}": {
      "get": {
        "operationId": "ping_ingest",
        "summary": "The ingest URL: this is where your cron or webhook proves it is alive.",
        "description": "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.\nReturns: { ok }",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always `true` when the ping was recorded."
                    }
                  },
                  "required": [
                    "ok"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The monitor does not exist or is deactivated."
          }
        }
      },
      "post": {
        "operationId": "post_in_by_id",
        "summary": "The same ingest by POST, for whoever can only send webhooks.",
        "description": "The body is optional and records `status` and `latency_ms` along with the ping.\nReturns: { ok }",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "integer",
                    "description": "Status of the job that is pinging, to show in `Ping.status`."
                  },
                  "latency_ms": {
                    "type": "integer",
                    "description": "How long the job took, in ms."
                  }
                }
              },
              "example": {
                "status": 200,
                "latency_ms": 431
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always `true` when the ping was recorded."
                    }
                  },
                  "required": [
                    "ok"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The monitor does not exist or is deactivated."
          }
        }
      }
    },
    "/in/{id}/start": {
      "get": {
        "operationId": "signal_start",
        "summary": "Says the routine STARTED — it is what turns silence into duration.",
        "description": "`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.\nReturns: { ok, kind }",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "rid",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "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."
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, kind }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always `true` when the signal was recorded."
                    },
                    "kind": {
                      "type": "string",
                      "description": "The signal recorded: `start`."
                    }
                  },
                  "required": [
                    "ok",
                    "kind"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The monitor does not exist or is deactivated."
          }
        }
      }
    },
    "/in/{id}/fail": {
      "get": {
        "operationId": "signal_fail",
        "summary": "Says the routine FAILED — the alert leaves on the next 5-minute check.",
        "description": "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.\nReturns: { ok, kind }",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "rid",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "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."
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, kind }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always `true` when the signal was recorded."
                    },
                    "kind": {
                      "type": "string",
                      "description": "The signal recorded: `fail`."
                    }
                  },
                  "required": [
                    "ok",
                    "kind"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The monitor does not exist or is deactivated."
          }
        }
      }
    },
    "/in/{id}/{exit_code}": {
      "get": {
        "operationId": "signal_exit_code",
        "summary": "The shell's exit status, straight from `$?`: 0 is success, anything else is a failure.",
        "description": "`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.\nReturns: { ok, kind }",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "exit_code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "rid",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "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."
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, kind }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always `true` when the signal was recorded."
                    },
                    "kind": {
                      "type": "string",
                      "description": "`ping` for 0, `fail` for anything else."
                    }
                  },
                  "required": [
                    "ok",
                    "kind"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`exit_code_fora_da_faixa` — the exit status must be 0 to 255."
          },
          "404": {
            "description": "The monitor does not exist or is deactivated."
          }
        }
      }
    },
    "/api/status-feed": {
      "get": {
        "operationId": "status_feed_url",
        "summary": "Returns (minting it the first time) the public status URL of all your monitors.",
        "description": "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.\nReturns: { token, rotated, json, rss, note, api_index }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "{ token, rotated, json, rss, note, api_index }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Feed"
                }
              }
            }
          },
          "401": {
            "description": "No credential, or an invalid one. See this endpoint's auth."
          }
        }
      },
      "delete": {
        "operationId": "status_feed_rotate",
        "summary": "Rotates the feed token: the previous URL stops working immediately.",
        "description": "Returns: { token, rotated, json, rss, note, api_index }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "{ token, rotated, json, rss, note, api_index }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Feed"
                }
              }
            }
          },
          "401": {
            "description": "No credential, or an invalid one. See this endpoint's auth."
          }
        }
      }
    },
    "/s/{token}": {
      "get": {
        "operationId": "get_s_by_token",
        "summary": "The status dashboard as a page — the link you send to the team, no header, no JS needed.",
        "description": "`/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.\nReturns: `text/html` — the dashboard with the summary, one row per monitor and its badge.",
        "security": [],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`text/html` — the dashboard with the summary, one row per monitor and its badge."
          },
          "404": {
            "description": "Unknown feed token."
          }
        }
      }
    },
    "/s/{token}.json": {
      "get": {
        "operationId": "get_s_by_token_json",
        "summary": "The status dashboard as JSON, with no header at all — the token in the path is the credential.",
        "description": "`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.\nReturns: { 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 }",
        "security": [],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ 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 }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeedStatus"
                }
              }
            }
          },
          "404": {
            "description": "Unknown feed token."
          }
        }
      }
    },
    "/s/{token}.rss": {
      "get": {
        "operationId": "get_s_by_token_rss",
        "summary": "The same dashboard as RSS 2.0, for a feed reader, a chat bot or a status page.",
        "description": "The `guid` of each item changes when the STATE changes, not on every generation — with a fixed guid the reader would never re-notify.\nReturns: `application/rss+xml` — one item per monitor, with the state in the title.",
        "security": [],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`application/rss+xml` — one item per monitor, with the state in the title."
          },
          "404": {
            "description": "Unknown feed token."
          }
        }
      }
    },
    "/s/{token}/metrics": {
      "get": {
        "operationId": "get_s_by_token_metrics",
        "summary": "The same dashboard as a Prometheus scrape target — point your existing alerting at it.",
        "description": "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.\nReturns: `text/plain; version=0.0.4` — the Prometheus exposition format.",
        "security": [],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`text/plain; version=0.0.4` — the Prometheus exposition format."
          },
          "404": {
            "description": "Unknown feed token."
          }
        }
      }
    },
    "/s/{token}/{monitor}.svg": {
      "get": {
        "operationId": "get_s_by_token_by_monitor_svg",
        "summary": "A badge of one monitor, as an SVG you paste straight into a README.",
        "description": "`: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`.\nReturns: `image/svg+xml` — a 20px-high badge with the monitor name and its state.",
        "security": [],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "monitor",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`image/svg+xml` — a 20px-high badge with the monitor name and its state."
          },
          "404": {
            "description": "Unknown feed token, or a monitor that does not belong to this feed."
          }
        }
      }
    },
    "/api/templates": {
      "get": {
        "operationId": "list_templates",
        "summary": "Ingest snippets (curl, cron, n8n) and the exact JSON we POST on a miss and on a recovery.",
        "description": "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.\nReturns: { origin, templates{ingest_curl,ingest_cron,ingest_n8n,miss_json,recovery_json,miss_url_hint} }",
        "security": [],
        "responses": {
          "200": {
            "description": "{ origin, templates{ingest_curl,ingest_cron,ingest_n8n,miss_json,recovery_json,miss_url_hint} }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "origin": {
                      "type": "string",
                      "description": "Origin used to build the snippets."
                    },
                    "templates": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Templates"
                        }
                      ],
                      "description": "The snippets and the alert body."
                    }
                  },
                  "required": [
                    "origin",
                    "templates"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/billing": {
      "get": {
        "operationId": "billing",
        "summary": "Free allowance, x402 prices in force and the state of the account's trial.",
        "description": "Check before creating the second monitor or lowering the interval: it is the number IN FORCE, not the documentation's.\nReturns: { provider, mode, network, chain_id, pay_to, homolog, dev, dev_gate, gratis?, 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,contact_agent_usd}, usage, trial{days,active,days_left?,ends_at,granted?} }",
        "security": [],
        "responses": {
          "200": {
            "description": "{ provider, mode, network, chain_id, pay_to, homolog, dev, dev_gate, gratis?, 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,contact_agent_usd}, usage, trial{days,active,days_left?,ends_at,granted?} }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Billing"
                }
              }
            }
          }
        }
      }
    },
    "/api/me": {
      "get": {
        "operationId": "get_api_me",
        "summary": "The session's account, its monitors and the state of the trial.",
        "description": "Returns: { user{id,email}, endpoints[{id,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,alerted_at,alert_repeat_sec,tags,paused_until,state,active,healthy,overdue,waiting_first_ping,created_at,ingest_url,token?,status_url?,events_url?,curl_example?,templates?}], trial{days,active,days_left?,ends_at,granted?} }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "{ user{id,email}, endpoints[{id,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,alerted_at,alert_repeat_sec,tags,paused_until,state,active,healthy,overdue,waiting_first_ping,created_at,ingest_url,token?,status_url?,events_url?,curl_example?,templates?}], trial{days,active,days_left?,ends_at,granted?} }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Conta"
                        }
                      ],
                      "description": "The person who owns the session."
                    },
                    "endpoints": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Monitor"
                      },
                      "description": "The account's monitors."
                    },
                    "trial": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Trial"
                        }
                      ],
                      "description": "The trial: how many days, whether active and when it ends."
                    }
                  },
                  "required": [
                    "user",
                    "endpoints",
                    "trial"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No credential, or an invalid one. See this endpoint's auth."
          }
        }
      }
    },
    "/api/auth/start": {
      "post": {
        "operationId": "post_api_auth_start",
        "summary": "Sends the 6-digit code by e-mail to create the account or sign in to it.",
        "description": "Returns: { ok }",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "description": "E-mail that will receive the code."
                  }
                },
                "required": [
                  "email"
                ]
              },
              "example": {
                "email": "you@example.com"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "description": "E-mail missing or malformed."
          },
          "429": {
            "description": "Too many requests for the same e-mail."
          }
        }
      }
    },
    "/api/auth/verify": {
      "post": {
        "operationId": "post_api_auth_verify",
        "summary": "Exchanges the code for a session — and confirming the e-mail grants the trial on the spot.",
        "description": "It is the free way out: confirming the e-mail is worth a period without the usage paywall (extra monitor and fast interval). E-mail alerts beyond the first stay paid, because they are SES cost per miss.\nReturns: { ok, token, user{id,email}, trial{days,active,days_left?,ends_at,granted?} }",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "description": "The same e-mail as in `/api/auth/start`."
                  },
                  "code": {
                    "type": "string",
                    "description": "The 6 digits that arrived by e-mail."
                  }
                },
                "required": [
                  "email",
                  "code"
                ]
              },
              "example": {
                "email": "you@example.com",
                "code": "123456"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok, token, user{id,email}, trial{days,active,days_left?,ends_at,granted?} }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always `true` when the code matched."
                    },
                    "token": {
                      "type": "string",
                      "description": "Session `sess_…` to use in `Authorization: Bearer`."
                    },
                    "user": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Conta"
                        }
                      ],
                      "description": "The person who just signed in."
                    },
                    "trial": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/Trial"
                        }
                      ],
                      "description": "The trial, with `granted: true` when this call is the one that granted it."
                    }
                  },
                  "required": [
                    "ok",
                    "token",
                    "user",
                    "trial"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Wrong or expired code."
          },
          "429": {
            "description": "Too many attempts."
          }
        }
      }
    },
    "/api/auth/claim": {
      "post": {
        "operationId": "post_api_auth_claim",
        "summary": "Moves the guest's monitors to the signed-in account.",
        "description": "Returns: { ok, claimed }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "guest_token": {
                    "type": "string",
                    "description": "Guest `hp_…` whose monitors move to the account."
                  }
                },
                "required": [
                  "guest_token"
                ]
              },
              "example": {
                "guest_token": "hp_…"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok, claimed }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always `true`."
                    },
                    "claimed": {
                      "type": "integer",
                      "description": "How many monitors changed owner."
                    }
                  },
                  "required": [
                    "ok",
                    "claimed"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`guest_token` missing."
          },
          "401": {
            "description": "No credential, or an invalid one. See this endpoint's auth."
          }
        }
      }
    },
    "/api/auth/logout": {
      "post": {
        "operationId": "post_api_auth_logout",
        "summary": "Invalidates the current session.",
        "description": "Returns: { ok }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "description": "No credential, or an invalid one. See this endpoint's auth."
          }
        }
      }
    },
    "/api/contact": {
      "post": {
        "operationId": "contact",
        "summary": "Talks to support: a human solves Turnstile, an agent pays $0.10 in x402.",
        "description": "The first agent message is free; after that the backoff is 60s doubling up to a 1-hour cap, announced in `Retry-After`.\nReturns: { ok, path }",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "What to call the person writing."
                  },
                  "email": {
                    "type": "string",
                    "description": "Where to reply."
                  },
                  "message": {
                    "type": "string",
                    "description": "What you want to say."
                  },
                  "form_ts": {
                    "type": "integer",
                    "description": "When the form was opened; the anti-robot of the human path, and only it requires this."
                  },
                  "cf_turnstile_response": {
                    "type": "string",
                    "description": "Turnstile response; present only on the human path."
                  },
                  "tipo": {
                    "type": "string",
                    "description": "Proposal: `patrocinio`, `parceria` or `anuncio`. Turns on the fields below."
                  },
                  "empresa": {
                    "type": "string",
                    "description": "Who is proposing, when it is a company."
                  },
                  "site": {
                    "type": "string",
                    "description": "Website of who is proposing."
                  },
                  "orcamento": {
                    "type": "string",
                    "description": "`ate_100`, `100_500`, `500_2000`, `2000_mais` or `a_combinar`."
                  },
                  "espaco": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Placement ids from `GET /api/partners`, up to 6."
                  },
                  "duracao": {
                    "type": "string",
                    "description": "Exposure in days: `30`, `90` or `365`."
                  },
                  "pagamento": {
                    "type": "string",
                    "description": "`usdc`, `deposito` or `a_combinar`."
                  }
                },
                "required": [
                  "name",
                  "email",
                  "message"
                ]
              },
              "example": {
                "name": "…",
                "email": "a@example.com",
                "message": "…"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok, path }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always `true` when the message was accepted."
                    },
                    "path": {
                      "type": "string",
                      "description": "Which path it came through: human with captcha or paid agent."
                    }
                  },
                  "required": [
                    "ok",
                    "path"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Required field missing."
          },
          "402": {
            "description": "Quota exceeded. The response carries `accepts[]` (x402, USDC on Base): pay and repeat the same call with `X-PAYMENT`."
          },
          "429": {
            "description": "Agent backoff: wait for `Retry-After`."
          }
        }
      }
    },
    "/api/visit": {
      "post": {
        "operationId": "post_api_visit",
        "summary": "Ping from the interface that increments the day's visits. Agents need not call it.",
        "description": "Smoke does not count: `X-MM-Smoke`, User-Agent `mm-smoke` or `smoke: true` in the body come in as `counted: false`.\nReturns: { ok, counted, reason? }",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "p": {
                    "type": "string",
                    "description": "Path of the visited page."
                  },
                  "smoke": {
                    "type": "boolean",
                    "description": "`true` marks the call as a test and it does not enter the count."
                  }
                }
              },
              "example": {
                "p": "/"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok, counted, reason? }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Always `true`."
                    },
                    "counted": {
                      "type": "boolean",
                      "description": "Whether the visit entered the day's count."
                    },
                    "reason": {
                      "type": "string",
                      "description": "Why it did not count, when `counted` is `false`."
                    }
                  },
                  "required": [
                    "ok",
                    "counted"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/erro-cliente": {
      "post": {
        "operationId": "post_api_erro_cliente",
        "summary": "Browser error report, sent by the interface itself. Agents need not call it.",
        "description": "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.\nReturns: 204 with no body, always — an invalid, repeated or over-cap report also gets 204.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string",
                    "description": "Failure code, `UI-` + letters/digits (`UI-JS-001` global error, `UI-PROMESSA-001`, `UI-RECURSO-001`, `UI-CSP-001`, `UI-APP-001` app report)."
                  },
                  "phase": {
                    "type": "string",
                    "description": "Where it broke, lowercase: `global`, `promessa`, `script`, `load_list`…"
                  },
                  "path": {
                    "type": "string",
                    "description": "Path of the open page, without query."
                  },
                  "message": {
                    "type": "string",
                    "description": "Error message, up to 2000 characters."
                  },
                  "stack": {
                    "type": "string",
                    "description": "Stack trace, up to 12000 characters."
                  },
                  "source": {
                    "type": "string",
                    "description": "Originating script; only its path is kept."
                  },
                  "line": {
                    "type": "integer",
                    "description": "Line in the originating script."
                  },
                  "column": {
                    "type": "integer",
                    "description": "Column in the originating script."
                  },
                  "visivel": {
                    "type": "boolean",
                    "description": "Whether the tab was visible when it broke."
                  }
                },
                "required": [
                  "code",
                  "phase"
                ]
              },
              "example": {
                "code": "UI-APP-001",
                "phase": "carregar_lista",
                "path": "/",
                "message": "lista 500"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "204 with no body, always — an invalid, repeated or over-cap report also gets 204."
          }
        }
      }
    },
    "/api/vitrine": {
      "get": {
        "operationId": "get_api_vitrine",
        "summary": "The product's public numbers: traffic, agents, usage and reliability, no money.",
        "description": "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.\nReturns: { v, produto, publicado, atualizado_em, stale, nome?, desde?, fuso?, hoje?, dias?, janelas?, visitantes?, pessoas?, agentes?, superficies?, mcp?, uso?, contas?, confiabilidade?, catalogo?, apoio? }",
        "security": [],
        "responses": {
          "200": {
            "description": "{ v, produto, publicado, atualizado_em, stale, nome?, desde?, fuso?, hoje?, dias?, janelas?, visitantes?, pessoas?, agentes?, superficies?, mcp?, uso?, contas?, confiabilidade?, catalogo?, apoio? }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "v": {
                      "type": "integer",
                      "description": "Contract version (1)."
                    },
                    "produto": {
                      "type": "string",
                      "description": "Product id."
                    },
                    "publicado": {
                      "type": "boolean",
                      "description": "`false` before the collector's first publication; then only these five keys come."
                    },
                    "atualizado_em": {
                      "type": "string",
                      "description": "When the collector published (ISO 8601).",
                      "nullable": true
                    },
                    "stale": {
                      "type": "boolean",
                      "description": "`true` when the projection is older than 26 h."
                    },
                    "nome": {
                      "type": "string",
                      "description": "Product name."
                    },
                    "desde": {
                      "type": "string",
                      "description": "First day the series covers.",
                      "nullable": true
                    },
                    "fuso": {
                      "type": "string",
                      "description": "Time zone of the days (`UTC`)."
                    },
                    "hoje": {
                      "type": "object",
                      "description": "Today: pages by class (human, AI, bot), API calls by class, machine-surface reads and product usage."
                    },
                    "dias": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Up to 31 days, oldest first: `dia`, `paginas`, `api`, `api_ia`, `maquina`, `visitantes`, `uso`."
                    },
                    "janelas": {
                      "type": "object",
                      "description": "7- and 30-day sums (`d7`, `d30`)."
                    },
                    "visitantes": {
                      "type": "object",
                      "description": "Unique visitors at the edge over 7 days."
                    },
                    "pessoas": {
                      "type": "object",
                      "description": "GA4 when available: users, sessions, countries, devices and who arrived from AI.",
                      "nullable": true
                    },
                    "agentes": {
                      "type": "object",
                      "description": "The AI agents and bots that read the most, 7 days."
                    },
                    "superficies": {
                      "type": "object",
                      "description": "Reads of OKF, llms, well-known, OpenAPI and MCP over 7 days."
                    },
                    "mcp": {
                      "type": "object",
                      "description": "MCP calls over 7 days."
                    },
                    "uso": {
                      "type": "object",
                      "description": "Real product usage per resource: label, today, 7 and 30 days."
                    },
                    "contas": {
                      "type": "object",
                      "description": "Users and guests.",
                      "nullable": true
                    },
                    "confiabilidade": {
                      "type": "object",
                      "description": "Share of requests without 5xx over 7 days, and the live build."
                    },
                    "catalogo": {
                      "type": "object",
                      "description": "Size of the catalog, when the product has one.",
                      "nullable": true
                    },
                    "apoio": {
                      "type": "object",
                      "description": "Impressions and clicks per sponsor, when any."
                    }
                  },
                  "required": [
                    "v",
                    "produto",
                    "publicado",
                    "atualizado_em",
                    "stale"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/vitrine/operador": {
      "get": {
        "operationId": "get_api_vitrine_operador",
        "summary": "The product's full document on the operator panel — operator token only.",
        "description": "Returns: { produto, atualizado_em, operador }",
        "security": [],
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`Bearer <METRICS_TOKEN>` — the operator class."
          }
        ],
        "responses": {
          "200": {
            "description": "{ produto, atualizado_em, operador }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "produto": {
                      "type": "string",
                      "description": "Product id."
                    },
                    "atualizado_em": {
                      "type": "string",
                      "description": "When the collector published.",
                      "nullable": true
                    },
                    "operador": {
                      "type": "object",
                      "description": "The collector's full document, with what the public projection leaves out.",
                      "nullable": true
                    }
                  },
                  "required": [
                    "produto",
                    "atualizado_em",
                    "operador"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No token, wrong token or a token of another class."
          },
          "503": {
            "description": "Worker without `METRICS_TOKEN` or without the control plane."
          }
        }
      }
    },
    "/api/vitrine/painel": {
      "get": {
        "operationId": "get_api_vitrine_painel",
        "summary": "The whole house panel, in the shape the gm reads — operator token only.",
        "description": "Returns: { apps, updated?, totals? }",
        "security": [],
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`Bearer <METRICS_TOKEN>` — the operator class."
          }
        ],
        "responses": {
          "200": {
            "description": "{ apps, updated?, totals? }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "apps": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "One operator document per product, ordered by id."
                    },
                    "updated": {
                      "type": "string",
                      "description": "When the collector closed the round."
                    },
                    "totals": {
                      "type": "object",
                      "description": "House totals."
                    }
                  },
                  "required": [
                    "apps"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No token, wrong token or a token of another class."
          },
          "503": {
            "description": "Worker without `METRICS_TOKEN` or without the control plane."
          }
        }
      }
    },
    "/api/vitrine/cursores": {
      "get": {
        "operationId": "get_api_vitrine_cursores",
        "summary": "The resolved-error cursor per product (`borda`, `cli`) — operator token only.",
        "description": "Returns: JSON: `{ [product]: { borda?: ISO, cli?: ISO } }`; empty is `{}`.",
        "security": [],
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`Bearer <METRICS_TOKEN>` — the operator class."
          }
        ],
        "responses": {
          "200": {
            "description": "JSON: `{ [product]: { borda?: ISO, cli?: ISO } }`; empty is `{}`."
          },
          "401": {
            "description": "No token, wrong token or a token of another class."
          },
          "503": {
            "description": "Worker without `METRICS_TOKEN` or without the control plane."
          }
        }
      }
    },
    "/api/partners": {
      "get": {
        "operationId": "get_api_partners",
        "summary": "Partnership, sponsorship and advertising: the product's placements with a suggested price, the public numbers next to them and how to propose.",
        "description": "Information on request, no activation: placements from the house catalogue priced in USD per 30 days (90 and 365 days discounted), sponsors in effect, an excerpt of `/api/vitrine`, the house wallet (USDC on Base) and the contact path — bank deposit, PIX or invoice are arranged in the reply. Cached for 1 hour.\nReturns: { status, produto, idioma, titulo, descricao, publico, modalidades, placements, house_bundle, parcerias, current_sponsors, stats, payment, contact, politica, _links }",
        "security": [],
        "responses": {
          "200": {
            "description": "{ status, produto, idioma, titulo, descricao, publico, modalidades, placements, house_bundle, parcerias, current_sponsors, stats, payment, contact, politica, _links }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "description": "`sob_consulta`: information and proposal, no activation and no charge."
                    },
                    "produto": {
                      "type": "string",
                      "description": "Product name."
                    },
                    "idioma": {
                      "type": "string",
                      "description": "Language of the texts (the product's)."
                    },
                    "titulo": {
                      "type": "string",
                      "description": "Title of the offer."
                    },
                    "descricao": {
                      "type": "string",
                      "description": "One sentence about the offer."
                    },
                    "publico": {
                      "type": "string",
                      "description": "Who uses the product — the audience a sponsor reaches."
                    },
                    "modalidades": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "`{ id, nome }`: patrocinio, parceria, anuncio."
                    },
                    "placements": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "The product's placements: `id`, `nome`, `onde`, `formato`, `exclusivo`, `medicao`, `price_usd_30d` (suggested; `null` is on request), `exposure[{ dias, price_usd }]` for 30, 90 and 365 days, `disponivel`."
                    },
                    "house_bundle": {
                      "type": "object",
                      "description": "The house bundle: footer and agent mention across the ten products, discounted."
                    },
                    "parcerias": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Partnership ideas the product is open to discuss."
                    },
                    "current_sponsors": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Sponsors in effect: `id`, `nome`, `url`, `frase`, `espacos`, `ate`."
                    },
                    "stats": {
                      "type": "object",
                      "description": "Excerpt of the public numbers (`hoje`, `janelas`, `agentes`, `confiabilidade`) and the `link` to `/api/vitrine`; `publicado: false` before the first publication."
                    },
                    "payment": {
                      "type": "object",
                      "description": "How to pay: `rede`, `chain_id`, `ativo`, `pay_to`, `eip681` (the house wallet, when declared), `alternativas` and the `nota` — bank deposit, PIX or invoice in the reply."
                    },
                    "contact": {
                      "type": "object",
                      "description": "`email`, `form_url`, `api_url` (`POST /api/contact` where the handler exists), `campos` (required), `campos_proposta` (the optional proposal fields, each with its accepted values), `price_agent_usd`, `message_template`, `instructions`."
                    },
                    "politica": {
                      "type": "object",
                      "description": "Placement label, refused sectors, prepayment, deadlines."
                    },
                    "_links": {
                      "type": "object",
                      "description": "`self`, `stats`, `page` (`null` until the page exists), `contact`, `casa` (the same path on the ten products)."
                    }
                  },
                  "required": [
                    "status",
                    "produto",
                    "idioma",
                    "titulo",
                    "descricao",
                    "publico",
                    "modalidades",
                    "placements",
                    "house_bundle",
                    "parcerias",
                    "current_sponsors",
                    "stats",
                    "payment",
                    "contact",
                    "politica",
                    "_links"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/metrics": {
      "get": {
        "operationId": "get_api_metrics",
        "summary": "Metrics of the last 7 days. With the operator token, includes payments.",
        "description": "Without credentials it returns visits, usage and accounts. With `METRICS_TOKEN` as Bearer it adds `payments` — and only on Base mainnet, because staging numbers on a finance panel mislead.\nReturns: { app, today, today_visits, today_contacts?, days, usage, accounts, financeiro?, payments? }",
        "security": [],
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "`Bearer <METRICS_TOKEN>` to include the finance block."
          }
        ],
        "responses": {
          "200": {
            "description": "{ app, today, today_visits, today_contacts?, days, usage, accounts, financeiro?, payments? }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Metricas"
                }
              }
            }
          }
        }
      }
    },
    "/api/credito": {
      "post": {
        "operationId": "post_api_credito",
        "summary": "Top up prepaid credit: pay once with x402 and get the token that debits on any API of the house.",
        "description": "Returns: { token, saldo_usd, guarde, usar, saldo_em }",
        "security": [],
        "parameters": [
          {
            "name": "usd",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Package: 1, 5, 10 ou 25 dollars."
          }
        ],
        "responses": {
          "200": {
            "description": "{ token, saldo_usd, guarde, usar, saldo_em }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string",
                      "description": "Bearer token for the balance (`cred_…`). Shown ONCE — it cannot be recovered."
                    },
                    "saldo_usd": {
                      "type": "string",
                      "description": "Credited balance."
                    },
                    "guarde": {
                      "type": "string",
                      "description": "Warning that the token is the bearer of the credit."
                    },
                    "usar": {
                      "type": "string",
                      "description": "How to present the token on paid routes."
                    },
                    "saldo_em": {
                      "type": "string",
                      "description": "Where to check balance and statement."
                    }
                  },
                  "required": [
                    "token",
                    "saldo_usd",
                    "guarde",
                    "usar",
                    "saldo_em"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Package outside the list (1, 5, 10 ou 25)."
          },
          "402": {
            "description": "Unpaid — the body carries the x402 `accepts[]`."
          }
        }
      },
      "get": {
        "operationId": "get_api_credito",
        "summary": "Credit balance and statement — the latest movements, without returning the token.",
        "description": "Returns: { saldo_micros, saldo_usd, criado_em, movimentos }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "{ saldo_micros, saldo_usd, criado_em, movimentos }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "saldo_micros": {
                      "type": "integer",
                      "description": "Balance in micro-dollars (1e-6 USD)."
                    },
                    "saldo_usd": {
                      "type": "string",
                      "description": "Formatted balance."
                    },
                    "criado_em": {
                      "type": "string",
                      "description": "When the credit was opened."
                    },
                    "movimentos": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Recent credits and debits, with product and resource."
                    }
                  },
                  "required": [
                    "saldo_micros",
                    "saldo_usd",
                    "criado_em",
                    "movimentos"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No token, or unknown token."
          }
        }
      }
    },
    "/api/pricing": {
      "get": {
        "operationId": "pricing",
        "summary": "Current prices and free allowances.",
        "description": "Returns: { product, quota{free,paid,how_to_pay,live,free_now?,trial?}, pricing, billing, api_index }",
        "security": [],
        "responses": {
          "200": {
            "description": "{ product, quota{free,paid,how_to_pay,live,free_now?,trial?}, pricing, billing, api_index }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "product": {
                      "type": "string",
                      "description": "Product name."
                    },
                    "quota": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/PaymentQuota"
                        }
                      ],
                      "description": "Public allowances and current list prices; not personal usage."
                    },
                    "pricing": {
                      "type": "string",
                      "description": "Absolute URL of the current price list."
                    },
                    "billing": {
                      "type": "string",
                      "description": "Absolute URL of payment discovery or the existing billing summary."
                    },
                    "api_index": {
                      "type": "string",
                      "description": "Absolute URL of the API catalog."
                    }
                  },
                  "required": [
                    "product",
                    "quota",
                    "pricing",
                    "billing",
                    "api_index"
                  ]
                }
              }
            }
          },
          "405": {
            "description": "Use GET or HEAD."
          }
        }
      }
    }
  }
}