Resource APIs
Group Management API
Groups in Flow provide a powerful way to organize users and manage permissions at scale. This guide covers creating groups, managing members, setting up hierarchies, and using groups for access control.
Understanding Groups
Groups represent teams, departments, or any collection of users who need shared access to resources.
Group Structure
# A typical group contains:
group = {
"id": 1,
"name": "Smith Lab",
"slug": "smith-lab",
"description": "Dr. Smith's research laboratory",
"created": "2024-01-01T10:00:00Z",
"owner": {"id": 1, "username": "dr_smith"},
"parent": {"id": 5, "name": "Biology Department"},
"member_count": 12,
"admin_count": 3,
"metadata": {
"institution": "University of Science",
"department": "Biology",
"pi": "Dr. Jane Smith",
"focus": "Cancer genomics",
"room": "Building A, Room 123"
},
"permissions": {
"can_add_members": True,
"can_remove_members": True,
"can_edit": True
}
}
Creating Groups
Basic Group Creation
# Python client
group = client.create_group(
name="RNA-seq Analysis Team",
description="Team focused on RNA-seq data analysis"
)
print(f"Created group: {group.slug}")
REST API Group Creation
curl -X POST https://api.flow.bio/groups/create \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"name": "RNA-seq Analysis Team",
"description": "Team focused on RNA-seq data analysis",
"metadata": {
"team_lead": "john_doe",
"created_date": "2024-01-15"
}
}'
GraphQL Group Creation
mutation CreateGroup($input: CreateGroupInput!) {
createGroup(input: $input) {
id
name
slug
description
created
owner {
username
}
}
}
# Variables
{
"input": {
"name": "RNA-seq Analysis Team",
"description": "Team focused on RNA-seq data analysis",
"metadata": {
"team_lead": "john_doe",
"focus_area": "transcriptomics"
}
}
}
Create with Hierarchy
# Create parent group
department = client.create_group(
name="Genomics Department",
description="Main genomics research department"
)
# Create child groups
wet_lab = client.create_group(
name="Wet Lab Team",
description="Sample preparation and sequencing",
parent=department.id
)
dry_lab = client.create_group(
name="Dry Lab Team",
description="Computational analysis",
parent=department.id
)
# Create sub-teams
rnaseq_team = client.create_group(
name="RNA-seq Analysis",
parent=dry_lab.id
)
chipseq_team = client.create_group(
name="ChIP-seq Analysis",
parent=dry_lab.id
)
Managing Group Members
Add Members
# Add single member
group.add_member("new_member@example.com")
# Add multiple members
group.add_members([
"member1@example.com",
"member2@example.com",
"member3@example.com"
])
# Add with specific role
group.add_member("admin@example.com", role="admin")
REST API Member Management
# Add members
curl -X POST https://api.flow.bio/groups/smith-lab/members/add \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"users": ["user1", "user2"],
"role": "member"
}'
# Remove member
curl -X POST https://api.flow.bio/groups/smith-lab/members/remove \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"user": "former_member"
}'
GraphQL Member Management
mutation AddGroupMembers($groupId: ID!, $users: [ID!]!, $role: GroupRole!) {
addGroupMembers(groupId: $groupId, users: $users, role: $role) {
id
members {
edges {
node {
id
username
role
joinedAt
}
}
}
}
}
Member Roles
# Available roles
ROLES = {
"member": "Can access shared resources",
"admin": "Can manage group and members",
"owner": "Full control including deletion"
}
# Update member role
group.update_member_role("member@example.com", "admin")
# List members by role
admins = group.get_members(role="admin")
members = group.get_members(role="member")
Group Hierarchies
Managing Parent-Child Relationships
# Get group hierarchy
def get_group_hierarchy(group_id):
"""Get full hierarchy for a group"""
group = client.get_group(group_id)
hierarchy = {
"group": group,
"parent_chain": [],
"children": []
}
# Walk up to root
current = group
while current.parent:
current = current.parent
hierarchy["parent_chain"].append(current)
# Get all children
def get_children_recursive(parent):
children = parent.get_children()
result = []
for child in children:
result.append({
"group": child,
"children": get_children_recursive(child)
})
return result
hierarchy["children"] = get_children_recursive(group)
return hierarchy
Permission Inheritance
# Check effective permissions
def get_effective_permissions(user, resource):
"""Get user's highest permission from all groups"""
user_groups = user.get_groups()
max_permission = 0
for group in user_groups:
# Check direct group permissions
perm = resource.get_group_permission(group.id)
max_permission = max(max_permission, perm)
# Check parent group permissions
parent = group.parent
while parent:
parent_perm = resource.get_group_permission(parent.id)
max_permission = max(max_permission, parent_perm)
parent = parent.parent
return max_permission
Retrieving Groups
Get Group Details
# Python client
group = client.get_group("smith-lab") # by slug
# or
group = client.get_group(group_id=123) # by ID
print(f"Name: {group.name}")
print(f"Members: {group.member_count}")
print(f"Created: {group.created}")
REST API Group Retrieval
curl -H "Authorization: Bearer <token>" \
https://api.flow.bio/groups/smith-lab
GraphQL Group Query
query GetGroupDetails($slug: String!) {
group(slug: $slug) {
id
name
slug
description
created
owner {
id
username
}
parent {
id
name
slug
}
children {
id
name
slug
}
memberCount
adminCount
members(limit: 100) {
edges {
node {
id
username
email
role
joinedAt
}
}
}
metadata
permissions {
canAddMembers
canRemoveMembers
canEdit
canDelete
}
}
}
List Groups
# Get user's groups
my_groups = client.get_my_groups()
# Get all accessible groups
all_groups = client.get_groups()
# Search groups
lab_groups = client.search_groups("lab")
# Get groups with filters
large_groups = client.get_groups(
min_members=10,
has_children=True
)
Group Administration
Update Group Information
# Update basic info
group.update(
name="Updated Group Name",
description="New description"
)
# Update metadata
group.update_metadata({
"budget_code": "ABC123",
"expiry_date": "2025-12-31",
"contact_email": "lab@university.edu"
})
# Set group settings
group.update_settings({
"auto_approve_members": False,
"require_admin_approval": True,
"member_can_invite": False
})
Transfer Ownership
# Transfer group ownership
group.transfer_ownership("new_owner@example.com")
# Batch transfer for user leaving
def transfer_all_groups(from_user, to_user):
"""Transfer all groups from one user to another"""
groups = client.get_user_owned_groups(from_user)
for group in groups:
group.transfer_ownership(to_user)
print(f"Transferred {group.name} to {to_user}")
Delete Groups
# Delete group (must be owner)
group.delete()
# Safe delete with checks
def safe_delete_group(group_id):
"""Delete group with safety checks"""
group = client.get_group(group_id)
# Check for shared resources
shared_samples = client.search_samples(shared_with_groups=[group.slug])
shared_projects = client.search_projects(shared_with_groups=[group.slug])
if shared_samples or shared_projects:
print(f"Warning: Group has {len(shared_samples)} samples and {len(shared_projects)} projects")
confirm = input("Continue with deletion? (yes/no): ")
if confirm.lower() != "yes":
return False
# Check for child groups
if group.get_children():
print("Error: Cannot delete group with child groups")
return False
group.delete()
return True
Group Permissions
Managing Group Access
# Share resources with group
sample.share(groups=["smith-lab"], permission="read")
project.share(groups=["smith-lab"], permission="edit")
execution.share(groups=["smith-lab"], permission="read")
# Bulk share with group
client.bulk_share(
resource_type="sample",
resource_ids=[123, 124, 125],
groups=["smith-lab"],
permission="read"
)
Group-Based Access Control
def setup_lab_permissions(lab_group_slug):
"""Set up standard permissions for a lab"""
group = client.get_group(lab_group_slug)
# Create standard sub-groups
pi_group = client.create_group(
name=f"{group.name} - PIs",
parent=group.id
)
postdoc_group = client.create_group(
name=f"{group.name} - Postdocs",
parent=group.id
)
student_group = client.create_group(
name=f"{group.name} - Students",
parent=group.id
)
# Set up permission templates
permissions = {
pi_group.slug: "share", # Full access
postdoc_group.slug: "edit", # Can modify
student_group.slug: "read" # View only
}
return permissions
Group Analytics
Group Statistics
def get_group_statistics(group_slug):
"""Get comprehensive group statistics"""
group = client.get_group(group_slug)
members = group.get_members()
stats = {
"members": {
"total": len(members),
"admins": len([m for m in members if m.role == "admin"]),
"active": len([m for m in members if m.last_login_days < 30])
},
"resources": {
"samples": 0,
"projects": 0,
"executions": 0,
"total_storage_gb": 0
},
"activity": {
"samples_created_this_month": 0,
"executions_this_month": 0
}
}
# Count shared resources
stats["resources"]["samples"] = len(
client.search_samples(shared_with_groups=[group.slug])
)
stats["resources"]["projects"] = len(
client.search_projects(shared_with_groups=[group.slug])
)
return stats
Group Activity Report
def generate_group_activity_report(group_slug, days=30):
"""Generate activity report for group"""
group = client.get_group(group_slug)
since = datetime.now() - timedelta(days=days)
report = {
"group": group.name,
"period": f"Last {days} days",
"members": {},
"resources_created": {
"samples": [],
"projects": [],
"executions": []
}
}
# Get member activity
for member in group.get_members():
member_activity = {
"last_login": member.last_login,
"samples_created": 0,
"executions_run": 0
}
# Count samples created
samples = client.search_samples(
owner=member.username,
created_after=since
)
member_activity["samples_created"] = len(samples)
report["members"][member.username] = member_activity
return report
Group Workflows
Onboarding New Lab Members
def onboard_lab_member(username, lab_group_slug, role="member"):
"""Complete onboarding process for new lab member"""
# Add to group
group = client.get_group(lab_group_slug)
group.add_member(username, role=role)
# Share standard resources
standard_resources = group.metadata.get("onboarding_resources", [])
for resource_id in standard_resources:
resource = client.get_resource(resource_id)
resource.share(users=[username], permission="read")
# Create welcome project
welcome_project = client.create_project(
name=f"Welcome - {username}",
organism="human",
description="Getting started with Flow"
)
welcome_project.share(users=[username], permission="edit")
# Send notification
client.send_notification(
to_user=username,
subject=f"Welcome to {group.name}",
message=f"You've been added to {group.name}. Check your welcome project for getting started resources."
)
return {
"user": username,
"group": group.name,
"role": role,
"welcome_project": welcome_project.id
}
Lab Collaboration Setup
class LabCollaboration:
"""Manage collaboration between labs"""
def __init__(self, lab1_slug, lab2_slug):
self.lab1 = client.get_group(lab1_slug)
self.lab2 = client.get_group(lab2_slug)
def create_shared_workspace(self, project_name):
"""Create shared project and groups"""
# Create collaboration group
collab_group = client.create_group(
name=f"{self.lab1.name} + {self.lab2.name} Collaboration",
description=f"Shared workspace for {project_name}"
)
# Add PIs from both labs
for lab in [self.lab1, self.lab2]:
pis = lab.get_members(role="admin")
for pi in pis:
collab_group.add_member(pi.username, role="admin")
# Create shared project
project = client.create_project(
name=project_name,
organism="human",
metadata={
"collaboration": True,
"labs": [self.lab1.name, self.lab2.name]
}
)
project.share(groups=[collab_group.slug], permission="edit")
return collab_group, project
Group Templates
Research Lab Template
def create_research_lab(pi_username, lab_name, department):
"""Create standard research lab structure"""
# Main lab group
lab = client.create_group(
name=lab_name,
description=f"{pi_username}'s research laboratory",
metadata={
"pi": pi_username,
"department": department,
"type": "research_lab"
}
)
# Add PI as owner
lab.add_member(pi_username, role="owner")
# Create standard sub-groups
subgroups = {
"admins": "Lab administrators and senior members",
"postdocs": "Postdoctoral researchers",
"grad_students": "Graduate students",
"undergrads": "Undergraduate researchers",
"collaborators": "External collaborators"
}
created_groups = {"main": lab}
for key, description in subgroups.items():
subgroup = client.create_group(
name=f"{lab_name} - {key.replace('_', ' ').title()}",
description=description,
parent=lab.id
)
created_groups[key] = subgroup
# Set up permissions template
lab.update_metadata({
"permission_template": {
"admins": "share",
"postdocs": "edit",
"grad_students": "edit",
"undergrads": "read",
"collaborators": "read"
}
})
return created_groups
Course/Class Template
def create_course_groups(course_code, instructor, year, semester):
"""Create groups for academic course"""
# Main course group
course = client.create_group(
name=f"{course_code} - {year} {semester}",
description=f"Course group for {course_code}",
metadata={
"instructor": instructor,
"year": year,
"semester": semester,
"type": "course"
}
)
# Sub-groups
instructors = client.create_group(
name=f"{course_code} - Instructors",
parent=course.id
)
tas = client.create_group(
name=f"{course_code} - Teaching Assistants",
parent=course.id
)
students = client.create_group(
name=f"{course_code} - Students",
parent=course.id
)
# Add instructor
instructors.add_member(instructor, role="admin")
# Create course resources
course_project = client.create_project(
name=f"{course_code} Resources",
organism="human",
description="Course materials and examples"
)
# Set permissions
course_project.share(groups=[instructors.slug], permission="share")
course_project.share(groups=[tas.slug], permission="edit")
course_project.share(groups=[students.slug], permission="read")
return {
"course": course,
"instructors": instructors,
"tas": tas,
"students": students,
"project": course_project
}
Best Practices
1. Naming Conventions
def validate_group_name(name):
"""Validate group name follows conventions"""
import re
# Check length
if len(name) < 3 or len(name) > 50:
return False, "Name must be 3-50 characters"
# Check format
if not re.match(r'^[A-Za-z0-9\s\-_]+$', name):
return False, "Name can only contain letters, numbers, spaces, hyphens, and underscores"
# Check for reserved words
reserved = ["admin", "system", "root", "flow"]
if any(word in name.lower() for word in reserved):
return False, "Name contains reserved words"
return True, "Valid"
2. Group Lifecycle Management
def archive_inactive_group(group_slug, days_inactive=180):
"""Archive groups with no recent activity"""
group = client.get_group(group_slug)
# Check last activity
last_activity = get_group_last_activity(group)
if (datetime.now() - last_activity).days > days_inactive:
# Archive process
group.update_metadata({
"status": "archived",
"archived_date": str(datetime.now()),
"archived_by": "system"
})
# Remove from active listings
group.update_settings({"visible": False})
# Notify admins
for admin in group.get_members(role="admin"):
client.send_notification(
to_user=admin.username,
subject=f"Group {group.name} archived",
message=f"Due to {days_inactive} days of inactivity"
)
3. Security Best Practices
def audit_group_permissions(group_slug):
"""Audit group access and permissions"""
group = client.get_group(group_slug)
audit = {
"group": group.name,
"audit_date": datetime.now(),
"members": {
"total": group.member_count,
"by_role": {}
},
"shared_resources": {
"samples": 0,
"projects": 0,
"executions": 0
},
"permission_levels": {},
"external_members": []
}
# Count members by role
for member in group.get_members():
role = member.role
audit["members"]["by_role"][role] = \
audit["members"]["by_role"].get(role, 0) + 1
# Check for external members
if not member.email.endswith("@company.com"):
audit["external_members"].append(member.username)
# Audit shared resources
for resource_type in ["samples", "projects", "executions"]:
resources = client.search(
resource_type=resource_type,
shared_with_groups=[group.slug]
)
audit["shared_resources"][resource_type] = len(resources)
return audit
Next Steps
- Permissions Guide - Understanding group permissions
- API Authentication - Group authentication
- Search API - Finding groups and members
- Error Handling - Handling group-related errors