A Workspace API Key authenticates your backend to MIOSA. One key per workspace; the key resolves to a tenant_id, scopes, and rate limits.
Create a key
From the dashboard: Workspace → Settings → API Keys → New key. Or via API:
POST /api/v1/api-keys The plaintext msk_* value is returned exactly once at creation. The dashboard shows only a prefix (first 12 chars) thereafter; the server stores only a hash.
If you lose a key, rotate — create a new one, swap it in, delete the old.
Key shape
msk_<env>_<random> msk_live_*— production keys, full access.msk_test_*— test-mode keys (planned; usage doesn’t count toward billing).msk_u_*— user-bound keys for personal CLI use; scopes limited to the user’s own resources.
For platform integration (ClinicIQ-style), use msk_live_* and treat it like a Stripe secret key.
Scopes
Each key has a list of scopes. MIOSA enforces scopes on every mutation. Reads typically require the matching :read scope; writes require :write.
Common scopes:
| Scope | Allows |
|---|---|
sandboxes:read | List, get sandboxes |
sandboxes:write | Create, exec, files.write, destroy |
deployments:read | List, get deployments, versions, domains |
deployments:write | Create, publish, rollback, attach domains |
domains:write | Create, verify, delete custom domains |
data:write | Provision managed Postgres / Redis / buckets |
usage:read | Read usage events |
audit:read | Read audit log |
For server-side platform integration, start with sandboxes:write + deployments:write + domains:write. Add more as needed. Principle of least privilege: don’t give a key *:write unless that’s what it actually needs to do.
Rotation
Rotate keys on a schedule (we recommend every 90 days, or on incident). The flow:
- Create a new key with the same scopes.
- Roll out the new key to your backend (deploy + restart).
- Confirm the new key works (recent activity logged with
key_idof the new key).
4. Delete the old key
Deleting a key invalidates it immediately. Any in-flight requests using the old key will start getting 401.
Rate limits
Each key has a rate limit (request-per-minute) scoped by:
- Key ID (the key’s own bucket)
- Organization (the account’s overall bucket)
- Endpoint (publish has its own bucket, separate from list operations)
Limits scale with the plan. Headers in every response tell you where you stand:
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 547
X-RateLimit-Reset: 1715712060 429 responses include a Retry-After header. SDKs back off automatically.
Audit
Every action authenticated by a key emits an audit event with:
api_key_id(so you know which key did what)actor(the organization, plus the key’s name)workspace_id,project_id,external_workspace_id,external_user_id(if passed or resolved in the request)- The action (e.g.
deployments.publish,domains.create) - Timestamp + outcome
Read your audit log via:
GET /api/v1/audit?api_key_id=key_...
GET /api/v1/audit?workspace_id=550e8400-e29b-41d4-a716-446655440000 Browser keys (no)
There is no such thing as a browser-safe msk_*. If you need a browser to call MIOSA-like functionality, mint a scoped browser token server-side. See Browser Tokens.
See also
- Attribution — the IDs your API key carries on each call
- Browser Tokens — for end-user-facing access
- Authentication — the wider auth model (JWT for sessions, etc.)