View as Markdown

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 (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.

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/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

link-cli 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:

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 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).

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.

From an MCP client

The MCP server 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:

# 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:

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.
  • 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:

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 problem details; see the MPP protocol for the full list.