API keys
WhatIsUp.dev uses Stripe-style bearer tokens for API auth. Generate one in the dashboard, drop it in an Authorization: Bearer … header, you're done.
Format
Keys look like:
zpk_live_abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGH
zpk_live_andzpk_test_are the two prefixes. (We don't gate features by prefix today, but tooling can use it as a hint.)- The tail is 32 random URL-safe bytes. The prefix is in plaintext on the row; the tail is HMAC-SHA256'd with an env-side pepper before being stored. You see the full key exactly once, at creation; we never log it, we never can show it again.
Scope
Every key belongs to a customer. By default, the key can act on every instance the customer owns. You can also issue an instance-scoped key by passing instance_id at create time — that key can only send messages from / read deliveries for that one instance. Instance keys are useful for least-privilege apps (e.g. a marketing tool that should only ever post from one number).
Authentication header
Authorization: Bearer zpk_live_…Same shape as Stripe and 90% of the modern API world. 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 built-in "rotate without downtime" because issue-then-revoke covers it: both keys are valid in the overlap window.
Step 1 — 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"}'Step 2 — your app reads the new key from env, redeploys.
Step 3 — revoke the old one (the new key authenticates this call):
curl -sX DELETE "$WHATISUP_API/v1/api-keys/key_01J..." \
-H "Authorization: Bearer $WHATISUP_API_KEY"Rate limiting
Every authenticated request charges 1 token from a per-customer token bucket. The defaults are:
| Setting | Default |
|---|---|
RATE_LIMIT_BURST | 60 |
RATE_LIMIT_REFILL_PER_SEC | 1 |
Translation: 60-request burst, then sustained 1 req/s. Plenty for v1 customer workloads.
The response carries:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47
…and on rejection:
HTTP/1.1 429 Too Many Requests
Retry-After: 4
{"error":"rate_limited","message":"…"}
If you bump up against the limit a lot, talk to us before you start sharding API keys — that's a load-shedding workaround, and we'd rather raise your bucket.
Audit log
Every issue, every revoke, every authentication failure lands in the audit_events table. You can query it via the dashboard's Activity tab (or the underlying API). Audit rows survive the resource they reference — ON DELETE SET NULL on the FKs — so the trail outlives what it points at.