Authentication & API keys
How KinoCloud API authentication works — API keys vs sessions, environments, scopes, rotation, and error responses.
Type: How-to · Audience: Anyone connecting to the KinoCloud API
How authentication works
Every request to /api/v1/* requires one of:
- API key —
Authorization: Bearer kc_live_xxxorkc_test_xxx - Browser session cookie — set automatically when you sign in to the web app (used by DCC plugins)
API keys are the right choice for scripts, CI, CLI tools, and AI agents. Browser sessions are for the DCC plugin auto-login flow.
Key environments
| Prefix | Environment | Billing |
|---|---|---|
kc_live_ |
Live | Charges real credits |
kc_test_ |
Test | Never billed; cannot submit jobs (see note) |
Use kc_test_ keys during development. Switch to kc_live_ for production.
Note: Test keys cannot place a billable job. POST /v1/jobs/submit returns 403 with code: TEST_KEY_LIVE_BILLING when called with a kc_test_ key — there is no sandbox execution backend yet, so a test key is rejected rather than charged. The non-billing endpoints (POST /v1/jobs/upload-url, POST /v1/jobs/estimate, GET /v1/credits/balance) accept test keys normally, so you can exercise the full pre-submit flow without a live key.
Scopes
Keys carry a set of scopes that limit what they can do. Request only the scopes you need.
| Scope | Permitted endpoints |
|---|---|
jobs:read |
GET /v1/jobs, GET /v1/jobs/{id}/status, GET /v1/jobs/{id}/outputs |
jobs:write |
POST /v1/jobs/submit, POST /v1/jobs/upload-url, POST /v1/jobs/{id}/cancel, POST /v1/jobs/{id}/resume |
credits:read |
GET /v1/credits/balance, GET /v1/credits/transactions |
Calling an endpoint without the required scope returns 403 Forbidden with code: INSUFFICIENT_SCOPE.
Key management endpoints (GET /v1/keys, POST /v1/keys, DELETE /v1/keys/{id}) require a browser session — they cannot be called from an API key to prevent privilege escalation.
Create a key via the API
# Requires an active browser session (web app login)
curl -X POST https://app.kinocloud.io/api/v1/keys \
-H "Cookie: YOUR_SESSION_COOKIE" \
-H "Content-Type: application/json" \
-d '{
"name": "ci-pipeline",
"environment": "live",
"scopes": ["jobs:read", "jobs:write", "credits:read"]
}'
The response includes the key field exactly once. Store it securely — it cannot be retrieved again.
Revocation
curl -X DELETE https://app.kinocloud.io/api/v1/keys/KEY_ID \
-H "Cookie: YOUR_SESSION_COOKIE"
Revocation SLA: A revoked key stops authenticating fleet-wide within a few seconds. The instance that handles the revoke invalidates immediately; every other instance picks it up on its next revocation-epoch poll (a short, fixed interval), so propagation is bounded in seconds rather than the cache TTL.
Key rotation checklist
- Create the new key with the same scopes
- Update your secret store / environment variable
- Verify the new key works (
GET /v1/credits/balance) - Revoke the old key (
DELETE /v1/keys/{old_id}) - Confirm the old key returns
401
Storing keys securely
- Never commit keys to version control. Use environment variables or a secrets manager.
- CI/CD: Store as a masked secret (
KINOCLOUD_API_KEY) and inject at runtime. - Local development: Use a
.env.localfile (excluded by.gitignore).
# .env.local
KINOCLOUD_API_KEY=kc_test_your_test_key_here
Error responses
All auth failures return JSON in the canonical error envelope:
{ "error": "Unauthorized", "code": "UNAUTHORIZED" }
A missing, expired, or unknown key returns 401 with code: UNAUTHORIZED. A valid key that lacks the scope for an endpoint returns 403 with code: INSUFFICIENT_SCOPE.
| HTTP status | Cause |
|---|---|
401 |
Missing or malformed Authorization header; key not found |
403 |
Key found but missing the required scope; test key used on a live-only endpoint |
429 |
Per-key rate limit exceeded; see Retry-After response header |