diff --git a/pkg/clients/client.go b/pkg/clients/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..caead6372e74191cfdc3b2d5f4fffccf72cab274
--- /dev/null
+++ b/pkg/clients/client.go
@@ -0,0 +1,93 @@
+package clients
+
+// Client - приложение имеющее доступ к API
+type Client struct {
+	// Внутренний идентификатор клиента внутри системы
+	ID string `json:"id" bson:"_id"`
+
+	// Идентификатор пространства
+	SpaceID string `json:"space_id" bson:"-"`
+
+	// Имя приложения (обязательное поле)
+	Name string `json:"name" bson:"name"`
+
+	// Параметры аутентификации клиента
+	OAuth  *OAuth  `json:"oauth,omitempty" bson:"oauth,omitempty"`
+	TLS    *TLS    `json:"tls,omitempty" bson:"tls,omitempty"`
+	APIKey *APIKey `json:"api_key,omitempty" bson:"api_key,omitempty"`
+
+	// Описание клиента, назначение
+	Description string `json:"description" bson:"description"`
+
+	// Приложение отключено и не может авторизоваться
+	Disabled *bool `json:"disabled,omitempty" bson:"disabled,omitempty"`
+
+	// Роль приложения в пространстве
+	RoleID string `json:"role_id" bson:"role_id"`
+
+	// Valid Until - время до которого действует клиент
+	// Inactive Lifetime - длительность периода неактивности после чего клиент будет отключен (Disabled)
+	// RateLimit - ограничение количества запросов ?
+
+}
+
+type OAuth struct {
+	ClientID     string `bson:"client_id,omitempty" json:"client_id,omitempty"`         // Идентификатор клиента выданные IdP сервером, используется для идентификации клиента
+	AuthID       string `bson:"auth_id,omitempty" json:"auth_id,omitempty"`             // Сервис, который используется для авторизации клиента
+	TokenURL     string `bson:"token_url,omitempty" json:"token_url,omitempty"`         // URL для получения/обновления access token клиента (опционально)
+	ClientSecret string `bson:"client_secret,omitempty" json:"client_secret,omitempty"` // Секретный Ключ клиента, используется для идентификации клиента (опционально)
+}
+
+type APIKey struct {
+	Key    string `bson:"key,omitempty" json:"key,omitempty"`
+	Rotate bool   `bson:"-" json:"rotate,omitempty"`
+}
+
+type TLS struct {
+	Subject string `json:"subject,omitempty"`
+	CACert  string `json:"ca_cert,omitempty"`
+	Cert    string `json:"cert,omitempty"`
+	Key     string `json:"key,omitempty"`
+}
+
+func (c *Client) SetDisabled(b bool) *Client {
+	c.Disabled = &b
+	return c
+}
+
+func (c *Client) IsDisabled() bool {
+	if c.Disabled != nil && *c.Disabled {
+		return true
+	}
+	return false
+}
+
+func (c Client) Clone() *Client {
+	clone := &Client{
+		ID:          c.ID,
+		SpaceID:     c.SpaceID,
+		Name:        c.Name,
+		Description: c.Description,
+		RoleID:      c.RoleID,
+		//Environments: append([]string(nil), c.Environments...),
+	}
+
+	if c.OAuth != nil {
+		temp := *c.OAuth
+		clone.OAuth = &temp
+	}
+	if c.TLS != nil {
+		temp := *c.TLS
+		clone.TLS = &temp
+	}
+	if c.APIKey != nil {
+		temp := *c.APIKey
+		clone.APIKey = &temp
+	}
+	if c.Disabled != nil {
+		temp := *c.Disabled
+		clone.Disabled = &temp
+	}
+
+	return clone
+}
diff --git a/pkg/clients/mocks/Clients.go b/pkg/clients/mocks/Clients.go
new file mode 100644
index 0000000000000000000000000000000000000000..bfeb7e946a1fe50d044479ba785aac15f484ac31
--- /dev/null
+++ b/pkg/clients/mocks/Clients.go
@@ -0,0 +1,149 @@
+// Code generated by mockery v2.7.4. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	clients "git.perx.ru/perxis/perxis-go/pkg/clients"
+	mock "github.com/stretchr/testify/mock"
+)
+
+// Clients is an autogenerated mock type for the Clients type
+type Clients struct {
+	mock.Mock
+}
+
+// Create provides a mock function with given fields: ctx, client
+func (_m *Clients) Create(ctx context.Context, client *clients.Client) (*clients.Client, error) {
+	ret := _m.Called(ctx, client)
+
+	var r0 *clients.Client
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.Client) *clients.Client); ok {
+		r0 = rf(ctx, client)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*clients.Client)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *clients.Client) error); ok {
+		r1 = rf(ctx, client)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: ctx, spaceId, id
+func (_m *Clients) Delete(ctx context.Context, spaceId string, id string) error {
+	ret := _m.Called(ctx, spaceId, id)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
+		r0 = rf(ctx, spaceId, id)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// Enable provides a mock function with given fields: ctx, spaceId, id, enable
+func (_m *Clients) Enable(ctx context.Context, spaceId string, id string, enable bool) error {
+	ret := _m.Called(ctx, spaceId, id, enable)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, bool) error); ok {
+		r0 = rf(ctx, spaceId, id, enable)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// Get provides a mock function with given fields: ctx, spaceId, id
+func (_m *Clients) Get(ctx context.Context, spaceId string, id string) (*clients.Client, error) {
+	ret := _m.Called(ctx, spaceId, id)
+
+	var r0 *clients.Client
+	if rf, ok := ret.Get(0).(func(context.Context, string, string) *clients.Client); ok {
+		r0 = rf(ctx, spaceId, id)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*clients.Client)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
+		r1 = rf(ctx, spaceId, id)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetBy provides a mock function with given fields: ctx, spaceId, params
+func (_m *Clients) GetBy(ctx context.Context, spaceId string, params *clients.GetByParams) (*clients.Client, error) {
+	ret := _m.Called(ctx, spaceId, params)
+
+	var r0 *clients.Client
+	if rf, ok := ret.Get(0).(func(context.Context, string, *clients.GetByParams) *clients.Client); ok {
+		r0 = rf(ctx, spaceId, params)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*clients.Client)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, string, *clients.GetByParams) error); ok {
+		r1 = rf(ctx, spaceId, params)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// List provides a mock function with given fields: ctx, spaceId
+func (_m *Clients) List(ctx context.Context, spaceId string) ([]*clients.Client, error) {
+	ret := _m.Called(ctx, spaceId)
+
+	var r0 []*clients.Client
+	if rf, ok := ret.Get(0).(func(context.Context, string) []*clients.Client); ok {
+		r0 = rf(ctx, spaceId)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]*clients.Client)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
+		r1 = rf(ctx, spaceId)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Update provides a mock function with given fields: ctx, client
+func (_m *Clients) Update(ctx context.Context, client *clients.Client) error {
+	ret := _m.Called(ctx, client)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.Client) error); ok {
+		r0 = rf(ctx, client)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
diff --git a/pkg/clients/service.go b/pkg/clients/service.go
new file mode 100644
index 0000000000000000000000000000000000000000..823de82bdbe720e2a9ff24f069cb94458bf1cf25
--- /dev/null
+++ b/pkg/clients/service.go
@@ -0,0 +1,37 @@
+package clients
+
+import (
+	"context"
+)
+
+type GetByParams struct {
+	OAuthClientID string `json:"oauth_client_id,omitempty"`
+	APIKey        string `json:"api_key,omitempty"`
+	TLSSubject    string `json:"tls_subject,omitempty"`
+}
+
+// @microgen grpc, recovering, middleware
+// @protobuf git.perx.ru/perxis/perxis-go/proto/clients
+// @grpc-addr content.clients.Clients
+type Clients interface {
+	// Create - создает клиента (приложение) для работы с API
+	Create(ctx context.Context, client *Client) (created *Client, err error)
+
+	// Get - возвращает клиента по id
+	Get(ctx context.Context, spaceId, id string) (client *Client, err error)
+
+	// GetBy - возвращает клиента по идентификатору системы авторизации
+	GetBy(ctx context.Context, spaceId string, params *GetByParams) (client *Client, err error)
+
+	// List - возвращает список клиентов созданных в пространстве
+	List(ctx context.Context, spaceId string) (clients []*Client, err error)
+
+	// Update - обновляет параметры клиента
+	Update(ctx context.Context, client *Client) (err error)
+
+	// Delete - удаляет указанного клиента из пространстве
+	Delete(ctx context.Context, spaceId, id string) (err error)
+
+	// Enable - активирует/деактивирует клиента. Клиент не сможет обращаться к API платформы
+	Enable(ctx context.Context, spaceId, id string, enable bool) (err error)
+}
diff --git a/pkg/clients/transport/client.microgen.go b/pkg/clients/transport/client.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..633855af4d1f7fe74260ec7b4ece23db6063ebfb
--- /dev/null
+++ b/pkg/clients/transport/client.microgen.go
@@ -0,0 +1,108 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transport
+
+import (
+	"context"
+	"errors"
+	clients "git.perx.ru/perxis/perxis-go/pkg/clients"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+func (set EndpointsSet) Create(arg0 context.Context, arg1 *clients.Client) (res0 *clients.Client, res1 error) {
+	request := CreateRequest{Client: arg1}
+	response, res1 := set.CreateEndpoint(arg0, &request)
+	if res1 != nil {
+		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res1 = errors.New(e.Message())
+		}
+		return
+	}
+	return response.(*CreateResponse).Created, res1
+}
+
+func (set EndpointsSet) Get(arg0 context.Context, arg1 string, arg2 string) (res0 *clients.Client, res1 error) {
+	request := GetRequest{
+		Id:      arg2,
+		SpaceId: arg1,
+	}
+	response, res1 := set.GetEndpoint(arg0, &request)
+	if res1 != nil {
+		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res1 = errors.New(e.Message())
+		}
+		return
+	}
+	return response.(*GetResponse).Client, res1
+}
+
+func (set EndpointsSet) GetBy(arg0 context.Context, arg1 string, arg2 *clients.GetByParams) (res0 *clients.Client, res1 error) {
+	request := GetByRequest{
+		Config:  arg2,
+		SpaceId: arg1,
+	}
+	response, res1 := set.GetByEndpoint(arg0, &request)
+	if res1 != nil {
+		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res1 = errors.New(e.Message())
+		}
+		return
+	}
+	return response.(*GetByResponse).Client, res1
+}
+
+func (set EndpointsSet) List(arg0 context.Context, arg1 string) (res0 []*clients.Client, res1 error) {
+	request := ListRequest{SpaceId: arg1}
+	response, res1 := set.ListEndpoint(arg0, &request)
+	if res1 != nil {
+		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res1 = errors.New(e.Message())
+		}
+		return
+	}
+	return response.(*ListResponse).Clients, res1
+}
+
+func (set EndpointsSet) Update(arg0 context.Context, arg1 *clients.Client) (res0 error) {
+	request := UpdateRequest{Client: arg1}
+	_, res0 = set.UpdateEndpoint(arg0, &request)
+	if res0 != nil {
+		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res0 = errors.New(e.Message())
+		}
+		return
+	}
+	return res0
+}
+
+func (set EndpointsSet) Delete(arg0 context.Context, arg1 string, arg2 string) (res0 error) {
+	request := DeleteRequest{
+		Id:      arg2,
+		SpaceId: arg1,
+	}
+	_, res0 = set.DeleteEndpoint(arg0, &request)
+	if res0 != nil {
+		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res0 = errors.New(e.Message())
+		}
+		return
+	}
+	return res0
+}
+
+func (set EndpointsSet) Enable(arg0 context.Context, arg1 string, arg2 string, arg3 bool) (res0 error) {
+	request := EnableRequest{
+		Enable:  arg3,
+		Id:      arg2,
+		SpaceId: arg1,
+	}
+	_, res0 = set.EnableEndpoint(arg0, &request)
+	if res0 != nil {
+		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res0 = errors.New(e.Message())
+		}
+		return
+	}
+	return res0
+}
diff --git a/pkg/clients/transport/endpoints.microgen.go b/pkg/clients/transport/endpoints.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..bf73c784e68eb95f1dc631184046a4016bcca7a1
--- /dev/null
+++ b/pkg/clients/transport/endpoints.microgen.go
@@ -0,0 +1,16 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transport
+
+import endpoint "github.com/go-kit/kit/endpoint"
+
+// EndpointsSet implements Clients API and used for transport purposes.
+type EndpointsSet struct {
+	CreateEndpoint endpoint.Endpoint
+	GetEndpoint    endpoint.Endpoint
+	GetByEndpoint  endpoint.Endpoint
+	ListEndpoint   endpoint.Endpoint
+	UpdateEndpoint endpoint.Endpoint
+	DeleteEndpoint endpoint.Endpoint
+	EnableEndpoint endpoint.Endpoint
+}
diff --git a/pkg/clients/transport/exchanges.microgen.go b/pkg/clients/transport/exchanges.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..2a1a8e65967661b117a20ff62fc6e20837afce2b
--- /dev/null
+++ b/pkg/clients/transport/exchanges.microgen.go
@@ -0,0 +1,58 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transport
+
+import clients "git.perx.ru/perxis/perxis-go/pkg/clients"
+
+type (
+	CreateRequest struct {
+		Client *clients.Client `json:"client"`
+	}
+	CreateResponse struct {
+		Created *clients.Client `json:"created"`
+	}
+
+	GetRequest struct {
+		SpaceId string `json:"space_id"`
+		Id      string `json:"id"`
+	}
+	GetResponse struct {
+		Client *clients.Client `json:"client"`
+	}
+
+	GetByRequest struct {
+		SpaceId string               `json:"space_id"`
+		Config  *clients.GetByParams `json:"config"`
+	}
+	GetByResponse struct {
+		Client *clients.Client `json:"client"`
+	}
+
+	ListRequest struct {
+		SpaceId string `json:"space_id"`
+	}
+	ListResponse struct {
+		Clients []*clients.Client `json:"clients"`
+	}
+
+	UpdateRequest struct {
+		Client *clients.Client `json:"client"`
+	}
+	// Formal exchange type, please do not delete.
+	UpdateResponse struct{}
+
+	DeleteRequest struct {
+		SpaceId string `json:"space_id"`
+		Id      string `json:"id"`
+	}
+	// Formal exchange type, please do not delete.
+	DeleteResponse struct{}
+
+	EnableRequest struct {
+		SpaceId string `json:"space_id"`
+		Id      string `json:"id"`
+		Enable  bool   `json:"enable"`
+	}
+	// Formal exchange type, please do not delete.
+	EnableResponse struct{}
+)
diff --git a/pkg/clients/transport/grpc/client.microgen.go b/pkg/clients/transport/grpc/client.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..421a0178c29b691c33085761bc1710e3c4cbe4eb
--- /dev/null
+++ b/pkg/clients/transport/grpc/client.microgen.go
@@ -0,0 +1,68 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	transport "git.perx.ru/perxis/perxis-go/pkg/clients/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/clients"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	empty "github.com/golang/protobuf/ptypes/empty"
+	grpc "google.golang.org/grpc"
+)
+
+func NewGRPCClient(conn *grpc.ClientConn, addr string, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	if addr == "" {
+		addr = "content.clients.Clients"
+	}
+	return transport.EndpointsSet{
+		CreateEndpoint: grpckit.NewClient(
+			conn, addr, "Create",
+			_Encode_Create_Request,
+			_Decode_Create_Response,
+			pb.CreateResponse{},
+			opts...,
+		).Endpoint(),
+		DeleteEndpoint: grpckit.NewClient(
+			conn, addr, "Delete",
+			_Encode_Delete_Request,
+			_Decode_Delete_Response,
+			empty.Empty{},
+			opts...,
+		).Endpoint(),
+		EnableEndpoint: grpckit.NewClient(
+			conn, addr, "Enable",
+			_Encode_Enable_Request,
+			_Decode_Enable_Response,
+			empty.Empty{},
+			opts...,
+		).Endpoint(),
+		GetByEndpoint: grpckit.NewClient(
+			conn, addr, "GetBy",
+			_Encode_GetBy_Request,
+			_Decode_GetBy_Response,
+			pb.GetByResponse{},
+			opts...,
+		).Endpoint(),
+		GetEndpoint: grpckit.NewClient(
+			conn, addr, "Get",
+			_Encode_Get_Request,
+			_Decode_Get_Response,
+			pb.GetResponse{},
+			opts...,
+		).Endpoint(),
+		ListEndpoint: grpckit.NewClient(
+			conn, addr, "List",
+			_Encode_List_Request,
+			_Decode_List_Response,
+			pb.ListResponse{},
+			opts...,
+		).Endpoint(),
+		UpdateEndpoint: grpckit.NewClient(
+			conn, addr, "Update",
+			_Encode_Update_Request,
+			_Decode_Update_Response,
+			empty.Empty{},
+			opts...,
+		).Endpoint(),
+	}
+}
diff --git a/pkg/clients/transport/grpc/protobuf_endpoint_converters.microgen.go b/pkg/clients/transport/grpc/protobuf_endpoint_converters.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..fea7765facc0f5ba2c6f07a5c7016d2e326a5d23
--- /dev/null
+++ b/pkg/clients/transport/grpc/protobuf_endpoint_converters.microgen.go
@@ -0,0 +1,295 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+// Please, do not change functions names!
+package transportgrpc
+
+import (
+	"context"
+	"errors"
+
+	"git.perx.ru/perxis/perxis-go/pkg/clients"
+	transport "git.perx.ru/perxis/perxis-go/pkg/clients/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/clients"
+	empty "github.com/golang/protobuf/ptypes/empty"
+)
+
+func _Encode_Get_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil GetRequest")
+	}
+	req := request.(*transport.GetRequest)
+	return &pb.GetRequest{
+		Id:      req.Id,
+		SpaceId: req.SpaceId,
+	}, nil
+}
+
+func _Encode_List_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil ListRequest")
+	}
+	req := request.(*transport.ListRequest)
+	return &pb.ListRequest{SpaceId: req.SpaceId}, nil
+}
+
+func _Encode_Delete_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil DeleteRequest")
+	}
+	req := request.(*transport.DeleteRequest)
+	return &pb.DeleteRequest{
+		Id:      req.Id,
+		SpaceId: req.SpaceId,
+	}, nil
+}
+
+func _Encode_Enable_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil EnableRequest")
+	}
+	req := request.(*transport.EnableRequest)
+	return &pb.EnableRequest{
+		Enable:  req.Enable,
+		Id:      req.Id,
+		SpaceId: req.SpaceId,
+	}, nil
+}
+
+func _Encode_Get_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil GetResponse")
+	}
+	resp := response.(*transport.GetResponse)
+	respClient, err := PtrClientToProto(resp.Client)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.GetResponse{Client: respClient}, nil
+}
+
+func _Encode_List_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil ListResponse")
+	}
+	resp := response.(*transport.ListResponse)
+	respClients, err := ListPtrClientToProto(resp.Clients)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.ListResponse{Clients: respClients}, nil
+}
+
+func _Encode_Update_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Encode_Delete_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Encode_Enable_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Decode_Get_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil GetRequest")
+	}
+	req := request.(*pb.GetRequest)
+	return &transport.GetRequest{
+		Id:      string(req.Id),
+		SpaceId: string(req.SpaceId),
+	}, nil
+}
+
+func _Decode_List_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil ListRequest")
+	}
+	req := request.(*pb.ListRequest)
+	return &transport.ListRequest{SpaceId: string(req.SpaceId)}, nil
+}
+
+func _Decode_Delete_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil DeleteRequest")
+	}
+	req := request.(*pb.DeleteRequest)
+	return &transport.DeleteRequest{
+		Id:      string(req.Id),
+		SpaceId: string(req.SpaceId),
+	}, nil
+}
+
+func _Decode_Enable_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil EnableRequest")
+	}
+	req := request.(*pb.EnableRequest)
+	return &transport.EnableRequest{
+		Enable:  bool(req.Enable),
+		Id:      string(req.Id),
+		SpaceId: string(req.SpaceId),
+	}, nil
+}
+
+func _Decode_Get_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil GetResponse")
+	}
+	resp := response.(*pb.GetResponse)
+	respClient, err := ProtoToPtrClient(resp.Client)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.GetResponse{Client: respClient}, nil
+}
+
+func _Decode_List_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil ListResponse")
+	}
+	resp := response.(*pb.ListResponse)
+	respClients, err := ProtoToListPtrClient(resp.Clients)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.ListResponse{Clients: respClients}, nil
+}
+
+func _Decode_Update_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Decode_Delete_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Decode_Enable_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Encode_Create_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil CreateRequest")
+	}
+	req := request.(*transport.CreateRequest)
+	reqClient, err := PtrClientToProto(req.Client)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.CreateRequest{Client: reqClient}, nil
+}
+
+func _Encode_Update_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil UpdateRequest")
+	}
+	req := request.(*transport.UpdateRequest)
+	reqClient, err := PtrClientToProto(req.Client)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.UpdateRequest{Client: reqClient}, nil
+}
+
+func _Encode_Create_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil CreateResponse")
+	}
+	resp := response.(*transport.CreateResponse)
+	respCreated, err := PtrClientToProto(resp.Created)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.CreateResponse{Created: respCreated}, nil
+}
+
+func _Decode_Create_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil CreateRequest")
+	}
+	req := request.(*pb.CreateRequest)
+	reqClient, err := ProtoToPtrClient(req.Client)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.CreateRequest{Client: reqClient}, nil
+}
+
+func _Decode_Update_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil UpdateRequest")
+	}
+	req := request.(*pb.UpdateRequest)
+	reqClient, err := ProtoToPtrClient(req.Client)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.UpdateRequest{Client: reqClient}, nil
+}
+
+func _Decode_Create_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil CreateResponse")
+	}
+	resp := response.(*pb.CreateResponse)
+	respCreated, err := ProtoToPtrClient(resp.Created)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.CreateResponse{Created: respCreated}, nil
+}
+
+func _Encode_GetBy_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil GetByRequest")
+	}
+	req := request.(*transport.GetByRequest)
+	pbreq := &pb.GetByRequest{SpaceId: req.SpaceId}
+	if req != nil && req.Config != nil {
+		pbreq.ApiKey = req.Config.APIKey
+		pbreq.TlsSubject = req.Config.TLSSubject
+		pbreq.OauthClientId = req.Config.OAuthClientID
+	}
+	return pbreq, nil
+}
+
+func _Encode_GetBy_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil GetByResponse")
+	}
+	resp := response.(*transport.GetByResponse)
+	respClient, err := PtrClientToProto(resp.Client)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.GetByResponse{Client: respClient}, nil
+}
+
+func _Decode_GetBy_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil GetByRequest")
+	}
+	req := request.(*pb.GetByRequest)
+	return &transport.GetByRequest{
+		Config: &clients.GetByParams{
+			OAuthClientID: req.OauthClientId,
+			APIKey:        req.ApiKey,
+			TLSSubject:    req.TlsSubject,
+		},
+		SpaceId: string(req.SpaceId),
+	}, nil
+}
+
+func _Decode_GetBy_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil GetByResponse")
+	}
+	resp := response.(*pb.GetByResponse)
+	respClient, err := ProtoToPtrClient(resp.Client)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.GetByResponse{Client: respClient}, nil
+}
diff --git a/pkg/clients/transport/grpc/protobuf_type_converters.microgen.go b/pkg/clients/transport/grpc/protobuf_type_converters.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..5212c0b5d2dc9c96533f5f64aee9ed58ab241b41
--- /dev/null
+++ b/pkg/clients/transport/grpc/protobuf_type_converters.microgen.go
@@ -0,0 +1,164 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+// It is better for you if you do not change functions names!
+// This file will never be overwritten.
+package transportgrpc
+
+import (
+	service "git.perx.ru/perxis/perxis-go/pkg/clients"
+	permission "git.perx.ru/perxis/perxis-go/pkg/permission"
+	pb "git.perx.ru/perxis/perxis-go/proto/clients"
+	commonpb "git.perx.ru/perxis/perxis-go/proto/common"
+)
+
+func ListStringToProto(environments []string) ([]string, error) {
+	return environments, nil
+}
+
+func ProtoToListString(protoEnvironments []string) ([]string, error) {
+	return protoEnvironments, nil
+}
+
+func PtrClientToProto(client *service.Client) (*pb.Client, error) {
+	if client == nil {
+		return nil, nil
+	}
+
+	var oauth *pb.Client_OAuth
+	var tls *pb.Client_TLS
+	var apikey *pb.Client_APIKey
+
+	if client.OAuth != nil {
+		oauth = &pb.Client_OAuth{
+			ClientId:     client.OAuth.ClientID,
+			AuthId:       client.OAuth.AuthID,
+			TokenUrl:     client.OAuth.TokenURL,
+			ClientSecret: client.OAuth.ClientSecret,
+		}
+	}
+	if client.TLS != nil {
+		tls = &pb.Client_TLS{
+			Subject: client.TLS.Subject,
+		}
+	}
+	if client.APIKey != nil {
+		apikey = &pb.Client_APIKey{
+			Key:    client.APIKey.Key,
+			Rotate: client.APIKey.Rotate,
+		}
+	}
+
+	return &pb.Client{
+		Id:          client.ID,
+		SpaceId:     client.SpaceID,
+		Name:        client.Name,
+		Description: client.Description,
+		Disabled:    client.Disabled,
+		RoleId:      client.RoleID,
+		//Environments: client.Environments,
+		//Rules:        rules,
+		Oauth:  oauth,
+		Tls:    tls,
+		ApiKey: apikey,
+	}, nil
+}
+
+func ProtoToPtrClient(protoClient *pb.Client) (*service.Client, error) {
+	if protoClient == nil {
+		return nil, nil
+	}
+
+	var oauth *service.OAuth
+	var tls *service.TLS
+	var apikey *service.APIKey
+
+	if protoClient.Oauth != nil {
+		oauth = &service.OAuth{
+			ClientID:     protoClient.Oauth.ClientId,
+			AuthID:       protoClient.Oauth.AuthId,
+			TokenURL:     protoClient.Oauth.TokenUrl,
+			ClientSecret: protoClient.Oauth.ClientSecret,
+		}
+	}
+	if protoClient.Tls != nil {
+		tls = &service.TLS{
+			Subject: protoClient.Tls.Subject,
+		}
+	}
+	if protoClient.ApiKey != nil {
+		apikey = &service.APIKey{
+			Key:    protoClient.ApiKey.Key,
+			Rotate: protoClient.ApiKey.Rotate,
+		}
+	}
+
+	return &service.Client{
+		ID:          protoClient.Id,
+		SpaceID:     protoClient.SpaceId,
+		Name:        protoClient.Name,
+		Description: protoClient.Description,
+		Disabled:    protoClient.Disabled,
+		RoleID:      protoClient.RoleId,
+		OAuth:       oauth,
+		TLS:         tls,
+		APIKey:      apikey,
+	}, nil
+}
+
+func ListPtrClientToProto(clients []*service.Client) ([]*pb.Client, error) {
+	protoClients := make([]*pb.Client, 0, len(clients))
+	for _, c := range clients {
+		protoClient, _ := PtrClientToProto(c)
+		protoClients = append(protoClients, protoClient)
+	}
+	return protoClients, nil
+}
+
+func ProtoToListPtrClient(protoClients []*pb.Client) ([]*service.Client, error) {
+	clients := make([]*service.Client, 0, len(protoClients))
+	for _, c := range protoClients {
+		client, _ := ProtoToPtrClient(c)
+		clients = append(clients, client)
+	}
+	return clients, nil
+}
+
+func PtrPermissionRuleToProto(rule *permission.Rule) (*commonpb.Rule, error) {
+	if rule == nil {
+		return nil, nil
+	}
+	actions := make([]commonpb.Action, 0, len(rule.Actions))
+	for _, a := range rule.Actions {
+		actions = append(actions, commonpb.Action(a))
+	}
+	return &commonpb.Rule{
+		CollectionId:    rule.CollectionID,
+		Actions:         actions,
+		Access:          commonpb.Access(rule.Access),
+		HiddenFields:    rule.HiddenFields,
+		ReadonlyFields:  rule.ReadonlyFields,
+		WriteonlyFields: rule.WriteonlyFields,
+		ReadFilter:      rule.ReadFilter,
+		WriteFilter:     rule.WriteFilter,
+	}, nil
+}
+
+func ProtoToPtrPermissionRule(protoRule *commonpb.Rule) (*permission.Rule, error) {
+	if protoRule == nil {
+		return nil, nil
+	}
+	actions := make([]permission.Action, 0, len(protoRule.Actions))
+	for _, a := range protoRule.Actions {
+		actions = append(actions, permission.Action(a))
+	}
+	return &permission.Rule{
+		CollectionID:    protoRule.CollectionId,
+		Actions:         actions,
+		Access:          permission.Access(protoRule.Access),
+		HiddenFields:    protoRule.HiddenFields,
+		ReadonlyFields:  protoRule.ReadonlyFields,
+		WriteonlyFields: protoRule.WriteonlyFields,
+		ReadFilter:      protoRule.ReadFilter,
+		WriteFilter:     protoRule.WriteFilter,
+	}, nil
+}
diff --git a/pkg/clients/transport/grpc/server.microgen.go b/pkg/clients/transport/grpc/server.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..7408e691dd4fb1e8e772ffe9e530fcd112d02729
--- /dev/null
+++ b/pkg/clients/transport/grpc/server.microgen.go
@@ -0,0 +1,127 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+// DO NOT EDIT.
+package transportgrpc
+
+import (
+	transport "git.perx.ru/perxis/perxis-go/pkg/clients/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/clients"
+	grpc "github.com/go-kit/kit/transport/grpc"
+	empty "github.com/golang/protobuf/ptypes/empty"
+	context "golang.org/x/net/context"
+)
+
+type clientsServer struct {
+	create grpc.Handler
+	get    grpc.Handler
+	getBy  grpc.Handler
+	list   grpc.Handler
+	update grpc.Handler
+	delete grpc.Handler
+	enable grpc.Handler
+
+	pb.UnimplementedClientsServer
+}
+
+func NewGRPCServer(endpoints *transport.EndpointsSet, opts ...grpc.ServerOption) pb.ClientsServer {
+	return &clientsServer{
+		create: grpc.NewServer(
+			endpoints.CreateEndpoint,
+			_Decode_Create_Request,
+			_Encode_Create_Response,
+			opts...,
+		),
+		delete: grpc.NewServer(
+			endpoints.DeleteEndpoint,
+			_Decode_Delete_Request,
+			_Encode_Delete_Response,
+			opts...,
+		),
+		enable: grpc.NewServer(
+			endpoints.EnableEndpoint,
+			_Decode_Enable_Request,
+			_Encode_Enable_Response,
+			opts...,
+		),
+		get: grpc.NewServer(
+			endpoints.GetEndpoint,
+			_Decode_Get_Request,
+			_Encode_Get_Response,
+			opts...,
+		),
+		getBy: grpc.NewServer(
+			endpoints.GetByEndpoint,
+			_Decode_GetBy_Request,
+			_Encode_GetBy_Response,
+			opts...,
+		),
+		list: grpc.NewServer(
+			endpoints.ListEndpoint,
+			_Decode_List_Request,
+			_Encode_List_Response,
+			opts...,
+		),
+		update: grpc.NewServer(
+			endpoints.UpdateEndpoint,
+			_Decode_Update_Request,
+			_Encode_Update_Response,
+			opts...,
+		),
+	}
+}
+
+func (S *clientsServer) Create(ctx context.Context, req *pb.CreateRequest) (*pb.CreateResponse, error) {
+	_, resp, err := S.create.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*pb.CreateResponse), nil
+}
+
+func (S *clientsServer) Get(ctx context.Context, req *pb.GetRequest) (*pb.GetResponse, error) {
+	_, resp, err := S.get.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*pb.GetResponse), nil
+}
+
+func (S *clientsServer) GetBy(ctx context.Context, req *pb.GetByRequest) (*pb.GetByResponse, error) {
+	_, resp, err := S.getBy.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*pb.GetByResponse), nil
+}
+
+func (S *clientsServer) List(ctx context.Context, req *pb.ListRequest) (*pb.ListResponse, error) {
+	_, resp, err := S.list.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*pb.ListResponse), nil
+}
+
+func (S *clientsServer) Update(ctx context.Context, req *pb.UpdateRequest) (*empty.Empty, error) {
+	_, resp, err := S.update.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*empty.Empty), nil
+}
+
+func (S *clientsServer) Delete(ctx context.Context, req *pb.DeleteRequest) (*empty.Empty, error) {
+	_, resp, err := S.delete.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*empty.Empty), nil
+}
+
+func (S *clientsServer) Enable(ctx context.Context, req *pb.EnableRequest) (*empty.Empty, error) {
+	_, resp, err := S.enable.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*empty.Empty), nil
+}
diff --git a/pkg/clients/transport/server.microgen.go b/pkg/clients/transport/server.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..4c8b6a3e538dd5d1f28c3e7fd2b9f56af54cae8e
--- /dev/null
+++ b/pkg/clients/transport/server.microgen.go
@@ -0,0 +1,77 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transport
+
+import (
+	"context"
+	clients "git.perx.ru/perxis/perxis-go/pkg/clients"
+	endpoint "github.com/go-kit/kit/endpoint"
+)
+
+func Endpoints(svc clients.Clients) EndpointsSet {
+	return EndpointsSet{
+		CreateEndpoint: CreateEndpoint(svc),
+		DeleteEndpoint: DeleteEndpoint(svc),
+		EnableEndpoint: EnableEndpoint(svc),
+		GetByEndpoint:  GetByEndpoint(svc),
+		GetEndpoint:    GetEndpoint(svc),
+		ListEndpoint:   ListEndpoint(svc),
+		UpdateEndpoint: UpdateEndpoint(svc),
+	}
+}
+
+func CreateEndpoint(svc clients.Clients) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*CreateRequest)
+		res0, res1 := svc.Create(arg0, req.Client)
+		return &CreateResponse{Created: res0}, res1
+	}
+}
+
+func GetEndpoint(svc clients.Clients) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*GetRequest)
+		res0, res1 := svc.Get(arg0, req.SpaceId, req.Id)
+		return &GetResponse{Client: res0}, res1
+	}
+}
+
+func GetByEndpoint(svc clients.Clients) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*GetByRequest)
+		res0, res1 := svc.GetBy(arg0, req.SpaceId, req.Config)
+		return &GetByResponse{Client: res0}, res1
+	}
+}
+
+func ListEndpoint(svc clients.Clients) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*ListRequest)
+		res0, res1 := svc.List(arg0, req.SpaceId)
+		return &ListResponse{Clients: res0}, res1
+	}
+}
+
+func UpdateEndpoint(svc clients.Clients) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*UpdateRequest)
+		res0 := svc.Update(arg0, req.Client)
+		return &UpdateResponse{}, res0
+	}
+}
+
+func DeleteEndpoint(svc clients.Clients) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*DeleteRequest)
+		res0 := svc.Delete(arg0, req.SpaceId, req.Id)
+		return &DeleteResponse{}, res0
+	}
+}
+
+func EnableEndpoint(svc clients.Clients) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*EnableRequest)
+		res0 := svc.Enable(arg0, req.SpaceId, req.Id, req.Enable)
+		return &EnableResponse{}, res0
+	}
+}