Skip to content

Teams

Teams are named subgroups inside an org. They give you finer-grained channel visibility and let you pre-assign new members at invite time.

  • A team belongs to exactly one org.
  • A user can be in any number of teams within an org.
  • A channel can be team-scoped; only team members (plus org Owners/Admins) see its items.

Data model

Migration 0017_teams.sql adds:

TableKey columns
teamsid, org_id, name, slug, created_at — unique on (org_id, slug)
team_membershipsid, team_id, user_id, role (lead / member), created_at — unique on (team_id, user_id)

Related team_id foreign keys:

  • channels.team_id — nullable; ON DELETE SET NULL so a deleted team downgrades its channels to org-wide visibility rather than breaking them.
  • invitations.team_id — nullable; the accept handler adds the accepted user to that team automatically (see Invitations).

Endpoints

All under /api/admin/teams. All require the caller to be authenticated in the target org.

Method & PathPermissionPurpose
GET /api/admin/teamsteams.manage_membersList teams in the org
POST /api/admin/teamsteams.createCreate a team — gated on max_teams plan limit
PATCH /api/admin/teams/{team_id}teams.manage_membersRename or change slug
DELETE /api/admin/teams/{team_id}teams.deleteDelete the team
GET /api/admin/teams/{team_id}/membersteams.manage_membersList members
POST /api/admin/teams/{team_id}/membersteams.manage_membersAdd a member with role lead or member
DELETE /api/admin/teams/{team_id}/members/{user_id}teams.manage_membersRemove from team

Create a team

bash
curl -X POST https://api.klaxon.sh/api/admin/teams \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"name":"Platform","slug":"platform"}'

When the plan limit is hit you'll get a 402 Payment Required with plan_limit_exceeded — see Billing.

Add a member

bash
curl -X POST https://api.klaxon.sh/api/admin/teams/$TEAM_ID/members \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"user_id":"…","role":"member"}'

Users must already belong to the same org (via org_memberships) — the handler rejects cross-org adds.

Team-scoped channels

Set team_id on a channel to restrict it:

bash
curl -X PATCH https://api.klaxon.sh/api/channels/$CHANNEL_ID \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"team_id":"…"}'

Visibility rules enforced in the channel handler:

  1. If the channel's team_id is NULL → all org members see it (subject to the channel's own mode).
  2. If it's set → only users in team_memberships for that team see it. Org Owners and Admins always see it (for incident response).
  3. If the team is deleted, ON DELETE SET NULL reverts the channel to case 1 automatically.

This means deleting a team is always safe — no items are lost, they just become visible at the org level until an admin re-scopes them.

Invite straight into a team

An invitation can carry a team_id:

bash
curl -X POST https://api.klaxon.sh/api/admin/invitations \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"email":"new@example.com","role":"member","team_id":"…"}'

On accept, the server creates both an org_memberships row and a team_memberships row. If the team was deleted between invite and accept, the team assignment is silently dropped (the org membership still succeeds).

UI surface

The web admin panel exposes a Teams tab with a list, create/edit dialog, and member management. The mobile app currently reads team membership for visibility but doesn't expose admin actions — those live on web.