---
title: Payments
description: How agents pay for extra monitors with the Machine Payments Protocol (MPP) over HTTP 402, with stablecoins or cards through Stripe. Monthly or lifetime pricing, prepaid credit, renewals, receipts and VAT.
---

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