diff --git a/pkg/invitations/invitation.go b/pkg/invitations/invitation.go
new file mode 100644
index 0000000000000000000000000000000000000000..5dc5913774fc5774076823309abdf52b195c87c2
--- /dev/null
+++ b/pkg/invitations/invitation.go
@@ -0,0 +1,16 @@
+package invitations
+
+import "time"
+
+const InvitationTTL = 7 * 24 * time.Hour
+
+type Invitation struct {
+	ID         string     `bson:"_id"`
+	Email      string     `bson:"email"`
+	OrgID      string     `bson:"orgId"`
+	SpaceID    string     `bson:"spaceId"`
+	OwnerID    string     `bson:"ownerId"` // Invitation owner
+	Role       string     `bson:"role"`
+	CreatedAt  *time.Time `bson:"createdAt"`
+	ValidUntil *time.Time `bson:"validUntil"`
+}
diff --git a/pkg/invitations/mocks/Invitations.go b/pkg/invitations/mocks/Invitations.go
new file mode 100644
index 0000000000000000000000000000000000000000..610f9fff80d4867b35b80ff464bcf2a8a9d0e764
--- /dev/null
+++ b/pkg/invitations/mocks/Invitations.go
@@ -0,0 +1,120 @@
+// Code generated by mockery v2.7.4. DO NOT EDIT.
+
+package mocks
+
+import (
+	"context"
+
+	"git.perx.ru/perxis/perxis-go/pkg/invitations"
+	"git.perx.ru/perxis/perxis-go/pkg/options"
+	"github.com/stretchr/testify/mock"
+)
+
+// Invitations is an autogenerated mock type for the Invitations type
+type Invitations struct {
+	mock.Mock
+}
+
+// Accept provides a mock function with given fields: ctx, invitationId, userId
+func (_m *Invitations) Accept(ctx context.Context, invitationId string, userId string) error {
+	ret := _m.Called(ctx, invitationId, userId)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
+		r0 = rf(ctx, invitationId, userId)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// Create provides a mock function with given fields: ctx, invitation
+func (_m *Invitations) Create(ctx context.Context, invitation *invitations.Invitation) (*invitations.Invitation, error) {
+	ret := _m.Called(ctx, invitation)
+
+	var r0 *invitations.Invitation
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.Invitation) *invitations.Invitation); ok {
+		r0 = rf(ctx, invitation)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*invitations.Invitation)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *invitations.Invitation) error); ok {
+		r1 = rf(ctx, invitation)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: ctx, invitationId
+func (_m *Invitations) Delete(ctx context.Context, invitationId string) error {
+	ret := _m.Called(ctx, invitationId)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
+		r0 = rf(ctx, invitationId)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// Find provides a mock function with given fields: ctx, filter, opts
+func (_m *Invitations) Find(ctx context.Context, filter *invitations.Filter, opts *options.FindOptions) ([]*invitations.Invitation, int, error) {
+	ret := _m.Called(ctx, filter, opts)
+
+	var r0 []*invitations.Invitation
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.Filter, *options.FindOptions) []*invitations.Invitation); ok {
+		r0 = rf(ctx, filter, opts)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]*invitations.Invitation)
+		}
+	}
+
+	var r1 int
+	if rf, ok := ret.Get(1).(func(context.Context, *invitations.Filter, *options.FindOptions) int); ok {
+		r1 = rf(ctx, filter, opts)
+	} else {
+		r1 = ret.Get(1).(int)
+	}
+
+	var r2 error
+	if rf, ok := ret.Get(2).(func(context.Context, *invitations.Filter, *options.FindOptions) error); ok {
+		r2 = rf(ctx, filter, opts)
+	} else {
+		r2 = ret.Error(2)
+	}
+
+	return r0, r1, r2
+}
+
+// Get provides a mock function with given fields: ctx, invitationId
+func (_m *Invitations) Get(ctx context.Context, invitationId string) (*invitations.Invitation, error) {
+	ret := _m.Called(ctx, invitationId)
+
+	var r0 *invitations.Invitation
+	if rf, ok := ret.Get(0).(func(context.Context, string) *invitations.Invitation); ok {
+		r0 = rf(ctx, invitationId)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*invitations.Invitation)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
+		r1 = rf(ctx, invitationId)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
diff --git a/pkg/invitations/service.go b/pkg/invitations/service.go
new file mode 100644
index 0000000000000000000000000000000000000000..26426d67462cdcb51fc7fe8f1bd60dc1fde69260
--- /dev/null
+++ b/pkg/invitations/service.go
@@ -0,0 +1,27 @@
+package invitations
+
+import (
+	"context"
+
+	"git.perx.ru/perxis/perxis-go/pkg/options"
+)
+
+type Filter struct {
+	ID      []string
+	Email   []string
+	OrgID   []string
+	SpaceID []string
+	OwnerID []string
+	Role    []string
+}
+
+// @microgen grpc
+// @protobuf git.perx.ru/perxis/perxis-go/proto/invitations
+// @grpc-addr content.invitations.Invitations
+type Invitations interface {
+	Create(ctx context.Context, invitation *Invitation) (created *Invitation, err error)
+	Get(ctx context.Context, invitationId string) (invitation *Invitation, err error)
+	Accept(ctx context.Context, invitationId, userId string) (err error)
+	Find(ctx context.Context, filter *Filter, opts *options.FindOptions) (invitations []*Invitation, total int, err error)
+	Delete(ctx context.Context, invitationId string) (err error)
+}
diff --git a/pkg/invitations/transport/client.microgen.go b/pkg/invitations/transport/client.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..6f896b0e67fa88a1eaa539c305a47fada11c9c8b
--- /dev/null
+++ b/pkg/invitations/transport/client.microgen.go
@@ -0,0 +1,79 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transport
+
+import (
+	"context"
+	"errors"
+
+	invitations "git.perx.ru/perxis/perxis-go/pkg/invitations"
+	"git.perx.ru/perxis/perxis-go/pkg/options"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+func (set EndpointsSet) Create(arg0 context.Context, arg1 *invitations.Invitation) (res0 *invitations.Invitation, res1 error) {
+	request := CreateRequest{Invitation: 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) (res0 *invitations.Invitation, res1 error) {
+	request := GetRequest{InvitationId: 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).Invitation, res1
+}
+
+func (set EndpointsSet) Accept(arg0 context.Context, arg1 string, arg2 string) (res0 error) {
+	request := AcceptRequest{
+		InvitationId: arg1,
+		UserId:       arg2,
+	}
+	_, res0 = set.AcceptEndpoint(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) Find(arg0 context.Context, arg1 *invitations.Filter, arg2 *options.FindOptions) (res0 []*invitations.Invitation, res1 int, res2 error) {
+	request := FindRequest{
+		Filter: arg1,
+		Opts:   arg2,
+	}
+	response, res2 := set.FindEndpoint(arg0, &request)
+	if res2 != nil {
+		if e, ok := status.FromError(res2); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res2 = errors.New(e.Message())
+		}
+		return
+	}
+	return response.(*FindResponse).Invitations, response.(*FindResponse).Total, res2
+}
+
+func (set EndpointsSet) Delete(arg0 context.Context, arg1 string) (res0 error) {
+	request := DeleteRequest{InvitationId: 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
+}
diff --git a/pkg/invitations/transport/endpoints.microgen.go b/pkg/invitations/transport/endpoints.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..70d78bbcf04e68ad97c36d90b4f66f3287f108d2
--- /dev/null
+++ b/pkg/invitations/transport/endpoints.microgen.go
@@ -0,0 +1,14 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transport
+
+import endpoint "github.com/go-kit/kit/endpoint"
+
+// EndpointsSet implements Invitations API and used for transport purposes.
+type EndpointsSet struct {
+	CreateEndpoint endpoint.Endpoint
+	GetEndpoint    endpoint.Endpoint
+	AcceptEndpoint endpoint.Endpoint
+	FindEndpoint   endpoint.Endpoint
+	DeleteEndpoint endpoint.Endpoint
+}
diff --git a/pkg/invitations/transport/exchanges.microgen.go b/pkg/invitations/transport/exchanges.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..bb36e466cbcb25584c462931a3eab55ffd90732c
--- /dev/null
+++ b/pkg/invitations/transport/exchanges.microgen.go
@@ -0,0 +1,46 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transport
+
+import (
+	"git.perx.ru/perxis/perxis-go/pkg/invitations"
+	"git.perx.ru/perxis/perxis-go/pkg/options"
+)
+
+type (
+	CreateRequest struct {
+		Invitation *invitations.Invitation `json:"invitation"`
+	}
+	CreateResponse struct {
+		Created *invitations.Invitation `json:"created"`
+	}
+
+	GetRequest struct {
+		InvitationId string `json:"invitation_id"`
+	}
+	GetResponse struct {
+		Invitation *invitations.Invitation `json:"invitation"`
+	}
+
+	AcceptRequest struct {
+		InvitationId string `json:"invitation_id"`
+		UserId       string `json:"user_id"`
+	}
+	// Formal exchange type, please do not delete.
+	AcceptResponse struct{}
+
+	FindRequest struct {
+		Filter *invitations.Filter  `json:"filter"`
+		Opts   *options.FindOptions `json:"opts"`
+	}
+	FindResponse struct {
+		Invitations []*invitations.Invitation `json:"invitations"`
+		Total       int                       `json:"total"`
+	}
+
+	DeleteRequest struct {
+		InvitationId string `json:"invitation_id"`
+	}
+	// Formal exchange type, please do not delete.
+	DeleteResponse struct{}
+)
diff --git a/pkg/invitations/transport/grpc/client.microgen.go b/pkg/invitations/transport/grpc/client.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..e319e19f9d32b9d3efa1e0d6fe485372e177efa2
--- /dev/null
+++ b/pkg/invitations/transport/grpc/client.microgen.go
@@ -0,0 +1,54 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	transport "git.perx.ru/perxis/perxis-go/pkg/invitations/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/invitations"
+	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.invitations.Invitations"
+	}
+	return transport.EndpointsSet{
+		AcceptEndpoint: grpckit.NewClient(
+			conn, addr, "Accept",
+			_Encode_Accept_Request,
+			_Decode_Accept_Response,
+			empty.Empty{},
+			opts...,
+		).Endpoint(),
+		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(),
+		FindEndpoint: grpckit.NewClient(
+			conn, addr, "Find",
+			_Encode_Find_Request,
+			_Decode_Find_Response,
+			pb.FindResponse{},
+			opts...,
+		).Endpoint(),
+		GetEndpoint: grpckit.NewClient(
+			conn, addr, "Get",
+			_Encode_Get_Request,
+			_Decode_Get_Response,
+			pb.GetResponse{},
+			opts...,
+		).Endpoint(),
+	}
+}
diff --git a/pkg/invitations/transport/grpc/protobuf_endpoint_converters.microgen.go b/pkg/invitations/transport/grpc/protobuf_endpoint_converters.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..85403cab8c13fb71612e275a2851c02a904d8abc
--- /dev/null
+++ b/pkg/invitations/transport/grpc/protobuf_endpoint_converters.microgen.go
@@ -0,0 +1,223 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+// Please, do not change functions names!
+package transportgrpc
+
+import (
+	"context"
+	"errors"
+
+	transport "git.perx.ru/perxis/perxis-go/pkg/invitations/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/invitations"
+	empty "github.com/golang/protobuf/ptypes/empty"
+)
+
+func _Encode_Create_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil CreateRequest")
+	}
+	req := request.(*transport.CreateRequest)
+	pbInvitation, err := PtrInvitationToProto(req.Invitation)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.CreateRequest{Invitation: pbInvitation}, nil
+}
+
+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{InvitationId: req.InvitationId}, nil
+}
+
+func _Encode_Accept_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil AcceptRequest")
+	}
+	req := request.(*transport.AcceptRequest)
+	return &pb.AcceptRequest{
+		InvitationId: req.InvitationId,
+		UserId:       req.UserId,
+	}, nil
+}
+
+func _Encode_Find_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil FindRequest")
+	}
+	req := request.(*transport.FindRequest)
+	reqFilter, err := PtrFilterToProto(req.Filter)
+	if err != nil {
+		return nil, err
+	}
+	reqOpts, err := PtrServicesFindOptionsToProto(req.Opts)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.FindRequest{
+		Filter: reqFilter,
+		Opts:   reqOpts,
+	}, 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{InvitationId: req.InvitationId}, 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)
+	respInvitation, err := PtrInvitationToProto(resp.Created)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.CreateResponse{Invitation: respInvitation}, 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)
+	respInvitation, err := PtrInvitationToProto(resp.Invitation)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.GetResponse{Invitation: respInvitation}, nil
+}
+
+func _Encode_Accept_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Encode_Find_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil FindResponse")
+	}
+	resp := response.(*transport.FindResponse)
+	respInvitations, err := ListPtrInvitationToProto(resp.Invitations)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.FindResponse{
+		Invitations: respInvitations,
+		Total:       int64(resp.Total),
+	}, nil
+}
+
+func _Encode_Delete_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, 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)
+	invitation, err := ProtoToPtrInvitation(req.Invitation)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.CreateRequest{Invitation: invitation}, 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{InvitationId: string(req.InvitationId)}, nil
+}
+
+func _Decode_Accept_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil AcceptRequest")
+	}
+	req := request.(*pb.AcceptRequest)
+	return &transport.AcceptRequest{
+		InvitationId: string(req.InvitationId),
+		UserId:       string(req.UserId),
+	}, nil
+}
+
+func _Decode_Find_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil FindRequest")
+	}
+	req := request.(*pb.FindRequest)
+	reqFilter, err := ProtoToPtrFilter(req.Filter)
+	if err != nil {
+		return nil, err
+	}
+	reqOpts, err := ProtoToPtrServicesFindOptions(req.Opts)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.FindRequest{
+		Filter: reqFilter,
+		Opts:   reqOpts,
+	}, 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{InvitationId: string(req.InvitationId)}, 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)
+	respInvitation, err := ProtoToPtrInvitation(resp.Invitation)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.CreateResponse{Created: respInvitation}, 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)
+	respInvitation, err := ProtoToPtrInvitation(resp.Invitation)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.GetResponse{Invitation: respInvitation}, nil
+}
+
+func _Decode_Accept_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Decode_Find_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil FindResponse")
+	}
+	resp := response.(*pb.FindResponse)
+	respInvitations, err := ProtoToListPtrInvitation(resp.Invitations)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.FindResponse{
+		Invitations: respInvitations,
+		Total:       int(resp.Total),
+	}, nil
+}
+
+func _Decode_Delete_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
diff --git a/pkg/invitations/transport/grpc/protobuf_type_converters.microgen.go b/pkg/invitations/transport/grpc/protobuf_type_converters.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..9899e39885a8ca07de15f74f2eeda24d65296928
--- /dev/null
+++ b/pkg/invitations/transport/grpc/protobuf_type_converters.microgen.go
@@ -0,0 +1,161 @@
+// 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 (
+	"time"
+
+	service "git.perx.ru/perxis/perxis-go/pkg/invitations"
+	"git.perx.ru/perxis/perxis-go/pkg/options"
+	pb "git.perx.ru/perxis/perxis-go/proto/invitations"
+	"github.com/golang/protobuf/ptypes"
+	timestamp "github.com/golang/protobuf/ptypes/timestamp"
+)
+
+func PtrTimeTimeToProto(validUntil *time.Time) (*timestamp.Timestamp, error) {
+	if validUntil == nil {
+		return nil, nil
+	}
+	t, err := ptypes.TimestampProto(*validUntil)
+	if err != nil {
+		return nil, err
+	}
+	return t, err
+}
+
+func ProtoToPtrTimeTime(protoValidUntil *timestamp.Timestamp) (*time.Time, error) {
+	if protoValidUntil == nil {
+		return nil, nil
+	}
+	t, err := ptypes.Timestamp(protoValidUntil)
+	if err != nil {
+		return nil, err
+	}
+	return &t, nil
+}
+
+func PtrInvitationToProto(invitation *service.Invitation) (*pb.Invitation, error) {
+	if invitation == nil {
+		return nil, nil
+	}
+	pi := &pb.Invitation{
+		Id:      invitation.ID,
+		Email:   invitation.Email,
+		OrgId:   invitation.OrgID,
+		SpaceId: invitation.SpaceID,
+		OwnerId: invitation.OwnerID,
+		Role:    invitation.Role,
+	}
+	if invitation.CreatedAt != nil && !invitation.CreatedAt.IsZero() {
+		t, _ := ptypes.TimestampProto(*invitation.CreatedAt)
+		pi.CreatedAt = t
+	}
+	if invitation.ValidUntil != nil && !invitation.ValidUntil.IsZero() {
+		t, _ := ptypes.TimestampProto(*invitation.ValidUntil)
+		pi.ValidUntil = t
+	}
+	return pi, nil
+}
+
+func ProtoToPtrInvitation(protoInvitation *pb.Invitation) (*service.Invitation, error) {
+	if protoInvitation == nil {
+		return nil, nil
+	}
+	i := &service.Invitation{
+		ID:      protoInvitation.Id,
+		Email:   protoInvitation.Email,
+		OrgID:   protoInvitation.OrgId,
+		SpaceID: protoInvitation.SpaceId,
+		OwnerID: protoInvitation.OwnerId,
+		Role:    protoInvitation.Role,
+	}
+	if protoInvitation.CreatedAt != nil {
+		t, _ := ptypes.Timestamp(protoInvitation.CreatedAt)
+		i.CreatedAt = &t
+	}
+	if protoInvitation.ValidUntil != nil {
+		t, _ := ptypes.Timestamp(protoInvitation.ValidUntil)
+		i.ValidUntil = &t
+	}
+	return i, nil
+}
+
+func PtrFilterToProto(filter *service.Filter) (*pb.Filter, error) {
+	if filter == nil {
+		return nil, nil
+	}
+	return &pb.Filter{
+		Id:      filter.ID,
+		Email:   filter.Email,
+		OrgId:   filter.OrgID,
+		SpaceId: filter.SpaceID,
+		OwnerId: filter.OwnerID,
+		Role:    filter.Role,
+	}, nil
+}
+
+func ProtoToPtrFilter(protoFilter *pb.Filter) (*service.Filter, error) {
+	if protoFilter == nil {
+		return nil, nil
+	}
+	return &service.Filter{
+		ID:      protoFilter.Id,
+		Email:   protoFilter.Email,
+		OrgID:   protoFilter.OrgId,
+		SpaceID: protoFilter.SpaceId,
+		OwnerID: protoFilter.OwnerId,
+		Role:    protoFilter.Role,
+	}, nil
+}
+
+func PtrServicesFindOptionsToProto(opts *options.FindOptions) (*pb.FindOptions, error) {
+	if opts == nil {
+		return nil, nil
+	}
+	return &pb.FindOptions{
+		Sort:     opts.Sort,
+		PageNum:  int32(opts.PageNum),
+		PageSize: int32(opts.PageSize),
+	}, nil
+}
+
+func ProtoToPtrServicesFindOptions(protoOpts *pb.FindOptions) (*options.FindOptions, error) {
+	if protoOpts == nil {
+		return nil, nil
+	}
+	return &options.FindOptions{
+		SortOptions: options.SortOptions{
+			Sort: protoOpts.Sort,
+		},
+		PaginationOptions: options.PaginationOptions{
+			PageNum:  int(protoOpts.PageNum),
+			PageSize: int(protoOpts.PageSize),
+		},
+	}, nil
+}
+
+func ListPtrInvitationToProto(invitations []*service.Invitation) ([]*pb.Invitation, error) {
+	protoInvitations := make([]*pb.Invitation, 0, len(invitations))
+	for _, i := range invitations {
+		pi, err := PtrInvitationToProto(i)
+		if err != nil {
+			return nil, err
+		}
+		protoInvitations = append(protoInvitations, pi)
+	}
+	return protoInvitations, nil
+}
+
+func ProtoToListPtrInvitation(protoInvitations []*pb.Invitation) ([]*service.Invitation, error) {
+	invitations := make([]*service.Invitation, 0, len(protoInvitations))
+	for _, pi := range protoInvitations {
+		p, err := ProtoToPtrInvitation(pi)
+		if err != nil {
+			return nil, err
+		}
+		invitations = append(invitations, p)
+	}
+	return invitations, nil
+}
diff --git a/pkg/invitations/transport/grpc/server.microgen.go b/pkg/invitations/transport/grpc/server.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..fa7cf241737173d3c41cadaa2ae9fa17ecb43fe7
--- /dev/null
+++ b/pkg/invitations/transport/grpc/server.microgen.go
@@ -0,0 +1,97 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+// DO NOT EDIT.
+package transportgrpc
+
+import (
+	transport "git.perx.ru/perxis/perxis-go/pkg/invitations/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/invitations"
+	grpc "github.com/go-kit/kit/transport/grpc"
+	empty "github.com/golang/protobuf/ptypes/empty"
+	context "golang.org/x/net/context"
+)
+
+type invitationsServer struct {
+	create grpc.Handler
+	get    grpc.Handler
+	accept grpc.Handler
+	find   grpc.Handler
+	delete grpc.Handler
+
+	pb.UnimplementedInvitationsServer
+}
+
+func NewGRPCServer(endpoints *transport.EndpointsSet, opts ...grpc.ServerOption) pb.InvitationsServer {
+	return &invitationsServer{
+		accept: grpc.NewServer(
+			endpoints.AcceptEndpoint,
+			_Decode_Accept_Request,
+			_Encode_Accept_Response,
+			opts...,
+		),
+		create: grpc.NewServer(
+			endpoints.CreateEndpoint,
+			_Decode_Create_Request,
+			_Encode_Create_Response,
+			opts...,
+		),
+		delete: grpc.NewServer(
+			endpoints.DeleteEndpoint,
+			_Decode_Delete_Request,
+			_Encode_Delete_Response,
+			opts...,
+		),
+		find: grpc.NewServer(
+			endpoints.FindEndpoint,
+			_Decode_Find_Request,
+			_Encode_Find_Response,
+			opts...,
+		),
+		get: grpc.NewServer(
+			endpoints.GetEndpoint,
+			_Decode_Get_Request,
+			_Encode_Get_Response,
+			opts...,
+		),
+	}
+}
+
+func (S *invitationsServer) 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 *invitationsServer) 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 *invitationsServer) Accept(ctx context.Context, req *pb.AcceptRequest) (*empty.Empty, error) {
+	_, resp, err := S.accept.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*empty.Empty), nil
+}
+
+func (S *invitationsServer) Find(ctx context.Context, req *pb.FindRequest) (*pb.FindResponse, error) {
+	_, resp, err := S.find.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*pb.FindResponse), nil
+}
+
+func (S *invitationsServer) 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
+}
diff --git a/pkg/invitations/transport/server.microgen.go b/pkg/invitations/transport/server.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..326659f2b813d8f6b306d8b9498c0590c6434011
--- /dev/null
+++ b/pkg/invitations/transport/server.microgen.go
@@ -0,0 +1,63 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transport
+
+import (
+	"context"
+
+	invitations "git.perx.ru/perxis/perxis-go/pkg/invitations"
+	endpoint "github.com/go-kit/kit/endpoint"
+)
+
+func Endpoints(svc invitations.Invitations) EndpointsSet {
+	return EndpointsSet{
+		AcceptEndpoint: AcceptEndpoint(svc),
+		CreateEndpoint: CreateEndpoint(svc),
+		DeleteEndpoint: DeleteEndpoint(svc),
+		FindEndpoint:   FindEndpoint(svc),
+		GetEndpoint:    GetEndpoint(svc),
+	}
+}
+
+func CreateEndpoint(svc invitations.Invitations) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*CreateRequest)
+		res0, res1 := svc.Create(arg0, req.Invitation)
+		return &CreateResponse{Created: res0}, res1
+	}
+}
+
+func GetEndpoint(svc invitations.Invitations) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*GetRequest)
+		res0, res1 := svc.Get(arg0, req.InvitationId)
+		return &GetResponse{Invitation: res0}, res1
+	}
+}
+
+func AcceptEndpoint(svc invitations.Invitations) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*AcceptRequest)
+		res0 := svc.Accept(arg0, req.InvitationId, req.UserId)
+		return &AcceptResponse{}, res0
+	}
+}
+
+func FindEndpoint(svc invitations.Invitations) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*FindRequest)
+		res0, res1, res2 := svc.Find(arg0, req.Filter, req.Opts)
+		return &FindResponse{
+			Invitations: res0,
+			Total:       res1,
+		}, res2
+	}
+}
+
+func DeleteEndpoint(svc invitations.Invitations) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*DeleteRequest)
+		res0 := svc.Delete(arg0, req.InvitationId)
+		return &DeleteResponse{}, res0
+	}
+}