Teams
How users group, who owns which API, who approves subscriptions — the multi-tenant model behind every action in the platform.
Teams are how Spec0 answers who owns what. Every API is owned by a team. Every user is a member of one or more teams in their org. Every action that touches an API or a subscription runs through a team's grants.
On the dashboard
| Route | What it shows |
|---|---|
/teams | Every team in your org. Create, rename, invite members. |
/teams/[id] | One team's members, the APIs the team owns, the subscriptions the team has approved on its APIs, and the subscriptions the team holds against other teams' APIs. |
/organisation-profile | Org-level settings: members across teams, API key management, billing. |
The model
Three layers, smallest to largest:
- User. A person. Members of one or more teams in one org.
- Team. The ownership boundary. APIs, mocks, and subscribers all hang off a team.
- Org. The billing and isolation boundary. One org per Spec0 customer; users from one org never see another org's APIs, teams, or subscriptions.
The first user to sign up for an org becomes its admin. From there, the admin invites members and creates teams. Members can be added to multiple teams; APIs they have access to depend on which teams they're in.
What a team can do
- Own and publish APIs. Members of the team can publish new versions, edit metadata, configure governance.
- Approve subscriptions to APIs the team owns. A consumer team requests a subscription with a set of permissions on specific operations; the owning team's admin approves, trims, or rejects. See Subscriptions.
- Subscribe to other teams' APIs. A team that holds active subscriptions sees them under
/subscriptionsand receives notifications when those APIs change. - Run mock servers scoped to the team's APIs — mock data is isolated per team. See Mock server.
How permissions are enforced
Two lines of defence — both checked on every request.
- ABAC at the request boundary. Each endpoint declares the permission it needs (
api:publish,subscription:approve, etc.); a Spring Security check resolves the caller's identity (Clerk JWT for the web app, API key for the CLI / CI / agents) to a(userId, orgId, teams[])and rejects any mismatch. - Hibernate
@Filterat the ORM layer. Every query against a multi-tenant entity automatically carriesWHERE org_id = :orgId— see ADR-0010. A bug in application code that forgot to filter by org couldn't leak data because the database itself wouldn't return it.
Together these mean cross-org leaks are structurally impossible: the org-id filter is a SQL WHERE clause that lives below the application logic, not a check the application has to remember.
Inviting and removing
From the UI: Teams → [team] → Members → Invite. The invitee receives an email; their next sign-in places them in the team. Removing a member revokes their access immediately — the next API call from a removed member fails ABAC.
For non-human callers (CI runners, CLI bots, AI agents) use Settings → API keys and create a key tied to a team rather than inviting a synthetic user. The key carries the same team membership a real user would have.
API keys and tokens
| Use case | Credential |
|---|---|
| Web app session | Clerk JWT (auto, short-lived) |
spec0 login from a developer laptop | Clerk-issued PAT |
| CI runner publishing on behalf of a team | API key, tied to the team |
| AI agent over MCP | API key — the same one |
There is no separate credential system for MCP, REST, or CLI. The same key works across all three. See the agents page for the wiring.
Where to go next
- APIs — what teams own and govern.
- Subscriptions — what teams approve and consume.
- Events — team-scoped notifications.
spec0 login— wiring a CLI install into a team.