Skip to main content

Credentials & Security

CheckUpstream issues three kinds of credentials, each built for exactly one purpose. Pick the right one and the SDKs, REST API, and MCP server will refuse to accept any other type — so you can't accidentally ship a secret to the browser or paste a publishable key into a CI script.

The three credentials at a glance

CredentialPrefixWhere it livesWhat it doesIs it secret?
SDK Keycup_sdk_Server, browser, mobile SDK bundlesSends telemetry to the ingest endpointNo — safe to ship in client bundles
API Tokencup_api_Servers, CI, curl, Terraform, MCP env varsReads from the REST API and MCP toolsYes — treat like a password
AI Connectionn/a (OAuth)Claude Desktop, Cursor, any MCP clientAuthorizes an AI tool via OAuth 2.1 + PKCEShort-lived; refresh handled for you

The names and prefixes are load-bearing. Every SDK, server endpoint, and settings screen uses them in exactly one sense, so reading a snippet like cup_sdk_abc123 is enough to know it's safe to commit to source.


SDK Keys — publishable, write-only telemetry credentials

SDK Keys authenticate the CheckUpstream SDKs so they can send telemetry to https://ingest.checkupstream.com/v1/events. They are designed to be public — we expect them to appear in browser bundles, source repositories, mobile app binaries, and public CI configs. The security model does not depend on keeping them secret.

What an SDK Key can do:

  • POST telemetry events to the ingest endpoint, scoped to one project

What an SDK Key cannot do:

  • Read anything from the REST API
  • Call MCP tools
  • Read your dependency graph, incidents, runbooks, or team members
  • Access a different project, even in the same org

Safe-by-default mechanics:

  • Project scope — every SDK Key is bound to one project. A leak only affects that project's telemetry.
  • Origin allowlist — you can optionally restrict an SDK Key to specific browser origins (https://app.example.com). The ingest endpoint enforces the allowlist by checking the Origin header.
  • Rate limits — per-key and per-origin, so a bad actor can't flood the endpoint.
  • Revocable in one click — rotate from Settings → Credentials → SDK Keys.

Creating an SDK Key

  1. Go to Settings → Credentials → SDK Keys.
  2. Click Generate SDK Key.
  3. Pick a project (required) and optionally an allowed-origins list for production.
  4. Copy the key — you'll see the full value once; after that we only store the hash.
import { checkupstream } from "@checkupstream/sdk";

checkupstream.init({
  sdkKey: "cup_sdk_a1b2c3d4e5f6...",
});

API Tokens — secret, scoped, expiring

API Tokens are the credential for programmatic access to CheckUpstream. REST API calls, CI scripts, curl commands, and Terraform providers all use API Tokens.

They're secret. Treat them like a database password: never commit them to source, never put them in browser bundles, never paste them into public chat. If one leaks, revoke it and generate a new one.

What an API Token can do (subject to its scopes):

  • Read or mutate any org-scoped resource via /api/v1/*
  • Manage projects, incidents, alerts, runbooks

Scopes:

ScopeGrants
readAll GET endpoints across the org
writeGET + mutation endpoints (create/update)
adminFull access, including billing and team management

Lifecycle:

  • Required expiration — defaults to 90 days. Maximum 365. You can opt into a non-expiring token, but the UI makes you confirm it deliberately.
  • Rotation with grace period — rotating an API Token issues a new one and gives the old one a 24-hour grace window so long-running services don't drop requests.
  • Revoke — revocation is immediate and permanent.
  • Last-used-at — visible in the settings UI so you can spot tokens that are no longer in use.

Creating an API Token

  1. Go to Settings → Credentials → API Tokens.
  2. Click Generate API Token.
  3. Pick the minimum set of scopes you need.
  4. Pick an expiry (keep the default unless you have a reason not to).
  5. Copy the token — you'll see the full value once.
curl https://checkupstream.com/api/v1/incidents \
  -H "Authorization: Bearer cup_api_a1b2c3d4e5f6..."

AI Connections — OAuth for Claude, Cursor, and other MCP clients

Modern AI coding tools (Claude Desktop, Claude Code, Cursor, and other MCP clients) authorize against CheckUpstream over OAuth 2.1 + PKCE. You click Connect in your AI tool once, approve the CheckUpstream consent screen, and the tool gets a short-lived bearer token it refreshes automatically.

Why this is better than pasting a long-lived token into a config file:

  • No long-lived secret sitting in ~/.config/claude/ or .mcp.json
  • Tokens are short-lived and auto-rotate
  • You can see every connected AI tool and revoke it in one click
  • You can't accidentally commit a credential that no longer exists as a static string

You can see and manage connected tools in Settings → Credentials → AI Connections. See MCP Setup for the three-line config that wires up Claude Desktop, Claude Code, or Cursor.


Badges — no credential at all

Public dependency-health badges (/api/v1/badge/{org_slug}) are unauthenticated. This matches how shields.io, Codecov, Vercel, and every other README badge endpoint works: anyone with the slug can fetch an SVG of your current worst status.

Badge data is coarse (one of four statuses aggregated across all services) and doesn't expose service names, projects, or timelines. If your org has infrastructure sensitive enough that even the aggregate status shouldn't be public, turn badges off in Settings → Organization → Public Badges and the endpoint will return a neutral Private badge for everyone, including you.


How the system prevents misuse

Naming is the first defense, but the code backs it up at every layer:

  1. Different prefixes. Every credential has a distinctive prefix (cup_sdk_, cup_api_). Humans, regex-based secret scanners, and CI checks can all tell them apart at a glance.
  2. Different config property names. The SDKs take sdkKey, the REST API auth helper takes an API Token. Pasting the wrong value produces a type error in TypeScript or a runtime error with a clear "wrong credential type" message.
  3. Endpoint-level enforcement. The REST and MCP endpoints reject anything that isn't an cup_api_* token. The ingest endpoint rejects anything that isn't an cup_sdk_* key. The mismatch returns a 403 with an error message explaining which credential type to use.
  4. Origin allowlist for SDK Keys. Browser/mobile SDK Keys can be restricted to specific origins, so a key leaked in a client bundle can't be reused from an attacker's origin.
  5. SDKs refuse to initialise with the wrong credential. Pass cup_api_* to checkupstream.init({ sdkKey: ... }) and it fails at init time, before a single request leaves your process.
  6. Expiring API Tokens. Default 90-day expiry on new API Tokens. Rotation has a 24-hour grace window so running services don't drop requests during a key swap.
  7. Revocation is instant. Revoking any credential invalidates the hash immediately — no propagation delay, no "up to an hour" caveats.

Common questions

I have an old CHECKUPSTREAM_API_KEY env var. Which credential type is it? It's an API Token. The environment variable name still works (the SDK auto-init reads both CHECKUPSTREAM_SDK_KEY and CHECKUPSTREAM_API_KEY), but if you're setting up a new project, prefer CHECKUPSTREAM_SDK_KEY for SDK auto-init — the name makes it clear at a glance that the value is publishable.

Can I use one credential for both telemetry and REST? No. And that's on purpose. An SDK Key cannot call the REST API, and an API Token cannot call the ingest endpoint. This is how we guarantee that a leak in one surface doesn't compromise the other.

What happens if I commit an SDK Key to GitHub? Nothing bad — SDK Keys are publishable. The worst a leak can do is let someone submit telemetry events for your project (which is what the key is for). If you've set an origin allowlist, even that is limited to the allowed origins. That said, good hygiene is still to rotate leaked keys, just to keep your audit trail clean.

What happens if I commit an API Token to GitHub? Revoke it immediately in Settings → Credentials → API Tokens. API Tokens are secrets; a leak means someone can read (or mutate, depending on scope) your org's data. Generate a new one, grep your code for the old token, and double-check nothing else references it.

Can I have an API Token with no expiry? Yes — pass expiresInDays: null when creating, but the UI will make you confirm deliberately. Non-expiring tokens are discouraged because they silently outlive the people who created them.