Skip to content
WhatIsUp.dev

API keys

WhatIsUp.dev uses bearer tokens for API auth. Sign up, grab a key from the dashboard, drop it in an Authorization: Bearer … header, you're done.

Lifecycle

Keys are auto-provisioned on signup; rotation is issue-then-revoke.

Format

Keys look like:

zpk_2tQfTk9bJtq1Vj8x4nKxqL2g
  • One prefix: zpk_. There is no zpk_live_ / zpk_test_ distinction and no sandbox environment — earlier revisions of this page described both; neither was ever implemented. Every key is a live key.
  • The tail is 24 URL-safe base64 characters (18 random bytes, 144 bits of entropy), so a full key is 28 characters.
  • The first 12 characters — e.g. zpk_2tQfTk9b — are the key's prefix. That is what the dashboard and GET /v1/api-keys display, and it is safe to log.
  • Keys are stored as an HMAC-SHA256 digest under a server-side pepper. The plaintext is shown exactly once at creation. We never log it and cannot show it again.

Scope

Every key belongs to a customer. By default, a key can act on every channel under that customer. You can also issue a channel-scoped key by passing channel_id at create time — that key can only send messages from / read deliveries for that one channel. Channel keys are useful for least-privilege apps (e.g. a marketing tool that should only ever post from one number).

Shortcut: POST /v1/channels with "issue_scoped_key": true creates the channel and its bound key in one call, returning scoped_key.secret once.

Scopes

scopes narrows what a key may do; channel_id narrows where. Both are enforced. The vocabulary is <resource>.<read|write>read covers GET, write covers every other method — plus * (everything) and <resource>.*.

Resources: channels, messages, media, chats, contacts, groups, communities, newsletters, labels, blacklist, stories, calls, presences, profile, settings, business, webhooks, keys, events, limits, metrics.

A send-only key for one channel — the shape most integrations want:

{
  "name": "notifier",
  "channel_id": "8d653c66-e4ff-43ee-97da-3de5ad5680d4",
  "scopes": ["messages.write", "messages.read", "channels.read"]
}

A call outside the grant gets 403 insufficient_scope, with details.required and details.granted so the gap is visible.

An empty scopes array means UNRESTRICTED, not "no access". Keys issued before scopes were enforced carry [], so empty is treated as full account scope — narrowing it would have revoked every existing key. Your key is restricted only when you explicitly pass a non-empty scopes array at issue time.

Authentication header

Authorization: Bearer zpk_2tQfTk9bJtq1Vj8x4nKxqL2g

Don't put the key in URL query strings — they leak to logs, referrer headers, and CI screenshots.

Rotation

Issue a new key, swap your env, then revoke the old one. There's no separate "rotate without downtime" endpoint because issue-then-revoke covers it: both keys are valid in the overlap window.

Issue a new key (the existing key authenticates this call):

curl -sX POST "$WHATISUP_API/v1/api-keys" \
  -H "Authorization: Bearer $WHATISUP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"app-2026-q2"}'

Then your app reads the new key from env and redeploys. After the new key is in use, revoke the old one:

curl -sX DELETE "$WHATISUP_API/v1/api-keys/0f2c5ad1-8b47-4e93-a5d2-6c81e04fb7a9" \
  -H "Authorization: Bearer $WHATISUP_API_KEY"

Rate limits

Every authenticated request charges 1 token from a per-customer bucket. The defaults give you a 60-request burst and ~1 request / second sustained — plenty for most workloads.

The response includes:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47

…and on rejection:

HTTP/1.1 429 Too Many Requests
Retry-After: 4
{"error": {"code": "rate_limited", "message": "…"}}

If you bump up against the limit a lot, talk to us before sharding API keys — we'd rather raise your bucket.

Audit log

Every issue, every revoke, every authentication failure leaves an entry in the audit log. View it from the dashboard's Activity tab. Audit entries survive the resource they reference, so the trail outlives what it points at.