diff --git a/template/builder.go b/template/builder.go
index 64530be62337de21dc5b93956df261ce5211a302..9944aab4f93631d849539fa74b1424f30a5aaa46 100644
--- a/template/builder.go
+++ b/template/builder.go
@@ -3,7 +3,10 @@ package template
 import (
 	"bytes"
 	"context"
-	"text/template"
+	html "html/template"
+	"io"
+	"maps"
+	text "text/template"
 
 	"git.perx.ru/perxis/perxis-go/pkg/collections"
 	"git.perx.ru/perxis/perxis-go/pkg/content"
@@ -11,38 +14,74 @@ import (
 	"git.perx.ru/perxis/perxis-go/pkg/spaces"
 )
 
+type Template interface {
+	Execute(w io.Writer, data any) error
+}
+
 type Builder struct {
 	ctx     context.Context
 	cnt     *content.Content
 	SpaceID string
 	EnvID   string
 	CollID  string
-	data    map[string]interface{}
+	data    map[string]any
 
 	// Для кеширования запросов
 	space       *spaces.Space
 	environment *environments.Environment
 	collection  *collections.Collection
+
+	template func(templ string) (Template, error)
+}
+
+func NewBuilder(cnt *content.Content, space, env, coll string) *Builder {
+	b := &Builder{
+		ctx:     context.Background(),
+		cnt:     cnt,
+		SpaceID: space,
+		EnvID:   env,
+		CollID:  coll,
+	}
+	b.template = func(templ string) (Template, error) {
+		t, err := text.New("main").
+			Funcs(b.getFuncs()).
+			Parse(templ)
+		if err != nil {
+			return nil, err
+		}
+		return t, nil
+	}
+	return b
 }
 
-func NewBuilder(cnt *content.Content, space, env, col string) *Builder {
-	return &Builder{
+func NewHTMLBuilder(cnt *content.Content, space, env, coll string) *Builder {
+	b := &Builder{
 		ctx:     context.Background(),
 		cnt:     cnt,
 		SpaceID: space,
 		EnvID:   env,
-		CollID:  col,
+		CollID:  coll,
 	}
+	b.template = func(templ string) (Template, error) {
+		t, err := html.New("main").
+			Funcs(b.getFuncs()).
+			Parse(templ)
+		if err != nil {
+			return nil, err
+		}
+		return t, nil
+	}
+	return b
 }
 
-func (b *Builder) getFuncs() template.FuncMap {
-	return template.FuncMap{
+func (b *Builder) getFuncs() map[string]any {
+	return map[string]any{
 		"lookup": getLookup(b),
 		"system": getSystem(b),
 	}
 }
 
-func (b *Builder) WithData(data map[string]interface{}) *Builder {
+func (b *Builder) WithData(data map[string]any) *Builder {
 	bld := *b
 	bld.data = data
 	return &bld
@@ -51,7 +90,8 @@ func (b *Builder) WithData(data map[string]interface{}) *Builder {
 func (b *Builder) WithKV(kv ...any) *Builder {
 	bld := *b
 	if bld.data == nil {
-		bld.data = make(map[string]interface{}, 10)
+		//nolint:mnd // Количество аргументов делится на 2, так как они представлены в виде пар ключ-значение.
+		bld.data = make(map[string]any, len(kv)/2)
 	}
 	for i := 0; i < len(kv)-1; i += 2 {
 		k, _ := kv[i].(string)
@@ -63,7 +103,7 @@ func (b *Builder) WithKV(kv ...any) *Builder {
 	return &bld
 }
 
-func (b *Builder) GetData() map[string]interface{} {
+func (b *Builder) GetData() map[string]any {
 	return b.data
 }
 
@@ -84,69 +124,68 @@ func (b *Builder) Context() context.Context {
 	return b.ctx
 }
 
-func (b *Builder) Template() *template.Template {
-	return template.New("main").Funcs(b.getFuncs())
+func (b *Builder) Template(text string) (Template, error) {
+	return b.template(text)
 }
 
-func (b *Builder) Execute(str string, data ...any) (string, error) {
-	t := b.Template()
-	buf := new(bytes.Buffer)
-	t, err := t.Parse(str)
+func (b *Builder) Execute(templ string, data ...any) (string, error) {
+	t, err := b.Template(templ)
 	if err != nil {
 		return "", err
 	}
+	buf := new(bytes.Buffer)
 	if err = t.Execute(buf, b.getData(data...)); err != nil {
 		return "", err
 	}
 	return buf.String(), nil
 }
 
-func (b *Builder) ExecuteList(str []string, data ...any) ([]string, error) {
-	t := b.Template()
-	result := make([]string, len(str))
-	buffer := new(bytes.Buffer)
-	for i, tmpl := range str {
-		if tmpl == "" {
-			continue
-		}
-		t, err := t.Parse(tmpl)
+func (b *Builder) ExecuteList(templs []string, data ...any) ([]string, error) {
+	result := make([]string, len(templs))
+	buf := new(bytes.Buffer)
+	d := b.getData(data...)
+
+	for i, templ := range templs {
+		t, err := b.Template(templ)
 		if err != nil {
-			return []string{}, err
+			return nil, err
 		}
-		if err = t.Execute(buffer, b.getData(data...)); err != nil {
-			return []string{}, err
+
+		buf.Reset()
+		err = t.Execute(buf, d)
+		if err != nil {
+			return nil, err
 		}
-		result[i] = buffer.String()
-		buffer.Reset()
+
+		result[i] = buf.String()
 	}
+
 	return result, nil
 }
 
-func (b *Builder) ExecuteMap(str map[string]interface{}, data ...any) (map[string]interface{}, error) {
-	result := make(map[string]interface{}, len(str))
-	for k, v := range str {
+func (b *Builder) ExecuteMap(templMap map[string]any, data ...any) (map[string]any, error) {
+	result := make(map[string]any, len(templMap))
+	d := b.getData(data...)
+
+	for k, v := range templMap {
 		switch t := v.(type) {
 		case string:
-			value, err := b.Execute(t, data...)
+			value, err := b.Execute(t, d)
 			if err != nil {
 				return nil, err
 			}
-			v = value
+			result[k] = value
 		case []string:
-			values, err := b.ExecuteList(append([]string{k}, t...), data...)
+			var err error
+			result[k], err = b.ExecuteList(t, d)
 			if err != nil {
 				return nil, err
 			}
-			k = values[0]
-			vv := make([]interface{}, 0, len(t))
-			for _, val := range values[1:] {
-				vv = append(vv, val)
-			}
-			v = vv
+		default:
+			result[k] = v
 		}
-
-		result[k] = v
 	}
+
 	return result, nil
 }
 
@@ -155,25 +194,19 @@ func (b *Builder) getData(data ...any) any {
 		return b.data
 	}
 
-	var res map[string]interface{}
+	res := maps.Clone(b.data)
+	if res == nil {
+		res = make(map[string]any)
+	}
 	for _, v := range data {
-		if m, ok := v.(map[string]interface{}); ok && b.data != nil {
-			res = mergeMaps(b.data, m)
+		if m, ok := v.(map[string]any); ok {
+			maps.Copy(res, m)
 		}
 	}
-	if res != nil {
-		return res
-	}
 
-	return data[0]
-}
-
-func mergeMaps(in ...map[string]interface{}) map[string]interface{} {
-	out := make(map[string]interface{})
-	for _, i := range in {
-		for k, v := range i {
-			out[k] = v
-		}
+	if len(res) == 0 {
+		return data[0]
 	}
-	return out
+
+	return res
 }
diff --git a/template/builder_test.go b/template/builder_test.go
index cd6a0807567a8618005eb0acc37fbbef84864ee4..48fdb2cb0826c6d392eaed0cb725f7ee5a6651df 100644
--- a/template/builder_test.go
+++ b/template/builder_test.go
@@ -2,48 +2,89 @@ package template
 
 import (
 	"context"
-	"errors"
 	"testing"
 
-	"git.perx.ru/perxis/perxis-go/pkg/collections"
-	colsmocks "git.perx.ru/perxis/perxis-go/pkg/collections/mocks"
 	"git.perx.ru/perxis/perxis-go/pkg/content"
-	"git.perx.ru/perxis/perxis-go/pkg/environments"
-	envsmocks "git.perx.ru/perxis/perxis-go/pkg/environments/mocks"
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
 	"git.perx.ru/perxis/perxis-go/pkg/items"
 	mocksitems "git.perx.ru/perxis/perxis-go/pkg/items/mocks"
-	"git.perx.ru/perxis/perxis-go/pkg/spaces"
-	spsmocks "git.perx.ru/perxis/perxis-go/pkg/spaces/mocks"
 	"github.com/stretchr/testify/assert"
 )
 
 func TestBuilder_Execute(t *testing.T) {
 	tests := []struct {
-		name    string
-		SpaceID string
-		EnvID   string
-		CollID  string
-		str     string
-		data    any
-		want    any
-		wantErr bool
+		name        string
+		SpaceID     string
+		EnvID       string
+		CollID      string
+		template    string
+		data        any
+		want        any
+		htmlBuilder bool
+		wantErr     bool
 
 		getCnt func(t *testing.T) *content.Content
 	}{
-		{name: "error", str: "hello {{ .a }}", data: "world", want: "", wantErr: true},
-		{name: "empty", str: "", data: "", want: "", wantErr: false},
-		{name: "#1", str: "hello {{ . }}", data: "world", want: "hello world", wantErr: false},
-		{name: "#2", str: "{{ . }}", data: "world", want: "world", wantErr: false},
-		{name: "#3 ", str: "", data: "world", want: "", wantErr: false},
-		{name: "#4 ", str: "hello", data: "world", want: "hello", wantErr: false},
 		{
-			name:    "lookup",
-			SpaceID: "space",
-			EnvID:   "env",
-			str:     "hello, {{ lookup \"secrets.dev.key\" }}",
-			data:    "",
-			want:    "hello, Luk",
-			wantErr: false,
+			name:     "invalid data",
+			template: "hello {{ .a }}",
+			data:     "world",
+			want:     "",
+			wantErr:  true,
+		},
+		{
+			name:     "invalid template",
+			template: "hello {{ . ",
+			data:     "world",
+			want:     "",
+			wantErr:  true,
+		},
+		{
+			name:     "empty template",
+			template: "",
+			data:     "world",
+			want:     "",
+			wantErr:  false,
+		},
+		{
+			name:     "empty data",
+			template: "{{ . }}",
+			data:     "",
+			want:     "",
+			wantErr:  false,
+		},
+		{
+			name:     "success",
+			template: "hello {{ . }}",
+			data:     "world",
+			want:     "hello world",
+			wantErr:  false,
+		},
+		{
+			name:     "success with html builder",
+			template: "<span class=\"comment\">{{ . }}</span>",
+			data:     "<script>alert(localStorage.getItem('secret'))</script>",
+			want: "<span class=\"comment\">" +
+				"&lt;script&gt;alert(localStorage.getItem(&#39;secret&#39;))&lt;/script&gt;" +
+				"</span>",
+			htmlBuilder: true,
+			wantErr:     false,
+		},
+		{
+			name:     "template as plain text",
+			template: "hello",
+			data:     "world",
+			want:     "hello",
+			wantErr:  false,
+		},
+		{
+			name:     "lookup function",
+			SpaceID:  "space",
+			EnvID:    "env",
+			template: "hello, {{ lookup \"secrets.dev.key\" }}",
+			data:     "",
+			want:     "hello, Luk",
+			wantErr:  false,
 			getCnt: func(t *testing.T) *content.Content {
 				itemsSvc := mocksitems.NewItems(t)
 				itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").
@@ -52,7 +93,7 @@ func TestBuilder_Execute(t *testing.T) {
 						SpaceID:      "space",
 						EnvID:        "env",
 						CollectionID: "secrets",
-						Data: map[string]interface{}{
+						Data: map[string]any{
 							"id":  "dev",
 							"key": "Luk",
 						},
@@ -61,13 +102,13 @@ func TestBuilder_Execute(t *testing.T) {
 			},
 		},
 		{
-			name:    "lookup with slice",
-			SpaceID: "space",
-			EnvID:   "env",
-			str:     "numbers {{ lookup \"secrets.dev.slice\" }}",
-			data:    "",
-			want:    "numbers [1 2 3]",
-			wantErr: false,
+			name:     "lookup function with slice",
+			SpaceID:  "space",
+			EnvID:    "env",
+			template: "numbers {{ lookup \"secrets.dev.slice\" }}",
+			data:     "",
+			want:     "numbers [1 2 3]",
+			wantErr:  false,
 			getCnt: func(t *testing.T) *content.Content {
 				itemsSvc := mocksitems.NewItems(t)
 				itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").
@@ -76,7 +117,7 @@ func TestBuilder_Execute(t *testing.T) {
 						SpaceID:      "space",
 						EnvID:        "env",
 						CollectionID: "secrets",
-						Data: map[string]interface{}{
+						Data: map[string]any{
 							"id":    "dev",
 							"slice": []int{1, 2, 3},
 						},
@@ -85,34 +126,13 @@ func TestBuilder_Execute(t *testing.T) {
 			},
 		},
 		{
-			name:    "lookup with empty Data",
-			SpaceID: "space",
-			EnvID:   "env",
-			str:     "numbers {{ lookup \"secrets.dev.slice\" }}",
-			data:    "",
-			want:    "numbers <no value>",
-			wantErr: false,
-			getCnt: func(t *testing.T) *content.Content {
-				itemsSvc := mocksitems.NewItems(t)
-				itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").
-					Return(&items.Item{
-						ID:           "dev",
-						SpaceID:      "space",
-						EnvID:        "env",
-						CollectionID: "secrets",
-						Data:         map[string]interface{}{},
-					}, nil).Once()
-				return &content.Content{Items: itemsSvc}
-			},
-		},
-		{
-			name:    "lookup with incorrect field",
-			SpaceID: "space",
-			EnvID:   "env",
-			str:     "hello {{ lookup \"secrets.dev.incorrect\" }}",
-			data:    "",
-			want:    "hello <no value>",
-			wantErr: false,
+			name:     "lookup function with unknown field",
+			SpaceID:  "space",
+			EnvID:    "env",
+			template: "numbers {{ lookup \"secrets.dev.slice\" }}",
+			data:     "",
+			want:     "numbers <no value>",
+			wantErr:  false,
 			getCnt: func(t *testing.T) *content.Content {
 				itemsSvc := mocksitems.NewItems(t)
 				itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").
@@ -121,22 +141,19 @@ func TestBuilder_Execute(t *testing.T) {
 						SpaceID:      "space",
 						EnvID:        "env",
 						CollectionID: "secrets",
-						Data: map[string]interface{}{
-							"id":  "dev",
-							"key": "1234",
-						},
+						Data:         map[string]any{},
 					}, nil).Once()
 				return &content.Content{Items: itemsSvc}
 			},
 		},
 		{
-			name:    "lookup not found",
-			SpaceID: "space",
-			EnvID:   "env",
-			str:     "hello {{ lookup \"secrets.prod.pass\" }}",
-			data:    "",
-			want:    "",
-			wantErr: true,
+			name:     "lookup function not found item",
+			SpaceID:  "space",
+			EnvID:    "env",
+			template: "hello {{ lookup \"secrets.prod.pass\" }}",
+			data:     "",
+			want:     "",
+			wantErr:  true,
 			getCnt: func(t *testing.T) *content.Content {
 				itemsSvc := mocksitems.NewItems(t)
 				itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "prod").
@@ -144,34 +161,14 @@ func TestBuilder_Execute(t *testing.T) {
 				return &content.Content{Items: itemsSvc}
 			},
 		},
-		{name: "lookup without itemID", SpaceID: "space", EnvID: "env", str: "hello {{ lookup \"secrets.pass\" }}", data: "", want: "", wantErr: true},
-		{name: "system#1", SpaceID: "space", str: "hello {{ system.SpaceID }}", data: "", want: "hello space", wantErr: false},
-		{name: "system space", SpaceID: "space", getCnt: func(t *testing.T) *content.Content {
-			spsSvc := spsmocks.NewSpaces(t)
-			spsSvc.On("Get", context.Background(), "space").Return(&spaces.Space{Description: "description"}, nil).Once()
-			return &content.Content{Spaces: spsSvc}
-		}, str: "{{ system.Space.Description }}", want: "description", wantErr: false},
-		{name: "system environment", SpaceID: "space", EnvID: "env", getCnt: func(t *testing.T) *content.Content {
-			envsSvc := envsmocks.NewEnvironments(t)
-			envsSvc.On("Get", context.Background(), "space", "env").Return(&environments.Environment{Aliases: []string{"master"}}, nil).Once()
-			return &content.Content{Environments: envsSvc}
-		}, str: "{{ system.Environment.Aliases }}", want: "[master]", wantErr: false},
 		{
-			name:    "system collection",
-			SpaceID: "space",
-			EnvID:   "env",
-			CollID:  "col",
-			getCnt: func(t *testing.T) *content.Content {
-				collsSvc := colsmocks.NewCollections(t)
-				collsSvc.On("Get", context.Background(), "space", "env", "col").
-					Return(&collections.Collection{Name: "cars"}, nil).Once()
-				return &content.Content{Collections: collsSvc}
-			},
-			str:     "{{ system.Collection.Name }}",
-			want:    "cars",
-			wantErr: false,
+			name:     "system function",
+			SpaceID:  "space",
+			template: "hello {{ system.SpaceID }}",
+			data:     "",
+			want:     "hello space",
+			wantErr:  false,
 		},
-		{name: "system without account", SpaceID: "space", str: "hello {{ system.Organization.Name }}", want: "", wantErr: true},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -180,16 +177,14 @@ func TestBuilder_Execute(t *testing.T) {
 				cnt = tt.getCnt(t)
 			}
 
-			b := &Builder{
-				ctx:     context.Background(),
-				cnt:     cnt,
-				SpaceID: tt.SpaceID,
-				EnvID:   tt.EnvID,
-				CollID:  tt.CollID,
+			var b *Builder
+			if tt.htmlBuilder {
+				b = NewHTMLBuilder(cnt, tt.SpaceID, tt.EnvID, tt.CollID)
+			} else {
+				b = NewBuilder(cnt, tt.SpaceID, tt.EnvID, tt.CollID)
 			}
-
-			got, err := b.Execute(tt.str, tt.data)
-			if tt.wantErr == true {
+			got, err := b.Execute(tt.template, tt.data)
+			if tt.wantErr {
 				assert.Error(t, err)
 			} else {
 				assert.NoError(t, err)
@@ -201,67 +196,110 @@ func TestBuilder_Execute(t *testing.T) {
 
 func TestBuilder_ExecuteList(t *testing.T) {
 	tests := []struct {
-		name    string
-		SpaceID string
-		EnvID   string
-		str     []string
-		data    any
-		want    []string
-		wantErr bool
+		name        string
+		templs      []string
+		data        any
+		want        []string
+		htmlBuilder bool
+		assertError assert.ErrorAssertionFunc
 
-		itemsCall func(itemsSvc *mocksitems.Items)
+		getCnt func(t *testing.T) *content.Content
 	}{
-		{name: "error", SpaceID: "space", EnvID: "env", str: []string{"hello { . }}", "go {{ . }"}, data: "world", want: []string{}, wantErr: true},
-		{name: "empty", SpaceID: "space", EnvID: "env", str: []string{""}, data: "world", want: []string{""}, wantErr: false},
-		{name: "#1", SpaceID: "space", EnvID: "env", str: []string{"hello {{ . }}", "go {{ . }}"}, data: "world", want: []string{"hello world", "go world"}, wantErr: false},
-		{name: "#2", SpaceID: "space", EnvID: "env", str: []string{"{{ . }}"}, data: "world", want: []string{"world"}, wantErr: false},
-		{name: "#3 ", SpaceID: "space", EnvID: "env", str: []string{""}, data: "world", want: []string{""}, wantErr: false},
-		{name: "#4 ", SpaceID: "space", EnvID: "env", str: []string{"hello"}, data: "world", want: []string{"hello"}, wantErr: false},
-		{name: "lookup", SpaceID: "space", EnvID: "env", str: []string{"hello {{ lookup \"secrets.dev.key\" }}"}, data: "", want: []string{"hello 1234"}, wantErr: false, itemsCall: func(itemsSvc *mocksitems.Items) {
-			itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").Return(&items.Item{
-				ID:           "dev",
-				SpaceID:      "space",
-				EnvID:        "env",
-				CollectionID: "secrets",
-				Data: map[string]interface{}{
-					"id":  "dev",
-					"key": "1234",
-				},
-			}, nil).Once()
-		}},
-		{name: "lookup with incorrect field", SpaceID: "space", EnvID: "env", str: []string{"hello {{ lookup \"secrets.dev.incorrect\" }}"}, data: "", want: []string{"hello <no value>"}, wantErr: false, itemsCall: func(itemsSvc *mocksitems.Items) {
-			itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").Return(&items.Item{
-				ID:           "dev",
-				SpaceID:      "space",
-				EnvID:        "env",
-				CollectionID: "secrets",
-				Data: map[string]interface{}{
-					"id":  "dev",
-					"key": "1234",
-				},
-			}, nil).Once()
-		}},
-		{name: "system ", SpaceID: "space", EnvID: "env", str: []string{"hello {{ system.SpaceID }}"}, data: "", want: []string{"hello space"}, wantErr: false},
+		{
+			name: "invalid data",
+			templs: []string{
+				"hello {{ .a }}",
+			},
+			data:        "world",
+			want:        nil,
+			assertError: assert.Error,
+		},
+		{
+			name: "invalid template",
+			templs: []string{
+				"hello {{ . ",
+			},
+			data:        "world",
+			want:        nil,
+			assertError: assert.Error,
+		},
+		{
+			name: "empty template",
+			templs: []string{
+				"",
+			},
+			data: "world",
+			want: []string{
+				"",
+			},
+			assertError: assert.NoError,
+		},
+		{
+			name: "empty data",
+			templs: []string{
+				"{{ . }}",
+			},
+			data: "",
+			want: []string{
+				"",
+			},
+			assertError: assert.NoError,
+		},
+		{
+			name: "success",
+			templs: []string{
+				"hello {{ . }}",
+				"world {{ . }}",
+			},
+			data: "world",
+			want: []string{
+				"hello world",
+				"world world",
+			},
+			assertError: assert.NoError,
+		},
+		{
+			name: "success with html builder",
+			templs: []string{
+				"<span class=\"comment\">{{ . }}</span>",
+			},
+			data: "<script>alert(localStorage.getItem('secret'))</script>",
+			want: []string{
+				"<span class=\"comment\">" +
+					"&lt;script&gt;alert(localStorage.getItem(&#39;secret&#39;))&lt;/script&gt;" +
+					"</span>",
+			},
+			htmlBuilder: true,
+			assertError: assert.NoError,
+		},
+		{
+			name: "template as plain text",
+			templs: []string{
+				"hello",
+			},
+			data: "world",
+			want: []string{
+				"hello",
+			},
+			assertError: assert.NoError,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			itemsSvc := mocksitems.NewItems(t)
-			if tt.itemsCall != nil {
-				tt.itemsCall(itemsSvc)
-			}
-			b := &Builder{
-				ctx:     context.Background(),
-				cnt:     &content.Content{Items: itemsSvc},
-				SpaceID: tt.SpaceID,
-				EnvID:   tt.EnvID,
+			var cnt *content.Content
+			if tt.getCnt != nil {
+				cnt = tt.getCnt(t)
 			}
 
-			got, err := b.ExecuteList(tt.str, tt.data)
-			if tt.wantErr == true {
-				assert.Error(t, err)
+			var b *Builder
+			if tt.htmlBuilder {
+				b = NewHTMLBuilder(cnt, "space_id", "env_id", "coll_id")
 			} else {
-				assert.NoError(t, err)
+				b = NewBuilder(cnt, "space_id", "env_id", "coll_id")
 			}
+
+			got, err := b.ExecuteList(tt.templs, tt.data)
+			tt.assertError(t, err)
 			assert.Equal(t, tt.want, got)
 		})
 	}
@@ -269,68 +307,276 @@ func TestBuilder_ExecuteList(t *testing.T) {
 
 func TestBuilder_ExecuteMap(t *testing.T) {
 	tests := []struct {
-		name    string
-		SpaceID string
-		EnvID   string
-		str     map[string]interface{}
-		data    any
-		want    map[string]interface{}
-		wantErr bool
+		name        string
+		SpaceID     string
+		EnvID       string
+		templateMap map[string]any
+		data        any
+		want        map[string]any
+		wantErr     bool
 
 		itemsCall func(itemsSvc *mocksitems.Items)
 	}{
-		{name: "error", SpaceID: "space", EnvID: "env", str: map[string]interface{}{"hello": "{{ . }"}, data: "world", want: nil, wantErr: true},
-		{name: "empty", SpaceID: "space", EnvID: "env", str: map[string]interface{}{}, data: "", want: map[string]interface{}{}, wantErr: false},
-		{name: "#1", SpaceID: "space", EnvID: "env", str: map[string]interface{}{"hello": "{{ . }}"}, data: "world", want: map[string]interface{}{"hello": "world"}, wantErr: false},
-		{name: "#2", SpaceID: "space", EnvID: "env", str: map[string]interface{}{"hello": "{{ . }}", "go": "{{ . }}"}, data: "world", want: map[string]interface{}{"hello": "world", "go": "world"}, wantErr: false},
-		{name: "#3 ", SpaceID: "space", EnvID: "env", str: map[string]interface{}{}, data: "world", want: map[string]interface{}{}, wantErr: false},
-		{name: "#4 ", SpaceID: "space", EnvID: "env", str: map[string]interface{}{"a": "b"}, data: "world", want: map[string]interface{}{"a": "b"}, wantErr: false},
-		{name: "lookup ", SpaceID: "space", EnvID: "env", str: map[string]interface{}{"hello": "{{ lookup \"secrets.dev.key\" }}"}, data: "", want: map[string]interface{}{"hello": "1234"}, wantErr: false, itemsCall: func(itemsSvc *mocksitems.Items) {
-			itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").Return(&items.Item{
-				ID:           "dev",
-				SpaceID:      "space",
-				EnvID:        "env",
-				CollectionID: "secrets",
-				Data: map[string]interface{}{
-					"id":  "dev",
-					"key": "1234",
+		{
+			name:        "invalid template",
+			SpaceID:     "space",
+			EnvID:       "env",
+			templateMap: map[string]any{"hello": "{{ . }"},
+			data:        "world",
+			want:        nil,
+			wantErr:     true,
+		},
+		{
+			name:        "empty data",
+			SpaceID:     "space",
+			EnvID:       "env",
+			templateMap: map[string]any{},
+			data:        "",
+			want:        map[string]any{},
+			wantErr:     false,
+		},
+		{
+			name:    "one template",
+			SpaceID: "space",
+			EnvID:   "env",
+			templateMap: map[string]any{
+				"hello": "{{ . }}",
+			},
+			data: "world",
+			want: map[string]any{
+				"hello": "world",
+			},
+			wantErr: false,
+		},
+		{
+			name:    "one template with many value templates",
+			SpaceID: "space",
+			EnvID:   "env",
+			templateMap: map[string]any{
+				"hello": []string{
+					"{{ .foo }}",
+					"{{ .bar }}",
+				},
+			},
+			data: map[string]any{
+				"foo": "hello",
+				"bar": "world",
+			},
+			want: map[string]any{
+				"hello": []string{
+					"hello",
+					"world",
+				},
+			},
+			wantErr: false,
+		},
+		{
+			name:    "many template",
+			SpaceID: "space",
+			EnvID:   "env",
+			templateMap: map[string]any{
+				"hello": "{{ . }}",
+				"go":    "{{ . }}",
+			},
+			data: "world",
+			want: map[string]any{
+				"hello": "world",
+				"go":    "world",
+			},
+			wantErr: false,
+		},
+		{
+			name:    "template in key with one value",
+			SpaceID: "space",
+			EnvID:   "env",
+			templateMap: map[string]any{
+				"{{ .key }}": "value",
+			},
+			data: map[string]any{
+				"key": "foo",
+			},
+			want: map[string]any{
+				"{{ .key }}": "value",
+			},
+			wantErr: false,
+		},
+		{
+			name:    "template in key with many values",
+			SpaceID: "space",
+			EnvID:   "env",
+			templateMap: map[string]any{
+				"{{ .key }}": []string{
+					"value",
 				},
-			}, nil).Once()
-		}},
-		{name: "lookup with incorrect field", SpaceID: "space", EnvID: "env", str: map[string]interface{}{"hello": "{{ lookup \"secrets.dev.incorrect\" }}"}, data: "", want: map[string]interface{}{"hello": "<no value>"}, wantErr: false, itemsCall: func(itemsSvc *mocksitems.Items) {
-			itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").Return(&items.Item{
-				ID:           "dev",
-				SpaceID:      "space",
-				EnvID:        "env",
-				CollectionID: "secrets",
-				Data: map[string]interface{}{
-					"id":  "dev",
-					"key": "1234",
+			},
+			data: map[string]any{
+				"key": "foo",
+			},
+			want: map[string]any{
+				"{{ .key }}": []string{
+					"value",
 				},
-			}, nil).Once()
-		}},
-		{name: "system ", SpaceID: "space", EnvID: "env", str: map[string]interface{}{"hello": "{{ system.SpaceID }}"}, data: "", want: map[string]interface{}{"hello": "space"}, wantErr: false},
+			},
+			wantErr: false,
+		},
+		{
+			name:    "template as plain text",
+			SpaceID: "space",
+			EnvID:   "env",
+			templateMap: map[string]any{
+				"a": "b",
+			},
+			data: "world",
+			want: map[string]any{
+				"a": "b",
+			},
+			wantErr: false,
+		},
+		{
+			name:    "template is not template",
+			SpaceID: "space",
+			EnvID:   "env",
+			templateMap: map[string]any{
+				"a": "b",
+				"b": []int{1, 2, 3},
+			},
+			data: "world",
+			want: map[string]any{
+				"a": "b",
+				"b": []int{1, 2, 3},
+			},
+			wantErr: false,
+		},
+		{
+			name:    "lookup function",
+			SpaceID: "space",
+			EnvID:   "env",
+			templateMap: map[string]any{
+				"hello": "{{ lookup \"secrets.dev.key\" }}",
+			},
+			want: map[string]any{
+				"hello": "1234",
+			},
+			wantErr: false,
+			itemsCall: func(itemsSvc *mocksitems.Items) {
+				itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").Return(&items.Item{
+					ID:           "dev",
+					SpaceID:      "space",
+					EnvID:        "env",
+					CollectionID: "secrets",
+					Data: map[string]any{
+						"id":  "dev",
+						"key": "1234",
+					},
+				}, nil).Once()
+			},
+		},
+		{
+			name:        "lookup function with unknown field",
+			SpaceID:     "space",
+			EnvID:       "env",
+			templateMap: map[string]any{"hello": "{{ lookup \"secrets.dev.incorrect\" }}"},
+			want:        map[string]any{"hello": "<no value>"},
+			wantErr:     false,
+			itemsCall: func(itemsSvc *mocksitems.Items) {
+				itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").Return(&items.Item{
+					ID:           "dev",
+					SpaceID:      "space",
+					EnvID:        "env",
+					CollectionID: "secrets",
+					Data: map[string]any{
+						"id":  "dev",
+						"key": "1234",
+					},
+				}, nil).Once()
+			},
+		},
+		{
+			name:    "system function",
+			SpaceID: "space",
+			EnvID:   "env",
+			templateMap: map[string]any{
+				"hello": "{{ system.SpaceID }}",
+			},
+			want: map[string]any{
+				"hello": "space",
+			},
+			wantErr: false,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			itemsSvc := mocksitems.NewItems(t)
+			itemsSvc := &mocksitems.Items{}
 			if tt.itemsCall != nil {
 				tt.itemsCall(itemsSvc)
 			}
-			b := &Builder{
-				ctx:     context.Background(),
-				cnt:     &content.Content{Items: itemsSvc},
-				SpaceID: tt.SpaceID,
-				EnvID:   tt.EnvID,
-			}
+			b := NewBuilder(&content.Content{Items: itemsSvc}, tt.SpaceID, tt.EnvID, "")
 
-			got, err := b.ExecuteMap(tt.str, tt.data)
+			got, err := b.ExecuteMap(tt.templateMap, tt.data)
 			if tt.wantErr == true {
 				assert.Error(t, err)
 			} else {
 				assert.NoError(t, err)
 			}
 			assert.Equal(t, tt.want, got)
+			itemsSvc.AssertExpectations(t)
 		})
 	}
 }
+
+func TestBuilder_getData(t *testing.T) {
+	t.Run("empty data", func(t *testing.T) {
+		builder := NewBuilder(nil, "space_id", "env_id", "coll_id")
+		assert.Equal(t,
+			map[string]any(nil),
+			builder.getData(),
+		)
+	})
+	t.Run("with data", func(t *testing.T) {
+		builder := NewBuilder(nil, "space_id", "env_id", "coll_id").
+			WithData(map[string]any{
+				"foo": "bar",
+			})
+		assert.Equal(t,
+			map[string]any{
+				"foo": "bar",
+			},
+			builder.getData(),
+		)
+	})
+	t.Run("merge data", func(t *testing.T) {
+		builder := NewBuilder(nil, "space_id", "env_id", "coll_id")
+		assert.Equal(t,
+			map[string]any{
+				"a": 1,
+				"b": 2,
+			},
+			builder.getData(
+				map[string]any{},
+				map[string]any{"a": 1},
+				map[string]any{"b": 2},
+				[]string{"a", "b"},
+				"string",
+			),
+		)
+	})
+	t.Run("merge with original data", func(t *testing.T) {
+		builder := NewBuilder(nil, "space_id", "env_id", "coll_id").
+			WithData(map[string]any{
+				"foo": "bar",
+				"baz": "qux",
+			})
+		assert.Equal(t,
+			map[string]any{
+				"a":   1,
+				"b":   2,
+				"foo": "bar",
+				"baz": "qux",
+			},
+			builder.getData(
+				map[string]any{"a": 1},
+				map[string]any{"b": 2},
+			),
+		)
+	})
+}
diff --git a/template/funcs.go b/template/funcs.go
index 0c320ad139e964f002b691ce097b24e70e6cfaf3..55eab67682f799ca671e1650ebc6a45546722c20 100644
--- a/template/funcs.go
+++ b/template/funcs.go
@@ -9,11 +9,11 @@ import (
 // getLookup возвращает функцию для шаблонизатора для получения значений из записи коллекции
 // name указывается в виде "<collection id>.<item id>.<field>"
 // Использование в шаблонах:  {{ lookup "secrets.key.value" }}
-func getLookup(b *Builder) any {
+func getLookup(b *Builder) func(string) (any, error) {
 	return func(name string) (any, error) {
 		parsedName := strings.Split(name, ".")
 		if len(parsedName) < 3 {
-			return "", errors.Errorf("incorrect parameter \"%s\"", name)
+			return nil, errors.Errorf("incorrect parameter \"%s\"", name)
 		}
 
 		collectionID := parsedName[0]
@@ -21,7 +21,7 @@ func getLookup(b *Builder) any {
 		field := parsedName[2]
 		item, err := b.cnt.Items.Get(b.Context(), b.SpaceID, b.EnvID, collectionID, itemID)
 		if err != nil {
-			return "", errors.Wrapf(err, "failed to get \"%s\"")
+			return nil, errors.Wrapf(err, "failed to get \"%s\"")
 		}
 
 		if len(item.Data) > 0 {
@@ -36,7 +36,7 @@ func getLookup(b *Builder) any {
 
 // getSys возвращает функцию получения System
 // Использование в шаблонах: {{ system.SpaceID }}
-func getSystem(b *Builder) any {
+func getSystem(b *Builder) func() *System {
 	return func() *System {
 		return &System{builder: b}
 	}
diff --git a/template/funcs_test.go b/template/funcs_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..af4a7717328d8d8de3f3426136dfb1d91f0ba135
--- /dev/null
+++ b/template/funcs_test.go
@@ -0,0 +1,267 @@
+package template
+
+import (
+	"testing"
+
+	"git.perx.ru/perxis/perxis-go/pkg/collections"
+	mockscolls "git.perx.ru/perxis/perxis-go/pkg/collections/mocks"
+	"git.perx.ru/perxis/perxis-go/pkg/content"
+	"git.perx.ru/perxis/perxis-go/pkg/environments"
+	mocksenvs "git.perx.ru/perxis/perxis-go/pkg/environments/mocks"
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"git.perx.ru/perxis/perxis-go/pkg/items"
+	mocksitems "git.perx.ru/perxis/perxis-go/pkg/items/mocks"
+	"git.perx.ru/perxis/perxis-go/pkg/spaces"
+	mocksspaces "git.perx.ru/perxis/perxis-go/pkg/spaces/mocks"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+	"github.com/stretchr/testify/require"
+)
+
+func Test_getLookup(t *testing.T) {
+	tests := []struct {
+		name        string
+		setup       func(svc *mocksitems.Items)
+		input       string
+		want        any
+		assertError assert.ErrorAssertionFunc
+	}{
+		{
+			name:        "empty input",
+			input:       "",
+			assertError: assert.Error,
+		},
+		{
+			name:        "invalid format",
+			input:       "coll.key",
+			assertError: assert.Error,
+		},
+		{
+			name: "success",
+			setup: func(svc *mocksitems.Items) {
+				svc.On("Get", mock.Anything, "space_id", "env_id", "coll", "item").
+					Return(&items.Item{
+						Data: map[string]any{
+							"key": "value",
+						},
+					}, nil).Once()
+			},
+			input:       "coll.item.key",
+			want:        "value",
+			assertError: assert.NoError,
+		},
+		{
+			name: "non-existing field",
+			setup: func(svc *mocksitems.Items) {
+				svc.On("Get", mock.Anything, "space_id", "env_id", "coll", "item").
+					Return(&items.Item{
+						Data: map[string]any{
+							"key": "value",
+						},
+					}, nil).Once()
+			},
+			input:       "coll.item.unknown",
+			want:        nil,
+			assertError: assert.NoError,
+		},
+		{
+			name: "existing field with empty value",
+			setup: func(svc *mocksitems.Items) {
+				svc.On("Get", mock.Anything, "space_id", "env_id", "coll", "item").
+					Return(&items.Item{
+						Data: map[string]any{
+							"key": "",
+						},
+					}, nil).Once()
+			},
+			input:       "coll.item.key",
+			want:        "",
+			assertError: assert.NoError,
+		},
+	}
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			svc := mocksitems.NewItems(t)
+			if tc.setup != nil {
+				tc.setup(svc)
+			}
+			b := NewBuilder(&content.Content{Items: svc}, "space_id", "env_id", "coll_id")
+
+			lookup := getLookup(b)
+			got, err := lookup(tc.input)
+			tc.assertError(t, err)
+			assert.Equal(t, tc.want, got)
+		})
+	}
+}
+
+func Test_getSystem(t *testing.T) {
+	t.Run("get space id", func(t *testing.T) {
+		spacesSvc := mocksspaces.NewSpaces(t)
+		envsSvc := mocksenvs.NewEnvironments(t)
+		collsSvc := mockscolls.NewCollections(t)
+		b := NewBuilder(&content.Content{
+			Spaces:       spacesSvc,
+			Environments: envsSvc,
+			Collections:  collsSvc,
+		}, "space_id", "env_id", "coll_id")
+		system := getSystem(b)
+		assert.Equal(t, "space_id", system().SpaceID())
+	})
+	t.Run("get environment id", func(t *testing.T) {
+		spacesSvc := mocksspaces.NewSpaces(t)
+		envsSvc := mocksenvs.NewEnvironments(t)
+		collsSvc := mockscolls.NewCollections(t)
+		b := NewBuilder(&content.Content{
+			Spaces:       spacesSvc,
+			Environments: envsSvc,
+			Collections:  collsSvc,
+		}, "space_id", "env_id", "coll_id")
+		system := getSystem(b)
+		assert.Equal(t, "env_id", system().EnvID())
+	})
+	t.Run("get collection id", func(t *testing.T) {
+		spacesSvc := mocksspaces.NewSpaces(t)
+		envsSvc := mocksenvs.NewEnvironments(t)
+		collsSvc := mockscolls.NewCollections(t)
+		b := NewBuilder(&content.Content{
+			Spaces:       spacesSvc,
+			Environments: envsSvc,
+			Collections:  collsSvc,
+		}, "space_id", "env_id", "coll_id")
+		system := getSystem(b)
+		assert.Equal(t, "coll_id", system().CollectionID())
+	})
+	t.Run("get space", func(t *testing.T) {
+		t.Run("success", func(t *testing.T) {
+			spacesSvc := mocksspaces.NewSpaces(t)
+			spacesSvc.On("Get", mock.Anything, "space_id").
+				Return(&spaces.Space{
+					ID:    "space_id",
+					OrgID: "org_id",
+					Name:  "Space",
+				}, nil).
+				Once()
+			envsSvc := mocksenvs.NewEnvironments(t)
+			collsSvc := mockscolls.NewCollections(t)
+			b := NewBuilder(&content.Content{
+				Spaces:       spacesSvc,
+				Environments: envsSvc,
+				Collections:  collsSvc,
+			}, "space_id", "env_id", "coll_id")
+			system := getSystem(b)
+			got, err := system().Space()
+			require.NoError(t, err)
+			assert.Equal(t, &spaces.Space{
+				ID:    "space_id",
+				OrgID: "org_id",
+				Name:  "Space",
+			}, got)
+		})
+		t.Run("failure", func(t *testing.T) {
+			spacesSvc := mocksspaces.NewSpaces(t)
+			spacesSvc.On("Get", mock.Anything, "space_id").
+				Return(nil, errors.New("some error")).
+				Once()
+			envsSvc := mocksenvs.NewEnvironments(t)
+			collsSvc := mockscolls.NewCollections(t)
+			b := NewBuilder(&content.Content{
+				Spaces:       spacesSvc,
+				Environments: envsSvc,
+				Collections:  collsSvc,
+			}, "space_id", "env_id", "coll_id")
+			system := getSystem(b)
+			got, err := system().Space()
+			require.Error(t, err)
+			assert.Nil(t, got)
+		})
+	})
+	t.Run("get environment", func(t *testing.T) {
+		t.Run("success", func(t *testing.T) {
+			spacesSvc := mocksspaces.NewSpaces(t)
+			envsSvc := mocksenvs.NewEnvironments(t)
+			envsSvc.On("Get", mock.Anything, "space_id", "env_id").
+				Return(&environments.Environment{
+					ID:      "env_id",
+					SpaceID: "space_id",
+				}, nil).
+				Once()
+			collsSvc := mockscolls.NewCollections(t)
+			b := NewBuilder(&content.Content{
+				Spaces:       spacesSvc,
+				Environments: envsSvc,
+				Collections:  collsSvc,
+			}, "space_id", "env_id", "coll_id")
+			system := getSystem(b)
+			got, err := system().Environment()
+			require.NoError(t, err)
+			assert.Equal(t, &environments.Environment{
+				ID:      "env_id",
+				SpaceID: "space_id",
+			}, got)
+		})
+		t.Run("failure", func(t *testing.T) {
+			spacesSvc := mocksspaces.NewSpaces(t)
+			envsSvc := mocksenvs.NewEnvironments(t)
+			envsSvc.On("Get", mock.Anything, "space_id", "env_id").
+				Return(nil, errors.New("some error")).
+				Once()
+			collsSvc := mockscolls.NewCollections(t)
+			b := NewBuilder(&content.Content{
+				Spaces:       spacesSvc,
+				Environments: envsSvc,
+				Collections:  collsSvc,
+			}, "space_id", "env_id", "coll_id")
+			system := getSystem(b)
+			got, err := system().Environment()
+			require.Error(t, err)
+			assert.Nil(t, got)
+		})
+	})
+	t.Run("get collection", func(t *testing.T) {
+		t.Run("success", func(t *testing.T) {
+			spacesSvc := mocksspaces.NewSpaces(t)
+			envsSvc := mocksenvs.NewEnvironments(t)
+			collsSvc := mockscolls.NewCollections(t)
+			collsSvc.On("Get", mock.Anything, "space_id", "env_id", "coll_id").
+				Return(&collections.Collection{
+					ID:      "coll_id",
+					SpaceID: "space_id",
+					EnvID:   "env_id",
+					Name:    "Collection",
+				}, nil).
+				Once()
+			b := NewBuilder(&content.Content{
+				Spaces:       spacesSvc,
+				Environments: envsSvc,
+				Collections:  collsSvc,
+			}, "space_id", "env_id", "coll_id")
+			system := getSystem(b)
+			got, err := system().Collection()
+			require.NoError(t, err)
+			assert.Equal(t, &collections.Collection{
+				ID:      "coll_id",
+				SpaceID: "space_id",
+				EnvID:   "env_id",
+				Name:    "Collection",
+			}, got)
+		})
+		t.Run("failure", func(t *testing.T) {
+			spacesSvc := mocksspaces.NewSpaces(t)
+			envsSvc := mocksenvs.NewEnvironments(t)
+			collsSvc := mockscolls.NewCollections(t)
+			collsSvc.On("Get", mock.Anything, "space_id", "env_id", "coll_id").
+				Return(nil, errors.New("some error")).
+				Once()
+			b := NewBuilder(&content.Content{
+				Spaces:       spacesSvc,
+				Environments: envsSvc,
+				Collections:  collsSvc,
+			}, "space_id", "env_id", "coll_id")
+			system := getSystem(b)
+			got, err := system().Collection()
+			require.Error(t, err)
+			assert.Nil(t, got)
+		})
+	})
+}