> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/go-gitea/gitea/llms.txt
> Use this file to discover all available pages before exploring further.

# Teams

> Manage teams and team members within Gitea organizations

Teams are groups of organization members with specific access permissions to repositories. Teams provide fine-grained control over who can read, write, or administer repositories within an organization.

## Understanding Teams

Every organization has at least one team - the **Owners** team. Teams allow you to:

* Group users with similar responsibilities
* Grant specific permissions to repository units (code, issues, pull requests, etc.)
* Manage access across multiple repositories at once
* Create specialized roles beyond simple read/write access

## Default Teams

When you create an organization, an **Owners** team is automatically created:

```go theme={null}
// Create default owner team
t := &Team{
    OrgID:                   org.ID,
    LowerName:               strings.ToLower(OwnerTeamName),
    Name:                    OwnerTeamName,
    AccessMode:              perm.AccessModeOwner,
    NumMembers:              1,
    IncludesAllRepositories: true,
    CanCreateOrgRepo:        true,
}
```

Source: `models/organization/org.go:335-343`

The Owners team:

* Has full access to all repositories
* Can manage all organization settings
* Automatically includes all repositories (existing and future)
* Cannot be deleted
* Must have at least one member

## Creating a Team

Organization owners can create new teams:

1. Navigate to your organization
2. Go to the **Teams** tab
3. Click **New Team**
4. Configure team settings:
   * **Team Name** - Unique within the organization
   * **Description** - Optional team description
   * **Permission Level** - Overall access mode
   * **Repository Access** - Specific or all repositories
   * **Unit Permissions** - Granular access to repository features

### Team Creation Example

```go theme={null}
// NewTeam creates a record of new team.
func NewTeam(ctx context.Context, t *organization.Team) (err error) {
    if len(t.Name) == 0 {
        return util.NewInvalidArgumentErrorf("empty team name")
    }

    if err = organization.IsUsableTeamName(t.Name); err != nil {
        return err
    }

    t.LowerName = strings.ToLower(t.Name)
    has, err := db.Exist[organization.Team](ctx, builder.Eq{
        "org_id":     t.OrgID,
        "lower_name": t.LowerName,
    })
    if err != nil {
        return err
    }
    if has {
        return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
    }

    return db.WithTx(ctx, func(ctx context.Context) error {
        if err = db.Insert(ctx, t); err != nil {
            return err
        }

        // insert units for team
        if len(t.Units) > 0 {
            for _, unit := range t.Units {
                unit.TeamID = t.ID
            }
            if err = db.Insert(ctx, &t.Units); err != nil {
                return err
            }
        }

        // Add all repositories to the team if it has access to all of them
        if t.IncludesAllRepositories {
            err = repo_service.AddAllRepositoriesToTeam(ctx, t)
            if err != nil {
                return fmt.Errorf("addAllRepositories: %w", err)
            }
        }

        // Update organization number of teams
        _, err = db.Exec(ctx, "UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID)
        return err
    })
}
```

Source: `services/org/team.go:27-85`

## Team Permission Levels

Teams can have different base permission levels:

<CardGroup cols={2}>
  <Card title="Read" icon="book-open">
    View and clone repositories. Can view issues and pull requests but cannot modify them.
  </Card>

  <Card title="Write" icon="pen">
    Read access plus ability to push to repositories, create issues, and comment on discussions.
  </Card>

  <Card title="Admin" icon="shield">
    Write access plus ability to manage repository settings, webhooks, and collaborators.
  </Card>

  <Card title="Owner" icon="crown">
    Full access to all organization repositories and settings. Can delete the organization.
  </Card>
</CardGroup>

These permission levels are defined in the code:

```go theme={null}
type AccessMode int

const (
    AccessModeNone AccessMode = iota // 0: no access

    AccessModeRead  // 1: read access
    AccessModeWrite // 2: write access
    AccessModeAdmin // 3: admin access
    AccessModeOwner // 4: owner access
)
```

Source: `models/perm/access_mode.go:14-23`

## Repository Access

Teams can access repositories in two ways:

### Specific Repositories

Manually add repositories to the team. The team will only have access to explicitly added repositories.

### All Repositories

Enable **IncludesAllRepositories** to automatically grant the team access to all current and future repositories:

```go theme={null}
type Team struct {
    ID                      int64 `xorm:"pk autoincr"`
    OrgID                   int64 `xorm:"INDEX"`
    Name                    string
    Description             string
    AccessMode              perm.AccessMode    `xorm:"'authorize'"`
    Members                 []*user_model.User `xorm:"-"`
    NumRepos                int
    NumMembers              int
    Units                   []*TeamUnit `xorm:"-"`
    IncludesAllRepositories bool        `xorm:"NOT NULL DEFAULT false"`
    CanCreateOrgRepo        bool        `xorm:"NOT NULL DEFAULT false"`
}
```

Source: `models/organization/team.go:74-87`

## Unit-Level Permissions

Teams can have different permission levels for different repository units:

<Note>
  Unit permissions allow fine-grained access control. For example, a team can have write access to code but only read access to issues.
</Note>

### Available Units

* **Code** - Repository code and Git operations
* **Issues** - Issue tracking
* **Pull Requests** - Pull request management
* **Releases** - Release management
* **Wiki** - Repository wiki
* **External Wiki** - Link to external wiki
* **External Tracker** - Link to external issue tracker
* **Projects** - Project boards
* **Packages** - Package registry
* **Actions** - CI/CD workflows

### Team Unit Structure

```go theme={null}
// TeamUnit describes all units of a repository
type TeamUnit struct {
    ID         int64     `xorm:"pk autoincr"`
    OrgID      int64     `xorm:"INDEX"`
    TeamID     int64     `xorm:"UNIQUE(s)"`
    Type       unit.Type `xorm:"UNIQUE(s)"`
    AccessMode perm.AccessMode
}

// Unit returns Unit
func (t *TeamUnit) Unit() unit.Unit {
    return unit.Units[t.Type]
}
```

Source: `models/organization/team_unit.go:14-26`

## Managing Team Members

### Adding Members

Organization owners can add members to teams:

```go theme={null}
// AddTeamMember adds new membership of given team to given organization,
// the user will have membership to given organization automatically when needed.
func AddTeamMember(ctx context.Context, team *organization.Team, user *user_model.User) error {
    if user_model.IsUserBlockedBy(ctx, user, team.OrgID) {
        return user_model.ErrBlockedUser
    }

    isAlreadyMember, err := organization.IsTeamMember(ctx, team.OrgID, team.ID, user.ID)
    if err != nil || isAlreadyMember {
        return err
    }

    // Add user to organization if not already a member
    if err := organization.AddOrgUser(ctx, team.OrgID, user.ID); err != nil {
        return err
    }

    return db.WithTx(ctx, func(ctx context.Context) error {
        sess := db.GetEngine(ctx)

        if err := db.Insert(ctx, &organization.TeamUser{
            UID:    user.ID,
            OrgID:  team.OrgID,
            TeamID: team.ID,
        }); err != nil {
            return err
        }
        
        _, err := sess.Incr("num_members").ID(team.ID).Update(new(organization.Team))
        return err
    })
}
```

Source: `services/org/team.go:210-247`

### Removing Members

When removing a team member:

* User access to team repositories is recalculated
* If the user is not in any other teams, they're removed from the organization
* Cannot remove the last owner from the Owners team

```go theme={null}
func removeTeamMember(ctx context.Context, team *organization.Team, user *user_model.User) error {
    isMember, err := organization.IsTeamMember(ctx, team.OrgID, team.ID, user.ID)
    if err != nil || !isMember {
        return err
    }

    // Check if the user to delete is the last member in owner team
    if team.IsOwnerTeam() && team.NumMembers == 1 {
        return organization.ErrLastOrgOwner{UID: user.ID}
    }

    team.NumMembers--

    repos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{
        TeamID: team.ID,
    })
    if err != nil {
        return err
    }

    // Delete team membership
    if _, err := e.Delete(&organization.TeamUser{
        UID:    user.ID,
        OrgID:  team.OrgID,
        TeamID: team.ID,
    }); err != nil {
        return err
    }

    // Delete access to team repositories
    for _, repo := range repos {
        if err := access_model.RecalculateUserAccess(ctx, repo, user.ID); err != nil {
            return err
        }
    }

    return removeInvalidOrgUser(ctx, team.OrgID, user)
}
```

Source: `services/org/team.go:275-327`

## Team Invitations

Organization owners can invite users to teams via email:

1. Enter an email address when adding a team member
2. An invitation is sent to the email
3. The user can accept the invitation from the email link
4. Upon acceptance, they join the team

<Warning>
  Email invitations require that mail service is configured in Gitea settings.
</Warning>

## Updating Teams

When updating team settings:

```go theme={null}
// UpdateTeam updates information of team.
func UpdateTeam(ctx context.Context, t *organization.Team, authChanged, includeAllChanged bool) (err error) {
    return db.WithTx(ctx, func(ctx context.Context) error {
        sess := db.GetEngine(ctx)
        if _, err = sess.ID(t.ID).Cols("name", "lower_name", "description",
            "can_create_org_repo", "authorize", "includes_all_repositories").Update(t); err != nil {
            return fmt.Errorf("update: %w", err)
        }

        // update units for team
        if len(t.Units) > 0 {
            // Delete existing team-unit mappings
            if _, err := sess.Where("team_id=?", t.ID).Delete(new(organization.TeamUnit)); err != nil {
                return err
            }
            // Insert new team-unit mappings
            if _, err = sess.Insert(&t.Units); err != nil {
                return err
            }
        }

        // Update access for team members if permissions changed
        if authChanged {
            repos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{
                TeamID: t.ID,
            })
            if err != nil {
                return fmt.Errorf("GetTeamRepositories: %w", err)
            }

            for _, repo := range repos {
                if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
                    return fmt.Errorf("recalculateTeamAccesses: %w", err)
                }
            }
        }

        return nil
    })
}
```

Source: `services/org/team.go:88-158`

## Deleting Teams

<Warning>
  Deleting a team removes all members and repository access. Users who were only in this team will be removed from the organization.
</Warning>

When deleting a team:

1. All repository access is removed
2. Branch protection rules are updated
3. Team members are removed
4. Users with no other teams are removed from the organization

## Frequently Asked Questions

<AccordionGroup>
  <Accordion title="Can a user be in multiple teams?">
    Yes, users can be members of multiple teams within the same organization. Their effective permissions for a repository are the highest permission granted by any team they belong to.
  </Accordion>

  <Accordion title="What happens when team permissions conflict?">
    Users always receive the highest permission level granted by any team they belong to. For example, if a user is in Team A (read access) and Team B (write access), they will have write access.
  </Accordion>

  <Accordion title="Can teams have different permissions for different repositories?">
    Yes, when a team has access to multiple specific repositories, the same unit-level permissions apply to all of them. To have different permissions for different repositories, create separate teams.
  </Accordion>

  <Accordion title="What is the 'Can Create Org Repo' permission?">
    This permission allows team members to create new repositories within the organization. By default, only owners can create repositories, but you can grant this to other teams.
  </Accordion>

  <Accordion title="Can I rename a team?">
    Yes, organization owners can rename teams at any time. The team's permissions and members remain unchanged.
  </Accordion>

  <Accordion title="What's the difference between Admin and Owner?">
    * **Admin** teams have administrative access to repositories but cannot manage the organization itself
    * **Owner** teams (specifically the Owners team) can manage all organization settings, teams, and members
  </Accordion>
</AccordionGroup>

## Related Resources

* [Organizations](/users/organizations) - Learn about creating and managing organizations
* [Permissions](/users/permissions) - Detailed permission model documentation
* [Repository Access](/repositories/collaborators) - Managing repository collaborators
