Allow adding users as workspace owners via API
Context
Currently, when using POST /api/workspaces.inviteMember to add a user to a workspace, the role is hardcoded to "member" (see workspace_service.go:650), even when providing full owner permissions.
This creates a limitation for programmatic workspace provisioning where we need to automatically add users as owners without requiring email invitation acceptance + manual upgrade.
Current Behavior
For existing users
POST /api/workspaces.inviteMember
{
"workspace_id": "myworkspace",
"email": "user@example.com",
"permissions": { /* full permissions */ }
}
Result: User is added directly with role = "member" (line 650 in workspace_service.go)
For new users
Result: Invitation sent by email, user becomes "member" after accepting
Problem
There is no API endpoint to:
- Add a user as owner directly when they exist
- Promote a member to owner (without using
TransferOwnership which demotes the current owner)
Attempted Workarounds
- ❌
setUserPermissions: Only updates permissions, not the role
- ❌
TransferOwnership: Exists in service but not exposed via HTTP, and it demotes the current owner (not suitable for multi-owner workspaces)
- ✅ Direct DB UPDATE: Works but bypasses API validation
Proposed Solutions
Option 1: Add optional role parameter to inviteMember (Recommended)
Pros:
- Backward compatible (defaults to "member")
- Uses existing endpoint
- Simple implementation
Implementation:
// workspace_service.go
func (s *WorkspaceService) InviteMember(ctx context.Context, workspaceID, email string, permissions domain.UserPermissions, role string) (*domain.WorkspaceInvitation, string, error) {
// ...existing code...
// Default role to "member" if not specified
if role == "" {
role = "member"
}
// Validate role
if role != "member" && role != "owner" {
return nil, "", fmt.Errorf("invalid role: must be 'member' or 'owner'")
}
// Line 647: Use the provided role instead of hardcoded "member"
userWorkspace := &domain.UserWorkspace{
UserID: existingUser.ID,
WorkspaceID: workspaceID,
Role: role, // ✅ Use parameter
Permissions: permissions,
CreatedAt: time.Now().UTC(),
UpdatedAt: time.Now().UTC(),
}
// ...
}
HTTP Handler:
// workspace_handler.go
type InviteMemberRequest struct {
WorkspaceID string `json:"workspace_id"`
Email string `json:"email"`
Permissions domain.UserPermissions `json:"permissions"`
Role string `json:"role,omitempty"` // ✅ Optional, defaults to "member"
}
Option 2: Create new endpoint POST /api/workspaces.promoteToOwner
Pros:
- Explicit API
- No breaking changes
- Clear intent
Implementation:
// workspace_service.go
func (s *WorkspaceService) PromoteToOwner(ctx context.Context, workspaceID, userID string) error {
// ... auth checks ...
targetUserWorkspace, err := s.repo.GetUserWorkspace(ctx, userID, workspaceID)
if err != nil {
return fmt.Errorf("user is not a member of the workspace")
}
if targetUserWorkspace.Role == "owner" {
return nil // Already owner
}
targetUserWorkspace.Role = "owner"
targetUserWorkspace.Permissions = domain.FullPermissions
targetUserWorkspace.UpdatedAt = time.Now().UTC()
return s.repo.AddUserToWorkspace(ctx, targetUserWorkspace)
}
Use Case
Automated SaaS provisioning (e.g., Web-Dashboard provisioning Notifuse workspaces):
// 1. Root admin creates workspace
const workspace = await createWorkspace(workspaceId);
// 2. Invite user AS OWNER immediately
await inviteMember(workspaceId, userEmail, fullPermissions, 'owner');
// OR with Option 2:
// await inviteMember(workspaceId, userEmail, fullPermissions);
// await promoteToOwner(workspaceId, userId);
// ✅ User has immediate owner access (if existing user)
// ✅ No email acceptance required
// ✅ No database bypass needed
Testing
Tested with existing users in workspace testazernew:
- ✅
inviteMember adds user directly (has user_id)
- ❌ Role is hardcoded to "member"
- ❌
setUserPermissions doesn't change role
- ✅ Direct DB UPDATE works:
UPDATE user_workspaces SET role = 'owner'
Recommendation
Option 1 (add role parameter to inviteMember) is preferred because:
- Backward compatible
- Single API call
- Consistent with existing patterns
- Minimal code changes
Related Files
internal/service/workspace_service.go (lines 589-704)
internal/http/workspace_handler.go
internal/domain/workspace.go
Allow adding users as workspace owners via API
Context
Currently, when using
POST /api/workspaces.inviteMemberto add a user to a workspace, the role is hardcoded to "member" (seeworkspace_service.go:650), even when providing full owner permissions.This creates a limitation for programmatic workspace provisioning where we need to automatically add users as owners without requiring email invitation acceptance + manual upgrade.
Current Behavior
For existing users
POST /api/workspaces.inviteMember { "workspace_id": "myworkspace", "email": "user@example.com", "permissions": { /* full permissions */ } }Result: User is added directly with
role = "member"(line 650 in workspace_service.go)For new users
Result: Invitation sent by email, user becomes "member" after accepting
Problem
There is no API endpoint to:
TransferOwnershipwhich demotes the current owner)Attempted Workarounds
setUserPermissions: Only updates permissions, not the roleTransferOwnership: Exists in service but not exposed via HTTP, and it demotes the current owner (not suitable for multi-owner workspaces)Proposed Solutions
Option 1: Add optional
roleparameter toinviteMember(Recommended)Pros:
Implementation:
HTTP Handler:
Option 2: Create new endpoint
POST /api/workspaces.promoteToOwnerPros:
Implementation:
Use Case
Automated SaaS provisioning (e.g., Web-Dashboard provisioning Notifuse workspaces):
Testing
Tested with existing users in workspace
testazernew:inviteMemberadds user directly (has user_id)setUserPermissionsdoesn't change roleUPDATE user_workspaces SET role = 'owner'Recommendation
Option 1 (add
roleparameter toinviteMember) is preferred because:Related Files
internal/service/workspace_service.go(lines 589-704)internal/http/workspace_handler.gointernal/domain/workspace.go