API Key Lifecycle
API keys are the primary authentication method for the Soku Public API. This page explains how keys work from creation to revocation.
Overview
Creating a key
From the web app
- Go to Settings > API Keys
- Click Create Key
- Optionally enter a name (e.g., "Production", "Staging")
- Copy the key immediately — it is shown only once
From the API
Create keys programmatically using a Firebase ID token:
curl -X POST "https://<base-url>/api/v1/api-keys" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <firebase-id-token>" \
-d '{ "name": "My Production Key" }'
Response:
{ "apiKey": "sk_live_..." }
Key format
Keys follow the pattern: sk_live_ + 24 random bytes (base64url encoded).
Example: sk_live_a1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6
How keys are stored
When a key is created:
- The raw key (e.g.,
sk_live_...) is hashed using SHA-256 - The hash is stored as a document ID in the
apiKeyscollection - The document contains:
userId— the owner's Firebase UIDname— optional human-readable label (1-100 characters)createdAt— server timestamprevokedAt—null(set later if revoked)
The raw key is never stored. Only the hash exists in the database. If you lose your key, you must create a new one.
How authentication works
When you send a request with soku-api-key: sk_live_...:
- The
authenticateApiKeymiddleware extracts the header value - It computes
SHA-256(raw_key)to get the hash - It looks up
apiKeys/{hash}in Firestore - If the document doesn't exist →
401 unauthorized(invalid key) - If
revokedAtis set →401 unauthorized(key revoked) - If valid,
req.authis set to{ userId, keyHash, name }
After authentication, the requireActiveSubscription middleware checks:
- The user has a subscription document at
users/{uid}/subscription/current - The status is
trialingoractive currentPeriodEndis in the future
If the subscription check fails → 403 forbidden.
Reference: apps/functions/middleware/auth.js (both authenticateApiKey and requireActiveSubscription).
Using your key
Include the key in every Public API request:
curl -X POST "https://<base-url>/api/v1/posts" \
-H "Content-Type: application/json" \
-H "soku-api-key: sk_live_your_key_here" \
-d '{ ... }'
Important:
- The header name is
soku-api-key(lowercase) - Do not include the key in the URL or request body
- Keep your key secret — treat it like a password
Listing keys
List all keys for your account (requires Firebase ID token):
curl "https://<base-url>/api/v1/api-keys" \
-H "Authorization: Bearer <firebase-id-token>"
Response:
{
"keys": [
{
"id": "a1b2c3d4e5...",
"name": "Production",
"createdAt": "2025-01-15T10:00:00Z",
"revokedAt": null,
"tokenPreview": "sk_live_********c3d4"
}
]
}
The tokenPreview shows a masked version so you can identify which key is which. The id is the SHA-256 hash.
Revoking a key
Revoke a key when it's compromised or no longer needed:
curl -X DELETE "https://<base-url>/api/v1/api-keys/<key-hash-id>" \
-H "Authorization: Bearer <firebase-id-token>"
Response:
{ "revoked": true }
What happens:
revokedAtis set to the current server timestamp- All future API requests with this key immediately fail with
401 unauthorized - In-flight requests that already passed authentication are not affected
- The document is kept for audit purposes (not deleted)
You can only revoke your own keys. Attempting to revoke another user's key returns 403 forbidden.
Security best practices
- Never commit keys to source control. Use environment variables or a secrets manager.
- Use separate keys per environment (development, staging, production).
- Name your keys so you can identify them later (e.g., "CI/CD Pipeline", "Zapier Integration").
- Revoke unused keys immediately.
- Rotate keys periodically — create a new key, update your systems, then revoke the old one.
- Monitor usage — check
users/{uid}/apiRequestsin Firestore for unexpected activity.
Related
- Public API Reference — Full API documentation
- Rate Limiting — Request limits by tier
- Security Overview — Security architecture