Authentication
API key shape
ck_live_[32 alphanumeric characters]
ck_test_[32 alphanumeric characters]
Prefix declares environment:
ck_live_*— production keys. Hit live data + bill against your tier quota.ck_test_*— sandbox keys. Same endpoints; flagged on the server side for "this is a test integration"; useful for early integration before you flip a live key into production.
The 32-character body is base62 — ~190 bits of entropy, more than enough collision resistance even at billions of keys.
How to use the key
Set the X-API-Key header on every authenticated request:
curl 'https://crate.0xhoneyjar.xyz/api/v1/masters/12345' \
-H 'X-API-Key: ck_live_AbCdEfGhIjKlMnOpQrStUvWxYz012345'
That's it. No OAuth dance, no token refresh, no signed-request gymnastics.
Where keys come from
Self-serve (cards via Stripe Checkout):
- Sign up at
crate.0xhoneyjar.xyz/api/signup - Stripe Checkout flow at your chosen tier
- Key is issued + emailed when the webhook fires (typically under 2s after Stripe payment confirmation)
- The key plaintext is shown EXACTLY ONCE — in the email + the dashboard at first load. After that, the dashboard only shows the prefix (e.g.
ck_live_AbCdEfGh). Lost the key? Rotate it.
Sync tier (manual contract):
- Email
jani@hosaka.fmwith your use case + expected volume - Contract conversation; key issued via the admin route
- Personal Slack channel for ongoing support
Key lifecycle
| Action | Endpoint | Effect |
|---|---|---|
| Create | Stripe Checkout webhook OR /api/v1/admin/keys (Sync) | New active key issued |
| Rotate | POST /api/dashboard/keys/<id> | New key issued; old key → rotated status (returns 401) |
| Revoke | DELETE /api/dashboard/keys/<id> | Key → revoked status (returns 401 immediately) |
| Suspend | Automatic on 7d-grace after invoice.payment_failed | Key → suspended (returns 402 payment_required) |
| Reactivate | invoice.payment_succeeded webhook | suspended → active |
Authentication failures (errors)
| Code | Body | Meaning |
|---|---|---|
| 401 | {"error": "missing_api_key"} | No X-API-Key header on an authenticated endpoint |
| 401 | {"error": "invalid_api_key"} | Key doesn't match any active row |
| 401 | {"error": "revoked_api_key"} | Key was explicitly revoked or rotated |
| 402 | {"error": "payment_required"} | Customer is past_due or key is suspended |
See error codes for the full list.
Storing keys securely
- Use a secret manager (1Password, AWS Secrets Manager, GitHub Actions secrets, Vault, etc.)
- Never commit the plaintext key to a public repo
- Never log the plaintext key
- The prefix (first 12 chars) is safe to share for support — never share the full key
- Rotate immediately if the key may have been exposed (commit, log dump, screenshot)
What's public (no key)
Two surfaces are unauthenticated so you can discover the API before you have a key: GET /api/v1 (the self-describing root index) and GET /api/v1/openapi.json (the spec). Every data endpoint — search, the dossiers, bandcamp, breakouts, tastemakers, resolve, masters, facets — requires an X-API-Key.
(Earlier builds left /api/v1/search open at a per-IP limit; as of the cycle-078 wall it requires a key like every other data endpoint.)