API keys, scopes, IP allowlists, and key rotation.
Every data route on api.kash.bot requires an API key. The only unauthenticated endpoints are the infra-meta routes: /v1/health, /v1/ready, /v1/openapi.json, /v1/docs.
The 32-character random suffix gives roughly 190 bits of entropy. The prefix is informational — the server validates the full key against a stored HMAC under a server-side pepper. Never log or commit the plaintext.
A scope is a coarse-grained permission. Every endpoint declares the scope(s) it requires. The requireScope middleware fails closed with 403 INSUFFICIENT_SCOPE when a key lacks one.
Scope
What it grants
markets:read
GET /v1/markets, GET /v1/markets/{id}, GET /v1/markets/{id}/predictions
markets:quote
GET /v1/markets/{id}/quote
portfolio:read
GET /v1/portfolio, GET /v1/portfolio/positions
trades:read
GET /v1/trades, GET /v1/trades/{id}, GET /v1/traces/{id}
trades:write
POST /v1/trades, POST /v1/trades/{id}/confirm
webhooks:manage
GET /v1/webhooks/events, POST /v1/webhooks/events/{id}/redeliver
auth:manage
POST /v1/auth/api-keys/me/webhook-secret/rotate, GET /v1/account/usage
Default scopes by tier. Self-served free and developer keys default to markets:read, markets:quote, portfolio:read. Admin-issued enterprise and mm keys add trades:read, trades:write. You can override at issuance — request the scopes that match your use case, no more.
Optional per-key. When set, requests from any IP not in the list fail closed with 403 IP_NOT_ALLOWEDbefore the route handler runs.Format: comma-separated IPv4/IPv6 addresses or CIDR ranges. Set in the webapp UI or via the admin CLI at issuance time.
10.0.0.0/8,2001:db8::/32,203.0.113.45
We strongly recommend setting an allowlist for production keys. It collapses an entire class of “key was leaked from a logfile” incidents.
API keys do not expire automatically. Rotate them when:
A team member with access leaves
A key may have been logged or committed to a repo
You’re tightening security posture before a major release
To rotate:
Issue a new key in the webapp.
Roll it out to your services.
Verify the new key is in use (check /v1/account/usage — request counts on the old key should be zero).
Revoke the old key in the webapp.
There is no automated dual-validity overlap window for API keys; the rotation is sequential. (Webhook secrets do have a 7-day rotation overlap — see Secret Rotation.)
Body signing for trades:write (optional, hardened)
For the POST /v1/trades route, you can opt into HMAC-signed request bodies as a defence-in-depth layer against in-flight tampering. Send the signature in X-Kash-Signature using the same Stripe-style format as our webhook signatures:
The HMAC is computed over <unix-ms>.<raw-body-bytes> under your key’s webhook_secret. If the header is present, the server verifies it; if absent, the route still works (the API key is the canonical authentication). Use it when your threat model includes a compromised TLS-terminating proxy.
Key is valid but doesn’t carry the scope this route requires.
403
IP_NOT_ALLOWED
Source IP not in this key’s allowlist.
401
REQUEST_SIGNATURE_INVALID
Body-signing was opted into and failed verification.
All auth failures return RFC 7807 application/problem+json and are written to the per-key audit log. Repeated failures from the same source trigger the per-IP brute-force lockout.
The GET /v1/account/usage endpoint returns telemetry for the calling key — including 24h auth-failure and rate-limit-rejection counts. Useful for spotting key misuse early.