Shell API reference
Every endpoint (except /health and /metrics) requires a valid Firebase ID
token in Authorization: Bearer <token>. Tokens are verified with the Firebase
Admin SDK; a 60-second Redis cache avoids redundant verification on hot paths.
The OpenAPI document is emitted at /api-json and committed as
apps/shell-backend/openapi.json; the frontend client is generated from it via
@hey-api/openapi-ts.
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /health | none | Liveness/readiness ({ status, db, redis }) |
| GET | /metrics | none | Prometheus metrics |
| GET | /users/me | user | Current profile + global roles |
| PUT | /users/me | user | Upsert profile on first login |
| GET | /roles | admin | List all distinct global roles |
| POST | /roles/assign | admin | Assign a global role to a user |
| DELETE | /roles/{firebaseUid}/{role} | admin | Revoke a global role |
| GET | /permissions/{productId} | user | Current user's grants for a product |
| POST | /permissions/{productId} | admin | Grant a product permission |
| DELETE | /permissions/{productId}/{key} | admin | Revoke a product permission |
Role-based access in detailโ
- Unauthenticated requests receive 401.
- Authenticated requests missing the required role receive 403.
- Role assignment/revocation use
checkRevoked: truein token verification โ the only endpoints where the extra network call is warranted.
Exampleโ
curl -H "Authorization: Bearer $ID_TOKEN" http://localhost:3000/users/me
# { "id": "...", "firebaseUid": "...", "email": "m.vance@chrono.internal",
# "displayName": "Marcus Vance", "photoUrl": null, "globalRoles": ["admin"] }