# UpHeron API and agent guide UpHeron checks websites, APIs, certificates, DNS, mail servers and more from several locations, and tells you when something breaks. The API lets your scripts and AI agents do everything the dashboard does: read the status of every monitor, look at results and incidents, and add or change monitors. - **Reading is free.** Status, results, uptime and incidents cost nothing, within the [rate limits](/docs/rate-limits). - **Monitors in your plan are free.** Creating a monitor costs nothing while your plan has a free slot. - **Extra monitors cost $0.01 per location per month, or $0.10 per location for life.** Beyond your plan's slots, an agent pays from prepaid credit or on the spot with the [Machine Payments Protocol](/docs/payments) (MPP). Payments are processed by Stripe; prices include VAT. - **Paid monitors check as often as every 10 seconds**, on any plan. - **No account needed.** An agent can create a monitor by paying alone; the response returns an API key for the new monitor's workspace. See [Without an account](/docs/payments#without-an-account). - **Vulnerability alerts** (Pro plans). Website monitors can alert when a vulnerability is published for the software the site runs, found by scanning it or imported from a lockfile, Dockerfile or a list an agent writes. See [Software lists](/docs/software). - **MCP server.** Connect Claude, Cursor or any MCP client to `https://upheron.com/mcp`. See [MCP server](/docs/mcp). - **Agents sign up by themselves.** With [auth.md](/docs/auth-md), an agent gets access for its user (who types a 6-digit code) or on its own, without anyone creating a token. Agents start at [`/auth.md`](https://upheron.com/auth.md). ## Quick start 1. **Create a token.** In the dashboard, open **Settings → API tokens** and create a token. Give it the `write` scope if it should create or change monitors; `read` is enough for status. The token is shown once. 2. **Read your monitors.** ```bash curl https://upheron.com/api/v1/monitors \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` 3. **Add a monitor.** ```bash curl https://upheron.com/api/v1/monitors \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{"name": "Example", "url": "https://example.com", "interval_seconds": 300}' ``` While your plan has a free slot this returns `201 Created`. When it has none, it returns `402 Payment Required` with an MPP challenge for the price; an MPP client pays and retries automatically. See [Payments](/docs/payments). ## What it costs | Action | Price | |---|---| | Read monitors, results, uptime, incidents, receipts | Free, within the rate limits | | Create a monitor while the plan has a free slot | Free | | Create a monitor when every plan slot is used | $0.01 per location per 30 days, renewed from credit | | … or for life (`"billing_period": "lifetime"`) | $0.10 per location, once | | Create a monitor without an account | Same prices, paid on the spot | | Add a location to a paid monitor | One period's price (or the lifetime price) per new location | | Pause, resume, edit or delete any monitor | Free | Prices are in US dollars and include VAT. A card payment is at least $0.50 (Stripe's minimum); the rest stays as credit, so $0.50 covers 50 location-months or 5 lifetime locations. `GET /api/v1/account` always returns the current price, your free slots and your credit. ## Conventions - **Base URL:** `https://upheron.com/api/v1` - **Authentication:** an API token in the `X-API-Key` header. See [Authentication](/docs/authentication). - **Format:** JSON in and out, UTF-8. Timestamps are ISO 8601 in UTC. Money is in US cents (`amount_cents`), except MPP amounts, which are decimal dollars (`"0.02"`). - **Errors:** a JSON body with a machine-readable `error` and a human-readable `message`. See [API reference](/docs/api#errors). - **Retries:** send an `Idempotency-Key` header when creating monitors, so a retry never creates or pays for a second one. ## For AI agents Everything here is available in a form agents read well: - Ask for any page with `Accept: text/markdown`, or add `.md` to its URL, to get Markdown instead of HTML: [`/docs/payments.md`](/docs/payments.md). - [`/llms.txt`](/llms.txt) lists every page; [`/llms-full.txt`](/llms-full.txt) is the whole documentation in one file. - [`/openapi.json`](/openapi.json) describes the API, including MPP payment offers for the paid operations. [Using UpHeron from an AI agent](/docs/agents) has a recommended workflow and instructions you can give your agent. ## Pages - [Authentication](/docs/authentication): tokens, scopes and headers - [MCP server](/docs/mcp): connect AI agents as tools - [Payments](/docs/payments): pay-per-monitor pricing, MPP, credit, receipts and VAT - [API reference](/docs/api): every endpoint with examples - [Rate limits](/docs/rate-limits): limits, headers and fair use - [AI agents](/docs/agents): discovery, workflow and agent instructions - [Software lists](/docs/software): telling a monitor what a website runs, for vulnerability alerts: the list format and supported files --- # MCP server UpHeron runs a [Model Context Protocol](https://modelcontextprotocol.io) server, so an AI agent can check websites, read your monitors and add new ones as tools, without writing API calls. - **Endpoint:** `https://upheron.com/mcp` (Streamable HTTP) - **Authentication:** your API token in the `X-API-Key` header, or `Authorization: Bearer `. Create one in **Settings → API tokens**; a `read` token is enough for status, `write` lets the agent create monitors and pay. An agent can also get an access token itself with [auth.md](/docs/auth-md); it works here the same way. - **No token?** `check_website` works without one, and `create_monitor` works by paying alone (see [below](#without-a-token)). ## Connect ### Claude Code ```bash claude mcp add --transport http upheron https://upheron.com/mcp \ --header "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### Claude Desktop, Cursor, VS Code and other clients Most clients take a JSON entry like this (the file and key names vary by client): ```json { "mcpServers": { "upheron": { "type": "http", "url": "https://upheron.com/mcp", "headers": { "X-API-Key": "pgk_…" } } } } ``` Clients that only support local (stdio) servers can bridge with `npx mcp-remote https://upheron.com/mcp --header "X-API-Key: pgk_…"`. ## Tools | Tool | What it does | Cost | |---|---|---| | `check_website` | Checks whether a public website responds right now | Free, no token needed | | `get_account` | Plan, free monitor slots, credit and prices | Free | | `list_locations` | Location codes for monitors | Free | | `list_monitors` | Every monitor and its current status | Free | | `get_monitor` | Uptime (24 h, 7 d, 30 d), latest results, incidents | Free | | `get_monitor_results` | Up to 500 recent results | Free | | `get_uptime_history` | Daily uptime for any range, back to the first check | Free | | `list_incidents` | Outages, newest first | Free | | `quote_monitor` | What a monitor would cost, without creating it | Free | | `create_monitor` | Creates a monitor | Free in a plan slot; otherwise paid | | `update_monitor` | Changes a monitor | Paid only for extra locations on a paid monitor | | `pause_monitor`, `resume_monitor`, `delete_monitor` | Manage monitors | Free | | `renew_monitor` | Adds 30 days to a monthly paid monitor | Paid from credit | | `top_up_credit` | Buys prepaid credit | Paid | | `list_payments` | Payments and their VAT | Free | | `add_notification_contact`, `list_notification_contacts` | Email or webhook alerts | Free | | `get_vulnerabilities` | Vulnerabilities and unsupported releases found in a website monitor's software, with fixes (Pro plans) | Free | | `import_software` | Tells a monitor what its website runs: a lockfile, Dockerfile or a "name version" list | Free | | `add_software`, `remove_software` | Adds or removes one piece of software | Free | | `mark_vulnerability_fixed` | Records that a finding's software was upgraded, by default to the first version that fixes it | Free | | `scan_website_software` | Scans the website for its software and checks it now | Free | An agent that can see a server is the best source for what it runs: ask it to read `/etc/os-release`, `nginx -v`, `php -v` or the project's lockfile and send the result with `import_software`, after turning on `vulnerability_alerts` with `update_monitor`. [Software lists](/docs/software) describes the list format and the files it reads. Tools carry MCP annotations (`readOnlyHint`, `destructiveHint`, `idempotentHint`), so clients can ask before running anything that changes or costs something. The documentation is also available as MCP resources (`upheron://docs/…`). ## Paying from MCP Paid tools follow the [MPP transport binding for MCP](https://paymentauth.org/draft-payment-transport-mcp-00): 1. When credit does not cover a paid tool, the call fails with JSON-RPC error **-32042** (payment required). Its `data.challenges` holds the MPP challenge: what to pay, how, and how much. 2. The agent's MPP client pays it and repeats the tool call with the credential in `params._meta["org.paymentauth/credential"]`. Your API key stays in the connection header. 3. The result carries the receipt in `_meta["org.paymentauth/receipt"]`. A card pays at least $0.50 (Stripe's minimum), and everything above the price stays as credit: at the default prices $0.50 covers 50 location-months or 5 lifetime locations, used automatically by later calls. See [Payments](/docs/payments) for prices, credit and VAT. Clients without MPP support can still use every free tool, and create monitors while the plan has free slots or the workspace has credit. Top up credit from the [HTTP API](/docs/payments#prepaid-credit) or with an MPP-capable tool such as Stripe's Link CLI. ## Without a token Without an API key, `create_monitor` is always paid. After payment it creates a pay-as-you-go workspace that owns the monitor and returns its `api_key` once. Configure that key as the server's `X-API-Key` header to manage the monitor, renew it and add more. ## Good instructions for your agent ```text Use the upheron MCP tools to monitor websites. - Reading is free. Before create_monitor, call quote_monitor; if amount_due_cents > 0, tell me the price and ask first unless I approved a budget. - Always pass idempotency_key to create_monitor. - Monthly is the default; use billing_period "lifetime" only when I ask for it. ``` --- # Authentication Every API request needs a workspace API token. A token acts for its workspace: it sees and changes that workspace's monitors and spends that workspace's credit. ## Create a token 1. Sign in and open **Settings → API tokens**. 2. Name the token after what will use it, for example `deploy-bot` or `claude-agent`. 3. Choose its scope and, optionally, an expiry date. 4. Copy the token. It starts with `pgk_` and is **shown only once**; UpHeron stores only a hash of it. You can have up to 50 active tokens per workspace. Revoke a token in the same place; it stops working immediately. ## Agents without a token: auth.md An AI agent that supports [auth.md](/docs/auth-md) does not need a token from you. It registers at `https://upheron.com/agent/identity`, you type the 6-digit code it shows you, and it receives access tokens starting with `pga_` that last an hour and refresh themselves. They work exactly like API tokens with the `read` and `write` scopes. **Settings → Connected agents** lists these agents; disconnect one there. Agents read the details at [`/auth.md`](https://upheron.com/auth.md). ## Scopes | Scope | Allows | |---|---| | `read` | Every `GET`: account, locations, monitors, results, incidents, payments, receipts | | `write` | Everything `read` allows, plus creating, changing, pausing and deleting monitors, acknowledging incidents, and paying | Give each agent the smallest scope it needs. An agent that only reports status should have a `read` token, which can never spend money. ## Send the token Send the token in the `X-API-Key` header: ```bash curl https://upheron.com/api/v1/account -H "X-API-Key: pgk_…" ``` `Authorization: Bearer pgk_…` also works for requests that do not carry a payment. > **Use `X-API-Key` for anything that can be paid.** When an MPP client retries a paid request it puts its payment credential in the `Authorization` header (`Authorization: Payment …`), and a request can only have one `Authorization` header. `X-API-Key` keeps your token alongside it. ## Errors | Status | `error` | Meaning | |---|---|---| | 401 | `missing_api_key` | No token was sent | | 401 | `invalid_api_key` | The token is wrong or was revoked. An agent can get access itself with [auth.md](/docs/auth-md): the `WWW-Authenticate` header points to `https://upheron.com/.well-known/oauth-protected-resource` | | 401 | `token_expired` | The token passed its expiry date | | 403 | `insufficient_scope` | The request needs the `write` scope | | 429 | `rate_limited` | Too many requests or failed sign-ins; wait for `Retry-After` seconds | Repeated failed authentication from one IP address is rate limited, so tokens cannot be guessed. ## Keep tokens safe - Store tokens in your agent's secret store or environment, never in code or prompts that are logged. - Use one token per agent or integration, so you can revoke one without breaking the others. - Set an expiry date on tokens for experiments. - Tokens are not tied to an IP address; anyone with the token can use it. Revoke a token you think has leaked. --- # Payments Reading is always free. You pay only for monitors beyond your plan's free slots, and for monitors created without an account. Agents pay with the [Machine Payments Protocol](https://mpp.dev) (MPP), an open standard by Stripe and Tempo that lets a client pay inside an ordinary HTTP request, using the `402 Payment Required` status code. Payments are processed by Stripe for Symelo LTD, a VAT-registered company in Bulgaria (VAT BG207464756). ## Prices | | Price per location | Renews | |---|---|---| | **Monthly** (default) | $0.01 for 30 days | Automatically, from credit | | **Lifetime** | $0.10 once | Never | - A monitor's price is the per-location price times its number of locations. A heartbeat or webhook monitor counts as one location. - Choose lifetime with `"billing_period": "lifetime"` when creating the monitor. Without it, a paid monitor is monthly. - Adding a location to a paid monitor costs one period's price for that location (or the lifetime price, for a lifetime monitor). - Prices are in US dollars and **include VAT**. `GET /api/v1/account` returns the current prices. - Monitors that fit in your plan's free slots never cost anything and have no billing period. - Paid monitors may check as often as every 10 seconds on any plan, the free plan included. Monitors in plan slots follow the plan (every 5 minutes at the fastest on Free). ## How a payment happens Every payment goes into your workspace's **prepaid credit**, and paid actions take their price from that credit. If the credit covers the price, nothing else happens: the request succeeds. If it does not, the response is a `402` with an MPP challenge for the difference. ```text Agent UpHeron Stripe | POST /api/v1/monitors | | |------------------------------------>| no free slot, credit too low | | 402 + WWW-Authenticate: Payment ... | | |<------------------------------------| | | (wallet signs or issues a token) | | | POST /api/v1/monitors | | | Authorization: Payment ... | | |------------------------------------>| verify and settle -------------->| | | add to credit, create monitor | | 201 Created + Payment-Receipt | | |<------------------------------------| | ``` 1. The agent sends the request with its `X-API-Key`. 2. UpHeron answers `402 Payment Required`. The `WWW-Authenticate: Payment` header carries one challenge per payment method, and the JSON body says what is due and why. 3. The agent's MPP client pays the challenge and repeats the request with an `Authorization: Payment …` credential. Keep sending `X-API-Key`. 4. UpHeron verifies the payment with Stripe, adds it to your credit, takes the price from it, and runs the request. The response carries a `Payment-Receipt` header. Because payments land in credit first, money is never lost: if the request fails after payment (for example, the monitor is invalid), the payment stays in your credit for the next request. ### A 402 response ```http HTTP/1.1 402 Payment Required Content-Type: application/problem+json Cache-Control: no-store WWW-Authenticate: Payment id="…", realm="upheron.com", method="stripe", intent="charge", request="…", expires="…" { "type": "https://paymentauth.org/problems/payment-required", "title": "Payment Required", "status": 402, "error": "payment_required", "amount_due": "0.50", "currency": "usd", "quote": { "billing": "paid", "billing_period": "monthly", "locations": ["fra-1", "nyc-1"], "price_cents": 2, "credit_cents": 0, "amount_due_cents": 2 }, "offers": [{ "method": "stripe", "amount_cents": 50 }], "credit_after_cents": 48, "help": "Pay with an MPP client …" } ``` You rarely read this yourself: MPP clients handle the challenge and retry automatically. ## Payment methods | Method | Minimum | Notes | |---|---|---| | **Card** through a Stripe shared payment token (MPP method `stripe`), for example from a Link wallet | $0.50 | Anything above the price stays as credit | | **Stablecoin**: USDC on Tempo (MPP method `tempo`) | $0.01 | Coming soon: enabled once Stripe approves stablecoins for this account. Until then, pay by card. | Stripe cannot charge a card less than $0.50, so **a card payment is at least $0.50 and the rest is kept as credit** for later monitors and renewals. At the default prices, $0.50 covers 50 location-months or 5 lifetime locations: one $0.01 monitor and 49 more months of monitoring, already paid for. The `402` response lists the methods it accepts and the amount for each in `offers`, and `credit_after_cents` says how much credit will be left. ## Paying from code ### With a card, using Stripe's Link CLI [`link-cli`](https://link.com/agents) issues one-time shared payment tokens from your Link wallet, after you approve the purchase. It pays any `402` from UpHeron, for example creating a monitor: ```bash npx @stripe/link-cli auth login npx @stripe/link-cli mpp pay https://upheron.com/api/v1/monitors \ -X POST \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "Checkout API", "url": "https://shop.example.com/health", "interval_seconds": 60}' \ --context "Create an UpHeron uptime monitor for my checkout API. The card is charged the 0.50 USD minimum; the rest stays as UpHeron credit for more monitors." ``` ### With the mppx SDK (TypeScript) [`mppx`](https://mpp.dev/sdk/typescript) wraps `fetch`: when a response is a `402`, it pays the challenge with the configured method and retries. This example uses a stablecoin wallet (accepted once enabled; until then use a card). ```ts import { Mppx, tempo } from "mppx/client"; import { privateKeyToAccount } from "viem/accounts"; const mppx = Mppx.create({ methods: [tempo({ account: privateKeyToAccount(process.env.WALLET_PRIVATE_KEY as `0x${string}`) })], polyfill: false, }); const response = await mppx.fetch("https://upheron.com/api/v1/monitors", { method: "POST", headers: { "X-API-Key": process.env.UPTIME_MONITOR_API_KEY!, "Idempotency-Key": crypto.randomUUID(), "Content-Type": "application/json", }, body: JSON.stringify({ name: "Checkout API", url: "https://shop.example.com/health", locations: ["fra-1", "nyc-1"] }), }); console.log(response.status, await response.json()); ``` Fund the wallet with USDC on Tempo. MPP also has SDKs for [Python, Go and Rust](https://mpp.dev/overview). ### From an MCP client The [MCP server](/docs/mcp) takes MPP payments too: a paid tool answers with error -32042 and the challenge, and the agent's MPP client pays and repeats the call. ### Any other MPP client Any MPP-compatible client works the same way: send the request with your `X-API-Key`, let the client answer the `402`, and keep the header on the retry. Validate the exchange end to end with `npx mppx@latest validate`. ## Prepaid credit Top up credit to avoid a payment per request, or to pay by card: ```bash # Answers 402 with a challenge for $5.00; pay it with an MPP client. curl https://upheron.com/api/v1/credits \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"amount_cents": 500}' ``` - Top-ups run from $0.50 to $1,000.00, in whole cents. - `GET /api/v1/account` shows your balance; `GET /api/v1/account/credit` lists every movement. - Credit does not expire and is not refunded as cash. ## Monthly renewals A monthly paid monitor has a `paid_until` date. Every hour, UpHeron renews monitors whose period has ended for another 30 days, from credit, at the current price for their current locations. - **Keep credit available** and renewals happen without any action. - **If credit runs out**, the monitor keeps running for a grace period of 3 days. After that it pauses, with `"billing_state": "overdue"`, and cannot be resumed until it is renewed. - **To renew** early or after a pause, call `POST /api/v1/monitors/{id}/renew`. It pays from credit, or answers `402` for the price. Renewing early adds 30 days to the remaining time. Topping up credit is also enough: the next hourly run renews and resumes the monitor. - Lifetime monitors never renew and never pause for billing. ## Without an account An agent with an MPP wallet can create a monitor without any account or token. Send `POST /api/v1/monitors` **without** `X-API-Key`: ```ts const response = await mppx.fetch("https://upheron.com/api/v1/monitors", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: "My API", url: "https://api.example.com/health", billing_period: "lifetime" }), }); const { data, api_key, workspace } = await response.json(); // Store api_key: it is shown once and controls this monitor. ``` - The request is always paid: it gets a `402` for the monitor's price, monthly or lifetime. - After payment UpHeron creates a **pay-as-you-go workspace** that owns the monitor, and returns its `api_key` (shown once) and `workspace`. Send that key as `X-API-Key` to read status, change the monitor, add more monitors, renew, top up credit, or [add notification contacts](/docs/api#notification-contacts). - Pay-as-you-go workspaces have no free slots and cannot create group monitors. Their monitors may check every 10 seconds. - A card pays at least $0.50; whatever the monitor does not use stays as the new workspace's credit. - If the monitor could not be created after payment, the response still contains `api_key`, and the payment is credit in that workspace: fix the request and retry with the key. - Monthly monitors in such a workspace renew from its credit; top it up with `POST /api/v1/credits` using its key. ## Quotes and idempotency - **Ask before paying.** `POST /api/v1/monitors/quote` takes the same body as creating a monitor and returns the price, your credit and the amount due, without creating anything or charging. `POST /api/v1/monitors/{id}/quote` prices a change and `POST /api/v1/monitors/{id}/renew/quote` a renewal. - **Retry safely.** Send an `Idempotency-Key` header (any unique string up to 255 characters) when creating a monitor. Repeating the request with the same key within 24 hours returns the monitor the first request created, with `Idempotent-Replayed: true`, and charges nothing. - **Payment credentials are single-use.** A replayed MPP credential is rejected, so a payment can never be counted twice. ## Receipts and VAT - Every paid response has a `Payment-Receipt` header with the payment method and its reference (a Stripe PaymentIntent ID for cards, a transaction hash for stablecoins). - `GET /api/v1/payments` lists your payments; `GET /api/v1/payments/{id}/receipt` returns a receipt with the seller's details and the VAT breakdown, as JSON or, with `Accept: text/markdown`, as Markdown. - Prices include VAT. The seller is Symelo LTD, a VAT-registered company in Bulgaria (VAT BG207464756). The VAT treatment of each payment depends on where the buyer is: | Buyer | Treatment | |---|---| | In Bulgaria | Bulgarian VAT included | | A business elsewhere in the EU, with a VAT number on the account | Reverse charge: no VAT; the buyer accounts for it | | A consumer elsewhere in the EU | VAT included | | Outside the EU | Outside the scope of EU VAT | Set your invoice details so receipts name your business and EU businesses get the reverse charge: ```bash curl -X PUT https://upheron.com/api/v1/account/billing \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"billing_name": "Example GmbH", "billing_address": "Musterstraße 1, 10115 Berlin", "billing_country": "DE", "billing_vat_id": "DE123456789"}' ``` Without invoice details, the country is taken from the request's IP address when available. ## Errors | Status | `error` | What to do | |---|---|---| | 402 | `payment_required` | Pay the MPP challenge, or top up credit and retry | | 402 | `renewal_required` | The monitor is paused for non-payment; call `POST /monitors/{id}/renew` | | 402 | MPP problem `verification-failed`, `invalid-challenge`, `malformed-credential` | The credential was rejected; the response carries a fresh challenge to pay instead | | 422 | `monitor_limit_reached` | No free slots and payments are switched off | | 422 | `paid_monitor_limit_reached` | The workspace has reached the maximum number of paid monitors | | 422 | `invalid_amount` | Top-ups must be whole cents between the minimum and $1,000 | | 500 | `payment_not_recorded` | The payment went through but was not credited yet; keep the receipt and contact support | | 503 | `payments_disabled` | Payments are temporarily unavailable; monitors in free slots still work | MPP error responses follow [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem details; see the [MPP protocol](https://mpp.dev/protocol) for the full list. --- # API reference Base URL: `https://upheron.com/api/v1`. Send your token as `X-API-Key` (see [Authentication](/docs/authentication)). Requests and responses are JSON; lists come back as `{"data": [...]}`. The machine-readable version of this page is [`/openapi.json`](/openapi.json). Operations marked **Paid** can answer `402 Payment Required` with an MPP challenge; see [Payments](/docs/payments). Everything else is free within the [rate limits](/docs/rate-limits). ## Account ### GET /account Plan slots, credit and prices Responses: 200, 401, 429. ```bash curl -X GET https://upheron.com/api/v1/account \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### PUT /account/billing Set invoice details (name, address, country, VAT number) | Body field | Type | Notes | |---|---|---| | `billing_name` | string,null | | | `billing_address` | string,null | | | `billing_country` | string,null | ISO 3166-1 alpha-2, decides the VAT treatment. | | `billing_vat_id` | string,null | EU VAT number with country prefix; enables the reverse charge for EU businesses outside Bulgaria. | Responses: 200, 401, 403, 422, 429. ```bash curl -X PUT https://upheron.com/api/v1/account/billing \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"billing_country": "DE", "billing_vat_id": "DE123456789"}' ``` ### GET /account/credit Credit movements, newest first Responses: 200, 401, 429. ```bash curl -X GET https://upheron.com/api/v1/account/credit \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### GET /alert-channels Notification contacts Responses: 200, 401, 429. ```bash curl -X GET https://upheron.com/api/v1/alert-channels \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### POST /alert-channels Add a notification contact (email contacts must confirm a link first) | Body field | Type | Notes | |---|---|---| | `name` (required) | string | | | `kind` (required) | string | | | `destination` (required) | string | Email address or URL. | Responses: 201, 401, 403, 422, 429. ```bash curl -X POST https://upheron.com/api/v1/alert-channels \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "Ops", "kind": "email", "destination": "ops@example.com"}' ``` ### DELETE /alert-channels/{id} Remove a notification contact | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | Responses: 204, 401, 403, 404, 429. ```bash curl -X DELETE https://upheron.com/api/v1/alert-channels/$ID \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### POST /alert-channels/{id}/test Send a test alert | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | Responses: 200, 401, 403, 404, 422, 429. ```bash curl -X POST https://upheron.com/api/v1/alert-channels/$ID/test \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ## Monitors ### GET /locations Monitoring locations you can choose Responses: 200, 401, 429. ```bash curl -X GET https://upheron.com/api/v1/locations \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### GET /monitors Every monitor and its current status Responses: 200, 401, 429. ```bash curl -X GET https://upheron.com/api/v1/monitors \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### POST /monitors Create a monitor (paid when no plan slot is free, or without an account) · **Paid** · token optional Free while the plan has a free monitor slot. Otherwise costs the per-location price for 30 days (`billing_period: monthly`) or ten times that for life (`lifetime`), taken from credit; if credit is short the response is 402 with an MPP challenge for the difference. Ask POST /monitors/quote first to see the price. Without an API token the request is always paid (agent-plan limits: 60-second minimum interval, no group monitors). After payment the 201 response also contains `api_key` and `workspace`: a new pay-as-you-go workspace that owns the monitor. The key is shown once. | Parameter | In | Notes | |---|---|---| | `Idempotency-Key` | header | Repeating a request with the same key within 24 hours returns the monitor the first request created, without charging again. | | Body field | Type | Notes | |---|---|---| | `name` (required) | string | Shown in alerts and on status pages. | | `kind` | `http`, `api`, `ssl`, `domain`, `dns`, `blacklist`, `email_auth`, `malware`, `tcp`, `ssh`, `smtp`, `imap`, `pop`, `ftps`, `ping`, `ntp`, `udp`, `minecraft`, `transaction`, `micro_transaction`, `extended_transaction`, `group`, `cloud_status`, `page_speed`, `webhook`, `heartbeat`, `repository` | What to check. `heartbeat` and `webhook` wait for your calls instead of probing. `repository` reads a GitHub or GitLab repository's dependency manifests daily and alerts on vulnerabilities in them, on every plan; put the repository URL in `url` (optionally …/tree/branch/folder) and, for a private repository (Pro plan or a paid monitor), an access token in `check_options.token`. Default `"http"`. | | `url` | string | URL, or host[:port] for network checks. | | `method` | `GET`, `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE` | Default `"GET"`. | | `interval_seconds` | integer | Seconds between checks. Paid monitors may use 10 or more on any plan; monitors in plan slots follow the plan (300 on Free, 10 on Pro). Default `300`. | | `timeout_ms` | integer | Default `10000`. | | `locations` | array | Location codes from GET /locations. Defaults to the first location. | | `expected_status_min` | integer | Default `200`. | | `expected_status_max` | integer | Default `399`. | | `keyword` | string | Text the response body must contain. | | `request_headers` | object | Sent with the check; stored encrypted and never returned. | | `request_body` | string | | | `check_options` | object | Kind-specific options, such as `json_path`/`expected_value` for `api`, `steps` for transactions, `warn_days` for ssl. Ports go in `url` as host:port. See /docs/api#check-options. | | `failure_threshold` | integer | Failed checks in a row before an incident opens. The default alerts on the first failed check. Default `1`. | | `recovery_threshold` | integer | Default `2`. | | `confirmation_threshold` | integer | Locations that must agree a check failed. Default `1`. | | `heartbeat_grace_seconds` | integer | For heartbeat and webhook monitors. | | `expiry_alerts` | boolean | `http` and `api` monitors: check the domain registration and (for HTTPS) the TLS certificate daily. Domain alerts come 30, 14, 7, 3 and 1 days before expiry; certificate alerts only once automatic renewal should have happened (a sixth of the certificate's lifetime, at most 14 days). Uses no extra monitor. Default `true`. | | `vulnerability_alerts` | boolean | Paid plans, `http` and `api` monitors: scan the website weekly for the software it runs and alert on newly published vulnerabilities in it (OSV.dev with the GitHub Advisory Database, NVD, new CVE records, CISA KEV, endoflife.date). Default `false`. | | `vulnerability_severity` | `low`, `medium`, `high`, `critical` | The lowest severity that alerts. Actively exploited vulnerabilities and releases reaching end of support always alert. Default `"low"`. | | `charts` | `both`, `uptime`, `response` | Charts on public status pages: uptime bars (the last 90 days, or the whole history), the last 24 hours of response time, or both. Default `"both"`. | | `alert_channel_ids` | array | Notification contacts to alert. Defaults to all of the workspace's contacts. | | `billing_period` | `monthly`, `lifetime` | Only for paid monitors (no free plan slot). `monthly` costs the per-location price every 30 days, renewed from credit; `lifetime` costs ten times that once and never renews. Default `"monthly"`. | Responses: 200, 201, 401, 402, 403, 422, 429. ```bash curl -X POST https://upheron.com/api/v1/monitors \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "Example", "url": "https://example.com"}' ``` ### POST /monitors/quote Price a monitor without creating it | Parameter | In | Notes | |---|---|---| | `Idempotency-Key` | header | Repeating a request with the same key within 24 hours returns the monitor the first request created, without charging again. | | Body field | Type | Notes | |---|---|---| | `name` (required) | string | Shown in alerts and on status pages. | | `kind` | `http`, `api`, `ssl`, `domain`, `dns`, `blacklist`, `email_auth`, `malware`, `tcp`, `ssh`, `smtp`, `imap`, `pop`, `ftps`, `ping`, `ntp`, `udp`, `minecraft`, `transaction`, `micro_transaction`, `extended_transaction`, `group`, `cloud_status`, `page_speed`, `webhook`, `heartbeat`, `repository` | What to check. `heartbeat` and `webhook` wait for your calls instead of probing. `repository` reads a GitHub or GitLab repository's dependency manifests daily and alerts on vulnerabilities in them, on every plan; put the repository URL in `url` (optionally …/tree/branch/folder) and, for a private repository (Pro plan or a paid monitor), an access token in `check_options.token`. Default `"http"`. | | `url` | string | URL, or host[:port] for network checks. | | `method` | `GET`, `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE` | Default `"GET"`. | | `interval_seconds` | integer | Seconds between checks. Paid monitors may use 10 or more on any plan; monitors in plan slots follow the plan (300 on Free, 10 on Pro). Default `300`. | | `timeout_ms` | integer | Default `10000`. | | `locations` | array | Location codes from GET /locations. Defaults to the first location. | | `expected_status_min` | integer | Default `200`. | | `expected_status_max` | integer | Default `399`. | | `keyword` | string | Text the response body must contain. | | `request_headers` | object | Sent with the check; stored encrypted and never returned. | | `request_body` | string | | | `check_options` | object | Kind-specific options, such as `json_path`/`expected_value` for `api`, `steps` for transactions, `warn_days` for ssl. Ports go in `url` as host:port. See /docs/api#check-options. | | `failure_threshold` | integer | Failed checks in a row before an incident opens. The default alerts on the first failed check. Default `1`. | | `recovery_threshold` | integer | Default `2`. | | `confirmation_threshold` | integer | Locations that must agree a check failed. Default `1`. | | `heartbeat_grace_seconds` | integer | For heartbeat and webhook monitors. | | `expiry_alerts` | boolean | `http` and `api` monitors: check the domain registration and (for HTTPS) the TLS certificate daily. Domain alerts come 30, 14, 7, 3 and 1 days before expiry; certificate alerts only once automatic renewal should have happened (a sixth of the certificate's lifetime, at most 14 days). Uses no extra monitor. Default `true`. | | `vulnerability_alerts` | boolean | Paid plans, `http` and `api` monitors: scan the website weekly for the software it runs and alert on newly published vulnerabilities in it (OSV.dev with the GitHub Advisory Database, NVD, new CVE records, CISA KEV, endoflife.date). Default `false`. | | `vulnerability_severity` | `low`, `medium`, `high`, `critical` | The lowest severity that alerts. Actively exploited vulnerabilities and releases reaching end of support always alert. Default `"low"`. | | `charts` | `both`, `uptime`, `response` | Charts on public status pages: uptime bars (the last 90 days, or the whole history), the last 24 hours of response time, or both. Default `"both"`. | | `alert_channel_ids` | array | Notification contacts to alert. Defaults to all of the workspace's contacts. | | `billing_period` | `monthly`, `lifetime` | Only for paid monitors (no free plan slot). `monthly` costs the per-location price every 30 days, renewed from credit; `lifetime` costs ten times that once and never renews. Default `"monthly"`. | Responses: 200, 401, 403, 422, 429. ```bash curl -X POST https://upheron.com/api/v1/monitors/quote \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "Example", "url": "https://example.com"}' ``` ### GET /monitors/{id} A monitor with uptime, recent results, 90 daily bars, its whole history and incidents | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | Responses: 200, 401, 404, 429. ```bash curl -X GET https://upheron.com/api/v1/monitors/$ID \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### PATCH /monitors/{id} Change a monitor (adding locations to a paid monitor is paid) · **Paid** | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | | Body field | Type | Notes | |---|---|---| | `name` (required) | string | Shown in alerts and on status pages. | | `kind` | `http`, `api`, `ssl`, `domain`, `dns`, `blacklist`, `email_auth`, `malware`, `tcp`, `ssh`, `smtp`, `imap`, `pop`, `ftps`, `ping`, `ntp`, `udp`, `minecraft`, `transaction`, `micro_transaction`, `extended_transaction`, `group`, `cloud_status`, `page_speed`, `webhook`, `heartbeat`, `repository` | What to check. `heartbeat` and `webhook` wait for your calls instead of probing. `repository` reads a GitHub or GitLab repository's dependency manifests daily and alerts on vulnerabilities in them, on every plan; put the repository URL in `url` (optionally …/tree/branch/folder) and, for a private repository (Pro plan or a paid monitor), an access token in `check_options.token`. Default `"http"`. | | `url` | string | URL, or host[:port] for network checks. | | `method` | `GET`, `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE` | Default `"GET"`. | | `interval_seconds` | integer | Seconds between checks. Paid monitors may use 10 or more on any plan; monitors in plan slots follow the plan (300 on Free, 10 on Pro). Default `300`. | | `timeout_ms` | integer | Default `10000`. | | `locations` | array | Location codes from GET /locations. Defaults to the first location. | | `expected_status_min` | integer | Default `200`. | | `expected_status_max` | integer | Default `399`. | | `keyword` | string | Text the response body must contain. | | `request_headers` | object | Sent with the check; stored encrypted and never returned. | | `request_body` | string | | | `check_options` | object | Kind-specific options, such as `json_path`/`expected_value` for `api`, `steps` for transactions, `warn_days` for ssl. Ports go in `url` as host:port. See /docs/api#check-options. | | `failure_threshold` | integer | Failed checks in a row before an incident opens. The default alerts on the first failed check. Default `1`. | | `recovery_threshold` | integer | Default `2`. | | `confirmation_threshold` | integer | Locations that must agree a check failed. Default `1`. | | `heartbeat_grace_seconds` | integer | For heartbeat and webhook monitors. | | `expiry_alerts` | boolean | `http` and `api` monitors: check the domain registration and (for HTTPS) the TLS certificate daily. Domain alerts come 30, 14, 7, 3 and 1 days before expiry; certificate alerts only once automatic renewal should have happened (a sixth of the certificate's lifetime, at most 14 days). Uses no extra monitor. Default `true`. | | `vulnerability_alerts` | boolean | Paid plans, `http` and `api` monitors: scan the website weekly for the software it runs and alert on newly published vulnerabilities in it (OSV.dev with the GitHub Advisory Database, NVD, new CVE records, CISA KEV, endoflife.date). Default `false`. | | `vulnerability_severity` | `low`, `medium`, `high`, `critical` | The lowest severity that alerts. Actively exploited vulnerabilities and releases reaching end of support always alert. Default `"low"`. | | `charts` | `both`, `uptime`, `response` | Charts on public status pages: uptime bars (the last 90 days, or the whole history), the last 24 hours of response time, or both. Default `"both"`. | | `alert_channel_ids` | array | Notification contacts to alert. Defaults to all of the workspace's contacts. | | `billing_period` | `monthly`, `lifetime` | Only for paid monitors (no free plan slot). `monthly` costs the per-location price every 30 days, renewed from credit; `lifetime` costs ten times that once and never renews. Default `"monthly"`. | Responses: 200, 401, 402, 403, 404, 422, 429. ```bash curl -X PATCH https://upheron.com/api/v1/monitors/$ID \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "Example", "url": "https://example.com"}' ``` ### DELETE /monitors/{id} Delete a monitor (frees its plan slot; paid monitors are not refunded) | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | Responses: 204, 401, 403, 404, 429. ```bash curl -X DELETE https://upheron.com/api/v1/monitors/$ID \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### POST /monitors/{id}/quote Price a monitor change without applying it | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | | Body field | Type | Notes | |---|---|---| | `name` (required) | string | Shown in alerts and on status pages. | | `kind` | `http`, `api`, `ssl`, `domain`, `dns`, `blacklist`, `email_auth`, `malware`, `tcp`, `ssh`, `smtp`, `imap`, `pop`, `ftps`, `ping`, `ntp`, `udp`, `minecraft`, `transaction`, `micro_transaction`, `extended_transaction`, `group`, `cloud_status`, `page_speed`, `webhook`, `heartbeat`, `repository` | What to check. `heartbeat` and `webhook` wait for your calls instead of probing. `repository` reads a GitHub or GitLab repository's dependency manifests daily and alerts on vulnerabilities in them, on every plan; put the repository URL in `url` (optionally …/tree/branch/folder) and, for a private repository (Pro plan or a paid monitor), an access token in `check_options.token`. Default `"http"`. | | `url` | string | URL, or host[:port] for network checks. | | `method` | `GET`, `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE` | Default `"GET"`. | | `interval_seconds` | integer | Seconds between checks. Paid monitors may use 10 or more on any plan; monitors in plan slots follow the plan (300 on Free, 10 on Pro). Default `300`. | | `timeout_ms` | integer | Default `10000`. | | `locations` | array | Location codes from GET /locations. Defaults to the first location. | | `expected_status_min` | integer | Default `200`. | | `expected_status_max` | integer | Default `399`. | | `keyword` | string | Text the response body must contain. | | `request_headers` | object | Sent with the check; stored encrypted and never returned. | | `request_body` | string | | | `check_options` | object | Kind-specific options, such as `json_path`/`expected_value` for `api`, `steps` for transactions, `warn_days` for ssl. Ports go in `url` as host:port. See /docs/api#check-options. | | `failure_threshold` | integer | Failed checks in a row before an incident opens. The default alerts on the first failed check. Default `1`. | | `recovery_threshold` | integer | Default `2`. | | `confirmation_threshold` | integer | Locations that must agree a check failed. Default `1`. | | `heartbeat_grace_seconds` | integer | For heartbeat and webhook monitors. | | `expiry_alerts` | boolean | `http` and `api` monitors: check the domain registration and (for HTTPS) the TLS certificate daily. Domain alerts come 30, 14, 7, 3 and 1 days before expiry; certificate alerts only once automatic renewal should have happened (a sixth of the certificate's lifetime, at most 14 days). Uses no extra monitor. Default `true`. | | `vulnerability_alerts` | boolean | Paid plans, `http` and `api` monitors: scan the website weekly for the software it runs and alert on newly published vulnerabilities in it (OSV.dev with the GitHub Advisory Database, NVD, new CVE records, CISA KEV, endoflife.date). Default `false`. | | `vulnerability_severity` | `low`, `medium`, `high`, `critical` | The lowest severity that alerts. Actively exploited vulnerabilities and releases reaching end of support always alert. Default `"low"`. | | `charts` | `both`, `uptime`, `response` | Charts on public status pages: uptime bars (the last 90 days, or the whole history), the last 24 hours of response time, or both. Default `"both"`. | | `alert_channel_ids` | array | Notification contacts to alert. Defaults to all of the workspace's contacts. | | `billing_period` | `monthly`, `lifetime` | Only for paid monitors (no free plan slot). `monthly` costs the per-location price every 30 days, renewed from credit; `lifetime` costs ten times that once and never renews. Default `"monthly"`. | Responses: 200, 401, 403, 404, 422, 429. ```bash curl -X POST https://upheron.com/api/v1/monitors/$ID/quote \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "Example", "url": "https://example.com"}' ``` ### POST /monitors/{id}/renew Renew a monthly paid monitor for 30 more days · **Paid** Renewals happen automatically every hour from credit. Call this to renew early (the 30 days are added to the remaining time) or to resume a monitor that was paused for non-payment. Pays from credit, or answers 402 with an MPP challenge. | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | Responses: 200, 401, 402, 403, 404, 422, 429. ```bash curl -X POST https://upheron.com/api/v1/monitors/$ID/renew \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "Example", "url": "https://example.com"}' ``` ### POST /monitors/{id}/renew/quote Price a renewal | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | Responses: 200, 401, 403, 404, 422, 429. ```bash curl -X POST https://upheron.com/api/v1/monitors/$ID/renew/quote \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### POST /monitors/{id}/pause Stop checking | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | Responses: 200, 401, 403, 404, 429. ```bash curl -X POST https://upheron.com/api/v1/monitors/$ID/pause \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### POST /monitors/{id}/resume Start checking again | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | Responses: 200, 401, 403, 404, 429. ```bash curl -X POST https://upheron.com/api/v1/monitors/$ID/resume \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ## Vulnerabilities ### GET /monitors/{id}/vulnerabilities Findings in the website's software (exploited and most severe first) and the software they were matched on | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | Responses: 200, 401, 404, 429. ```bash curl -X GET https://upheron.com/api/v1/monitors/$ID/vulnerabilities \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### POST /monitors/{id}/vulnerabilities/scan Scan the website for its software and check it now instead of waiting for the weekly scan | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | Responses: 202, 401, 402, 403, 404, 422, 429. ```bash curl -X POST https://upheron.com/api/v1/monitors/$ID/vulnerabilities/scan \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### POST /monitors/{id}/vulnerabilities/{finding_id}/fixed Record that a finding's software was upgraded: to version, or by default to the first version that fixes it; the monitor is checked again | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | | `finding_id` (required) | path | string | | Body field | Type | Notes | |---|---|---| | `version` | string | The version installed; defaults to the finding's fix_version. | Responses: 200, 401, 402, 403, 404, 422, 429. ```bash curl -X POST https://upheron.com/api/v1/monitors/$ID/vulnerabilities/{finding_id}/fixed \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "Example", "url": "https://example.com"}' ``` ### POST /monitors/{id}/software Add software the website runs, by name (Debian, nginx), package (npm:lodash, wp-plugin:slug) or NVD product (cpe:vendor:product) | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | | Body field | Type | Notes | |---|---|---| | `name` (required) | string | | | `version` | string,null | | Responses: 201, 401, 402, 403, 404, 422, 429. ```bash curl -X POST https://upheron.com/api/v1/monitors/$ID/software \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "Debian", "version": "12"}' ``` ### POST /monitors/{id}/software/import Import a manifest, lockfile, Dockerfile, Compose file or a plain list (one "name version" per line); importing a filename again replaces what it added | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | | Body field | Type | Notes | |---|---|---| | `content` (required) | string | The file's text, or the list. | | `filename` | string | Tells the format; leave out for a plain list. | | `name` | string | Labels the import; importing under the same label again replaces it. Defaults to the filename, or "software list" for a list. | Responses: 200, 401, 402, 403, 404, 422, 429. ```bash curl -X POST https://upheron.com/api/v1/monitors/$ID/software/import \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"content": "Debian 12\nnginx 1.24.0\nPHP 8.2.12\nwp-plugin:woocommerce 9.3.3"}' ``` ### DELETE /monitors/{id}/software/import Remove everything a file imported | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | | `file` (required) | query | string | Responses: 200, 401, 402, 403, 404, 422, 429. ```bash curl -X DELETE https://upheron.com/api/v1/monitors/$ID/software/import \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### PATCH /monitors/{id}/software/{software_id} Set an entry's version (it then counts as added by hand), or ignore or restore it | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | | `software_id` (required) | path | string | | Body field | Type | Notes | |---|---|---| | `version` | string,null | | | `ignored` | boolean | | Responses: 200, 401, 402, 403, 404, 422, 429. ```bash curl -X PATCH https://upheron.com/api/v1/monitors/$ID/software/$SOFTWARE_ID \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"version": "1.24.0"}' ``` ### DELETE /monitors/{id}/software/{software_id} Remove an entry; software the scan found is ignored instead, so it is not added back | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | | `software_id` (required) | path | string | Responses: 200, 401, 402, 403, 404, 422, 429. ```bash curl -X DELETE https://upheron.com/api/v1/monitors/$ID/software/$SOFTWARE_ID \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### GET /software-catalog Software known by name; any package (npm:, composer:, pypi:, gem:, go:, maven:, nuget:, cargo:), wp-plugin:/wp-theme: slug or cpe:vendor:product works too Responses: 200, 401, 429. ```bash curl -X GET https://upheron.com/api/v1/software-catalog \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ## Results ### GET /monitors/{id}/results Recent check results, newest first | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | | `limit` | query | integer | Responses: 200, 401, 404, 429. ```bash curl -X GET https://upheron.com/api/v1/monitors/$ID/results \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### GET /monitors/{id}/uptime Daily uptime for any range, kept for as long as the monitor One entry per UTC day, oldest first. Without `from` and `to`, every day from the first check (`since`) to today. At most 3660 days per request. | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | | `from` | query | string | | `to` | query | string | Responses: 200, 401, 404, 422, 429. ```bash curl -X GET https://upheron.com/api/v1/monitors/$ID/uptime \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### GET /incidents Incidents, newest first Responses: 200, 401, 429. ```bash curl -X GET https://upheron.com/api/v1/incidents \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### GET /incidents/{id} One incident with its events | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | Responses: 200, 401, 404, 429. ```bash curl -X GET https://upheron.com/api/v1/incidents/$ID \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### POST /incidents/{id}/acknowledge Acknowledge an incident | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | Responses: 200, 401, 403, 404, 429. ```bash curl -X POST https://upheron.com/api/v1/incidents/$ID/acknowledge \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ## Payments ### POST /credits Buy prepaid credit with MPP · **Paid** Always answered with 402 and an MPP challenge for `amount_cents` until paid. Cards (Stripe shared payment tokens) need at least $0.50. | Body field | Type | Notes | |---|---|---| | `amount_cents` (required) | integer | | Responses: 200, 401, 402, 403, 422, 429, 503. ```bash curl -X POST https://upheron.com/api/v1/credits \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"amount_cents": 500}' ``` ### GET /payments Payments, newest first Responses: 200, 401, 429. ```bash curl -X GET https://upheron.com/api/v1/payments \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### GET /payments/{id} One payment | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | Responses: 200, 401, 404, 429. ```bash curl -X GET https://upheron.com/api/v1/payments/$ID \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ### GET /payments/{id}/receipt Receipt with seller and VAT details (JSON, or Markdown with Accept: text/markdown) | Parameter | In | Notes | |---|---|---| | `id` (required) | path | string | Responses: 200, 401, 404, 429. ```bash curl -X GET https://upheron.com/api/v1/payments/$ID/receipt \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" ``` ## Check options `check_options` carries the settings specific to a monitor `kind`. The most common: | Kind | Options | |---|---| | `http`, `api` | `json_path` (dot-separated) and `expected_value` (JSON), `max_latency_ms` (100–60000), `headers_only` (`http` only: stop after the response headers; ignored with `keyword` or `json_path`) | | `transaction`, `micro_transaction`, `extended_transaction` | `steps`: up to 5, 3 or 10 HTTP requests, each with `url`, `method`, `headers`, `body`, `keyword`, `expected_status_min`, `expected_status_max`, `json_path`, `expected_value`, `max_latency_ms` | | `ssl`, `domain` | `warn_days` (1–365): fail this many days before expiry | | `dns` | `record_type` (`A`, `AAAA`, `CNAME`, `MX`, `TXT`, `NS`, `SOA`, `SRV`) and an optional `expected_answer` | | `blacklist` | `dnsbl_zone`: the provider's query zone; `lookup`: `domain` (default, a domain list), `mail_servers` (the IPv4 addresses of the domain's MX servers) or `host` (the target's own IPv4 addresses), for IP lists | | `email_auth` | `dkim_selectors` (up to 10, for example `["google"]`) and `dmarc_policy` (`none`, `quarantine` or `reject`: the weakest policy that passes). `url` is the sending domain | | `smtp`, `imap`, `pop`, `ftps` | `security`: `tls` (from the start) or `starttls` | | `udp` | `payload_base64` (1–512 bytes) and an optional `expected_text` | | `minecraft` | `edition`: `java` (default; TCP, the SRV record or port 25565) or `bedrock` (UDP, port 19132) | | `group` | `member_ids` (monitors in the same workspace) and `group_policy` (`all` or `any`) | | `page_speed` | `api_key` (Google PageSpeed Insights), `strategy` (`mobile` or `desktop`), `minimum_score` (0–100) | | `malware` | `api_key` (Google Web Risk) | Network checks (`tcp`, `ssh`, mail, `ftps`, `ntp`, `udp`, `minecraft`, `ssl`) take `host` or `host:port` in `url`; `tcp` and `udp` need the port. `http` and `api` monitors alert 30, 14, 7, 3 and 1 days before their domain registration expires and, for HTTPS, before their TLS certificate expires without having been renewed: certificate alerts start at a sixth of the certificate's lifetime, at most 14 days ahead, after automatic renewal (Let's Encrypt, Cloudflare) should have happened. Send `"expiry_alerts": false` to turn both off. The monitor reports `domain_name`, `domain_expires_at`, `cert_issuer` and `cert_expires_at` after the daily checks. On paid plans, `http` and `api` monitors can also alert on vulnerabilities in the software their website runs: send `"vulnerability_alerts": true`, and optionally `"vulnerability_severity"` (`low`, the default, `medium`, `high` or `critical`) for the lowest severity that alerts. Actively exploited vulnerabilities and releases reaching end of support always alert. Free plans get `422` with `plan_required`. The software is found by a weekly scan of the website (`POST /monitors/{id}/vulnerabilities/scan` runs one now) and can be told directly: `POST /monitors/{id}/software/import` takes a file's text and optional `filename` (package.json, package-lock.json, yarn.lock, pnpm-lock.yaml, composer.json/.lock, requirements.txt, Pipfile.lock, poetry.lock, Gemfile.lock, go.mod, Cargo.lock, pom.xml, .csproj, Dockerfile, docker-compose.yml, .nvmrc, .tool-versions) or a plain list with one `name version` per line, such as `Debian 12`, `nginx 1.24.0` or `npm:lodash 4.17.21` ([format and supported files](/docs/software)). `POST /monitors/{id}/software` adds one entry. `GET /monitors/{id}/vulnerabilities` returns the software and the findings: exploited and most severe first, each with `severity`, `fixed_in`, `fix_version` (the first version that fixes it), `exploited_on` (CISA's exploited list) and `version_known` (false when the advisory only may apply). After upgrading, `POST /monitors/{id}/vulnerabilities/{finding_id}/fixed` records the new version (by default `fix_version`, or `{"version": "…"}`) and checks again. These endpoints answer `402` with `plan_required` on plans without vulnerability alerts. A `heartbeat` monitor's `heartbeat_url` takes a GET or POST after each successful run. Add `/fail` to the URL to report a failed run: the incident opens at once instead of after the failure threshold. Invalid options are reported as `422` with the offending fields. ## Notification contacts A workspace alerts its notification contacts when a monitor goes down and recovers. Add them with `POST /api/v1/alert-channels`: ```bash curl https://upheron.com/api/v1/alert-channels \ -H "X-API-Key: $UPTIME_MONITOR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "Ops", "kind": "email", "destination": "ops@example.com"}' ``` An email contact receives a confirmation link and is alerted only after it is opened. New monitors alert every contact unless you pass `alert_channel_ids`. Pay-as-you-go workspaces can use email and webhook contacts. ## Errors Errors have a JSON body with a machine-readable `error` and a human-readable `message`; validation errors add `fields`. ```json { "error": "validation_failed", "message": "Some fields are invalid.", "fields": { "url": ["can't be blank"] } } ``` | Status | Meaning | |---|---| | 400, 422 | The request is invalid; read `error`, `message` and `fields` | | 401 | Missing, invalid, revoked or expired token | | 402 | Payment required; see [Payments](/docs/payments) | | 403 | The token lacks the `write` scope | | 404 | No such resource in this workspace | | 429 | Rate limited; wait `Retry-After` seconds | | 5xx | Temporary problem; retry with backoff (and an `Idempotency-Key` when creating) | --- # Rate limits The API is free to read, so it is rate limited to keep it fast for everyone. Limits apply per API token, in one-minute windows. | What | Limit | |---|---| | Reads (`GET`) | 300 requests per minute per token | | Writes (everything else, including quotes and payments) | 60 requests per minute per token | | Failed authentication | 60 per minute per IP address | | Quotes without an account | 30 per minute per IP address | | Paid monitors | 1,000 per workspace | ## Headers Successful responses report the current window: | Header | Meaning | |---|---| | `RateLimit-Limit` | Requests allowed in this window | | `RateLimit-Remaining` | Requests left in this window | | `RateLimit-Reset` | Seconds until the window resets | When you go over, the response is `429 Too Many Requests` with `"error": "rate_limited"` and a `Retry-After` header in seconds. Wait that long, then continue. ```python import time, requests def get(url, key): while True: response = requests.get(url, headers={"X-API-Key": key}) if response.status_code != 429: return response time.sleep(int(response.headers.get("Retry-After", "5"))) ``` ## Polling status Monitors are checked on their own schedule (every 10 seconds at the fastest), so polling more often than a monitor's interval returns the same answer. To watch many monitors, call `GET /api/v1/monitors` once per interval instead of fetching each monitor; it returns every monitor's `current_status`. To be told about outages instead of polling, add a [notification contact](/docs/api#notification-contacts). ## Fair use UpHeron monitors services you operate or are allowed to monitor. Using monitors to load-test, flood or probe third-party services is not allowed. Tokens or workspaces used for abuse are revoked without refund. --- # Using UpHeron from an AI agent UpHeron is built to be used by agents as well as people. An agent can find out what UpHeron does, check whether a site is up, add monitors, and pay for them, without a human filling in forms. ## Discovery | Resource | What it is | |---|---| | [`/auth.md`](https://upheron.com/auth.md) | How an agent gets API access by itself, for its user or on its own ([auth.md](/docs/auth-md)) | | [`/mcp`](/docs/mcp) | MCP server: every operation as a tool, with MPP payments | | [`/llms.txt`](/llms.txt) | An index of the documentation for language models | | [`/llms-full.txt`](/llms-full.txt) | The whole documentation as one Markdown file | | [`/openapi.json`](/openapi.json) | OpenAPI 3.1 description of the API, with MPP payment offers (`x-payment-info`) on the paid operations and service details (`x-service-info`) | | Any page with `Accept: text/markdown` | The page as Markdown instead of HTML | | Any page URL plus `.md` | The same, for clients that cannot set headers: [`/docs/agents.md`](/docs/agents.md) | ```bash curl -H "Accept: text/markdown" https://upheron.com/docs/payments ``` Markdown responses have `Content-Type: text/markdown; charset=utf-8`, an `x-markdown-tokens` header with an estimate of their length in tokens, and `Vary: Accept`. ## Getting access - **Your user gives you a token.** They create one in **Settings → API tokens**; send it in `X-API-Key`. - **You get access yourself with [auth.md](/docs/auth-md).** Register at `POST https://upheron.com/agent/identity` with your user's email (`service_auth`); they open a link and type the code you show them, and you receive access tokens for their workspace. Or register as `anonymous` to act alone in an agent workspace of your own that your user can claim later. Follow [`/auth.md`](https://upheron.com/auth.md) step by step. - **You pay without an account.** `POST /monitors` without a token answers `402`; paying creates an agent workspace and returns its key. See [Payments](/docs/payments#without-an-account). ## Recommended workflow 1. **Check your account.** `GET /api/v1/account` returns free plan slots, credit and current prices. `GET /api/v1/locations` lists the location codes you can use. 2. **Quote before creating.** `POST /api/v1/monitors/quote` with the monitor you want returns `amount_due_cents`. If it is `0`, creating is free (a plan slot or credit covers it). 3. **Create with an idempotency key.** `POST /api/v1/monitors` with `Idempotency-Key: `. If it answers `402`, let your MPP client pay; keep the same idempotency key on any retry of your own. 4. **Read status.** `GET /api/v1/monitors` returns every monitor's `current_status`. Poll no faster than the monitor's `interval_seconds`. 5. **Look closer when something is wrong.** `GET /api/v1/monitors/{id}` has uptime for 24 hours, 7 days and 30 days, recent results with errors, and incidents. `GET /api/v1/monitors/{id}/results` returns up to 500 results, and `GET /api/v1/monitors/{id}/uptime?from=2026-01-01&to=2026-03-31` daily uptime for any range: it is kept for as long as the monitor exists. 6. **Watch the website's software** (Pro plans). Set `"vulnerability_alerts": true` on a website monitor. If you can see the server, send what it runs with `POST /api/v1/monitors/{id}/software/import`: a lockfile or Dockerfile, or a list with one `name version` per line from `cat /etc/os-release`, `nginx -v`, `php -v` and the like. `GET /api/v1/monitors/{id}/vulnerabilities` lists what affects it and the versions that fix it. [Software lists](/docs/software) has the format. ### Reading status | `current_status` | Meaning | |---|---| | `pending` | Not checked yet | | `up` | The latest checks passed | | `down` | The latest checks failed at enough locations (see `confirmation_threshold`) | | `degraded` | Responding, but outside a threshold such as latency | An incident opens after `failure_threshold` failed checks in a row (by default the first) and resolves after `recovery_threshold` successes. A monitor with `"status": "paused"` is not being checked; one with `"billing_state": "overdue"` was paused because its monthly renewal was not paid. ## Instructions for your agent Paste this into your agent's instructions or tool description, and give it a token in `UPTIME_MONITOR_API_KEY`: ```text You can monitor websites and APIs with UpHeron (https://upheron.com). - API base: https://upheron.com/api/v1 ; send the header X-API-Key: $UPTIME_MONITOR_API_KEY on every request. - Docs as Markdown: https://upheron.com/llms-full.txt ; API schema: https://upheron.com/openapi.json - Reading is free: GET /monitors (all statuses), GET /monitors/{id} (uptime, results, incidents), GET /monitors/{id}/uptime (daily uptime, ?from=&to=), GET /incidents. - To add a monitor: POST /monitors/quote first; then POST /monitors with an Idempotency-Key header. Body: {"name": "...", "url": "https://...", "interval_seconds": 300, "locations": [""]} - If a request returns 402 Payment Required, it costs money: tell the user the amount_due and ask before paying, unless they have already approved a budget. Monitors cost $0.01 per location per month, or $0.10 per location for life. - On 429, wait Retry-After seconds. Never poll a monitor faster than its interval_seconds. - Pro plans: with "vulnerability_alerts": true on a website monitor, POST /monitors/{id}/software/import {"content": ""} tells it what the server runs; GET /monitors/{id}/vulnerabilities lists what affects it. ``` Asking before paying is a good default. Approve a budget once (for example "up to $1 for monitors this week") so the agent can act alone within it. ## Example prompts - "Is shop.example.com up right now, and has it had any incidents this week?" - "Monitor https://api.example.com/health every minute from Frankfurt and New York, and email ops@example.com when it goes down." - "Add lifetime monitors for these five client websites; you can spend up to $0.50." - "Turn on vulnerability alerts for shop.example.com and import this server's composer.lock and the output of `nginx -v` and `php -v`." ## No account? Pay as you go An agent with an MPP wallet can create a monitor without any account: send `POST /api/v1/monitors` without `X-API-Key`, pay the `402`, and store the `api_key` from the response. See [Payments → Without an account](/docs/payments#without-an-account). --- # Agent sign-up with auth.md UpHeron supports [auth.md](https://workos.com/auth-md), an open profile for agent registration. An AI agent that supports it gets API access by itself: - **For its user.** The agent knows its user's email (or its agent provider vouches for the user). The user opens a link, signs in and types a 6-digit code the agent shows them. The agent then works in the user's workspace. - **On its own.** The agent signs up anonymously and gets a pay-as-you-go agent workspace straight away, like an agent that [pays without an account](/docs/payments#without-an-account). Its user can claim it later: its monitors, history and credit move into the user's workspace. Nobody copies an API token around, and the user can disconnect the agent at any time. Agents read the instructions at [`/auth.md`](https://upheron.com/auth.md). Point an agent at it: ```text Get access to UpHeron by following https://upheron.com/auth.md, acting for me (my email is you@example.com). ``` ## How it works 1. The agent finds the endpoints in the [authorization server metadata](https://upheron.com/.well-known/oauth-authorization-server), or from the `WWW-Authenticate` header of any `401` from the API. 2. It registers at `POST /agent/identity` as `service_auth` (with the user's email), `identity_assertion` (with an ID-JAG from a trusted agent provider) or `anonymous`. 3. To act for a person, it shows them a link and a 6-digit code. The person signs in, checks what the agent asks for and types the code. The agent waits by polling the token endpoint. 4. The agent receives an **identity assertion** and exchanges it at `POST /oauth2/token` for access tokens that last an hour. The tokens start with `pga_` and work like [API tokens](/docs/authentication) on the API and the [MCP server](/docs/mcp), with the `read` and `write` scopes. | Endpoint | Purpose | |---|---| | `GET /.well-known/oauth-protected-resource` | Protected resource metadata (RFC 9728) | | `GET /.well-known/oauth-authorization-server` | Authorization server metadata (RFC 8414) with the `agent_auth` block | | `GET /auth.md` | Instructions for agents | | `POST /agent/identity` | Register | | `POST /agent/identity/claim` | Start or restart the code step | | `POST /oauth2/token` | Exchange an identity assertion (`urn:ietf:params:oauth:grant-type:jwt-bearer`) or poll the code step (`urn:workos:agent-auth:grant-type:claim`) | | `POST /oauth2/revoke` | Revoke an access token (RFC 7009) | | `POST /agent/event/notify` | Security events from trusted agent providers (RFC 8935) | ## What the user sees The link opens **Authorize an AI agent** on UpHeron. The person signs in or creates an account, and the page says what the agent asks for: - an agent asking to work in their workspace on their behalf; - an agent provider asking to link their account, so its agents can act for them from now on; - or an agent that signed up on its own, with how many monitors and how much credit move into their workspace. They type the code the agent showed them and press **Authorize agent**. The agent works in the workspace that is active when they authorize. Safeguards: - Only the person signed in with the email the agent gave can authorize it. - A code works for 10 minutes, and a link locks after 5 wrong codes. - The first link of an agent provider always asks the user, even when the provider vouches for the email. - Codes, links and tokens are stored only as hashes. ## Disconnect an agent **Settings → Connected agents** lists the agents with access to the workspace, when they were authorized and when they were last active. **Disconnect** ends an agent's access tokens and identity assertion immediately; it must be authorized again to come back. An agent can also revoke a single access token with `POST /oauth2/revoke`. An agent provider can revoke every token of a user it vouched for with a security event. ## Limits - At most 5 anonymous sign-ups per IP address per hour, and 20 `service_auth` sign-ups. - An anonymous agent's claim window is 7 days. An unclaimed agent keeps working in its own workspace. - A request to act for a user expires after 24 hours if nobody authorizes it. - Identity assertions from agent providers are accepted only from providers on this service's trust list, with a sign-in at the provider less than an hour old. --- # Software lists and imports On the Pro plan, website and API monitors can alert when a vulnerability is published for the software the website runs. A weekly scan finds what the website shows publicly: its CMS and plugins, framework, web server and some libraries. What it cannot see, such as the server's operating system, runtimes and the exact versions of your dependencies, you tell it with a file or a list. Add software in any of these places: - **The monitor form:** with "Newly discovered vulnerabilities" checked, choose files and type anything else under **Software the website runs**. Each is imported when you save. - **The monitor's page:** **Vulnerabilities → Import software** takes files and a list together; **Add software** adds one item. - **The API:** `POST /api/v1/monitors/{id}/software/import` with `{"content": "…", "filename": "…", "name": "…"}`, once per file, or `POST /api/v1/monitors/{id}/software` with `{"name": "…", "version": "…"}`. See the [API reference](/docs/api#vulnerabilities). - **MCP:** the `import_software` and `add_software` tools. See [MCP server](/docs/mcp). ## Code repositories A **Code repository** monitor watches a GitHub or GitLab repository instead of a website, on every plan. Every day (every hour on Pro and on paid monitors) it reads the repository's dependency manifests (the same files as below: package.json and lockfiles, Dockerfiles and compose files, composer, Python, Ruby, Go, Rust, Elixir, Java and .NET) and alerts on known and newly published vulnerabilities in them, like a website's software. - **URL:** `https://github.com/owner/repository` or `https://gitlab.com/group/project`. To read one branch or folder of a monorepo, use its URL: `…/tree/main/frontend` (GitLab: `…/-/tree/main/frontend`). - **What is read:** the default branch (or the one in the URL). Folders of installed dependencies, tests and examples are skipped (`node_modules`, `vendor`, `test`, `examples`…), and a folder's package.json or composer.json is skipped when its lockfile is next to it, since the lockfile has the exact versions. At most 50 files, 5 MB each. - **Private repositories** need the Pro plan, or a paid monitor bought through the API, and an access token: on GitHub a fine-grained token with read-only access to the repository's Contents, on GitLab a token with `read_api`. It is stored encrypted and sent only to GitHub's or GitLab's API. - **Alerts** go to the monitor's contacts for vulnerabilities at the severity you choose, and when the repository cannot be read (a moved repository, an expired token); the next read that works resolves that. - **Fixing:** update the dependency in the repository; the next read, or **Check now** on the monitor's page, picks it up. An hourly read that finds changed dependencies checks them at once; newly published vulnerabilities are checked for daily on every plan. Entries you add by hand stay, and **Ignore** stops checking an entry. Through the API or MCP, create a monitor with `"kind": "repository"`, the URL in `url` and, for a private repository, `"check_options": {"token": "…"}`. ## Plain-text list One piece of software per line: its name, then its version. Anything an AI assistant or a person writes in that shape works. ```text # Server Debian 12 nginx 1.24.0 PHP 8.2.12 OpenSSL 3.0.11 MariaDB 10.11.6 # Application WordPress 6.6.2 wp-plugin:woocommerce 9.3.3 wp-plugin:contact-form-7 5.9.8 npm:lodash 4.17.21 ``` The rules: - **One item per line**, the name first. The version is optional; without one, only advisories from the last 90 days that have no fixed release yet, or that attackers exploit, are listed for that item, marked "version unknown". - **Separators:** a space, `:`, `=`, `==`, `@` or `/`, or the version in parentheses. These all read as nginx 1.24.0: `nginx 1.24.0`, `nginx: 1.24.0`, `nginx==1.24.0`, `nginx@1.24.0`, `nginx/1.24.0`, `nginx (1.24.0)`, `nginx v1.24.0`. - **Operating systems** take their release number or codename: `Debian 12` or `Debian bookworm`, `Ubuntu 22.04` or `Ubuntu jammy`. A trailing `LTS` is ignored. - **Ignored:** empty lines, comments starting with `#`, headings and lines that end with `:` (`Server:`), list bullets (`-`, `*`, `1.`), Markdown emphasis and code marks, and Markdown table borders. A table row such as `| nginx | 1.24.0 |` reads as one item. - **Names** are either a name from the list below (case does not matter: `next.js`, `Next.js` and `nextjs` all work) or a prefixed package (next section). - **Use the installed version**, not a range: `1.24.0`, not `^1.24`. With several versions of one item, the lowest is kept. - **Lines that name nothing we know** are not imported; the import reports them back so you can fix them. The rest is imported. ### Prefixes for packages and anything else | Prefix | For | Example | |---|---|---| | `npm:` | npm packages | `npm:lodash 4.17.21`, `npm:@angular/core 17.0.8` | | `composer:` | PHP (Packagist) packages | `composer:guzzlehttp/guzzle 7.8.1` | | `pypi:` | Python packages | `pypi:requests 2.31.0` | | `gem:` | Ruby gems | `gem:nokogiri 1.15.4` | | `go:` | Go modules | `go:github.com/gin-gonic/gin 1.9.1` | | `maven:` | Java packages, as group:artifact | `maven:org.apache.logging.log4j:log4j-core 2.14.1` | | `nuget:` | .NET packages | `nuget:Newtonsoft.Json 13.0.1` | | `cargo:` | Rust crates | `cargo:tokio 1.32.0` | | `wp-plugin:` | Any WordPress plugin, by its wordpress.org slug | `wp-plugin:polylang 3.6.4` | | `wp-theme:` | Any WordPress theme, by its slug | `wp-theme:astra 4.6.0` | | `cpe:` | Any product NVD knows, as vendor:product | `cpe:zabbix:zabbix 6.0.21` | A plugin's slug is the folder name in its wordpress.org address: `wordpress.org/plugins/contact-form-7` is `wp-plugin:contact-form-7`. ### Names **Operating systems** (checked for end of support): AlmaLinux, Alpine Linux, Amazon Linux, CentOS, Debian, FreeBSD, Red Hat Enterprise Linux, Rocky Linux, Ubuntu, Windows Server. **Software:** .NET, Angular, AngularJS, Apache HTTP Server, Apache Tomcat, Astro, Bootstrap, Bun, Caddy, Concrete CMS, Confluence, Coolify, Craft CMS, curl, Discourse, Django, Docker Engine, Docusaurus, Drupal, Elasticsearch, Elixir, Erlang/OTP, Express, Flask, Gatsby, Ghost, Gitea, GitLab, Go, Grafana, Grav, Gunicorn, HAProxy, Hugo, Jekyll, Jenkins, Joomla!, jQuery, jQuery UI, Keycloak, Kubernetes, Laravel, LiteSpeed Web Server, Magento / Adobe Commerce, MariaDB, Matomo, MediaWiki, Microsoft IIS, MongoDB, Moodle, MySQL, Next.js, Nextcloud, nginx, Node.js, Nuxt, OpenLiteSpeed, OpenResty, OpenSSH, OpenSSL, PHP, phpMyAdmin, PostgreSQL, PrestaShop, Python, React, Redis, Roundcube, Ruby, Ruby on Rails, Shopware, Spring Boot, Spring Framework, Strapi, Svelte, SvelteKit, Symfony, Traefik, TYPO3, Vue.js, WordPress. **WordPress plugins** known by name (any other plugin works as `wp-plugin:`): Advanced Custom Fields, All in One SEO, Contact Form 7, Elementor, Jetpack, LiteSpeed Cache, Slider Revolution, UpdraftPlus, WooCommerce, Wordfence Security, WPForms, WPForms Lite, Yoast SEO. ### Ask an AI assistant to write it Give this to an assistant that can see your server or project, then paste its answer as the list. The same prompt is behind **Prompt for an AI assistant** in the dashboard. ```text List the software this website and its server run: the operating system, web server, runtimes, frameworks, databases, hosting tools and the CMS with its plugins. Write one item per line as "name version", such as "Debian 12" or "nginx 1.24.0". Prefix packages with their manager: npm:lodash 4.17.21, composer:vendor/package 1.2.3, pypi:django 4.2.7, wp-plugin:slug 1.0. Use exact installed versions, leave out a version you are unsure of, and write nothing else. ``` On a server, `cat /etc/os-release`, `nginx -v`, `apache2 -v`, `php -v`, `node -v`, `python3 --version`, `openssl version` and `mysql --version` give the exact versions. ## Files Choose one or more files, or send each with its `filename`, which tells the format; a file without a name is recognized by its contents. Alongside them, type anything the files do not cover, such as the server's operating system or Coolify, as a list. A website with a frontend and a backend often has two files with the same name. Give each a label, such as `frontend/package.json` and `backend/package.json` (in the API, the `name` field): each label keeps its own list, and importing it again replaces only that list. | File | What is read | |---|---| | `package-lock.json`, `npm-shrinkwrap.json` | Every installed package with its exact version, except dev dependencies | | `yarn.lock`, `pnpm-lock.yaml`, `bun.lock` | Every package with its exact version (for bun.lock, except the root's dev dependencies) | | `package.json` | `dependencies` (not dev dependencies) and the Node.js version in `engines`. Ranges such as `^14.1.0` are kept without a version: import the lockfile for exact versions | | `composer.lock` | Every package except `packages-dev`, with exact versions | | `composer.json` | `require`, without versions unless pinned | | `requirements.txt` | Packages; versions pinned with `==` | | `Pipfile.lock`, `poetry.lock` | Packages with exact versions, except dev ones | | `Gemfile.lock` | Every gem with its exact version | | `go.mod` | The Go version and every required module | | `Cargo.lock` | Every crate from crates.io | | `mix.lock` | Every Hex package with its exact version | | `pom.xml` | Dependencies with literal versions (not test scope), and the Spring Boot version of `spring-boot-starter-parent` | | `*.csproj`, `packages.config` | NuGet packages and the .NET version (`TargetFramework`) | | `Dockerfile` | The image the final stage runs, through any stages it starts from: `php:8.2-fpm-bookworm` is PHP 8.2 on Debian 12, `nginx:1.25-alpine3.18` is nginx 1.25 on Alpine 3.18. `ARG` defaults are applied | | `docker-compose.yml`, `compose.yaml` | Every service's image, the same way | | `.nvmrc`, `.node-version`, `.python-version`, `.ruby-version` | That runtime's version | | `.tool-versions` | Node.js, Python, Ruby, PHP, Go and .NET versions | ## What happens to imports - Each import is listed under its label (by default the file name) on the monitor's page. **Importing under the same label again replaces what it imported before**, so re-import a lockfile after an upgrade. Typed lists are labelled "software list", so a new list replaces the last one. - Software you add by hand wins over imports, and both win over what the scan detects. Removing a detected item ignores it, so the next scan does not add it back. - The monitor is checked again right after an import, and alerts follow the usual rules: new vulnerabilities from the last 30 days at the monitor's severity, actively exploited ones, and releases reaching end of support. Older vulnerabilities found in an import are listed without an alert. ## Marking a vulnerability fixed Each finding on the monitor's page, and in the alert email, has two actions: - **Mark fixed:** records that the software now runs the first version that fixes it, on the release line it was on (14.1.0 with a fix in 14.2.25 becomes 14.2.25). For a release that lost support, the newest supported release. - **Mark fixed with version…:** records the exact version you installed. Either one sets that software's version and checks the monitor again: findings that no longer apply disappear, and one that still applies stays, marked with the version. In the email they open a confirmation page; nothing changes until you press its button, so mail scanners that open links do not mark anything fixed. They work for 10 days, without signing in, until the contact is turned off. Marking something fixed only records the version. If the website's next scan still shows the old version, or a re-imported lockfile has it, that wins and the finding comes back. ## Limits Files up to 5 MB; a plain list up to 1,000 lines; up to 2,000 items per import and 3,000 per monitor.