From 8442288475664ab732f3db4ba5efed68b5eea0f5 Mon Sep 17 00:00:00 2001
From: alishakawaguchi <alisha@nucleuscloud.com>
Date: Tue, 7 Nov 2023 09:54:40 -0800
Subject: [PATCH] add create team account functionality (#520)

---
 backend/gen/go/db/mock_Querier.go             | 110 +++++++
 backend/gen/go/db/querier.go                  |   2 +
 backend/gen/go/db/users.sql.go                |  57 ++++
 .../mock_UserAccountServiceClient.go          |  55 ++++
 .../user_account.connect.go                   |  27 ++
 .../protos/mgmt/v1alpha1/user_account.pb.go   | 308 +++++++++++++-----
 .../mgmt/v1alpha1/user_account.pb.validate.go | 208 ++++++++++++
 backend/internal/nucleusdb/db.go              |   2 -
 backend/internal/nucleusdb/mock_Tx.go         |  69 ++++
 backend/internal/nucleusdb/users.go           |  40 +++
 backend/internal/nucleusdb/users_test.go      | 132 ++++++++
 .../protos/mgmt/v1alpha1/user_account.proto   |   8 +
 .../mgmt/v1alpha1/job-service/jobs.go         |   1 -
 .../v1alpha1/user-account-service/users.go    |  23 ++
 .../user-account-service/users_test.go        |  28 ++
 backend/sql/postgresql/queries/users.sql      |  14 +
 frontend/app/api/teams/route.ts               |  10 +
 frontend/components/AccountSwitcher.tsx       | 286 +++++++++++-----
 .../mgmt/v1alpha1/user_account_connect.ts     |  11 +-
 .../mgmt/v1alpha1/user_account_pb.ts          |  74 +++++
 20 files changed, 1287 insertions(+), 178 deletions(-)
 create mode 100644 backend/internal/nucleusdb/mock_Tx.go
 create mode 100644 backend/internal/nucleusdb/users_test.go
 create mode 100644 frontend/app/api/teams/route.ts

diff --git a/backend/gen/go/db/mock_Querier.go b/backend/gen/go/db/mock_Querier.go
index 0630ea9d75..a2f5cb7bb6 100644
--- a/backend/gen/go/db/mock_Querier.go
+++ b/backend/gen/go/db/mock_Querier.go
@@ -456,6 +456,60 @@ func (_c *MockQuerier_CreatePersonalAccount_Call) RunAndReturn(run func(context.
 	return _c
 }
 
+// CreateTeamAccount provides a mock function with given fields: ctx, db, accountSlug
+func (_m *MockQuerier) CreateTeamAccount(ctx context.Context, db DBTX, accountSlug string) (NeosyncApiAccount, error) {
+	ret := _m.Called(ctx, db, accountSlug)
+
+	var r0 NeosyncApiAccount
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, DBTX, string) (NeosyncApiAccount, error)); ok {
+		return rf(ctx, db, accountSlug)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, DBTX, string) NeosyncApiAccount); ok {
+		r0 = rf(ctx, db, accountSlug)
+	} else {
+		r0 = ret.Get(0).(NeosyncApiAccount)
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, DBTX, string) error); ok {
+		r1 = rf(ctx, db, accountSlug)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// MockQuerier_CreateTeamAccount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateTeamAccount'
+type MockQuerier_CreateTeamAccount_Call struct {
+	*mock.Call
+}
+
+// CreateTeamAccount is a helper method to define mock.On call
+//   - ctx context.Context
+//   - db DBTX
+//   - accountSlug string
+func (_e *MockQuerier_Expecter) CreateTeamAccount(ctx interface{}, db interface{}, accountSlug interface{}) *MockQuerier_CreateTeamAccount_Call {
+	return &MockQuerier_CreateTeamAccount_Call{Call: _e.mock.On("CreateTeamAccount", ctx, db, accountSlug)}
+}
+
+func (_c *MockQuerier_CreateTeamAccount_Call) Run(run func(ctx context.Context, db DBTX, accountSlug string)) *MockQuerier_CreateTeamAccount_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(context.Context), args[1].(DBTX), args[2].(string))
+	})
+	return _c
+}
+
+func (_c *MockQuerier_CreateTeamAccount_Call) Return(_a0 NeosyncApiAccount, _a1 error) *MockQuerier_CreateTeamAccount_Call {
+	_c.Call.Return(_a0, _a1)
+	return _c
+}
+
+func (_c *MockQuerier_CreateTeamAccount_Call) RunAndReturn(run func(context.Context, DBTX, string) (NeosyncApiAccount, error)) *MockQuerier_CreateTeamAccount_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
 // CreateUser provides a mock function with given fields: ctx, db
 func (_m *MockQuerier) CreateUser(ctx context.Context, db DBTX) (NeosyncApiUser, error) {
 	ret := _m.Called(ctx, db)
@@ -1472,6 +1526,62 @@ func (_c *MockQuerier_GetPersonalAccountByUserId_Call) RunAndReturn(run func(con
 	return _c
 }
 
+// GetTeamAccountsByUserId provides a mock function with given fields: ctx, db, userid
+func (_m *MockQuerier) GetTeamAccountsByUserId(ctx context.Context, db DBTX, userid pgtype.UUID) ([]NeosyncApiAccount, error) {
+	ret := _m.Called(ctx, db, userid)
+
+	var r0 []NeosyncApiAccount
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, DBTX, pgtype.UUID) ([]NeosyncApiAccount, error)); ok {
+		return rf(ctx, db, userid)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, DBTX, pgtype.UUID) []NeosyncApiAccount); ok {
+		r0 = rf(ctx, db, userid)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]NeosyncApiAccount)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, DBTX, pgtype.UUID) error); ok {
+		r1 = rf(ctx, db, userid)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// MockQuerier_GetTeamAccountsByUserId_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTeamAccountsByUserId'
+type MockQuerier_GetTeamAccountsByUserId_Call struct {
+	*mock.Call
+}
+
+// GetTeamAccountsByUserId is a helper method to define mock.On call
+//   - ctx context.Context
+//   - db DBTX
+//   - userid pgtype.UUID
+func (_e *MockQuerier_Expecter) GetTeamAccountsByUserId(ctx interface{}, db interface{}, userid interface{}) *MockQuerier_GetTeamAccountsByUserId_Call {
+	return &MockQuerier_GetTeamAccountsByUserId_Call{Call: _e.mock.On("GetTeamAccountsByUserId", ctx, db, userid)}
+}
+
+func (_c *MockQuerier_GetTeamAccountsByUserId_Call) Run(run func(ctx context.Context, db DBTX, userid pgtype.UUID)) *MockQuerier_GetTeamAccountsByUserId_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(context.Context), args[1].(DBTX), args[2].(pgtype.UUID))
+	})
+	return _c
+}
+
+func (_c *MockQuerier_GetTeamAccountsByUserId_Call) Return(_a0 []NeosyncApiAccount, _a1 error) *MockQuerier_GetTeamAccountsByUserId_Call {
+	_c.Call.Return(_a0, _a1)
+	return _c
+}
+
+func (_c *MockQuerier_GetTeamAccountsByUserId_Call) RunAndReturn(run func(context.Context, DBTX, pgtype.UUID) ([]NeosyncApiAccount, error)) *MockQuerier_GetTeamAccountsByUserId_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
 // GetTemporalConfigByAccount provides a mock function with given fields: ctx, db, id
 func (_m *MockQuerier) GetTemporalConfigByAccount(ctx context.Context, db DBTX, id pgtype.UUID) (*pg_models.TemporalConfig, error) {
 	ret := _m.Called(ctx, db, id)
diff --git a/backend/gen/go/db/querier.go b/backend/gen/go/db/querier.go
index 83ad9ba685..23161f8c98 100644
--- a/backend/gen/go/db/querier.go
+++ b/backend/gen/go/db/querier.go
@@ -20,6 +20,7 @@ type Querier interface {
 	CreateJobConnectionDestination(ctx context.Context, db DBTX, arg CreateJobConnectionDestinationParams) (NeosyncApiJobDestinationConnectionAssociation, error)
 	CreateJobConnectionDestinations(ctx context.Context, db DBTX, arg []CreateJobConnectionDestinationsParams) (int64, error)
 	CreatePersonalAccount(ctx context.Context, db DBTX, accountSlug string) (NeosyncApiAccount, error)
+	CreateTeamAccount(ctx context.Context, db DBTX, accountSlug string) (NeosyncApiAccount, error)
 	CreateUser(ctx context.Context, db DBTX) (NeosyncApiUser, error)
 	DeleteCustomTransformerById(ctx context.Context, db DBTX, id pgtype.UUID) error
 	DeleteJob(ctx context.Context, db DBTX, id pgtype.UUID) error
@@ -39,6 +40,7 @@ type Querier interface {
 	GetJobConnectionDestinationsByJobIds(ctx context.Context, db DBTX, jobids []pgtype.UUID) ([]NeosyncApiJobDestinationConnectionAssociation, error)
 	GetJobsByAccount(ctx context.Context, db DBTX, accountid pgtype.UUID) ([]NeosyncApiJob, error)
 	GetPersonalAccountByUserId(ctx context.Context, db DBTX, userid pgtype.UUID) (NeosyncApiAccount, error)
+	GetTeamAccountsByUserId(ctx context.Context, db DBTX, userid pgtype.UUID) ([]NeosyncApiAccount, error)
 	GetTemporalConfigByAccount(ctx context.Context, db DBTX, id pgtype.UUID) (*pg_models.TemporalConfig, error)
 	GetTemporalConfigByUserAccount(ctx context.Context, db DBTX, arg GetTemporalConfigByUserAccountParams) (*pg_models.TemporalConfig, error)
 	GetUser(ctx context.Context, db DBTX, id pgtype.UUID) (NeosyncApiUser, error)
diff --git a/backend/gen/go/db/users.sql.go b/backend/gen/go/db/users.sql.go
index 78a8378fe2..b4e5c5264c 100644
--- a/backend/gen/go/db/users.sql.go
+++ b/backend/gen/go/db/users.sql.go
@@ -89,6 +89,29 @@ func (q *Queries) CreatePersonalAccount(ctx context.Context, db DBTX, accountSlu
 	return i, err
 }
 
+const createTeamAccount = `-- name: CreateTeamAccount :one
+INSERT INTO neosync_api.accounts (
+  account_type, account_slug
+) VALUES (
+  1, $1
+)
+RETURNING id, created_at, updated_at, account_type, account_slug, temporal_config
+`
+
+func (q *Queries) CreateTeamAccount(ctx context.Context, db DBTX, accountSlug string) (NeosyncApiAccount, error) {
+	row := db.QueryRow(ctx, createTeamAccount, accountSlug)
+	var i NeosyncApiAccount
+	err := row.Scan(
+		&i.ID,
+		&i.CreatedAt,
+		&i.UpdatedAt,
+		&i.AccountType,
+		&i.AccountSlug,
+		&i.TemporalConfig,
+	)
+	return i, err
+}
+
 const createUser = `-- name: CreateUser :one
 INSERT INTO neosync_api.users (
   id, created_at, updated_at
@@ -216,6 +239,40 @@ func (q *Queries) GetPersonalAccountByUserId(ctx context.Context, db DBTX, useri
 	return i, err
 }
 
+const getTeamAccountsByUserId = `-- name: GetTeamAccountsByUserId :many
+SELECT a.id, a.created_at, a.updated_at, a.account_type, a.account_slug, a.temporal_config from neosync_api.accounts a
+INNER JOIN neosync_api.account_user_associations aua ON aua.account_id = a.id
+INNER JOIN neosync_api.users u ON u.id = aua.user_id
+WHERE u.id = $1 AND a.account_type = 1
+`
+
+func (q *Queries) GetTeamAccountsByUserId(ctx context.Context, db DBTX, userid pgtype.UUID) ([]NeosyncApiAccount, error) {
+	rows, err := db.Query(ctx, getTeamAccountsByUserId, userid)
+	if err != nil {
+		return nil, err
+	}
+	defer rows.Close()
+	var items []NeosyncApiAccount
+	for rows.Next() {
+		var i NeosyncApiAccount
+		if err := rows.Scan(
+			&i.ID,
+			&i.CreatedAt,
+			&i.UpdatedAt,
+			&i.AccountType,
+			&i.AccountSlug,
+			&i.TemporalConfig,
+		); err != nil {
+			return nil, err
+		}
+		items = append(items, i)
+	}
+	if err := rows.Err(); err != nil {
+		return nil, err
+	}
+	return items, nil
+}
+
 const getTemporalConfigByAccount = `-- name: GetTemporalConfigByAccount :one
 SELECT temporal_config
 FROM neosync_api.accounts
diff --git a/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect/mock_UserAccountServiceClient.go b/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect/mock_UserAccountServiceClient.go
index ced24de172..0f8689042f 100644
--- a/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect/mock_UserAccountServiceClient.go
+++ b/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect/mock_UserAccountServiceClient.go
@@ -79,6 +79,61 @@ func (_c *MockUserAccountServiceClient_ConvertPersonalToTeamAccount_Call) RunAnd
 	return _c
 }
 
+// CreateTeamAccount provides a mock function with given fields: _a0, _a1
+func (_m *MockUserAccountServiceClient) CreateTeamAccount(_a0 context.Context, _a1 *connect.Request[mgmtv1alpha1.CreateTeamAccountRequest]) (*connect.Response[mgmtv1alpha1.CreateTeamAccountResponse], error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *connect.Response[mgmtv1alpha1.CreateTeamAccountResponse]
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *connect.Request[mgmtv1alpha1.CreateTeamAccountRequest]) (*connect.Response[mgmtv1alpha1.CreateTeamAccountResponse], error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *connect.Request[mgmtv1alpha1.CreateTeamAccountRequest]) *connect.Response[mgmtv1alpha1.CreateTeamAccountResponse]); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*connect.Response[mgmtv1alpha1.CreateTeamAccountResponse])
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *connect.Request[mgmtv1alpha1.CreateTeamAccountRequest]) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// MockUserAccountServiceClient_CreateTeamAccount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateTeamAccount'
+type MockUserAccountServiceClient_CreateTeamAccount_Call struct {
+	*mock.Call
+}
+
+// CreateTeamAccount is a helper method to define mock.On call
+//   - _a0 context.Context
+//   - _a1 *connect.Request[mgmtv1alpha1.CreateTeamAccountRequest]
+func (_e *MockUserAccountServiceClient_Expecter) CreateTeamAccount(_a0 interface{}, _a1 interface{}) *MockUserAccountServiceClient_CreateTeamAccount_Call {
+	return &MockUserAccountServiceClient_CreateTeamAccount_Call{Call: _e.mock.On("CreateTeamAccount", _a0, _a1)}
+}
+
+func (_c *MockUserAccountServiceClient_CreateTeamAccount_Call) Run(run func(_a0 context.Context, _a1 *connect.Request[mgmtv1alpha1.CreateTeamAccountRequest])) *MockUserAccountServiceClient_CreateTeamAccount_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(context.Context), args[1].(*connect.Request[mgmtv1alpha1.CreateTeamAccountRequest]))
+	})
+	return _c
+}
+
+func (_c *MockUserAccountServiceClient_CreateTeamAccount_Call) Return(_a0 *connect.Response[mgmtv1alpha1.CreateTeamAccountResponse], _a1 error) *MockUserAccountServiceClient_CreateTeamAccount_Call {
+	_c.Call.Return(_a0, _a1)
+	return _c
+}
+
+func (_c *MockUserAccountServiceClient_CreateTeamAccount_Call) RunAndReturn(run func(context.Context, *connect.Request[mgmtv1alpha1.CreateTeamAccountRequest]) (*connect.Response[mgmtv1alpha1.CreateTeamAccountResponse], error)) *MockUserAccountServiceClient_CreateTeamAccount_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
 // GetAccountTemporalConfig provides a mock function with given fields: _a0, _a1
 func (_m *MockUserAccountServiceClient) GetAccountTemporalConfig(_a0 context.Context, _a1 *connect.Request[mgmtv1alpha1.GetAccountTemporalConfigRequest]) (*connect.Response[mgmtv1alpha1.GetAccountTemporalConfigResponse], error) {
 	ret := _m.Called(_a0, _a1)
diff --git a/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect/user_account.connect.go b/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect/user_account.connect.go
index 2fadb8d960..41f73bc13a 100644
--- a/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect/user_account.connect.go
+++ b/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect/user_account.connect.go
@@ -48,6 +48,9 @@ const (
 	// UserAccountServiceConvertPersonalToTeamAccountProcedure is the fully-qualified name of the
 	// UserAccountService's ConvertPersonalToTeamAccount RPC.
 	UserAccountServiceConvertPersonalToTeamAccountProcedure = "/mgmt.v1alpha1.UserAccountService/ConvertPersonalToTeamAccount"
+	// UserAccountServiceCreateTeamAccountProcedure is the fully-qualified name of the
+	// UserAccountService's CreateTeamAccount RPC.
+	UserAccountServiceCreateTeamAccountProcedure = "/mgmt.v1alpha1.UserAccountService/CreateTeamAccount"
 	// UserAccountServiceIsUserInAccountProcedure is the fully-qualified name of the
 	// UserAccountService's IsUserInAccount RPC.
 	UserAccountServiceIsUserInAccountProcedure = "/mgmt.v1alpha1.UserAccountService/IsUserInAccount"
@@ -66,6 +69,7 @@ type UserAccountServiceClient interface {
 	GetUserAccounts(context.Context, *connect.Request[v1alpha1.GetUserAccountsRequest]) (*connect.Response[v1alpha1.GetUserAccountsResponse], error)
 	SetPersonalAccount(context.Context, *connect.Request[v1alpha1.SetPersonalAccountRequest]) (*connect.Response[v1alpha1.SetPersonalAccountResponse], error)
 	ConvertPersonalToTeamAccount(context.Context, *connect.Request[v1alpha1.ConvertPersonalToTeamAccountRequest]) (*connect.Response[v1alpha1.ConvertPersonalToTeamAccountResponse], error)
+	CreateTeamAccount(context.Context, *connect.Request[v1alpha1.CreateTeamAccountRequest]) (*connect.Response[v1alpha1.CreateTeamAccountResponse], error)
 	IsUserInAccount(context.Context, *connect.Request[v1alpha1.IsUserInAccountRequest]) (*connect.Response[v1alpha1.IsUserInAccountResponse], error)
 	GetAccountTemporalConfig(context.Context, *connect.Request[v1alpha1.GetAccountTemporalConfigRequest]) (*connect.Response[v1alpha1.GetAccountTemporalConfigResponse], error)
 	SetAccountTemporalConfig(context.Context, *connect.Request[v1alpha1.SetAccountTemporalConfigRequest]) (*connect.Response[v1alpha1.SetAccountTemporalConfigResponse], error)
@@ -106,6 +110,11 @@ func NewUserAccountServiceClient(httpClient connect.HTTPClient, baseURL string,
 			baseURL+UserAccountServiceConvertPersonalToTeamAccountProcedure,
 			opts...,
 		),
+		createTeamAccount: connect.NewClient[v1alpha1.CreateTeamAccountRequest, v1alpha1.CreateTeamAccountResponse](
+			httpClient,
+			baseURL+UserAccountServiceCreateTeamAccountProcedure,
+			opts...,
+		),
 		isUserInAccount: connect.NewClient[v1alpha1.IsUserInAccountRequest, v1alpha1.IsUserInAccountResponse](
 			httpClient,
 			baseURL+UserAccountServiceIsUserInAccountProcedure,
@@ -131,6 +140,7 @@ type userAccountServiceClient struct {
 	getUserAccounts              *connect.Client[v1alpha1.GetUserAccountsRequest, v1alpha1.GetUserAccountsResponse]
 	setPersonalAccount           *connect.Client[v1alpha1.SetPersonalAccountRequest, v1alpha1.SetPersonalAccountResponse]
 	convertPersonalToTeamAccount *connect.Client[v1alpha1.ConvertPersonalToTeamAccountRequest, v1alpha1.ConvertPersonalToTeamAccountResponse]
+	createTeamAccount            *connect.Client[v1alpha1.CreateTeamAccountRequest, v1alpha1.CreateTeamAccountResponse]
 	isUserInAccount              *connect.Client[v1alpha1.IsUserInAccountRequest, v1alpha1.IsUserInAccountResponse]
 	getAccountTemporalConfig     *connect.Client[v1alpha1.GetAccountTemporalConfigRequest, v1alpha1.GetAccountTemporalConfigResponse]
 	setAccountTemporalConfig     *connect.Client[v1alpha1.SetAccountTemporalConfigRequest, v1alpha1.SetAccountTemporalConfigResponse]
@@ -161,6 +171,11 @@ func (c *userAccountServiceClient) ConvertPersonalToTeamAccount(ctx context.Cont
 	return c.convertPersonalToTeamAccount.CallUnary(ctx, req)
 }
 
+// CreateTeamAccount calls mgmt.v1alpha1.UserAccountService.CreateTeamAccount.
+func (c *userAccountServiceClient) CreateTeamAccount(ctx context.Context, req *connect.Request[v1alpha1.CreateTeamAccountRequest]) (*connect.Response[v1alpha1.CreateTeamAccountResponse], error) {
+	return c.createTeamAccount.CallUnary(ctx, req)
+}
+
 // IsUserInAccount calls mgmt.v1alpha1.UserAccountService.IsUserInAccount.
 func (c *userAccountServiceClient) IsUserInAccount(ctx context.Context, req *connect.Request[v1alpha1.IsUserInAccountRequest]) (*connect.Response[v1alpha1.IsUserInAccountResponse], error) {
 	return c.isUserInAccount.CallUnary(ctx, req)
@@ -183,6 +198,7 @@ type UserAccountServiceHandler interface {
 	GetUserAccounts(context.Context, *connect.Request[v1alpha1.GetUserAccountsRequest]) (*connect.Response[v1alpha1.GetUserAccountsResponse], error)
 	SetPersonalAccount(context.Context, *connect.Request[v1alpha1.SetPersonalAccountRequest]) (*connect.Response[v1alpha1.SetPersonalAccountResponse], error)
 	ConvertPersonalToTeamAccount(context.Context, *connect.Request[v1alpha1.ConvertPersonalToTeamAccountRequest]) (*connect.Response[v1alpha1.ConvertPersonalToTeamAccountResponse], error)
+	CreateTeamAccount(context.Context, *connect.Request[v1alpha1.CreateTeamAccountRequest]) (*connect.Response[v1alpha1.CreateTeamAccountResponse], error)
 	IsUserInAccount(context.Context, *connect.Request[v1alpha1.IsUserInAccountRequest]) (*connect.Response[v1alpha1.IsUserInAccountResponse], error)
 	GetAccountTemporalConfig(context.Context, *connect.Request[v1alpha1.GetAccountTemporalConfigRequest]) (*connect.Response[v1alpha1.GetAccountTemporalConfigResponse], error)
 	SetAccountTemporalConfig(context.Context, *connect.Request[v1alpha1.SetAccountTemporalConfigRequest]) (*connect.Response[v1alpha1.SetAccountTemporalConfigResponse], error)
@@ -219,6 +235,11 @@ func NewUserAccountServiceHandler(svc UserAccountServiceHandler, opts ...connect
 		svc.ConvertPersonalToTeamAccount,
 		opts...,
 	)
+	userAccountServiceCreateTeamAccountHandler := connect.NewUnaryHandler(
+		UserAccountServiceCreateTeamAccountProcedure,
+		svc.CreateTeamAccount,
+		opts...,
+	)
 	userAccountServiceIsUserInAccountHandler := connect.NewUnaryHandler(
 		UserAccountServiceIsUserInAccountProcedure,
 		svc.IsUserInAccount,
@@ -246,6 +267,8 @@ func NewUserAccountServiceHandler(svc UserAccountServiceHandler, opts ...connect
 			userAccountServiceSetPersonalAccountHandler.ServeHTTP(w, r)
 		case UserAccountServiceConvertPersonalToTeamAccountProcedure:
 			userAccountServiceConvertPersonalToTeamAccountHandler.ServeHTTP(w, r)
+		case UserAccountServiceCreateTeamAccountProcedure:
+			userAccountServiceCreateTeamAccountHandler.ServeHTTP(w, r)
 		case UserAccountServiceIsUserInAccountProcedure:
 			userAccountServiceIsUserInAccountHandler.ServeHTTP(w, r)
 		case UserAccountServiceGetAccountTemporalConfigProcedure:
@@ -281,6 +304,10 @@ func (UnimplementedUserAccountServiceHandler) ConvertPersonalToTeamAccount(conte
 	return nil, connect.NewError(connect.CodeUnimplemented, errors.New("mgmt.v1alpha1.UserAccountService.ConvertPersonalToTeamAccount is not implemented"))
 }
 
+func (UnimplementedUserAccountServiceHandler) CreateTeamAccount(context.Context, *connect.Request[v1alpha1.CreateTeamAccountRequest]) (*connect.Response[v1alpha1.CreateTeamAccountResponse], error) {
+	return nil, connect.NewError(connect.CodeUnimplemented, errors.New("mgmt.v1alpha1.UserAccountService.CreateTeamAccount is not implemented"))
+}
+
 func (UnimplementedUserAccountServiceHandler) IsUserInAccount(context.Context, *connect.Request[v1alpha1.IsUserInAccountRequest]) (*connect.Response[v1alpha1.IsUserInAccountResponse], error) {
 	return nil, connect.NewError(connect.CodeUnimplemented, errors.New("mgmt.v1alpha1.UserAccountService.IsUserInAccount is not implemented"))
 }
diff --git a/backend/gen/go/protos/mgmt/v1alpha1/user_account.pb.go b/backend/gen/go/protos/mgmt/v1alpha1/user_account.pb.go
index f5fdada13d..6fa735e196 100644
--- a/backend/gen/go/protos/mgmt/v1alpha1/user_account.pb.go
+++ b/backend/gen/go/protos/mgmt/v1alpha1/user_account.pb.go
@@ -902,6 +902,100 @@ func (x *AccountTemporalConfig) GetSyncJobQueueName() string {
 	return ""
 }
 
+type CreateTeamAccountRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+}
+
+func (x *CreateTeamAccountRequest) Reset() {
+	*x = CreateTeamAccountRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_mgmt_v1alpha1_user_account_proto_msgTypes[18]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CreateTeamAccountRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CreateTeamAccountRequest) ProtoMessage() {}
+
+func (x *CreateTeamAccountRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_mgmt_v1alpha1_user_account_proto_msgTypes[18]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use CreateTeamAccountRequest.ProtoReflect.Descriptor instead.
+func (*CreateTeamAccountRequest) Descriptor() ([]byte, []int) {
+	return file_mgmt_v1alpha1_user_account_proto_rawDescGZIP(), []int{18}
+}
+
+func (x *CreateTeamAccountRequest) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+type CreateTeamAccountResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	AccountId string `protobuf:"bytes,1,opt,name=account_id,json=accountId,proto3" json:"account_id,omitempty"`
+}
+
+func (x *CreateTeamAccountResponse) Reset() {
+	*x = CreateTeamAccountResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_mgmt_v1alpha1_user_account_proto_msgTypes[19]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CreateTeamAccountResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CreateTeamAccountResponse) ProtoMessage() {}
+
+func (x *CreateTeamAccountResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_mgmt_v1alpha1_user_account_proto_msgTypes[19]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use CreateTeamAccountResponse.ProtoReflect.Descriptor instead.
+func (*CreateTeamAccountResponse) Descriptor() ([]byte, []int) {
+	return file_mgmt_v1alpha1_user_account_proto_rawDescGZIP(), []int{19}
+}
+
+func (x *CreateTeamAccountResponse) GetAccountId() string {
+	if x != nil {
+		return x.AccountId
+	}
+	return ""
+}
+
 var File_mgmt_v1alpha1_user_account_proto protoreflect.FileDescriptor
 
 var file_mgmt_v1alpha1_user_account_proto_rawDesc = []byte{
@@ -983,82 +1077,96 @@ var file_mgmt_v1alpha1_user_account_proto_rawDesc = []byte{
 	0x73, 0x79, 0x6e, 0x63, 0x5f, 0x6a, 0x6f, 0x62, 0x5f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, 0x6e,
 	0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02,
 	0x10, 0x01, 0x52, 0x10, 0x73, 0x79, 0x6e, 0x63, 0x4a, 0x6f, 0x62, 0x51, 0x75, 0x65, 0x75, 0x65,
-	0x4e, 0x61, 0x6d, 0x65, 0x2a, 0x70, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f,
-	0x75, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x1d, 0x55, 0x53, 0x45, 0x52, 0x5f,
-	0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53,
-	0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x55, 0x53,
-	0x45, 0x52, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f,
-	0x50, 0x45, 0x52, 0x53, 0x4f, 0x4e, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x55, 0x53,
-	0x45, 0x52, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f,
-	0x54, 0x45, 0x41, 0x4d, 0x10, 0x02, 0x32, 0xeb, 0x06, 0x0a, 0x12, 0x55, 0x73, 0x65, 0x72, 0x41,
-	0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a,
-	0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1d, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e,
-	0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72,
-	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76,
-	0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x07, 0x53, 0x65, 0x74,
-	0x55, 0x73, 0x65, 0x72, 0x12, 0x1d, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c,
-	0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
-	0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f,
-	0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72,
-	0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x25, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e,
-	0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72,
-	0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
-	0x26, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
-	0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x12, 0x53, 0x65, 0x74,
-	0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12,
-	0x28, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
-	0x53, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x6f, 0x75,
-	0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6d, 0x67, 0x6d, 0x74,
-	0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x50, 0x65, 0x72,
-	0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70,
-	0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x89, 0x01, 0x0a, 0x1c, 0x43, 0x6f, 0x6e, 0x76, 0x65,
-	0x72, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x54, 0x6f, 0x54, 0x65, 0x61, 0x6d,
-	0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x32, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76,
-	0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x50,
-	0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x54, 0x6f, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63,
-	0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x6d, 0x67,
-	0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x76,
-	0x65, 0x72, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x54, 0x6f, 0x54, 0x65, 0x61,
-	0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
-	0x22, 0x00, 0x12, 0x62, 0x0a, 0x0f, 0x49, 0x73, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x41, 0x63,
-	0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61,
-	0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x73, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x41, 0x63,
-	0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6d,
-	0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x73, 0x55,
-	0x73, 0x65, 0x72, 0x49, 0x6e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70,
-	0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7d, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63,
-	0x6f, 0x75, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66,
-	0x69, 0x67, 0x12, 0x2e, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
-	0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x65, 0x6d,
-	0x70, 0x6f, 0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
-	0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x65, 0x6d,
-	0x70, 0x6f, 0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f,
-	0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7d, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f,
+	0x4e, 0x61, 0x6d, 0x65, 0x22, 0x37, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65,
+	0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07,
+	0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3a, 0x0a,
+	0x19, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75,
+	0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63,
+	0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
+	0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x2a, 0x70, 0x0a, 0x0f, 0x55, 0x73, 0x65,
+	0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x1d,
+	0x55, 0x53, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50,
+	0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12,
+	0x1e, 0x0a, 0x1a, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f,
+	0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x45, 0x52, 0x53, 0x4f, 0x4e, 0x41, 0x4c, 0x10, 0x01, 0x12,
+	0x1a, 0x0a, 0x16, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f,
+	0x54, 0x59, 0x50, 0x45, 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x10, 0x02, 0x32, 0xd5, 0x07, 0x0a, 0x12,
+	0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69,
+	0x63, 0x65, 0x12, 0x4a, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1d, 0x2e,
+	0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65,
+	0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d,
+	0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74,
+	0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4a,
+	0x0a, 0x07, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1d, 0x2e, 0x6d, 0x67, 0x6d, 0x74,
+	0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65,
+	0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e,
+	0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72,
+	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a, 0x0f, 0x47, 0x65,
+	0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x25, 0x2e,
+	0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65,
+	0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c,
+	0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f,
+	0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6b,
+	0x0a, 0x12, 0x53, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x41, 0x63, 0x63,
+	0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c,
+	0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c,
+	0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29,
+	0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53,
+	0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e,
+	0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x89, 0x01, 0x0a, 0x1c,
+	0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x54,
+	0x6f, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x32, 0x2e, 0x6d,
+	0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6e,
+	0x76, 0x65, 0x72, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x54, 0x6f, 0x54, 0x65,
+	0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x1a, 0x33, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
+	0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c,
+	0x54, 0x6f, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73,
+	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x68, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74,
+	0x65, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x27, 0x2e, 0x6d,
+	0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65,
+	0x61, 0x74, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65,
+	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61,
+	0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x61, 0x6d,
+	0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
+	0x00, 0x12, 0x62, 0x0a, 0x0f, 0x49, 0x73, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x41, 0x63, 0x63,
+	0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c,
+	0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x73, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x41, 0x63, 0x63,
+	0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6d, 0x67,
+	0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x73, 0x55, 0x73,
+	0x65, 0x72, 0x49, 0x6e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7d, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f,
 	0x75, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69,
 	0x67, 0x12, 0x2e, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
-	0x31, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70,
+	0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70,
 	0x6f, 0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
 	0x74, 0x1a, 0x2f, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
-	0x31, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70,
+	0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70,
 	0x6f, 0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x22, 0x00, 0x42, 0xcc, 0x01, 0x0a, 0x11, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x67, 0x6d,
-	0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x10, 0x55, 0x73, 0x65, 0x72,
-	0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x50,
-	0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x75, 0x63, 0x6c, 0x65,
-	0x75, 0x73, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6e, 0x65, 0x6f, 0x73, 0x79, 0x6e, 0x63, 0x2f,
-	0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x6d, 0x67, 0x6d, 0x74, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70,
-	0x68, 0x61, 0x31, 0x3b, 0x6d, 0x67, 0x6d, 0x74, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
-	0xa2, 0x02, 0x03, 0x4d, 0x58, 0x58, 0xaa, 0x02, 0x0d, 0x4d, 0x67, 0x6d, 0x74, 0x2e, 0x56, 0x31,
-	0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xca, 0x02, 0x0d, 0x4d, 0x67, 0x6d, 0x74, 0x5c, 0x56, 0x31,
-	0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xe2, 0x02, 0x19, 0x4d, 0x67, 0x6d, 0x74, 0x5c, 0x56, 0x31,
-	0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
-	0x74, 0x61, 0xea, 0x02, 0x0e, 0x4d, 0x67, 0x6d, 0x74, 0x3a, 0x3a, 0x56, 0x31, 0x61, 0x6c, 0x70,
-	0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x73, 0x65, 0x22, 0x00, 0x12, 0x7d, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75,
+	0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+	0x12, 0x2e, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
+	0x2e, 0x53, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6f,
+	0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x1a, 0x2f, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
+	0x2e, 0x53, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6f,
+	0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+	0x65, 0x22, 0x00, 0x42, 0xcc, 0x01, 0x0a, 0x11, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x67, 0x6d, 0x74,
+	0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x10, 0x55, 0x73, 0x65, 0x72, 0x41,
+	0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x50, 0x67,
+	0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x75, 0x63, 0x6c, 0x65, 0x75,
+	0x73, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6e, 0x65, 0x6f, 0x73, 0x79, 0x6e, 0x63, 0x2f, 0x62,
+	0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x6d, 0x67, 0x6d, 0x74, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
+	0x61, 0x31, 0x3b, 0x6d, 0x67, 0x6d, 0x74, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xa2,
+	0x02, 0x03, 0x4d, 0x58, 0x58, 0xaa, 0x02, 0x0d, 0x4d, 0x67, 0x6d, 0x74, 0x2e, 0x56, 0x31, 0x61,
+	0x6c, 0x70, 0x68, 0x61, 0x31, 0xca, 0x02, 0x0d, 0x4d, 0x67, 0x6d, 0x74, 0x5c, 0x56, 0x31, 0x61,
+	0x6c, 0x70, 0x68, 0x61, 0x31, 0xe2, 0x02, 0x19, 0x4d, 0x67, 0x6d, 0x74, 0x5c, 0x56, 0x31, 0x61,
+	0x6c, 0x70, 0x68, 0x61, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
+	0x61, 0xea, 0x02, 0x0e, 0x4d, 0x67, 0x6d, 0x74, 0x3a, 0x3a, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68,
+	0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -1074,7 +1182,7 @@ func file_mgmt_v1alpha1_user_account_proto_rawDescGZIP() []byte {
 }
 
 var file_mgmt_v1alpha1_user_account_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
-var file_mgmt_v1alpha1_user_account_proto_msgTypes = make([]protoimpl.MessageInfo, 18)
+var file_mgmt_v1alpha1_user_account_proto_msgTypes = make([]protoimpl.MessageInfo, 20)
 var file_mgmt_v1alpha1_user_account_proto_goTypes = []interface{}{
 	(UserAccountType)(0),                         // 0: mgmt.v1alpha1.UserAccountType
 	(*GetUserRequest)(nil),                       // 1: mgmt.v1alpha1.GetUserRequest
@@ -1095,6 +1203,8 @@ var file_mgmt_v1alpha1_user_account_proto_goTypes = []interface{}{
 	(*SetAccountTemporalConfigRequest)(nil),      // 16: mgmt.v1alpha1.SetAccountTemporalConfigRequest
 	(*SetAccountTemporalConfigResponse)(nil),     // 17: mgmt.v1alpha1.SetAccountTemporalConfigResponse
 	(*AccountTemporalConfig)(nil),                // 18: mgmt.v1alpha1.AccountTemporalConfig
+	(*CreateTeamAccountRequest)(nil),             // 19: mgmt.v1alpha1.CreateTeamAccountRequest
+	(*CreateTeamAccountResponse)(nil),            // 20: mgmt.v1alpha1.CreateTeamAccountResponse
 }
 var file_mgmt_v1alpha1_user_account_proto_depIdxs = []int32{
 	7,  // 0: mgmt.v1alpha1.GetUserAccountsResponse.accounts:type_name -> mgmt.v1alpha1.UserAccount
@@ -1107,19 +1217,21 @@ var file_mgmt_v1alpha1_user_account_proto_depIdxs = []int32{
 	5,  // 7: mgmt.v1alpha1.UserAccountService.GetUserAccounts:input_type -> mgmt.v1alpha1.GetUserAccountsRequest
 	10, // 8: mgmt.v1alpha1.UserAccountService.SetPersonalAccount:input_type -> mgmt.v1alpha1.SetPersonalAccountRequest
 	8,  // 9: mgmt.v1alpha1.UserAccountService.ConvertPersonalToTeamAccount:input_type -> mgmt.v1alpha1.ConvertPersonalToTeamAccountRequest
-	12, // 10: mgmt.v1alpha1.UserAccountService.IsUserInAccount:input_type -> mgmt.v1alpha1.IsUserInAccountRequest
-	14, // 11: mgmt.v1alpha1.UserAccountService.GetAccountTemporalConfig:input_type -> mgmt.v1alpha1.GetAccountTemporalConfigRequest
-	16, // 12: mgmt.v1alpha1.UserAccountService.SetAccountTemporalConfig:input_type -> mgmt.v1alpha1.SetAccountTemporalConfigRequest
-	2,  // 13: mgmt.v1alpha1.UserAccountService.GetUser:output_type -> mgmt.v1alpha1.GetUserResponse
-	4,  // 14: mgmt.v1alpha1.UserAccountService.SetUser:output_type -> mgmt.v1alpha1.SetUserResponse
-	6,  // 15: mgmt.v1alpha1.UserAccountService.GetUserAccounts:output_type -> mgmt.v1alpha1.GetUserAccountsResponse
-	11, // 16: mgmt.v1alpha1.UserAccountService.SetPersonalAccount:output_type -> mgmt.v1alpha1.SetPersonalAccountResponse
-	9,  // 17: mgmt.v1alpha1.UserAccountService.ConvertPersonalToTeamAccount:output_type -> mgmt.v1alpha1.ConvertPersonalToTeamAccountResponse
-	13, // 18: mgmt.v1alpha1.UserAccountService.IsUserInAccount:output_type -> mgmt.v1alpha1.IsUserInAccountResponse
-	15, // 19: mgmt.v1alpha1.UserAccountService.GetAccountTemporalConfig:output_type -> mgmt.v1alpha1.GetAccountTemporalConfigResponse
-	17, // 20: mgmt.v1alpha1.UserAccountService.SetAccountTemporalConfig:output_type -> mgmt.v1alpha1.SetAccountTemporalConfigResponse
-	13, // [13:21] is the sub-list for method output_type
-	5,  // [5:13] is the sub-list for method input_type
+	19, // 10: mgmt.v1alpha1.UserAccountService.CreateTeamAccount:input_type -> mgmt.v1alpha1.CreateTeamAccountRequest
+	12, // 11: mgmt.v1alpha1.UserAccountService.IsUserInAccount:input_type -> mgmt.v1alpha1.IsUserInAccountRequest
+	14, // 12: mgmt.v1alpha1.UserAccountService.GetAccountTemporalConfig:input_type -> mgmt.v1alpha1.GetAccountTemporalConfigRequest
+	16, // 13: mgmt.v1alpha1.UserAccountService.SetAccountTemporalConfig:input_type -> mgmt.v1alpha1.SetAccountTemporalConfigRequest
+	2,  // 14: mgmt.v1alpha1.UserAccountService.GetUser:output_type -> mgmt.v1alpha1.GetUserResponse
+	4,  // 15: mgmt.v1alpha1.UserAccountService.SetUser:output_type -> mgmt.v1alpha1.SetUserResponse
+	6,  // 16: mgmt.v1alpha1.UserAccountService.GetUserAccounts:output_type -> mgmt.v1alpha1.GetUserAccountsResponse
+	11, // 17: mgmt.v1alpha1.UserAccountService.SetPersonalAccount:output_type -> mgmt.v1alpha1.SetPersonalAccountResponse
+	9,  // 18: mgmt.v1alpha1.UserAccountService.ConvertPersonalToTeamAccount:output_type -> mgmt.v1alpha1.ConvertPersonalToTeamAccountResponse
+	20, // 19: mgmt.v1alpha1.UserAccountService.CreateTeamAccount:output_type -> mgmt.v1alpha1.CreateTeamAccountResponse
+	13, // 20: mgmt.v1alpha1.UserAccountService.IsUserInAccount:output_type -> mgmt.v1alpha1.IsUserInAccountResponse
+	15, // 21: mgmt.v1alpha1.UserAccountService.GetAccountTemporalConfig:output_type -> mgmt.v1alpha1.GetAccountTemporalConfigResponse
+	17, // 22: mgmt.v1alpha1.UserAccountService.SetAccountTemporalConfig:output_type -> mgmt.v1alpha1.SetAccountTemporalConfigResponse
+	14, // [14:23] is the sub-list for method output_type
+	5,  // [5:14] is the sub-list for method input_type
 	5,  // [5:5] is the sub-list for extension type_name
 	5,  // [5:5] is the sub-list for extension extendee
 	0,  // [0:5] is the sub-list for field type_name
@@ -1347,6 +1459,30 @@ func file_mgmt_v1alpha1_user_account_proto_init() {
 				return nil
 			}
 		}
+		file_mgmt_v1alpha1_user_account_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CreateTeamAccountRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_mgmt_v1alpha1_user_account_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CreateTeamAccountResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
 	}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
@@ -1354,7 +1490,7 @@ func file_mgmt_v1alpha1_user_account_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_mgmt_v1alpha1_user_account_proto_rawDesc,
 			NumEnums:      1,
-			NumMessages:   18,
+			NumMessages:   20,
 			NumExtensions: 0,
 			NumServices:   1,
 		},
diff --git a/backend/gen/go/protos/mgmt/v1alpha1/user_account.pb.validate.go b/backend/gen/go/protos/mgmt/v1alpha1/user_account.pb.validate.go
index 8d04f9d217..42d0978fc8 100644
--- a/backend/gen/go/protos/mgmt/v1alpha1/user_account.pb.validate.go
+++ b/backend/gen/go/protos/mgmt/v1alpha1/user_account.pb.validate.go
@@ -2020,3 +2020,211 @@ var _ interface {
 	Cause() error
 	ErrorName() string
 } = AccountTemporalConfigValidationError{}
+
+// Validate checks the field values on CreateTeamAccountRequest with the rules
+// defined in the proto definition for this message. If any rules are
+// violated, the first error encountered is returned, or nil if there are no violations.
+func (m *CreateTeamAccountRequest) Validate() error {
+	return m.validate(false)
+}
+
+// ValidateAll checks the field values on CreateTeamAccountRequest with the
+// rules defined in the proto definition for this message. If any rules are
+// violated, the result is a list of violation errors wrapped in
+// CreateTeamAccountRequestMultiError, or nil if none found.
+func (m *CreateTeamAccountRequest) ValidateAll() error {
+	return m.validate(true)
+}
+
+func (m *CreateTeamAccountRequest) validate(all bool) error {
+	if m == nil {
+		return nil
+	}
+
+	var errors []error
+
+	// no validation rules for Name
+
+	if len(errors) > 0 {
+		return CreateTeamAccountRequestMultiError(errors)
+	}
+
+	return nil
+}
+
+// CreateTeamAccountRequestMultiError is an error wrapping multiple validation
+// errors returned by CreateTeamAccountRequest.ValidateAll() if the designated
+// constraints aren't met.
+type CreateTeamAccountRequestMultiError []error
+
+// Error returns a concatenation of all the error messages it wraps.
+func (m CreateTeamAccountRequestMultiError) Error() string {
+	var msgs []string
+	for _, err := range m {
+		msgs = append(msgs, err.Error())
+	}
+	return strings.Join(msgs, "; ")
+}
+
+// AllErrors returns a list of validation violation errors.
+func (m CreateTeamAccountRequestMultiError) AllErrors() []error { return m }
+
+// CreateTeamAccountRequestValidationError is the validation error returned by
+// CreateTeamAccountRequest.Validate if the designated constraints aren't met.
+type CreateTeamAccountRequestValidationError struct {
+	field  string
+	reason string
+	cause  error
+	key    bool
+}
+
+// Field function returns field value.
+func (e CreateTeamAccountRequestValidationError) Field() string { return e.field }
+
+// Reason function returns reason value.
+func (e CreateTeamAccountRequestValidationError) Reason() string { return e.reason }
+
+// Cause function returns cause value.
+func (e CreateTeamAccountRequestValidationError) Cause() error { return e.cause }
+
+// Key function returns key value.
+func (e CreateTeamAccountRequestValidationError) Key() bool { return e.key }
+
+// ErrorName returns error name.
+func (e CreateTeamAccountRequestValidationError) ErrorName() string {
+	return "CreateTeamAccountRequestValidationError"
+}
+
+// Error satisfies the builtin error interface
+func (e CreateTeamAccountRequestValidationError) Error() string {
+	cause := ""
+	if e.cause != nil {
+		cause = fmt.Sprintf(" | caused by: %v", e.cause)
+	}
+
+	key := ""
+	if e.key {
+		key = "key for "
+	}
+
+	return fmt.Sprintf(
+		"invalid %sCreateTeamAccountRequest.%s: %s%s",
+		key,
+		e.field,
+		e.reason,
+		cause)
+}
+
+var _ error = CreateTeamAccountRequestValidationError{}
+
+var _ interface {
+	Field() string
+	Reason() string
+	Key() bool
+	Cause() error
+	ErrorName() string
+} = CreateTeamAccountRequestValidationError{}
+
+// Validate checks the field values on CreateTeamAccountResponse with the rules
+// defined in the proto definition for this message. If any rules are
+// violated, the first error encountered is returned, or nil if there are no violations.
+func (m *CreateTeamAccountResponse) Validate() error {
+	return m.validate(false)
+}
+
+// ValidateAll checks the field values on CreateTeamAccountResponse with the
+// rules defined in the proto definition for this message. If any rules are
+// violated, the result is a list of violation errors wrapped in
+// CreateTeamAccountResponseMultiError, or nil if none found.
+func (m *CreateTeamAccountResponse) ValidateAll() error {
+	return m.validate(true)
+}
+
+func (m *CreateTeamAccountResponse) validate(all bool) error {
+	if m == nil {
+		return nil
+	}
+
+	var errors []error
+
+	// no validation rules for AccountId
+
+	if len(errors) > 0 {
+		return CreateTeamAccountResponseMultiError(errors)
+	}
+
+	return nil
+}
+
+// CreateTeamAccountResponseMultiError is an error wrapping multiple validation
+// errors returned by CreateTeamAccountResponse.ValidateAll() if the
+// designated constraints aren't met.
+type CreateTeamAccountResponseMultiError []error
+
+// Error returns a concatenation of all the error messages it wraps.
+func (m CreateTeamAccountResponseMultiError) Error() string {
+	var msgs []string
+	for _, err := range m {
+		msgs = append(msgs, err.Error())
+	}
+	return strings.Join(msgs, "; ")
+}
+
+// AllErrors returns a list of validation violation errors.
+func (m CreateTeamAccountResponseMultiError) AllErrors() []error { return m }
+
+// CreateTeamAccountResponseValidationError is the validation error returned by
+// CreateTeamAccountResponse.Validate if the designated constraints aren't met.
+type CreateTeamAccountResponseValidationError struct {
+	field  string
+	reason string
+	cause  error
+	key    bool
+}
+
+// Field function returns field value.
+func (e CreateTeamAccountResponseValidationError) Field() string { return e.field }
+
+// Reason function returns reason value.
+func (e CreateTeamAccountResponseValidationError) Reason() string { return e.reason }
+
+// Cause function returns cause value.
+func (e CreateTeamAccountResponseValidationError) Cause() error { return e.cause }
+
+// Key function returns key value.
+func (e CreateTeamAccountResponseValidationError) Key() bool { return e.key }
+
+// ErrorName returns error name.
+func (e CreateTeamAccountResponseValidationError) ErrorName() string {
+	return "CreateTeamAccountResponseValidationError"
+}
+
+// Error satisfies the builtin error interface
+func (e CreateTeamAccountResponseValidationError) Error() string {
+	cause := ""
+	if e.cause != nil {
+		cause = fmt.Sprintf(" | caused by: %v", e.cause)
+	}
+
+	key := ""
+	if e.key {
+		key = "key for "
+	}
+
+	return fmt.Sprintf(
+		"invalid %sCreateTeamAccountResponse.%s: %s%s",
+		key,
+		e.field,
+		e.reason,
+		cause)
+}
+
+var _ error = CreateTeamAccountResponseValidationError{}
+
+var _ interface {
+	Field() string
+	Reason() string
+	Key() bool
+	Cause() error
+	ErrorName() string
+} = CreateTeamAccountResponseValidationError{}
diff --git a/backend/internal/nucleusdb/db.go b/backend/internal/nucleusdb/db.go
index f470022293..873914e8b3 100644
--- a/backend/internal/nucleusdb/db.go
+++ b/backend/internal/nucleusdb/db.go
@@ -2,7 +2,6 @@ package nucleusdb
 
 import (
 	"context"
-	"fmt"
 	"log/slog"
 
 	"github.com/jackc/pgx/v5"
@@ -107,7 +106,6 @@ func HandleSqlRollback(
 	tx SqlRollbackInterface,
 	logger *slog.Logger,
 ) {
-	fmt.Println("HERE")
 	if err := tx.Rollback(); err != nil && !isTxDone(err) {
 		logger.Error(err.Error())
 	}
diff --git a/backend/internal/nucleusdb/mock_Tx.go b/backend/internal/nucleusdb/mock_Tx.go
new file mode 100644
index 0000000000..1edee167ed
--- /dev/null
+++ b/backend/internal/nucleusdb/mock_Tx.go
@@ -0,0 +1,69 @@
+package nucleusdb
+
+import (
+	"context"
+
+	"github.com/jackc/pgx/v5"
+	pgconn "github.com/jackc/pgx/v5/pgconn"
+	mock "github.com/stretchr/testify/mock"
+)
+
+// MockTx is a mock type for the Tx interface
+type MockTx struct {
+	mock.Mock
+}
+
+func (m *MockTx) Begin(ctx context.Context) (pgx.Tx, error) {
+	args := m.Called(ctx)
+	return args.Get(0).(pgx.Tx), args.Error(1)
+}
+
+func (m *MockTx) Commit(ctx context.Context) error {
+	args := m.Called(ctx)
+	return args.Error(0)
+}
+
+func (m *MockTx) Rollback(ctx context.Context) error {
+	args := m.Called(ctx)
+	return args.Error(0)
+}
+
+func (m *MockTx) CopyFrom(ctx context.Context, tableName pgx.Identifier, columnNames []string, rowSrc pgx.CopyFromSource) (int64, error) {
+	args := m.Called(ctx, tableName, columnNames, rowSrc)
+	return args.Get(0).(int64), args.Error(1)
+}
+
+func (m *MockTx) SendBatch(ctx context.Context, b *pgx.Batch) pgx.BatchResults {
+	args := m.Called(ctx, b)
+	return args.Get(0).(pgx.BatchResults)
+}
+
+func (m *MockTx) LargeObjects() pgx.LargeObjects {
+	args := m.Called()
+	return args.Get(0).(pgx.LargeObjects)
+}
+
+func (m *MockTx) Prepare(ctx context.Context, name, sql string) (*pgconn.StatementDescription, error) {
+	args := m.Called(ctx, name, sql)
+	return args.Get(0).(*pgconn.StatementDescription), args.Error(1)
+}
+
+func (m *MockTx) Exec(ctx context.Context, sql string, arguments ...any) (pgconn.CommandTag, error) {
+	args := m.Called(ctx, sql, arguments)
+	return args.Get(0).(pgconn.CommandTag), args.Error(1)
+}
+
+func (m *MockTx) Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error) {
+	callArgs := m.Called(ctx, sql, args)
+	return callArgs.Get(0).(pgx.Rows), callArgs.Error(1)
+}
+
+func (m *MockTx) QueryRow(ctx context.Context, sql string, args ...any) pgx.Row {
+	callArgs := m.Called(ctx, sql, args)
+	return callArgs.Get(0).(pgx.Row)
+}
+
+func (m *MockTx) Conn() *pgx.Conn {
+	args := m.Called()
+	return args.Get(0).(*pgx.Conn)
+}
diff --git a/backend/internal/nucleusdb/users.go b/backend/internal/nucleusdb/users.go
index 4ea7c719e3..0b5fb4ff95 100644
--- a/backend/internal/nucleusdb/users.go
+++ b/backend/internal/nucleusdb/users.go
@@ -2,9 +2,12 @@ package nucleusdb
 
 import (
 	"context"
+	"fmt"
+	"strings"
 
 	"github.com/jackc/pgx/v5/pgtype"
 	db_queries "github.com/nucleuscloud/neosync/backend/gen/go/db"
+	nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
 )
 
 func (d *NucleusDb) SetUserByAuth0Id(
@@ -103,3 +106,40 @@ func (d *NucleusDb) SetPersonalAccount(
 	}
 	return personalAccount, nil
 }
+
+func (d *NucleusDb) CreateTeamAccount(
+	ctx context.Context,
+	userId pgtype.UUID,
+	teamName string,
+) (*db_queries.NeosyncApiAccount, error) {
+	var teamAccount *db_queries.NeosyncApiAccount
+	if err := d.WithTx(ctx, nil, func(dbtx BaseDBTX) error {
+		accounts, err := d.Q.GetAccountsByUser(ctx, dbtx, userId)
+		if err != nil && !IsNoRows(err) {
+			return err
+		} else if err != nil && IsNoRows(err) {
+			accounts = []db_queries.NeosyncApiAccount{}
+		}
+		for _, account := range accounts {
+			if strings.EqualFold(account.AccountSlug, teamName) {
+				return nucleuserrors.NewAlreadyExists(fmt.Sprintf("team account with the name %s already exists", teamName))
+			}
+		}
+		account, err := d.Q.CreateTeamAccount(ctx, dbtx, teamName)
+		if err != nil {
+			return err
+		}
+		teamAccount = &account
+		_, err = d.Q.CreateAccountUserAssociation(ctx, dbtx, db_queries.CreateAccountUserAssociationParams{
+			AccountID: account.ID,
+			UserID:    userId,
+		})
+		if err != nil {
+			return err
+		}
+		return nil
+	}); err != nil {
+		return nil, err
+	}
+	return teamAccount, nil
+}
diff --git a/backend/internal/nucleusdb/users_test.go b/backend/internal/nucleusdb/users_test.go
new file mode 100644
index 0000000000..a62fa32d02
--- /dev/null
+++ b/backend/internal/nucleusdb/users_test.go
@@ -0,0 +1,132 @@
+package nucleusdb
+
+import (
+	"context"
+	"database/sql"
+	"errors"
+	"testing"
+
+	db_queries "github.com/nucleuscloud/neosync/backend/gen/go/db"
+	"github.com/stretchr/testify/mock"
+	"github.com/zeebo/assert"
+)
+
+const (
+	anonymousUserId = "00000000-0000-0000-0000-000000000000"
+	mockUserId      = "d5e29f1f-b920-458c-8b86-f3a180e06d98"
+	mockAccountId   = "5629813e-1a35-4874-922c-9827d85f0378"
+	mockTeamName    = "team-name"
+)
+
+// CreateTeamAccount
+func Test_CreateTeamAccount(t *testing.T) {
+	dbtxMock := NewMockDBTX(t)
+	querierMock := db_queries.NewMockQuerier(t)
+	mockTx := new(MockTx)
+
+	userUuid, _ := ToUuid(mockUserId)
+	accountUuid, _ := ToUuid(mockAccountId)
+	ctx := context.Background()
+
+	service := New(dbtxMock, querierMock)
+
+	dbtxMock.On("Begin", ctx).Return(mockTx, nil)
+	querierMock.On("GetAccountsByUser", ctx, mockTx, userUuid).Return([]db_queries.NeosyncApiAccount{{AccountSlug: "other"}}, nil)
+	querierMock.On("CreateTeamAccount", ctx, mockTx, mockTeamName).Return(db_queries.NeosyncApiAccount{ID: accountUuid, AccountSlug: mockTeamName}, nil)
+	querierMock.On("CreateAccountUserAssociation", ctx, mockTx, db_queries.CreateAccountUserAssociationParams{
+		AccountID: accountUuid,
+		UserID:    userUuid,
+	}).Return(db_queries.NeosyncApiAccountUserAssociation{}, nil)
+	mockTx.On("Commit", ctx).Return(nil)
+	mockTx.On("Rollback", ctx).Return(nil)
+
+	resp, err := service.CreateTeamAccount(context.Background(), userUuid, mockTeamName)
+
+	assert.NoError(t, err)
+	assert.NotNil(t, resp)
+	assert.Equal(t, accountUuid, resp.ID)
+	assert.Equal(t, mockTeamName, resp.AccountSlug)
+}
+
+func Test_CreateTeamAccount_AlreadyExists(t *testing.T) {
+	dbtxMock := NewMockDBTX(t)
+	querierMock := db_queries.NewMockQuerier(t)
+	mockTx := new(MockTx)
+
+	userUuid, _ := ToUuid(mockUserId)
+	ctx := context.Background()
+
+	service := New(dbtxMock, querierMock)
+
+	dbtxMock.On("Begin", ctx).Return(mockTx, nil)
+	querierMock.On("GetAccountsByUser", ctx, mockTx, userUuid).Return([]db_queries.NeosyncApiAccount{{AccountSlug: mockTeamName}}, nil)
+	mockTx.On("Rollback", ctx).Return(nil)
+
+	resp, err := service.CreateTeamAccount(context.Background(), userUuid, mockTeamName)
+
+	querierMock.AssertNotCalled(t, "CreateTeamAccount", mock.Anything, mock.Anything, mock.Anything)
+	querierMock.AssertNotCalled(t, "CreateAccountUserAssociation", mock.Anything, mock.Anything, mock.Anything)
+	mockTx.AssertNotCalled(t, "Commit", mock.Anything)
+
+	assert.Error(t, err)
+	assert.Nil(t, resp)
+}
+
+func Test_CreateTeamAccount_NoRows(t *testing.T) {
+	dbtxMock := NewMockDBTX(t)
+	querierMock := db_queries.NewMockQuerier(t)
+	mockTx := new(MockTx)
+
+	userUuid, _ := ToUuid(mockUserId)
+	accountUuid, _ := ToUuid(mockAccountId)
+	var nilAccounts []db_queries.NeosyncApiAccount
+	ctx := context.Background()
+
+	service := New(dbtxMock, querierMock)
+
+	dbtxMock.On("Begin", ctx).Return(mockTx, nil)
+	querierMock.On("GetAccountsByUser", ctx, mockTx, userUuid).Return(nilAccounts, sql.ErrNoRows)
+	querierMock.On("CreateTeamAccount", ctx, mockTx, mockTeamName).Return(db_queries.NeosyncApiAccount{ID: accountUuid, AccountSlug: mockTeamName}, nil)
+	querierMock.On("CreateAccountUserAssociation", ctx, mockTx, db_queries.CreateAccountUserAssociationParams{
+		AccountID: accountUuid,
+		UserID:    userUuid,
+	}).Return(db_queries.NeosyncApiAccountUserAssociation{}, nil)
+	mockTx.On("Commit", ctx).Return(nil)
+	mockTx.On("Rollback", ctx).Return(nil)
+
+	resp, err := service.CreateTeamAccount(context.Background(), userUuid, mockTeamName)
+
+	assert.NoError(t, err)
+	assert.NotNil(t, resp)
+	assert.Equal(t, accountUuid, resp.ID)
+	assert.Equal(t, mockTeamName, resp.AccountSlug)
+}
+
+func Test_CreateTeamAccount_Rollback(t *testing.T) {
+	dbtxMock := NewMockDBTX(t)
+	querierMock := db_queries.NewMockQuerier(t)
+	mockTx := new(MockTx)
+
+	userUuid, _ := ToUuid(mockUserId)
+	accountUuid, _ := ToUuid(mockAccountId)
+	ctx := context.Background()
+	var nilAssociation db_queries.NeosyncApiAccountUserAssociation
+
+	service := New(dbtxMock, querierMock)
+
+	dbtxMock.On("Begin", ctx).Return(mockTx, nil)
+	querierMock.On("GetAccountsByUser", ctx, mockTx, userUuid).Return([]db_queries.NeosyncApiAccount{{AccountSlug: "other"}}, nil)
+	querierMock.On("CreateTeamAccount", ctx, mockTx, mockTeamName).Return(db_queries.NeosyncApiAccount{ID: accountUuid, AccountSlug: mockTeamName}, nil)
+	querierMock.On("CreateAccountUserAssociation", ctx, mockTx, db_queries.CreateAccountUserAssociationParams{
+		AccountID: accountUuid,
+		UserID:    userUuid,
+	}).Return(nilAssociation, errors.New("sad"))
+	mockTx.On("Rollback", ctx).Return(nil)
+
+	resp, err := service.CreateTeamAccount(context.Background(), userUuid, mockTeamName)
+
+	mockTx.AssertCalled(t, "Rollback", ctx)
+	mockTx.AssertNotCalled(t, "Commit", mock.Anything)
+	assert.Error(t, err)
+	assert.Nil(t, resp)
+}
diff --git a/backend/protos/mgmt/v1alpha1/user_account.proto b/backend/protos/mgmt/v1alpha1/user_account.proto
index 526cadfa6e..4b2e3cfaf7 100644
--- a/backend/protos/mgmt/v1alpha1/user_account.proto
+++ b/backend/protos/mgmt/v1alpha1/user_account.proto
@@ -67,6 +67,13 @@ message AccountTemporalConfig {
   string sync_job_queue_name = 3 [(buf.validate.field).string.min_len = 1];
 }
 
+message CreateTeamAccountRequest {
+  string name = 1 [(buf.validate.field).string.min_len = 1];
+}
+message CreateTeamAccountResponse {
+  string account_id = 1;
+}
+
 service UserAccountService {
   rpc GetUser(GetUserRequest) returns (GetUserResponse) {}
   rpc SetUser(SetUserRequest) returns (SetUserResponse) {}
@@ -76,6 +83,7 @@ service UserAccountService {
   rpc SetPersonalAccount(SetPersonalAccountRequest) returns (SetPersonalAccountResponse) {}
 
   rpc ConvertPersonalToTeamAccount(ConvertPersonalToTeamAccountRequest) returns (ConvertPersonalToTeamAccountResponse) {}
+  rpc CreateTeamAccount(CreateTeamAccountRequest) returns (CreateTeamAccountResponse) {}
 
   rpc IsUserInAccount(IsUserInAccountRequest) returns (IsUserInAccountResponse) {}
 
diff --git a/backend/services/mgmt/v1alpha1/job-service/jobs.go b/backend/services/mgmt/v1alpha1/job-service/jobs.go
index 868265e65a..9e163c87b5 100644
--- a/backend/services/mgmt/v1alpha1/job-service/jobs.go
+++ b/backend/services/mgmt/v1alpha1/job-service/jobs.go
@@ -410,7 +410,6 @@ func (s *Service) CreateJob(
 
 	hasNs, err := s.doesAccountHaveTemporalNamespace(ctx, *accountUuid, logger)
 	if err != nil {
-		fmt.Println("account doesn't have namespace")
 		return nil, err
 	}
 	if !hasNs {
diff --git a/backend/services/mgmt/v1alpha1/user-account-service/users.go b/backend/services/mgmt/v1alpha1/user-account-service/users.go
index e02c261f15..4dfabc0aa9 100644
--- a/backend/services/mgmt/v1alpha1/user-account-service/users.go
+++ b/backend/services/mgmt/v1alpha1/user-account-service/users.go
@@ -168,3 +168,26 @@ func (s *Service) IsUserInAccount(
 		Ok: count > 0,
 	}), nil
 }
+
+func (s *Service) CreateTeamAccount(
+	ctx context.Context,
+	req *connect.Request[mgmtv1alpha1.CreateTeamAccountRequest],
+) (*connect.Response[mgmtv1alpha1.CreateTeamAccountResponse], error) {
+	user, err := s.GetUser(ctx, connect.NewRequest(&mgmtv1alpha1.GetUserRequest{}))
+	if err != nil {
+		return nil, err
+	}
+	userId, err := nucleusdb.ToUuid(user.Msg.UserId)
+	if err != nil {
+		return nil, err
+	}
+
+	account, err := s.db.CreateTeamAccount(ctx, userId, req.Msg.Name)
+	if err != nil {
+		return nil, err
+	}
+
+	return connect.NewResponse(&mgmtv1alpha1.CreateTeamAccountResponse{
+		AccountId: nucleusdb.UUIDString(account.ID),
+	}), nil
+}
diff --git a/backend/services/mgmt/v1alpha1/user-account-service/users_test.go b/backend/services/mgmt/v1alpha1/user-account-service/users_test.go
index 4d86ad8eb2..53987c7040 100644
--- a/backend/services/mgmt/v1alpha1/user-account-service/users_test.go
+++ b/backend/services/mgmt/v1alpha1/user-account-service/users_test.go
@@ -241,6 +241,34 @@ func Test_IsUserInAccount_False(t *testing.T) {
 	assert.Equal(t, false, resp.Msg.Ok)
 }
 
+func Test_CreateTeamAccount(t *testing.T) {
+	m := createServiceMock(t, &Config{IsAuthEnabled: true})
+	mockTx := new(nucleusdb.MockTx)
+
+	mockTeamName := "team-name"
+	ctx := getAuthenticatedCtxMock(mockAuthProvider)
+	userAssociation := getUserIdentityProviderAssociationMock(mockUserId, mockAuthProvider)
+	accountUuid, _ := nucleusdb.ToUuid(mockAccountId)
+	userUuid, _ := nucleusdb.ToUuid(mockUserId)
+	m.QuerierMock.On("GetUserAssociationByAuth0Id", ctx, mock.Anything, mockAuthProvider).Return(userAssociation, nil)
+	m.DbtxMock.On("Begin", ctx).Return(mockTx, nil)
+	m.QuerierMock.On("GetAccountsByUser", ctx, mockTx, userUuid).Return([]db_queries.NeosyncApiAccount{{AccountSlug: "other"}}, nil)
+	m.QuerierMock.On("CreateTeamAccount", ctx, mockTx, mockTeamName).Return(db_queries.NeosyncApiAccount{ID: accountUuid, AccountSlug: mockTeamName}, nil)
+	m.QuerierMock.On("CreateAccountUserAssociation", ctx, mockTx, db_queries.CreateAccountUserAssociationParams{
+		AccountID: accountUuid,
+		UserID:    userUuid,
+	}).Return(db_queries.NeosyncApiAccountUserAssociation{}, nil)
+	mockTx.On("Commit", ctx).Return(nil)
+	mockTx.On("Rollback", ctx).Return(nil)
+
+	resp, err := m.Service.CreateTeamAccount(ctx, &connect.Request[mgmtv1alpha1.CreateTeamAccountRequest]{Msg: &mgmtv1alpha1.CreateTeamAccountRequest{Name: mockTeamName}})
+
+	assert.NoError(t, err)
+	assert.NotNil(t, resp)
+	assert.Equal(t, mockAccountId, resp.Msg.AccountId)
+
+}
+
 type serviceMocks struct {
 	Service     *Service
 	DbtxMock    *nucleusdb.MockDBTX
diff --git a/backend/sql/postgresql/queries/users.sql b/backend/sql/postgresql/queries/users.sql
index 78ffb88fdb..627aa51ef5 100644
--- a/backend/sql/postgresql/queries/users.sql
+++ b/backend/sql/postgresql/queries/users.sql
@@ -37,6 +37,12 @@ INNER JOIN neosync_api.account_user_associations aua ON aua.account_id = a.id
 INNER JOIN neosync_api.users u ON u.id = aua.user_id
 WHERE u.id = sqlc.arg('userId') AND a.account_type = 0;
 
+-- name: GetTeamAccountsByUserId :many
+SELECT a.* from neosync_api.accounts a
+INNER JOIN neosync_api.account_user_associations aua ON aua.account_id = a.id
+INNER JOIN neosync_api.users u ON u.id = aua.user_id
+WHERE u.id = sqlc.arg('userId') AND a.account_type = 1;
+
 -- name: CreatePersonalAccount :one
 INSERT INTO neosync_api.accounts (
   account_type, account_slug
@@ -45,6 +51,14 @@ INSERT INTO neosync_api.accounts (
 )
 RETURNING *;
 
+-- name: CreateTeamAccount :one
+INSERT INTO neosync_api.accounts (
+  account_type, account_slug
+) VALUES (
+  1, $1
+)
+RETURNING *;
+
 -- name: GetAccountsByUser :many
 SELECT a.* from neosync_api.accounts a
 INNER JOIN neosync_api.account_user_associations aua ON aua.account_id = a.id
diff --git a/frontend/app/api/teams/route.ts b/frontend/app/api/teams/route.ts
new file mode 100644
index 0000000000..aa5aa75153
--- /dev/null
+++ b/frontend/app/api/teams/route.ts
@@ -0,0 +1,10 @@
+import { withNeosyncContext } from '@/api-only/neosync-context';
+import { CreateTeamAccountRequest } from '@/neosync-api-client/mgmt/v1alpha1/user_account_pb';
+import { NextRequest, NextResponse } from 'next/server';
+
+export async function POST(req: NextRequest): Promise<NextResponse> {
+  return withNeosyncContext(async (ctx) => {
+    const body = CreateTeamAccountRequest.fromJson(await req.json());
+    return ctx.userClient.createTeamAccount(body);
+  })(req);
+}
diff --git a/frontend/components/AccountSwitcher.tsx b/frontend/components/AccountSwitcher.tsx
index 4172902f20..1adb119c12 100644
--- a/frontend/components/AccountSwitcher.tsx
+++ b/frontend/components/AccountSwitcher.tsx
@@ -1,13 +1,20 @@
 'use client';
-
-import { ReactElement } from 'react';
-
-import { CaretSortIcon, CheckIcon } from '@radix-ui/react-icons';
+import {
+  CaretSortIcon,
+  CheckIcon,
+  PlusCircledIcon,
+} from '@radix-ui/react-icons';
 import * as React from 'react';
+import { ReactElement } from 'react';
 
 import { useGetUserAccounts } from '@/libs/hooks/useUserAccounts';
 import { cn } from '@/libs/utils';
-import { UserAccountType } from '@/neosync-api-client/mgmt/v1alpha1/user_account_pb';
+import {
+  CreateTeamAccountRequest,
+  CreateTeamAccountResponse,
+  UserAccountType,
+} from '@/neosync-api-client/mgmt/v1alpha1/user_account_pb';
+import { getErrorMessage } from '@/util/util';
 import { useAccount } from './providers/account-provider';
 import { Avatar, AvatarFallback, AvatarImage } from './ui/avatar';
 import { Button } from './ui/button';
@@ -18,105 +25,208 @@ import {
   CommandInput,
   CommandItem,
   CommandList,
+  CommandSeparator,
 } from './ui/command';
+import {
+  Dialog,
+  DialogContent,
+  DialogDescription,
+  DialogFooter,
+  DialogHeader,
+  DialogTitle,
+  DialogTrigger,
+} from './ui/dialog';
+import { Input } from './ui/input';
+import { Label } from './ui/label';
 import { Popover, PopoverContent, PopoverTrigger } from './ui/popover';
+import { Skeleton } from './ui/skeleton';
+import { toast } from './ui/use-toast';
 
 interface Props {}
 
 export default function AccountSwitcher(_: Props): ReactElement {
   const { account, setAccount } = useAccount();
-  const { data } = useGetUserAccounts();
+  const { data, mutate, isLoading } = useGetUserAccounts();
   const [open, setOpen] = React.useState(false);
+  const [showNewTeamDialog, setShowNewTeamDialog] = React.useState(false);
+  const [teamName, setTeamName] = React.useState('');
 
   const personalAccounts =
     data?.accounts.filter((a) => a.type == UserAccountType.PERSONAL) || [];
   const teamAccounts =
     data?.accounts.filter((a) => a.type == UserAccountType.TEAM) || [];
 
+  async function onSubmit(teamName: string): Promise<void> {
+    try {
+      await createTeamAccount(teamName);
+      setShowNewTeamDialog(false);
+      mutate();
+      toast({
+        title: 'Successfully created team!',
+        variant: 'success',
+      });
+    } catch (err) {
+      console.error(err);
+      toast({
+        title: 'Unable to create team',
+        description: getErrorMessage(err),
+        variant: 'destructive',
+      });
+    }
+  }
+  if (isLoading) {
+    return <Skeleton className=" h-full w-[200px]" />;
+  }
+
   return (
-    <Popover open={open} onOpenChange={setOpen}>
-      <PopoverTrigger asChild>
-        <Button
-          variant="outline"
-          role="combobox"
-          aria-expanded={open}
-          aria-label="Select a team"
-          className="w-[200px] justify-between"
-        >
-          <Avatar className="mr-2 h-5 w-5">
-            <AvatarImage
-              src={`https://avatar.vercel.sh/${account?.id}.png`}
-              alt={account?.name}
-            />
-            <AvatarFallback>SC</AvatarFallback>
-          </Avatar>
-          {account?.name}
-          <CaretSortIcon className="ml-auto h-4 w-4 shrink-0 opacity-50" />
-        </Button>
-      </PopoverTrigger>
-      <PopoverContent className="w-[200px] p-0">
-        <Command>
-          <CommandList>
-            <CommandInput placeholder="Search account..." />
-            <CommandEmpty>No Account found.</CommandEmpty>
-            <CommandGroup key="personal" heading="Personal">
-              {personalAccounts.map((a) => (
-                <CommandItem
-                  key={a.id}
-                  onSelect={() => {
-                    setAccount(a);
-                    setOpen(false);
-                  }}
-                  className="text-sm"
-                >
-                  <Avatar className="mr-2 h-5 w-5">
-                    <AvatarImage
-                      src={`https://avatar.vercel.sh/${a.name}.png`}
-                      alt={a.name}
-                      className="grayscale"
+    <Dialog open={showNewTeamDialog} onOpenChange={setShowNewTeamDialog}>
+      <Popover open={open} onOpenChange={setOpen}>
+        <PopoverTrigger asChild>
+          <Button
+            variant="outline"
+            role="combobox"
+            aria-expanded={open}
+            aria-label="Select a team"
+            className="w-[200px] justify-between"
+          >
+            <Avatar className="mr-2 h-5 w-5">
+              <AvatarImage
+                src={`https://avatar.vercel.sh/${account?.id}.png`}
+                alt={account?.name}
+              />
+              <AvatarFallback>SC</AvatarFallback>
+            </Avatar>
+            {account?.name}
+            <CaretSortIcon className="ml-auto h-4 w-4 shrink-0 opacity-50" />
+          </Button>
+        </PopoverTrigger>
+        <PopoverContent className="w-[200px] p-0">
+          <Command>
+            <CommandList>
+              <CommandInput placeholder="Search account..." />
+              <CommandEmpty>No Account found.</CommandEmpty>
+              <CommandGroup key="personal" heading="Personal">
+                {personalAccounts.map((a) => (
+                  <CommandItem
+                    key={a.id}
+                    onSelect={() => {
+                      setAccount(a);
+                      setOpen(false);
+                    }}
+                    className="text-sm"
+                  >
+                    <Avatar className="mr-2 h-5 w-5">
+                      <AvatarImage
+                        src={`https://avatar.vercel.sh/${a.name}.png`}
+                        alt={a.name}
+                        className="grayscale"
+                      />
+                      <AvatarFallback>SC</AvatarFallback>
+                    </Avatar>
+                    {a.name}
+                    <CheckIcon
+                      className={cn(
+                        'ml-auto h-4 w-4',
+                        account?.id === a.id ? 'opacity-100' : 'opacity-0'
+                      )}
                     />
-                    <AvatarFallback>SC</AvatarFallback>
-                  </Avatar>
-                  {a.name}
-                  <CheckIcon
-                    className={cn(
-                      'ml-auto h-4 w-4',
-                      account?.id === a.id ? 'opacity-100' : 'opacity-0'
-                    )}
-                  />
-                </CommandItem>
-              ))}
-            </CommandGroup>
-            <CommandGroup key="team" heading="Team">
-              {teamAccounts.map((a) => (
-                <CommandItem
-                  key={a.id}
-                  onSelect={() => {
-                    setAccount(a);
-                    setOpen(false);
-                  }}
-                  className="text-sm"
-                >
-                  <Avatar className="mr-2 h-5 w-5">
-                    <AvatarImage
-                      src={`https://avatar.vercel.sh/${a.name}.png`}
-                      alt={a.name}
-                      className="grayscale"
+                  </CommandItem>
+                ))}
+              </CommandGroup>
+              <CommandGroup key="team" heading="Team">
+                {teamAccounts.map((a) => (
+                  <CommandItem
+                    key={a.id}
+                    onSelect={() => {
+                      setAccount(a);
+                      setOpen(false);
+                    }}
+                    className="text-sm"
+                  >
+                    <Avatar className="mr-2 h-5 w-5">
+                      <AvatarImage
+                        src={`https://avatar.vercel.sh/${a.name}.png`}
+                        alt={a.name}
+                        className="grayscale"
+                      />
+                    </Avatar>
+                    {a.name}
+                    <CheckIcon
+                      className={cn(
+                        'ml-auto h-4 w-4',
+                        account?.id === a.id ? 'opacity-100' : 'opacity-0'
+                      )}
                     />
-                  </Avatar>
-                  {a.name}
-                  <CheckIcon
-                    className={cn(
-                      'ml-auto h-4 w-4',
-                      account?.id === a.id ? 'opacity-100' : 'opacity-0'
-                    )}
-                  />
-                </CommandItem>
-              ))}
-            </CommandGroup>
-          </CommandList>
-        </Command>
-      </PopoverContent>
-    </Popover>
+                  </CommandItem>
+                ))}
+              </CommandGroup>
+            </CommandList>
+            <CommandSeparator />
+            <CommandList>
+              <CommandGroup>
+                <DialogTrigger asChild>
+                  <CommandItem
+                    onSelect={() => {
+                      setOpen(false);
+                      setShowNewTeamDialog(true);
+                    }}
+                  >
+                    <PlusCircledIcon className="mr-2 h-5 w-5" />
+                    Create Team
+                  </CommandItem>
+                </DialogTrigger>
+              </CommandGroup>
+            </CommandList>
+          </Command>
+        </PopoverContent>
+      </Popover>
+      <DialogContent>
+        <DialogHeader>
+          <DialogTitle>Create team</DialogTitle>
+          <DialogDescription>Add a new team to manage jobs.</DialogDescription>
+        </DialogHeader>
+        <div>
+          <div className="space-y-4 py-2 pb-4">
+            <div className="space-y-2">
+              <Label htmlFor="name">Team name</Label>
+              <Input
+                id="name"
+                placeholder="Acme Inc."
+                onChange={(event) => setTeamName(event.target.value)}
+              />
+            </div>
+          </div>
+        </div>
+        <DialogFooter>
+          <Button variant="outline" onClick={() => setShowNewTeamDialog(false)}>
+            Cancel
+          </Button>
+          <Button type="submit" onClick={() => onSubmit(teamName)}>
+            Continue
+          </Button>
+        </DialogFooter>
+      </DialogContent>
+    </Dialog>
   );
 }
+
+async function createTeamAccount(
+  teamName: string
+): Promise<CreateTeamAccountResponse | undefined> {
+  const res = await fetch(`/api/teams`, {
+    method: 'POST',
+    headers: {
+      'content-type': 'application/json',
+    },
+    body: JSON.stringify(
+      new CreateTeamAccountRequest({
+        name: teamName,
+      })
+    ),
+  });
+  if (!res.ok) {
+    const body = await res.json();
+    throw new Error(body.message);
+  }
+  return CreateTeamAccountResponse.fromJson(await res.json());
+}
diff --git a/frontend/neosync-api-client/mgmt/v1alpha1/user_account_connect.ts b/frontend/neosync-api-client/mgmt/v1alpha1/user_account_connect.ts
index 34eb165250..a8cd930449 100644
--- a/frontend/neosync-api-client/mgmt/v1alpha1/user_account_connect.ts
+++ b/frontend/neosync-api-client/mgmt/v1alpha1/user_account_connect.ts
@@ -3,7 +3,7 @@
 /* eslint-disable */
 // @ts-nocheck
 
-import { ConvertPersonalToTeamAccountRequest, ConvertPersonalToTeamAccountResponse, GetAccountTemporalConfigRequest, GetAccountTemporalConfigResponse, GetUserAccountsRequest, GetUserAccountsResponse, GetUserRequest, GetUserResponse, IsUserInAccountRequest, IsUserInAccountResponse, SetAccountTemporalConfigRequest, SetAccountTemporalConfigResponse, SetPersonalAccountRequest, SetPersonalAccountResponse, SetUserRequest, SetUserResponse } from "./user_account_pb";
+import { ConvertPersonalToTeamAccountRequest, ConvertPersonalToTeamAccountResponse, CreateTeamAccountRequest, CreateTeamAccountResponse, GetAccountTemporalConfigRequest, GetAccountTemporalConfigResponse, GetUserAccountsRequest, GetUserAccountsResponse, GetUserRequest, GetUserResponse, IsUserInAccountRequest, IsUserInAccountResponse, SetAccountTemporalConfigRequest, SetAccountTemporalConfigResponse, SetPersonalAccountRequest, SetPersonalAccountResponse, SetUserRequest, SetUserResponse } from "./user_account_pb";
 import { MethodKind } from "@bufbuild/protobuf";
 
 /**
@@ -57,6 +57,15 @@ export const UserAccountService = {
       O: ConvertPersonalToTeamAccountResponse,
       kind: MethodKind.Unary,
     },
+    /**
+     * @generated from rpc mgmt.v1alpha1.UserAccountService.CreateTeamAccount
+     */
+    createTeamAccount: {
+      name: "CreateTeamAccount",
+      I: CreateTeamAccountRequest,
+      O: CreateTeamAccountResponse,
+      kind: MethodKind.Unary,
+    },
     /**
      * @generated from rpc mgmt.v1alpha1.UserAccountService.IsUserInAccount
      */
diff --git a/frontend/neosync-api-client/mgmt/v1alpha1/user_account_pb.ts b/frontend/neosync-api-client/mgmt/v1alpha1/user_account_pb.ts
index 7840418cdd..80fc108e47 100644
--- a/frontend/neosync-api-client/mgmt/v1alpha1/user_account_pb.ts
+++ b/frontend/neosync-api-client/mgmt/v1alpha1/user_account_pb.ts
@@ -692,3 +692,77 @@ export class AccountTemporalConfig extends Message<AccountTemporalConfig> {
   }
 }
 
+/**
+ * @generated from message mgmt.v1alpha1.CreateTeamAccountRequest
+ */
+export class CreateTeamAccountRequest extends Message<CreateTeamAccountRequest> {
+  /**
+   * @generated from field: string name = 1;
+   */
+  name = "";
+
+  constructor(data?: PartialMessage<CreateTeamAccountRequest>) {
+    super();
+    proto3.util.initPartial(data, this);
+  }
+
+  static readonly runtime: typeof proto3 = proto3;
+  static readonly typeName = "mgmt.v1alpha1.CreateTeamAccountRequest";
+  static readonly fields: FieldList = proto3.util.newFieldList(() => [
+    { no: 1, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
+  ]);
+
+  static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): CreateTeamAccountRequest {
+    return new CreateTeamAccountRequest().fromBinary(bytes, options);
+  }
+
+  static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): CreateTeamAccountRequest {
+    return new CreateTeamAccountRequest().fromJson(jsonValue, options);
+  }
+
+  static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): CreateTeamAccountRequest {
+    return new CreateTeamAccountRequest().fromJsonString(jsonString, options);
+  }
+
+  static equals(a: CreateTeamAccountRequest | PlainMessage<CreateTeamAccountRequest> | undefined, b: CreateTeamAccountRequest | PlainMessage<CreateTeamAccountRequest> | undefined): boolean {
+    return proto3.util.equals(CreateTeamAccountRequest, a, b);
+  }
+}
+
+/**
+ * @generated from message mgmt.v1alpha1.CreateTeamAccountResponse
+ */
+export class CreateTeamAccountResponse extends Message<CreateTeamAccountResponse> {
+  /**
+   * @generated from field: string account_id = 1;
+   */
+  accountId = "";
+
+  constructor(data?: PartialMessage<CreateTeamAccountResponse>) {
+    super();
+    proto3.util.initPartial(data, this);
+  }
+
+  static readonly runtime: typeof proto3 = proto3;
+  static readonly typeName = "mgmt.v1alpha1.CreateTeamAccountResponse";
+  static readonly fields: FieldList = proto3.util.newFieldList(() => [
+    { no: 1, name: "account_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
+  ]);
+
+  static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): CreateTeamAccountResponse {
+    return new CreateTeamAccountResponse().fromBinary(bytes, options);
+  }
+
+  static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): CreateTeamAccountResponse {
+    return new CreateTeamAccountResponse().fromJson(jsonValue, options);
+  }
+
+  static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): CreateTeamAccountResponse {
+    return new CreateTeamAccountResponse().fromJsonString(jsonString, options);
+  }
+
+  static equals(a: CreateTeamAccountResponse | PlainMessage<CreateTeamAccountResponse> | undefined, b: CreateTeamAccountResponse | PlainMessage<CreateTeamAccountResponse> | undefined): boolean {
+    return proto3.util.equals(CreateTeamAccountResponse, a, b);
+  }
+}
+