Skip to content

Open Beta – help us test! All listings are examples only.

Authentication

API key types, scopes, origin allowlists, rotation, and how to read rate-limit and quota headers.

Every request to https://wohno.de/api/v1/* is authenticated with an API key sent in the X-API-Key header. There are two key types — choosing the right one is the most important authentication decision you make.

Publishable vs. secret keys

PrefixTypeUse whereAllowed scopes
sk_live_*SecretServer-to-serverAny scope (incl. write / delete / *)
pk_live_*PublishableBrowser / embedRead-only whitelist (see below)

sk_test_* / pk_test_* exist for sandbox setups.

When to use which

  • Secret (sk_) — anywhere the key can stay secret: backends, cron jobs, CRM sync, server-to-server integrations. It may read, write and delete. A secret key is stored only as a bcrypt hash and returned in cleartext exactly once — copy it at creation time.
  • Publishable (pk_) — anywhere the key lands in a browser: embed widgets, public HTML, client-side JS. It may only carry the read-only whitelist and must have an origin allowlist. Publishable keys are deliberately browser-safe: their security comes from the scope whitelist plus the origin allowlist, not from secrecy.

Rule of thumb: if a value ever reaches a browser, it must be a pk_ key. Never put a secret key in a frontend bundle.

Scopes

Scopes follow the resource:action format (e.g. listings:read) and are hierarchical:

  • * grants full access.
  • resource:* grants all actions on that resource.
  • delete implies write, and write implies read.

A missing scope returns 403 INSUFFICIENT_SCOPE. The full scope registry lives in the conventions reference.

Publishable whitelist

A pk_ key may carry only these read scopes:

  • organizations:read
  • listings:read
  • embed:read
  • appointments:read
  • appointments:book

This is enforced twice (defense-in-depth): in the application layer and by a database trigger.

Origin and IP allowlists

  • Origin allowlist is required for pk_ keys and optional for sk_ keys.
    • Subdomain wildcards like https://*.example.com match exactly one subdomain level.
    • HTTPS is mandatory (except http://localhost).
    • Browsers send the Origin header automatically. If it is missing on a pk_ call, the request is malformed and returns 403 ORIGIN_REQUIRED.
    • An origin that matches no allowlist entry returns 403 ORIGIN_NOT_ALLOWED.
  • IP allowlist only makes sense for sk_ keys (IPv4 + IPv4 CIDR, max. 10 entries). Recommended for production server-to-server keys. A request from an IP outside the list returns 403 IP_NOT_ALLOWED.

Key rotation

Rotate a key without downtime using an overlap period:

  1. A new key is created with the same profile (type, scopes, allowlists); its raw value is returned once.
  2. The old key gets a rotation_expires_at = now() + overlapDays (default 7 days, 1–30 allowed).
  3. Both keys validate during the overlap window — deploy the new key, then retire the old one.
  4. After the window, the old key returns 401 KEY_ROTATED_OUT and is revoked by a nightly cleanup job.

Recommendation: rotate every 90 days; a 7-day overlap is enough for one deploy.

Rate-limit and quota headers

Every /api/v1/* response carries informative headers so you can self-throttle.

Rate limits (per key, 1000 req/h)

HeaderMeaningExample
X-RateLimit-LimitMax requests in the window1000
X-RateLimit-RemainingRequests left in the current window950
X-RateLimit-ResetISO time when the window resets2026-06-03T11:00:00.000Z
Retry-AfterSeconds until the next allowed request (429 only)42

Exceeding the limit returns 429 RATE_LIMITED. Use exponential backoff and respect Retry-After.

Quotas (per organization, monthly)

HeaderMeaningExample
X-Quota-LimitMonthly plan limit or unlimited10000
X-Quota-UsedCurrent monthly counter or unknown3450
X-Quota-ResetISO time of the monthly reset (Europe/Berlin)2026-07-01T00:00:00.000Z
X-Quota-Statusok / soft-warning / unavailablesoft-warning

soft-warning is set at ≥ 80 % usage. Quotas are tracking-only by default; once enforced, an overage returns 429 QUOTA_EXCEEDED.

Authentication errors

HTTPCodeWhen
401UNAUTHORIZEDX-API-Key header missing entirely
401INVALID_API_KEYKey unknown, revoked or expired
401KEY_ROTATED_OUTRotated key used after its overlap window
403INSUFFICIENT_SCOPEKey lacks the required scope
403ORIGIN_REQUIREDpk_ key called without an Origin header
403ORIGIN_NOT_ALLOWEDOrigin not in the key's allowlist
403IP_NOT_ALLOWEDClient IP not in the key's allowlist

Branch on the stable error.code string, never on the human-readable message.

Reference

For the canonical, code-verified details on authentication, scopes, error formats and headers, see the API conventions reference.