Docs/API
Updated Oct 15, 2026 · 3 min read

API

Send logs and events with a key. Everything else runs on your signed-in session, so there is no admin token to leak.

Ingest: POST /i

POST /v1/event is the same endpoint.

curl -X POST https://upcontrol.io/i \
-H "X-Upcontrol-Key: $UPCONTROL_API_KEY" \
-d '{ "msg": "payment_failed", "level": "warn", "provider": "stripe" }'
# 200 {"accepted":1}
KeyX-Upcontrol-Key, Authorization: Bearer, ?key= or a top-level key field. The first one found wins.
BodyAny of NDJSON, JSON, plain text, syslog, OTLP, Sentry, Alertmanager or Loki. The format is detected, not declared.
Extra fieldsKept as labels, up to 64 per line.
Errors413 over 16 MiB. 503 spool_full with Retry-After: 30 under overload. A line is never refused for what it contains.
LimitsNone on secret keys; under load, debug lines go first. Public keys are rate-limited.
RetriesA byte-identical retry is counted once.

Management: /v1

PATCH /v1/monitors/{id}
{ "interval": "1m" } # from a Free project
# 402 {"error":{"code":"plan_limit_exceeded","message":"…","upgrade":{"reason":"…"}}}
AuthYour browser session (the uc_session cookie).
Errors{ "error": { "code", "message" } }. 400 for bad input; 402 for a plan limit, with error.upgrade.reason.
LimitsSign-in only: 10 magic links per IP per 5 minutes, then 429.
ListsBounded by your plan, no pagination. Incident history returns the latest 20.

Keys

Add, revoke and rotate them on the Connect page.

Secretuc_live_…. Lives in .env on your server. Sends logs and events, and reads, writes and adds dashboards (npx upcontrol board). It can't rename or delete one, read your logs, or change checks, alerts or billing.
Publicuc_pub_…. Safe in a browser bundle. Sends named events only, and only from the origins you list, matched exactly.
How manyUp to 5 live keys per project. Use one per environment or service, so revoking a leaked one breaks nothing else.
RevokeStops the key at once. The row stays in the list, marked revoked.
Rotate every keyIssues new keys. The old ones keep working for 24 hours, then stop.

From a browser

The first way in is one script tag. Paste it into the <head> of every page. The key comes from Sources, Add it to a website, and works only from the addresses listed on it.

With a coding agent it is one command: npx upcontrol web https://your-site.com http://localhost:3000 mints the key with the project key already in .env and prints the tag, and the agent puts it where your framework keeps the <head>. Name every address the site answers on, local dev included: a rerun never widens the key.

<script defer src="https://upcontrol.io/uc.js" data-key="uc_pub_…"></script>

How to add it on your platform

WordPressA header-code plugin such as WPCode, or the theme's header.php.
WebflowSite settings, Custom code, Head code.
ShopifyOnline Store, Themes, Edit code, theme.liquid, before </head>.
WixSettings, Custom code, Head.
SquarespaceSettings, Advanced, Code injection, Header.
TildaSite settings, More, HTML code for the HEAD.
Google Tag ManagerA Custom HTML tag firing on All Pages.
Next.js<Script src="https://upcontrol.io/uc.js" data-key="uc_pub_…" strategy="afterInteractive" /> in the root layout.
Any other sitePaste it before </head>.
What it recordsPage views with their path, referrer, UTM tags, country, device, operating system and browser; clicks, rage clicks, mouse movement and scroll depth for heatmaps.
PrivacyNo cookies, nothing stored on the visitor's device, and addresses are never stored: a visitor is a daily-rotating hash, so the same person counts once a day. Text on the page and anything typed are never read.
HeatmapsThe board's Heatmap card has a Heatmap button on every page. It opens the live page with the map drawn over it, for the device your window's width falls in (under 768px phones, under 1024px tablets).
Plan limitsA visit is one visitor on one day, counted per month across the workspace; past the plan's visits recording pauses until the 1st, unseen by visitors, and heatmaps are kept for the plan's busiest pages by views, at most 31 days back.

Sending your own events

What money depends onA payment or a signup is sent from your server, never from a page. A page sends clicks and views.
You have a backendPost events to your own route and call track() there. No key in the bundle, ad blockers do not cut a request to your own domain, and the visitor comes from your session.
A static siteUse a public key. It is not a secret: it sends named events only, only from the addresses you list, and known bots are dropped.
NeverA secret key in a bundle. A VITE_ or NEXT_PUBLIC_ variable ships its value to every visitor.

Through your own server:

const ALLOWED = new Set(['page_view', 'buy_clicked']); // the server names the events
app.post('/api/track', (req, res) => {
res.sendStatus(204); // answer first: telemetry never holds a page
const name = req.body?.name;
if (!ALLOWED.has(name)) return;
track(name, { 'uc.actor': 'u_' + req.session.userId }); // prefixed: a bare 13-19 digit id is scrubbed as a card
});

With a public key, from a static site. Get one on the Connect page, under Add it to a website.

const line = JSON.stringify({ msg: 'page_view', 'uc.event': true, 'uc.actor': visitorId }) + '\n';
fetch('https://upcontrol.io/i?key=' + PUBLIC_KEY, {
method: 'POST',
body: line,
keepalive: true, // survives the page closing
headers: { 'Content-Type': 'application/x-ndjson' },
});

Public endpoints

POST /public/checkProbe a URL. No account.
POST /public/watchOpen a host's status page. host is required. A page the host already has, claimed or not, is answered as it is (created: false). A new host gets an unclaimed page with one check, the host itself (created: true), if it answers: otherwise 400 unreachable carries what the probe found, and nothing is created. Signed in or not, the answer is the same: a page becomes yours when you claim it. targets counts only with email, which also creates an account and mails a sign-in code.
GET|POST /public/ping/{token}A heartbeat ping.