Skip to content

Teams

Teams in Albert allow organizations to manage groups of users that can be assigned in bulk to projects.

albert.collections.teams.TeamCollection

TeamCollection(*, session: AlbertSession)

Bases: BaseCollection

Manage Teams in the Albert platform.

A Team is a named group of users (User). Each member holds a team role (owner or viewer) that governs their rights within the team. Teams are used to share access: entity ACLs and Task assignments can reference a whole team rather than individual users. A team is identified by its Team ID (format TEM..., e.g. "TEM1").

This collection is accessed as client.teams.

Example

from albert import Albert
client = Albert()
team = client.teams.create(name="Coatings R&D")
for member in team.members or []:
    print(member.id, member.role)

Parameters:

Name Type Description Default
session AlbertSession

The authenticated Albert session used for API calls.

required

Attributes:

Name Type Description
base_path str

The base API route for team requests.

Methods:

Name Description
search

Search teams with free text and filters, returning partial items.

get_all

Lists all teams with optional filters.

get_by_id

Get a team by its ID.

create

Create a new team, optionally with initial members.

update

Update a team's name and membership.

delete

Deletes a team by its ID.

add_users

Adds users to a team.

remove_users

Removes users from a team.

Parameters:

Name Type Description Default
session AlbertSession

The authenticated Albert session used for API calls.

required
Source code in src/albert/collections/teams.py
def __init__(self, *, session: AlbertSession):
    """Initialize a TeamCollection.

    Parameters
    ----------
    session : AlbertSession
        The authenticated Albert session used for API calls.
    """
    super().__init__(session=session)
    self.base_path = f"/api/{TeamCollection._api_version}/teams"

base_path

base_path = f'/api/{TeamCollection._api_version}/teams'

search

search(
    *,
    text: str | None = None,
    status: str | None = None,
    team_id: str | list[str] | None = None,
    search_field: str | list[str] | None = None,
    source_field: str | list[str] | None = None,
    additional_field: str | list[str] | None = None,
    sort_by: str | None = None,
    order: OrderBy | None = None,
    max_items: int | None = None,
) -> Iterator[TeamSearchItem]

Search for teams matching the given filters.

Returns partial (unhydrated) TeamSearchItem entities, best for free-text lookups and pulling IDs. Results are returned lazily as an iterator that pages through matches on demand. Call hydrate() on an item to fetch its full Team. For exact name listing, use get_all.

Example

for item in client.teams.search(text="Coatings", max_items=10):
    print(item.id, item.name)

Parameters:

Name Type Description Default
text str

Free-text query matched against team fields.

None
status str

Filter by team status.

None
team_id str or list[str]

Filter by Team ID(s) (format TEM...).

None
search_field str or list[str]

Restrict which indexed fields the free-text query searches.

None
source_field str or list[str]

Restrict which fields are returned on each search hit.

None
additional_field str or list[str]

Additional fields to include on each search hit.

None
sort_by str

Attribute to sort results by.

None
order OrderBy

The order in which to sort results (asc or desc).

None
max_items int

Maximum number of items to return in total. If None, iterates over all matching items.

None

Returns:

Type Description
Iterator[TeamSearchItem]

A lazy iterator of matching partial teams. Call hydrate() on an item to fetch its full Team.

Source code in src/albert/collections/teams.py
@validate_call
def search(
    self,
    *,
    text: str | None = None,
    status: str | None = None,
    team_id: str | list[str] | None = None,
    search_field: str | list[str] | None = None,
    source_field: str | list[str] | None = None,
    additional_field: str | list[str] | None = None,
    sort_by: str | None = None,
    order: OrderBy | None = None,
    max_items: int | None = None,
) -> Iterator[TeamSearchItem]:
    """Search for teams matching the given filters.

    Returns partial (unhydrated)
    [`TeamSearchItem`][albert.resources.teams.TeamSearchItem] entities, best for
    free-text lookups and pulling IDs. Results are returned lazily as an
    iterator that pages through matches on demand. Call ``hydrate()`` on an item
    to fetch its full [`Team`][albert.resources.teams.Team]. For exact name
    listing, use [`get_all`][albert.collections.teams.TeamCollection.get_all].

    !!! example
        ```python
        for item in client.teams.search(text="Coatings", max_items=10):
            print(item.id, item.name)
        ```

    Parameters
    ----------
    text : str, optional
        Free-text query matched against team fields.
    status : str, optional
        Filter by team status.
    team_id : str or list[str], optional
        Filter by Team ID(s) (format ``TEM...``).
    search_field : str or list[str], optional
        Restrict which indexed fields the free-text query searches.
    source_field : str or list[str], optional
        Restrict which fields are returned on each search hit.
    additional_field : str or list[str], optional
        Additional fields to include on each search hit.
    sort_by : str, optional
        Attribute to sort results by.
    order : OrderBy, optional
        The order in which to sort results (``asc`` or ``desc``).
    max_items : int, optional
        Maximum number of items to return in total. If None, iterates over all
        matching items.

    Returns
    -------
    Iterator[TeamSearchItem]
        A lazy iterator of matching partial teams. Call ``hydrate()`` on an
        item to fetch its full [`Team`][albert.resources.teams.Team].
    """
    payload: dict[str, Any] = {
        "text": text,
        "status": status,
        "teamId": ensure_list(team_id),
        "searchField": ensure_list(search_field),
        "sourceField": ensure_list(source_field),
        "additionalField": ensure_list(additional_field),
        "sortBy": sort_by,
        "order": order,
    }

    return AlbertPaginator(
        mode=PaginationMode.OFFSET,
        path=f"{self.base_path}/search",
        session=self.session,
        method="POST",
        json=payload,
        max_items=max_items,
        deserialize=lambda items: [
            TeamSearchItem.model_validate(x)._bind_collection(self) for x in items
        ],
    )

get_all

get_all(
    *,
    name: str | list[str] | None = None,
    exact_match: bool = True,
    created_by: str | None = None,
    updated_by: str | None = None,
    user_id: str | list[str] | None = None,
    max_items: int | None = None,
) -> Iterator[Team]

List all teams with optional filters.

Best for exact name listing. For free-text search, use search.

Example

for team in client.teams.get_all(name="Coatings", exact_match=False):
    print(team.id, team.name)

Parameters:

Name Type Description Default
name str or list[str]

Filter teams by one or more names.

None
exact_match bool

Whether to match the name(s) exactly. Default is True.

True
created_by str

Filter teams by the user ID of their creator.

None
updated_by str

Filter teams by the user ID of their last updater.

None
user_id str or list[str]

Filter teams by user membership.

None
max_items int

Maximum total number of items to return. If None, fetches all available items.

None

Returns:

Type Description
Iterator[Team]

An iterator of Team entities matching the filters.

Source code in src/albert/collections/teams.py
def get_all(
    self,
    *,
    name: str | list[str] | None = None,
    exact_match: bool = True,
    created_by: str | None = None,
    updated_by: str | None = None,
    user_id: str | list[str] | None = None,
    max_items: int | None = None,
) -> Iterator[Team]:
    """List all teams with optional filters.

    Best for exact name listing. For free-text search, use
    [`search`][albert.collections.teams.TeamCollection.search].

    !!! example
        ```python
        for team in client.teams.get_all(name="Coatings", exact_match=False):
            print(team.id, team.name)
        ```

    Parameters
    ----------
    name : str or list[str], optional
        Filter teams by one or more names.
    exact_match : bool, optional
        Whether to match the name(s) exactly. Default is True.
    created_by : str, optional
        Filter teams by the user ID of their creator.
    updated_by : str, optional
        Filter teams by the user ID of their last updater.
    user_id : str or list[str], optional
        Filter teams by user membership.
    max_items : int, optional
        Maximum total number of items to return. If None, fetches all available items.

    Returns
    -------
    Iterator[Team]
        An iterator of Team entities matching the filters.
    """
    params: dict = {}
    if name is not None:
        params["name"] = ensure_list(name)
        params["exactMatch"] = exact_match
    if created_by is not None:
        params["createdBy"] = created_by
    if updated_by is not None:
        params["updatedBy"] = updated_by
    if user_id is not None:
        params["userId"] = ensure_list(user_id)

    return AlbertPaginator(
        mode=PaginationMode.KEY,
        path=self.base_path,
        session=self.session,
        params=params,
        max_items=max_items,
        deserialize=lambda items: [Team(**item) for item in items],
    )

get_by_id

get_by_id(*, id: TeamId) -> Team

Get a team by its ID.

Example

team = client.teams.get_by_id(id="TEM1")
team.name
# 'Coatings R&D'

Parameters:

Name Type Description Default
id TeamId

The ID of the team.

required

Returns:

Type Description
Team

The fully populated team.

Source code in src/albert/collections/teams.py
@validate_call
def get_by_id(self, *, id: TeamId) -> Team:
    """Get a team by its ID.

    !!! example
        ```python
        team = client.teams.get_by_id(id="TEM1")
        team.name
        # 'Coatings R&D'
        ```

    Parameters
    ----------
    id : TeamId
        The ID of the team.

    Returns
    -------
    Team
        The fully populated team.
    """
    url = f"{self.base_path}/{id}"
    response = self.session.get(url)
    return Team(**response.json())

create

create(
    *, name: str, members: list[TeamMember] | None = None
) -> Team

Create a new team, optionally with initial members.

Example

from albert.resources.teams import TeamMember
team = client.teams.create(
    name="Coatings R&D",
    members=[TeamMember(id="USR12", role="TeamOwner")],
)
team.id
# 'TEM1'

Parameters:

Name Type Description Default
name str

The name of the team.

required
members list[TeamMember]

Members to add to the team on creation, each with a User ID and a team role. Members default to the TeamViewer role when none is given.

None

Returns:

Type Description
Team

The created Team, populated with its assigned Team ID.

Source code in src/albert/collections/teams.py
@validate_call
def create(
    self,
    *,
    name: str,
    members: list[TeamMember] | None = None,
) -> Team:
    """Create a new team, optionally with initial members.

    !!! example
        ```python
        from albert.resources.teams import TeamMember
        team = client.teams.create(
            name="Coatings R&D",
            members=[TeamMember(id="USR12", role="TeamOwner")],
        )
        team.id
        # 'TEM1'
        ```

    Parameters
    ----------
    name : str
        The name of the team.
    members : list[TeamMember], optional
        Members to add to the team on creation, each with a User ID and a
        team role. Members default to the ``TeamViewer`` role when none is
        given.

    Returns
    -------
    Team
        The created Team, populated with its assigned Team ID.
    """
    payload: dict = {"name": name}
    if members:
        acl_entries = [{"id": m.id, "fgc": m.role or "TeamViewer"} for m in members]
        payload["ACL"] = acl_entries

    response = self.session.post(self.base_path, json=payload)
    return Team(**response.json())

update

update(*, team: Team) -> Team

Update a team's name and membership.

Example

team = client.teams.get_by_id(id="TEM1")
team.name = "Coatings & Adhesives R&D"
updated = client.teams.update(team=team)

Parameters:

Name Type Description Default
team Team

The team with desired changes applied. Modify the name, add or remove entries from members, or change a member's role before calling this method.

required

Returns:

Type Description
Team

The updated Team.

Notes

The following can be updated: the team name, its membership (adding or removing TeamMember entries), and each member's role. Setting members to an empty list removes all members; leaving it as None leaves membership unchanged.

Source code in src/albert/collections/teams.py
@validate_call
def update(self, *, team: Team) -> Team:
    """Update a team's name and membership.

    !!! example
        ```python
        team = client.teams.get_by_id(id="TEM1")
        team.name = "Coatings & Adhesives R&D"
        updated = client.teams.update(team=team)
        ```

    Parameters
    ----------
    team : Team
        The team with desired changes applied. Modify the ``name``,
        add or remove entries from ``members``, or change a member's
        ``role`` before calling this method.

    Returns
    -------
    Team
        The updated Team.

    Notes
    -----
    The following can be updated: the team ``name``, its membership (adding
    or removing [`TeamMember`][albert.resources.teams.TeamMember] entries), and
    each member's ``role``. Setting ``members`` to an empty list removes all
    members; leaving it as ``None`` leaves membership unchanged.
    """
    current = self.get_by_id(id=team.id)
    url = f"{self.base_path}/{team.id}"

    for operations in self._generate_patch_payloads(current=current, updated=team):
        response = self.session.patch(url, json={"data": operations})
        if response.status_code == 206:
            failed_items = (response.json() or {}).get("FailedItems") or []
            if failed_items:
                logger.warning(
                    "Team update partially succeeded", extra={"failed": failed_items}
                )

    return self.get_by_id(id=team.id)

delete

delete(*, id: TeamId) -> None

Delete a team by its ID.

Example

client.teams.delete(id="TEM1")

Parameters:

Name Type Description Default
id TeamId

The ID of the team to delete.

required

Returns:

Type Description
None
Source code in src/albert/collections/teams.py
@validate_call
def delete(self, *, id: TeamId) -> None:
    """Delete a team by its ID.

    !!! example
        ```python
        client.teams.delete(id="TEM1")
        ```

    Parameters
    ----------
    id : TeamId
        The ID of the team to delete.

    Returns
    -------
    None
    """
    url = f"{self.base_path}/{id}"
    self.session.delete(url)

add_users

add_users(*, id: TeamId, members: list[TeamMember]) -> Team

Add users to a team.

Example

from albert.resources.teams import TeamMember
client.teams.add_users(
    id="TEM1",
    members=[TeamMember(id="USR34", role="TeamViewer")],
)

Parameters:

Name Type Description Default
id TeamId

The ID of the team.

required
members list[TeamMember]

The members to add, each with a User ID and a team role. Members default to the TeamViewer role when none is given.

required

Raises:

Type Description
AlbertException

If any of the provided users is already a member. Use update to change an existing member's role.

Returns:

Type Description
Team

The updated Team.

Source code in src/albert/collections/teams.py
@validate_call
def add_users(
    self,
    *,
    id: TeamId,
    members: list[TeamMember],
) -> Team:
    """Add users to a team.

    !!! example
        ```python
        from albert.resources.teams import TeamMember
        client.teams.add_users(
            id="TEM1",
            members=[TeamMember(id="USR34", role="TeamViewer")],
        )
        ```

    Parameters
    ----------
    id : TeamId
        The ID of the team.
    members : list[TeamMember]
        The members to add, each with a User ID and a team role. Members
        default to the ``TeamViewer`` role when none is given.

    Raises
    ------
    AlbertException
        If any of the provided users is already a member. Use [`update`][albert.collections.teams.TeamCollection.update]
        to change an existing member's role.

    Returns
    -------
    Team
        The updated Team.
    """
    current = self.get_by_id(id=id)
    existing_ids = {m.id for m in current.members or []}

    new_ids = [m.id for m in members]
    already_present = [uid for uid in new_ids if uid in existing_ids]
    if already_present:
        raise AlbertException(
            f"Users already in team {id}: {already_present}. Use update() to change roles."
        )

    acl_entries = [{"id": m.id, "fgc": m.role or "TeamViewer"} for m in members]
    url = f"{self.base_path}/{id}"
    payload = {
        "data": [
            {
                "operation": "add",
                "attribute": "ACL",
                "newValue": acl_entries,
            }
        ],
    }
    self.session.patch(url, json=payload)
    return self.get_by_id(id=id)

remove_users

remove_users(
    *, id: TeamId, users: list[User | UserId]
) -> Team

Remove users from a team.

Example

client.teams.remove_users(id="TEM1", users=["USR34"])

Parameters:

Name Type Description Default
id TeamId

The ID of the team.

required
users list[User | UserId]

The users to remove. Accepts User objects or user ID strings.

required

Raises:

Type Description
AlbertException

If none of the provided users are members of the team.

Returns:

Type Description
Team

The updated Team.

Source code in src/albert/collections/teams.py
@validate_call
def remove_users(
    self,
    *,
    id: TeamId,
    users: list[User | UserId],
) -> Team:
    """Remove users from a team.

    !!! example
        ```python
        client.teams.remove_users(id="TEM1", users=["USR34"])
        ```

    Parameters
    ----------
    id : TeamId
        The ID of the team.
    users : list[User | UserId]
        The users to remove. Accepts User objects or user ID strings.

    Raises
    ------
    AlbertException
        If none of the provided users are members of the team.

    Returns
    -------
    Team
        The updated Team.
    """
    current = self.get_by_id(id=id)
    existing_ids = {m.id for m in current.members or []}

    user_ids = [self._resolve_user_id(u) for u in users]
    present = [uid for uid in user_ids if uid in existing_ids]
    if not present:
        raise AlbertException(f"None of the provided users are members of team {id}.")

    acl_entries = [{"id": uid} for uid in present]
    url = f"{self.base_path}/{id}"
    payload = {
        "data": [
            {
                "operation": "delete",
                "attribute": "ACL",
                "oldValue": acl_entries,
            }
        ],
    }
    self.session.patch(url, json=payload)
    return self.get_by_id(id=id)