Auth + quotas
ScaiScribe authenticates every request via ScaiKey, the ScaiLabs identity service. Tenants get limits per the quota model. Operators get aggregated rollups so they can see usage at the partner and tenant level.
Authentication#
There are two token types you'll meet:
1. Service tokens (the default for SDK / programmatic use)#
OAuth client_credentials against ScaiKey's /oauth/token endpoint. The SDKs fetch and refresh these transparently:
1 2 3 4 5 6 7 8 9 10 11 | |
Service tokens are issued globally to the application registered in ScaiKey — they don't carry a tenant_id claim by themselves. You tell ScaiScribe which tenant the request is acting on via the X-ScaiScribe-Tenant-Id header, which the SDK sends automatically when you pass default_tenant_id (Python) / defaultTenantId (TS/.NET) at construction time.
2. User tokens (browser / OIDC flows)#
When a user authenticates through ScaiKey's web flow, the resulting JWT does carry a tenant_id claim — the tenant the user belongs to. ScaiScribe trusts the JWT claim; the X-ScaiScribe-Tenant-Id header is ignored for these tokens (defence-in-depth — a user token can't be re-targeted to a different tenant).
The admin SPA at /admin/ uses this flow: backend-mediated Authorization Code with HttpOnly session cookies.
Defence-in-depth boundary#
The X-ScaiScribe-Tenant-Id header only takes effect when the underlying token has no tenant claim of its own. If the token already declares a tenant, that wins — full stop.
Quotas#
Five axes are tracked per tenant. v1.0 defaults are:
| Axis | Default | Type | HTTP code on violation |
|---|---|---|---|
documents_count |
10,000 | Absolute (live count from documents) |
403 QUOTA_EXCEEDED |
assets_bytes |
5 GB | Absolute (SUM over assets + fonts) |
403 QUOTA_EXCEEDED |
templates_count |
20 | Absolute (live count from templates) |
403 QUOTA_EXCEEDED |
renders_per_day |
1,000 | Windowed (per UTC day, rolls over at midnight) | 429 QUOTA_EXCEEDED |
max_doc_size_mb |
100 | Per-document size cap | 413 |
Absolute quotas use a 403 because the situation only resolves with cleanup (delete documents, remove assets). Windowed quotas use a 429 because retrying later might succeed — the bucket rolls over naturally.
Quota overrides per tenant land via a forthcoming tenant_quotas table; until then the defaults apply uniformly. Operator override requires a code change.
Inspecting usage#
Three admin endpoints help operators see what's happening. Access varies by role:
Per-partner rollup#
1 | |
Returns the aggregate across every tenant of a partner. Useful for provisioning reviews and capacity planning. Access: superadmin OR partner_admin on the queried partner.
1 2 3 4 5 6 7 8 9 10 11 12 | |
Per-user rollup#
1 | |
Breaks a tenant's usage down by acting user. Access: superadmin OR partner_admin on the tenant's partner OR tenant_admin on the tenant.
1 2 3 4 5 6 7 8 9 10 | |
The coverage field reports whether per-user attribution is available for the axis:
full— forrenders_per_dayanddocuments_count. Solid attribution.none— forassets_bytesandtemplates_count. The Asset/Font/Template tables don't carrycreated_byyet (model gap; v1.x).per_useris empty in this case and anotefield explains.
Admin UI#
The admin SPA exposes these inline on the Partners and Tenants pages (click "Quota" / "Per-user" on a row to expand), and as a dedicated /admin/quotas page with axis switching.
Roles#
| Role | Scope | Powers |
|---|---|---|
member |
One tenant | Standard tenant user — can use the document APIs as their tenant. |
tenant_admin |
One tenant | Above + manage that tenant's users, see its quota rollups. |
partner_admin |
One partner | Above + cross every tenant of that partner. |
superadmin |
Global | Above + manage every partner, rotate secrets, see global health. |
Roles are assigned via POST /v1/admin/users/{user_id}/roles (audit-logged with granted_by_scaikey_id). Revocation stamps revoked_at; rows stay for audit.
Where to next#
- Quickstart — set up auth + make your first request.
- Tutorials → Admin workflows — operator-side patterns.
- Reference → API —
/v1/admin/*shapes.