diff --git a/id/client.go b/id/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..56d0e2b283f82ea65c6e1cd892f06378b0346bb8
--- /dev/null
+++ b/id/client.go
@@ -0,0 +1,77 @@
+package id
+
+import (
+	jsoniter "github.com/json-iterator/go"
+)
+
+const (
+	Client        = "client"
+	ClientsPrefix = "clients"
+)
+
+type ClientID struct {
+	SpaceID
+	ClientID string `json:"client_id"`
+}
+
+func (t *ClientID) Type() string { return Client }
+
+func (t *ClientID) String() string {
+	return Join(t.SpaceID.String(), ClientsPrefix, t.ClientID)
+
+}
+
+func (t *ClientID) ToMap() map[string]any {
+	m := t.SpaceID.ToMap()
+	m["client_id"] = t.ClientID
+	return m
+}
+
+func (t *ClientID) FromMap(m map[string]any) error {
+	if err := t.SpaceID.FromMap(m); err != nil {
+		return err
+	}
+	t.ClientID = m["client_id"].(string)
+	return nil
+}
+
+func (t *ClientID) Validate() error {
+	if t.ClientID == "" {
+		return ErrInvalidID
+	}
+
+	return t.SpaceID.Validate()
+}
+
+func parseClientID(parts []string) (*ClientID, error) {
+	if len(parts) != 4 || parts[2] != ClientsPrefix {
+		return nil, ErrInvalidID
+	}
+
+	spaceID, err := parseSpaceID(parts[:2])
+	if err != nil {
+		return nil, err
+	}
+
+	var id ClientID
+	id.SpaceID = *spaceID
+	id.ClientID = parts[3]
+	return &id, nil
+}
+func (t *ClientID) UnmarshalJSON(b []byte) error {
+	var data string
+	if err := jsoniter.Unmarshal(b, &data); err != nil {
+		return err
+	}
+	knownID, err := Parse(data)
+	if err != nil {
+		return err
+	}
+	t.SpaceID = knownID.(*ClientID).SpaceID
+	t.ClientID = knownID.(*ClientID).ClientID
+	return nil
+}
+
+func (t *ClientID) MarshalJSON() ([]byte, error) {
+	return []byte(t.String()), nil
+}
diff --git a/id/collection.go b/id/collection.go
index 50bf64ee6b7c02cd5629120ccf288155d85877a8..029735ba377fa3996f0ba072be793992f6a803d8 100644
--- a/id/collection.go
+++ b/id/collection.go
@@ -1,5 +1,9 @@
 package id
 
+import (
+	jsoniter "github.com/json-iterator/go"
+)
+
 const (
 	Collection        = "collection"
 	CollectionsPrefix = "cols"
@@ -18,7 +22,7 @@ func (t *CollectionID) String() string {
 
 func (t *CollectionID) ToMap() map[string]any {
 	m := t.EnvironmentID.ToMap()
-	m["col_id"] = t.EnvironmentID
+	m["col_id"] = t.CollectionID
 	return m
 }
 
@@ -39,7 +43,7 @@ func (t *CollectionID) Validate() error {
 }
 
 func parseCollectionID(parts []string) (*CollectionID, error) {
-	if len(parts) != 6 || parts[0] != EnvironmentsPrefix {
+	if len(parts) != 6 || parts[4] != CollectionsPrefix {
 		return nil, ErrInvalidID
 	}
 
@@ -53,3 +57,21 @@ func parseCollectionID(parts []string) (*CollectionID, error) {
 	id.EnvironmentID = *envID
 	return &id, nil
 }
+
+func (t *CollectionID) UnmarshalJSON(b []byte) error {
+	var data string
+	if err := jsoniter.Unmarshal(b, &data); err != nil {
+		return err
+	}
+	knownID, err := Parse(data)
+	if err != nil {
+		return err
+	}
+	t.EnvironmentID = knownID.(*CollectionID).EnvironmentID
+	t.CollectionID = knownID.(*CollectionID).CollectionID
+	return nil
+}
+
+func (t *CollectionID) MarshalJSON() ([]byte, error) {
+	return []byte(t.String()), nil
+}
diff --git a/id/environment.go b/id/environment.go
index 4958a33f73b4e2c8d8edbb76bda7b1d7705cfbd2..b8b08e83012208e217f9b33d54f200fe2d3d6ff1 100644
--- a/id/environment.go
+++ b/id/environment.go
@@ -1,5 +1,9 @@
 package id
 
+import (
+	jsoniter "github.com/json-iterator/go"
+)
+
 const (
 	Environment        = "environment"
 	EnvironmentsPrefix = "envs"
@@ -13,9 +17,7 @@ type EnvironmentID struct {
 func (t *EnvironmentID) Type() string { return Environment }
 
 func (t *EnvironmentID) String() string {
-	return Join(t.SpaceID.String(),
-		EnvironmentsPrefix, t.EnvironmentID,
-	)
+	return Join(t.SpaceID.String(), EnvironmentsPrefix, t.EnvironmentID)
 
 }
 
@@ -56,3 +58,21 @@ func parseEnvironmentID(parts []string) (*EnvironmentID, error) {
 	id.SpaceID = *spaceID
 	return &id, nil
 }
+
+func (t *EnvironmentID) UnmarshalJSON(b []byte) error {
+	var data string
+	if err := jsoniter.Unmarshal(b, &data); err != nil {
+		return err
+	}
+	knownID, err := Parse(data)
+	if err != nil {
+		return err
+	}
+	t.SpaceID = knownID.(*EnvironmentID).SpaceID
+	t.EnvironmentID = knownID.(*EnvironmentID).EnvironmentID
+	return nil
+}
+
+func (t *EnvironmentID) MarshalJSON() ([]byte, error) {
+	return []byte(t.String()), nil
+}
diff --git a/id/field.go b/id/field.go
new file mode 100644
index 0000000000000000000000000000000000000000..d1539a2c8720809b9396c1dd5fdb2087f5a0cfab
--- /dev/null
+++ b/id/field.go
@@ -0,0 +1,78 @@
+package id
+
+import (
+	jsoniter "github.com/json-iterator/go"
+)
+
+const (
+	Field        = "field"
+	FieldsPrefix = "fields"
+)
+
+type FieldID struct {
+	ItemID
+	FieldName string `json:"field_name"`
+}
+
+func (t *FieldID) Type() string { return Field }
+
+func (t *FieldID) String() string {
+	return Join(t.ItemID.String(), FieldsPrefix, t.FieldName)
+
+}
+
+func (t *FieldID) ToMap() map[string]any {
+	m := t.ItemID.ToMap()
+	m["field_name"] = t.FieldName
+	return m
+}
+
+func (t *FieldID) FromMap(m map[string]any) error {
+	if err := t.ItemID.FromMap(m); err != nil {
+		return err
+	}
+	t.FieldName = m["field_name"].(string)
+	return nil
+}
+
+func (t *FieldID) Validate() error {
+	if t.FieldName == "" {
+		return ErrInvalidID
+	}
+
+	return t.ItemID.Validate()
+}
+
+func parseFieldID(parts []string) (*FieldID, error) {
+	if len(parts) != 10 || parts[8] != FieldsPrefix {
+		return nil, ErrInvalidID
+	}
+
+	itemID, err := parseItemID(parts[:8])
+	if err != nil {
+		return nil, err
+	}
+
+	var id FieldID
+	id.ItemID = *itemID
+	id.FieldName = parts[9]
+	return &id, nil
+}
+
+func (t *FieldID) UnmarshalJSON(b []byte) error {
+	var data string
+	if err := jsoniter.Unmarshal(b, &data); err != nil {
+		return err
+	}
+	knownID, err := Parse(data)
+	if err != nil {
+		return err
+	}
+	t.ItemID = knownID.(*FieldID).ItemID
+	t.FieldName = knownID.(*FieldID).FieldName
+	return nil
+}
+
+func (t *FieldID) MarshalJSON() ([]byte, error) {
+	return []byte(t.String()), nil
+}
diff --git a/id/id.go b/id/id.go
index a4ff8a5e16f7ef9959560b51b86f0d8f1a0e9137..88cad22a4e5a0a4b8363f07d08d5aa311b0d8173 100644
--- a/id/id.go
+++ b/id/id.go
@@ -1,13 +1,16 @@
 package id
 
 import (
-	"errors"
 	"strings"
+
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
 )
 
 const Separator = '/'
 
-var ErrInvalidID = errors.New("invalid id")
+var (
+	ErrInvalidID = errors.New("invalid id")
+)
 
 type ID interface {
 	String() string
@@ -20,9 +23,19 @@ type ID interface {
 func Parse(s string) (ID, error) {
 	parts := Split(s)
 
-	// TODO: Парсим строку, от коротких к длинным
+	if id, _ := parseServiceID(parts); id != nil {
+		return id, nil
+	}
+
+	if id, _ := parseUserID(parts); id != nil {
+		return id, nil
+	}
+
+	if id, _ := parseOrganizationID(parts); id != nil {
+		return id, nil
+	}
 
-	if id, err := parseSpaceID(parts); err != nil {
+	if id, _ := parseSpaceID(parts); id != nil {
 		return id, nil
 	}
 
@@ -30,10 +43,34 @@ func Parse(s string) (ID, error) {
 		return id, nil
 	}
 
+	if id, _ := parseClientID(parts); id != nil {
+		return id, nil
+	}
+
+	if id, _ := parseRoleID(parts); id != nil {
+		return id, nil
+	}
+
 	if id, _ := parseCollectionID(parts); id != nil {
 		return id, nil
 	}
 
+	if id, _ := parseSchemaID(parts); id != nil {
+		return id, nil
+	}
+
+	if id, _ := parseItemID(parts); id != nil {
+		return id, nil
+	}
+
+	if id, _ := parseRevisionID(parts); id != nil {
+		return id, nil
+	}
+
+	if id, _ := parseFieldID(parts); id != nil {
+		return id, nil
+	}
+
 	return nil, ErrInvalidID
 }
 
diff --git a/id/id_test.go b/id/id_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..181c7459b7b4580c4045ffed112e62d30a86496f
--- /dev/null
+++ b/id/id_test.go
@@ -0,0 +1,481 @@
+package id
+
+import (
+	"encoding/json"
+	"testing"
+
+	jsoniter "github.com/json-iterator/go"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func Test_ParseID(t *testing.T) {
+	tests := []struct {
+		name   string
+		id     string
+		result ID
+	}{
+		{
+			name:   Service,
+			id:     "/services/<service_id>",
+			result: &ServiceID{ServiceID: "<service_id>"},
+		},
+		{
+			name:   User,
+			id:     "/users/<user_id>",
+			result: &UserID{UserID: "<user_id>"},
+		},
+		{
+			name:   Organization,
+			id:     "/orgs/<org_id>",
+			result: &OrganizationID{OrganizationID: "<org_id>"},
+		},
+		{
+			name:   Space,
+			id:     "/spaces/<space_id>",
+			result: &SpaceID{SpaceID: "<space_id>"},
+		},
+		{
+			name: Client,
+			id:   "/spaces/<space_id>/clients/<client_id>",
+			result: &ClientID{
+				SpaceID:  SpaceID{SpaceID: "<space_id>"},
+				ClientID: "<client_id>",
+			},
+		},
+		{
+			name: Role,
+			id:   "/spaces/<space_id>/roles/<role_id>",
+			result: &RoleID{
+				SpaceID: SpaceID{SpaceID: "<space_id>"},
+				RoleID:  "<role_id>",
+			},
+		},
+		{
+			name: Environment,
+			id:   "/spaces/<space_id>/envs/<env_id>",
+			result: &EnvironmentID{
+				SpaceID:       SpaceID{SpaceID: "<space_id>"},
+				EnvironmentID: "<env_id>",
+			},
+		},
+		{
+			name: Collection,
+			id:   "/spaces/<space_id>/envs/<env_id>/cols/<collection_id>",
+			result: &CollectionID{
+				EnvironmentID: EnvironmentID{
+					SpaceID:       SpaceID{SpaceID: "<space_id>"},
+					EnvironmentID: "<env_id>",
+				},
+				CollectionID: "<collection_id>",
+			},
+		},
+		{
+			name: Schema,
+			id:   "/spaces/<space_id>/envs/<env_id>/schema/<collection_id>",
+			result: &SchemaID{
+				EnvironmentID: EnvironmentID{
+					SpaceID:       SpaceID{SpaceID: "<space_id>"},
+					EnvironmentID: "<env_id>",
+				},
+				SchemaID: "<collection_id>",
+			},
+		},
+		{
+			name: Item,
+			id:   "/spaces/<space_id>/envs/<env_id>/cols/<collection_id>/items/<item_id>",
+			result: &ItemID{
+				CollectionID: CollectionID{
+					EnvironmentID: EnvironmentID{
+						SpaceID:       SpaceID{SpaceID: "<space_id>"},
+						EnvironmentID: "<env_id>",
+					},
+					CollectionID: "<collection_id>",
+				},
+				ItemID: "<item_id>",
+			},
+		},
+		{
+			name: Revision,
+			id:   "/spaces/<space_id>/envs/<env_id>/cols/<collection_id>/items/<item_id>/revs/<rev_id>",
+			result: &RevisionID{
+				ItemID: ItemID{
+					CollectionID: CollectionID{
+						EnvironmentID: EnvironmentID{
+							SpaceID:       SpaceID{SpaceID: "<space_id>"},
+							EnvironmentID: "<env_id>",
+						},
+						CollectionID: "<collection_id>",
+					},
+					ItemID: "<item_id>",
+				},
+				RevisionID: "<rev_id>",
+			},
+		},
+		{
+			name: Field,
+			id:   "/spaces/<space_id>/envs/<env_id>/cols/<collection_id>/items/<item_id>/fields/<field_name>",
+			result: &FieldID{
+				ItemID: ItemID{
+					CollectionID: CollectionID{
+						EnvironmentID: EnvironmentID{
+							SpaceID:       SpaceID{SpaceID: "<space_id>"},
+							EnvironmentID: "<env_id>",
+						},
+						CollectionID: "<collection_id>",
+					},
+					ItemID: "<item_id>",
+				},
+				FieldName: "<field_name>",
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			id, _ := Parse(tt.id)
+			require.Equal(t, tt.result, id)
+			require.Equal(t, tt.id, id.String(), "проверяем корректность работы метода String, полученное ID должно совпадать с исходным")
+		})
+	}
+}
+
+func Test_Map(t *testing.T) {
+	tests := []struct {
+		name string
+		id   ID
+		from map[string]any
+		to   ID
+	}{
+		{
+			name: Service,
+			id:   &ServiceID{},
+			from: map[string]any{"service_id": "<service_id>"},
+			to:   &ServiceID{ServiceID: "<service_id>"},
+		},
+		{
+			name: User,
+			id:   &UserID{},
+			from: map[string]any{"user_id": "<user_id>"},
+			to:   &UserID{UserID: "<user_id>"},
+		},
+		{
+			name: Organization,
+			id:   &OrganizationID{},
+			from: map[string]any{"organization_id": "<org_id>"},
+			to:   &OrganizationID{OrganizationID: "<org_id>"},
+		},
+		{
+			name: Space,
+			id:   &SpaceID{},
+			from: map[string]any{"space_id": "<space_id>"},
+			to:   &SpaceID{SpaceID: "<space_id>"},
+		},
+		{
+			name: Client,
+			id:   &ClientID{},
+			from: map[string]any{"space_id": "<space_id>", "client_id": "<client_id>"},
+			to: &ClientID{
+				SpaceID:  SpaceID{SpaceID: "<space_id>"},
+				ClientID: "<client_id>",
+			},
+		},
+		{
+			name: Role,
+			id:   &RoleID{},
+			from: map[string]any{"space_id": "<space_id>", "role_id": "<role_id>"},
+			to: &RoleID{
+				SpaceID: SpaceID{SpaceID: "<space_id>"},
+				RoleID:  "<role_id>",
+			},
+		},
+		{
+			name: Environment,
+			id:   &EnvironmentID{},
+			from: map[string]any{"space_id": "<space_id>", "env_id": "<env_id>"},
+			to: &EnvironmentID{
+				SpaceID:       SpaceID{SpaceID: "<space_id>"},
+				EnvironmentID: "<env_id>",
+			},
+		},
+		{
+			name: Collection,
+			id:   &CollectionID{},
+			from: map[string]any{"space_id": "<space_id>", "env_id": "<env_id>", "col_id": "<collection_id>"},
+			to: &CollectionID{
+				EnvironmentID: EnvironmentID{
+					SpaceID:       SpaceID{SpaceID: "<space_id>"},
+					EnvironmentID: "<env_id>",
+				},
+				CollectionID: "<collection_id>",
+			},
+		},
+		{
+			name: Schema,
+			id:   &SchemaID{},
+			from: map[string]any{"space_id": "<space_id>", "env_id": "<env_id>", "schema_id": "<collection_id>"},
+			to: &SchemaID{
+				EnvironmentID: EnvironmentID{
+					SpaceID:       SpaceID{SpaceID: "<space_id>"},
+					EnvironmentID: "<env_id>",
+				},
+				SchemaID: "<collection_id>",
+			},
+		},
+		{
+			name: Item,
+			id:   &ItemID{},
+			from: map[string]any{"space_id": "<space_id>", "env_id": "<env_id>", "col_id": "<collection_id>", "item_id": "<item_id>"},
+			to: &ItemID{
+				CollectionID: CollectionID{
+					EnvironmentID: EnvironmentID{
+						SpaceID:       SpaceID{SpaceID: "<space_id>"},
+						EnvironmentID: "<env_id>",
+					},
+					CollectionID: "<collection_id>",
+				},
+				ItemID: "<item_id>",
+			},
+		},
+		{
+			name: Revision,
+			id:   &RevisionID{},
+			from: map[string]any{"space_id": "<space_id>", "env_id": "<env_id>", "col_id": "<collection_id>", "item_id": "<item_id>", "rev_id": "<rev_id>"},
+			to: &RevisionID{
+				ItemID: ItemID{
+					CollectionID: CollectionID{
+						EnvironmentID: EnvironmentID{
+							SpaceID:       SpaceID{SpaceID: "<space_id>"},
+							EnvironmentID: "<env_id>",
+						},
+						CollectionID: "<collection_id>",
+					},
+					ItemID: "<item_id>",
+				},
+				RevisionID: "<rev_id>",
+			},
+		},
+		{
+			name: Field,
+			id:   &FieldID{},
+			from: map[string]any{"space_id": "<space_id>", "env_id": "<env_id>", "col_id": "<collection_id>", "item_id": "<item_id>", "field_name": "<field_name>"},
+			to: &FieldID{
+				ItemID: ItemID{
+					CollectionID: CollectionID{
+						EnvironmentID: EnvironmentID{
+							SpaceID:       SpaceID{SpaceID: "<space_id>"},
+							EnvironmentID: "<env_id>",
+						},
+						CollectionID: "<collection_id>",
+					},
+					ItemID: "<item_id>",
+				},
+				FieldName: "<field_name>",
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			v := tt.id
+			require.NoError(t, v.FromMap(tt.from))
+			require.Equal(t, tt.to, v)
+			require.Equal(t, v.ToMap(), tt.from)
+		})
+	}
+}
+
+func Test_MarshalJSON(t *testing.T) {
+	tests := []struct {
+		name string
+		json string
+		id   json.Marshaler
+	}{
+		{
+			Organization,
+			"/orgs/<org_id>",
+			&OrganizationID{OrganizationID: "<org_id>"},
+		},
+		{
+			Service,
+			"/services/<service_id>",
+			&ServiceID{ServiceID: "<service_id>"},
+		},
+		{
+			Space,
+			"/spaces/<space_id>",
+			&SpaceID{SpaceID: "<space_id>"},
+		},
+		{
+			Environment,
+			"/spaces/<space_id>/envs/<env_id>",
+			&EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"},
+		},
+		{
+			Client,
+			"/spaces/<space_id>/clients/<client_id>",
+			&ClientID{SpaceID: SpaceID{SpaceID: "<space_id>"}, ClientID: "<client_id>"},
+		},
+		{
+			Role,
+			"/spaces/<space_id>/roles/<role_id>",
+			&RoleID{SpaceID: SpaceID{SpaceID: "<space_id>"}, RoleID: "<role_id>"},
+		},
+		{
+			Collection,
+			"/spaces/<space_id>/envs/<env_id>/cols/<coll_id>",
+			&CollectionID{EnvironmentID: EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, CollectionID: "<coll_id>"},
+		},
+		{
+			Schema,
+			"/spaces/<space_id>/envs/<env_id>/schema/<coll_id>",
+			&SchemaID{EnvironmentID: EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, SchemaID: "<coll_id>"},
+		},
+		{
+			Item,
+			"/spaces/<space_id>/envs/<env_id>/cols/<coll_id>/items/<item_id>",
+			&ItemID{CollectionID: CollectionID{EnvironmentID: EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, CollectionID: "<coll_id>"}, ItemID: "<item_id>"},
+		},
+		{
+			Revision,
+			"/spaces/<space_id>/envs/<env_id>/cols/<coll_id>/items/<item_id>/revs/<rev_id>",
+			&RevisionID{ItemID: ItemID{CollectionID: CollectionID{EnvironmentID: EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, CollectionID: "<coll_id>"}, ItemID: "<item_id>"}, RevisionID: "<rev_id>"},
+		},
+		{
+			Field,
+			"/spaces/<space_id>/envs/<env_id>/cols/<coll_id>/items/<item_id>/fields/<field_name>",
+			&FieldID{ItemID: ItemID{CollectionID: CollectionID{EnvironmentID: EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, CollectionID: "<coll_id>"}, ItemID: "<item_id>"}, FieldName: "<field_name>"},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			b, err := jsoniter.Marshal(tt.id)
+			require.NoError(t, err)
+			assert.Equal(t, tt.json, string(b))
+		})
+	}
+}
+
+func Test_UnmarshalJSON(t *testing.T) {
+	tests := []struct {
+		name string
+		json string
+		id   json.Unmarshaler
+	}{
+		{
+			Organization,
+			"\"/orgs/<org_id>\"",
+			&OrganizationID{OrganizationID: "<org_id>"},
+		},
+		{
+			Service,
+			"\"/services/<service_id>\"",
+			&ServiceID{ServiceID: "<service_id>"},
+		},
+		{
+			Space,
+			"\"/spaces/<space_id>\"",
+			&SpaceID{SpaceID: "<space_id>"},
+		},
+		{
+			Environment,
+			"\"/spaces/<space_id>/envs/<env_id>\"",
+			&EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"},
+		},
+		{
+			Client,
+			"\"/spaces/<space_id>/clients/<client_id>\"",
+			&ClientID{SpaceID: SpaceID{SpaceID: "<space_id>"}, ClientID: "<client_id>"},
+		},
+		{
+			Role,
+			"\"/spaces/<space_id>/roles/<role_id>\"",
+			&RoleID{SpaceID: SpaceID{SpaceID: "<space_id>"}, RoleID: "<role_id>"},
+		},
+		{
+			Collection,
+			"\"/spaces/<space_id>/envs/<env_id>/cols/<coll_id>\"",
+			&CollectionID{EnvironmentID: EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, CollectionID: "<coll_id>"},
+		},
+		{
+			Schema,
+			"\"/spaces/<space_id>/envs/<env_id>/schema/<coll_id>\"",
+			&SchemaID{EnvironmentID: EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, SchemaID: "<coll_id>"},
+		},
+		{
+			Item,
+			"\"/spaces/<space_id>/envs/<env_id>/cols/<coll_id>/items/<item_id>\"",
+			&ItemID{CollectionID: CollectionID{EnvironmentID: EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, CollectionID: "<coll_id>"}, ItemID: "<item_id>"},
+		},
+		{
+			Revision,
+			"\"/spaces/<space_id>/envs/<env_id>/cols/<coll_id>/items/<item_id>/revs/<rev_id>\"",
+			&RevisionID{ItemID: ItemID{CollectionID: CollectionID{EnvironmentID: EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, CollectionID: "<coll_id>"}, ItemID: "<item_id>"}, RevisionID: "<rev_id>"},
+		},
+		{
+			Field,
+			"\"/spaces/<space_id>/envs/<env_id>/cols/<coll_id>/items/<item_id>/fields/<field_name>\"",
+			&FieldID{ItemID: ItemID{CollectionID: CollectionID{EnvironmentID: EnvironmentID{SpaceID: SpaceID{SpaceID: "<space_id>"}, EnvironmentID: "<env_id>"}, CollectionID: "<coll_id>"}, ItemID: "<item_id>"}, FieldName: "<field_name>"},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			switch tt.name {
+			case Organization:
+				v := new(OrganizationID)
+				err := v.UnmarshalJSON([]byte(tt.json))
+				require.NoError(t, err)
+				assert.Equal(t, tt.id, v)
+			case Service:
+				v := new(ServiceID)
+				err := v.UnmarshalJSON([]byte(tt.json))
+				require.NoError(t, err)
+				assert.Equal(t, tt.id, v)
+			case Space:
+				v := new(SpaceID)
+				err := v.UnmarshalJSON([]byte(tt.json))
+				require.NoError(t, err)
+				assert.Equal(t, tt.id, v)
+			case Environment:
+				v := new(EnvironmentID)
+				err := v.UnmarshalJSON([]byte(tt.json))
+				require.NoError(t, err)
+				assert.Equal(t, tt.id, v)
+			case Client:
+				v := new(ClientID)
+				err := v.UnmarshalJSON([]byte(tt.json))
+				require.NoError(t, err)
+				assert.Equal(t, tt.id, v)
+			case Role:
+				v := new(RoleID)
+				err := v.UnmarshalJSON([]byte(tt.json))
+				require.NoError(t, err)
+				assert.Equal(t, tt.id, v)
+			case Collection:
+				v := new(CollectionID)
+				err := v.UnmarshalJSON([]byte(tt.json))
+				require.NoError(t, err)
+				assert.Equal(t, tt.id, v)
+			case Schema:
+				v := new(SchemaID)
+				err := v.UnmarshalJSON([]byte(tt.json))
+				require.NoError(t, err)
+				assert.Equal(t, tt.id, v)
+			case Item:
+				v := new(ItemID)
+				err := v.UnmarshalJSON([]byte(tt.json))
+				require.NoError(t, err)
+				assert.Equal(t, tt.id, v)
+			case Revision:
+				v := new(RevisionID)
+				err := v.UnmarshalJSON([]byte(tt.json))
+				require.NoError(t, err)
+				assert.Equal(t, tt.id, v)
+			case Field:
+				v := new(FieldID)
+				err := v.UnmarshalJSON([]byte(tt.json))
+				require.NoError(t, err)
+				assert.Equal(t, tt.id, v)
+			}
+		})
+	}
+}
diff --git a/id/item.go b/id/item.go
new file mode 100644
index 0000000000000000000000000000000000000000..224c3e19e360cceb7f89f56f9a552f47be6dc308
--- /dev/null
+++ b/id/item.go
@@ -0,0 +1,77 @@
+package id
+
+import (
+	jsoniter "github.com/json-iterator/go"
+)
+
+const (
+	Item        = "item"
+	ItemsPrefix = "items"
+)
+
+type ItemID struct {
+	CollectionID
+	ItemID string `json:"item_id"`
+}
+
+func (t *ItemID) Type() string { return Item }
+
+func (t *ItemID) String() string {
+	return Join(t.CollectionID.String(), ItemsPrefix, t.ItemID)
+
+}
+
+func (t *ItemID) ToMap() map[string]any {
+	m := t.CollectionID.ToMap()
+	m["item_id"] = t.ItemID
+	return m
+}
+
+func (t *ItemID) FromMap(m map[string]any) error {
+	if err := t.CollectionID.FromMap(m); err != nil {
+		return err
+	}
+	t.ItemID = m["item_id"].(string)
+	return nil
+}
+
+func (t *ItemID) Validate() error {
+	if t.ItemID == "" {
+		return ErrInvalidID
+	}
+
+	return t.CollectionID.Validate()
+}
+
+func parseItemID(parts []string) (*ItemID, error) {
+	if len(parts) != 8 || parts[6] != ItemsPrefix {
+		return nil, ErrInvalidID
+	}
+
+	collID, err := parseCollectionID(parts[:6])
+	if err != nil {
+		return nil, err
+	}
+
+	var id ItemID
+	id.CollectionID = *collID
+	id.ItemID = parts[7]
+	return &id, nil
+}
+func (t *ItemID) UnmarshalJSON(b []byte) error {
+	var data string
+	if err := jsoniter.Unmarshal(b, &data); err != nil {
+		return err
+	}
+	knownID, err := Parse(data)
+	if err != nil {
+		return err
+	}
+	t.CollectionID = knownID.(*ItemID).CollectionID
+	t.ItemID = knownID.(*ItemID).ItemID
+	return nil
+}
+
+func (t *ItemID) MarshalJSON() ([]byte, error) {
+	return []byte(t.String()), nil
+}
diff --git a/id/json.go b/id/json.go
index 04fa39faf5f812a26aa1f9cc2bb1e3cf98f59f1e..b64bd2a1d7888d90c05072d3c86f7810f5de1318 100644
--- a/id/json.go
+++ b/id/json.go
@@ -1,3 +1,6 @@
 package id
 
 // TODO: Сохранение/чтение в JSON (в строку)
+var knownImplementations = []ID{
+	&SpaceID{},
+}
diff --git a/id/organization.go b/id/organization.go
new file mode 100644
index 0000000000000000000000000000000000000000..2c88414031f9a0ca9d5d3a5a87e6cf20eabf4b29
--- /dev/null
+++ b/id/organization.go
@@ -0,0 +1,64 @@
+package id
+
+import (
+	jsoniter "github.com/json-iterator/go"
+)
+
+const (
+	Organization        = "organization"
+	OrganizationsPrefix = "orgs"
+)
+
+type OrganizationID struct {
+	OrganizationID string `json:"organization_id"`
+}
+
+func (t *OrganizationID) Type() string { return Organization }
+
+func (t *OrganizationID) String() string {
+	return Join(OrganizationsPrefix, t.OrganizationID)
+}
+
+func (t *OrganizationID) ToMap() map[string]any {
+	return map[string]any{
+		"organization_id": t.OrganizationID,
+	}
+}
+
+func (t *OrganizationID) FromMap(m map[string]any) error {
+	t.OrganizationID = m["organization_id"].(string)
+	return nil
+}
+
+func (t *OrganizationID) Validate() error {
+	if t.OrganizationID == "" {
+		return ErrInvalidID
+	}
+	return nil
+}
+
+func parseOrganizationID(parts []string) (*OrganizationID, error) {
+	var id OrganizationID
+	if len(parts) != 2 || parts[0] != OrganizationsPrefix {
+		return nil, ErrInvalidID
+	}
+
+	id.OrganizationID = parts[1]
+	return &id, nil
+}
+func (t *OrganizationID) UnmarshalJSON(b []byte) error {
+	var data string
+	if err := jsoniter.Unmarshal(b, &data); err != nil {
+		return err
+	}
+	knownID, err := Parse(data)
+	if err != nil {
+		return err
+	}
+	t.OrganizationID = knownID.(*OrganizationID).OrganizationID
+	return nil
+}
+
+func (t *OrganizationID) MarshalJSON() ([]byte, error) {
+	return []byte(t.String()), nil
+}
diff --git a/id/revision.go b/id/revision.go
new file mode 100644
index 0000000000000000000000000000000000000000..a5a1caf1b0376da20400d24b33b4ef5346db5386
--- /dev/null
+++ b/id/revision.go
@@ -0,0 +1,78 @@
+package id
+
+import (
+	jsoniter "github.com/json-iterator/go"
+)
+
+const (
+	Revision        = "revision"
+	RevisionsPrefix = "revs"
+)
+
+type RevisionID struct {
+	ItemID
+	RevisionID string `json:"rev_id"`
+}
+
+func (t *RevisionID) Type() string { return Revision }
+
+func (t *RevisionID) String() string {
+	return Join(t.ItemID.String(), RevisionsPrefix, t.RevisionID)
+
+}
+
+func (t *RevisionID) ToMap() map[string]any {
+	m := t.ItemID.ToMap()
+	m["rev_id"] = t.RevisionID
+	return m
+}
+
+func (t *RevisionID) FromMap(m map[string]any) error {
+	if err := t.ItemID.FromMap(m); err != nil {
+		return err
+	}
+	t.RevisionID = m["rev_id"].(string)
+	return nil
+}
+
+func (t *RevisionID) Validate() error {
+	if t.RevisionID == "" {
+		return ErrInvalidID
+	}
+
+	return t.ItemID.Validate()
+}
+
+func parseRevisionID(parts []string) (*RevisionID, error) {
+	if len(parts) != 10 || parts[8] != RevisionsPrefix {
+		return nil, ErrInvalidID
+	}
+
+	itemID, err := parseItemID(parts[:8])
+	if err != nil {
+		return nil, err
+	}
+
+	var id RevisionID
+	id.ItemID = *itemID
+	id.RevisionID = parts[9]
+	return &id, nil
+}
+
+func (t *RevisionID) UnmarshalJSON(b []byte) error {
+	var data string
+	if err := jsoniter.Unmarshal(b, &data); err != nil {
+		return err
+	}
+	knownID, err := Parse(data)
+	if err != nil {
+		return err
+	}
+	t.ItemID = knownID.(*RevisionID).ItemID
+	t.RevisionID = knownID.(*RevisionID).RevisionID
+	return nil
+}
+
+func (t *RevisionID) MarshalJSON() ([]byte, error) {
+	return []byte(t.String()), nil
+}
diff --git a/id/role.go b/id/role.go
new file mode 100644
index 0000000000000000000000000000000000000000..273f5e1e071943887ea8702fd266620aba86eb05
--- /dev/null
+++ b/id/role.go
@@ -0,0 +1,78 @@
+package id
+
+import (
+	jsoniter "github.com/json-iterator/go"
+)
+
+const (
+	Role        = "role"
+	RolesPrefix = "roles"
+)
+
+type RoleID struct {
+	SpaceID
+	RoleID string `json:"role_id"`
+}
+
+func (t *RoleID) Type() string { return Client }
+
+func (t *RoleID) String() string {
+	return Join(t.SpaceID.String(), RolesPrefix, t.RoleID)
+
+}
+
+func (t *RoleID) ToMap() map[string]any {
+	m := t.SpaceID.ToMap()
+	m["role_id"] = t.RoleID
+	return m
+}
+
+func (t *RoleID) FromMap(m map[string]any) error {
+	if err := t.SpaceID.FromMap(m); err != nil {
+		return err
+	}
+	t.RoleID = m["role_id"].(string)
+	return nil
+}
+
+func (t *RoleID) Validate() error {
+	if t.RoleID == "" {
+		return ErrInvalidID
+	}
+
+	return t.SpaceID.Validate()
+}
+
+func parseRoleID(parts []string) (*RoleID, error) {
+	if len(parts) != 4 || parts[2] != RolesPrefix {
+		return nil, ErrInvalidID
+	}
+
+	spaceID, err := parseSpaceID(parts[:2])
+	if err != nil {
+		return nil, err
+	}
+
+	var id RoleID
+	id.SpaceID = *spaceID
+	id.RoleID = parts[3]
+	return &id, nil
+}
+
+func (t *RoleID) UnmarshalJSON(b []byte) error {
+	var data string
+	if err := jsoniter.Unmarshal(b, &data); err != nil {
+		return err
+	}
+	knownID, err := Parse(data)
+	if err != nil {
+		return err
+	}
+	t.SpaceID = knownID.(*RoleID).SpaceID
+	t.RoleID = knownID.(*RoleID).RoleID
+	return nil
+}
+
+func (t *RoleID) MarshalJSON() ([]byte, error) {
+	return []byte(t.String()), nil
+}
diff --git a/id/schema.go b/id/schema.go
new file mode 100644
index 0000000000000000000000000000000000000000..43c97a4f0b8bd859937585d2771f7e4301d69a81
--- /dev/null
+++ b/id/schema.go
@@ -0,0 +1,78 @@
+package id
+
+import (
+	jsoniter "github.com/json-iterator/go"
+)
+
+const (
+	Schema       = "schema"
+	SchemaPrefix = "schema"
+)
+
+type SchemaID struct {
+	SpaceID
+	EnvironmentID
+	SchemaID string `json:"schema_id"`
+}
+
+func (t *SchemaID) Type() string { return Schema }
+
+func (t *SchemaID) String() string {
+	return Join(t.EnvironmentID.String(), SchemaPrefix, t.SchemaID)
+}
+
+func (t *SchemaID) ToMap() map[string]any {
+	m := t.EnvironmentID.ToMap()
+	m["schema_id"] = t.SchemaID
+	return m
+}
+
+func (t *SchemaID) FromMap(m map[string]any) error {
+	if err := t.EnvironmentID.FromMap(m); err != nil {
+		return err
+	}
+	t.SchemaID = m["schema_id"].(string)
+	return nil
+}
+
+func (t *SchemaID) Validate() error {
+	if t.SchemaID == "" {
+		return ErrInvalidID
+	}
+
+	return t.EnvironmentID.Validate()
+}
+
+func parseSchemaID(parts []string) (*SchemaID, error) {
+	if len(parts) != 6 || parts[4] != SchemaPrefix {
+		return nil, ErrInvalidID
+	}
+
+	envID, err := parseEnvironmentID(parts[:4])
+	if err != nil {
+		return nil, err
+	}
+
+	var id SchemaID
+	id.EnvironmentID = *envID
+	id.SchemaID = parts[5]
+	return &id, nil
+}
+
+func (t *SchemaID) UnmarshalJSON(b []byte) error {
+	var data string
+	if err := jsoniter.Unmarshal(b, &data); err != nil {
+		return err
+	}
+	knownID, err := Parse(data)
+	if err != nil {
+		return err
+	}
+	t.EnvironmentID = knownID.(*SchemaID).EnvironmentID
+	t.SchemaID = knownID.(*SchemaID).SchemaID
+	return nil
+}
+
+func (t *SchemaID) MarshalJSON() ([]byte, error) {
+	return []byte(t.String()), nil
+}
diff --git a/id/service.go b/id/service.go
new file mode 100644
index 0000000000000000000000000000000000000000..9a55357bea06564f89d0958a2e8be9e2dcb247bf
--- /dev/null
+++ b/id/service.go
@@ -0,0 +1,64 @@
+package id
+
+import (
+	jsoniter "github.com/json-iterator/go"
+)
+
+const (
+	Service        = "service"
+	ServicesPrefix = "services"
+)
+
+type ServiceID struct {
+	ServiceID string `json:"service_id"`
+}
+
+func (t *ServiceID) Type() string { return Service }
+
+func (t *ServiceID) String() string {
+	return Join(ServicesPrefix, t.ServiceID)
+}
+
+func (t *ServiceID) ToMap() map[string]any {
+	return map[string]any{
+		"service_id": t.ServiceID,
+	}
+}
+
+func (t *ServiceID) FromMap(m map[string]any) error {
+	t.ServiceID = m["service_id"].(string)
+	return nil
+}
+
+func (t *ServiceID) Validate() error {
+	if t.ServiceID == "" {
+		return ErrInvalidID
+	}
+	return nil
+}
+
+func parseServiceID(parts []string) (*ServiceID, error) {
+	var id ServiceID
+	if len(parts) != 2 || parts[0] != ServicesPrefix {
+		return nil, ErrInvalidID
+	}
+
+	id.ServiceID = parts[1]
+	return &id, nil
+}
+func (t *ServiceID) UnmarshalJSON(b []byte) error {
+	var data string
+	if err := jsoniter.Unmarshal(b, &data); err != nil {
+		return err
+	}
+	knownID, err := Parse(data)
+	if err != nil {
+		return err
+	}
+	t.ServiceID = knownID.(*ServiceID).ServiceID
+	return nil
+}
+
+func (t *ServiceID) MarshalJSON() ([]byte, error) {
+	return []byte(t.String()), nil
+}
diff --git a/id/space.go b/id/space.go
index 3d9d6c35cba03a45d1cee00d692865e0fc8a5037..401fd58d9254e78b4691590206ba4c656ff92096 100644
--- a/id/space.go
+++ b/id/space.go
@@ -1,5 +1,9 @@
 package id
 
+import (
+	jsoniter "github.com/json-iterator/go"
+)
+
 const (
 	Space        = "space"
 	SpacesPrefix = "spaces"
@@ -42,3 +46,20 @@ func parseSpaceID(parts []string) (*SpaceID, error) {
 	id.SpaceID = parts[1]
 	return &id, nil
 }
+
+func (t *SpaceID) UnmarshalJSON(b []byte) error {
+	var data string
+	if err := jsoniter.Unmarshal(b, &data); err != nil {
+		return err
+	}
+	knownID, err := Parse(data)
+	if err != nil {
+		return err
+	}
+	t.SpaceID = knownID.(*SpaceID).SpaceID
+	return nil
+}
+
+func (t *SpaceID) MarshalJSON() ([]byte, error) {
+	return []byte(t.String()), nil
+}
diff --git a/id/user.go b/id/user.go
new file mode 100644
index 0000000000000000000000000000000000000000..6ea9ea529c0d4606a5bfee14262c0405d5d5d812
--- /dev/null
+++ b/id/user.go
@@ -0,0 +1,65 @@
+package id
+
+import (
+	jsoniter "github.com/json-iterator/go"
+)
+
+const (
+	User        = "user"
+	UsersPrefix = "users"
+)
+
+type UserID struct {
+	UserID string `json:"user_id"`
+}
+
+func (t *UserID) Type() string { return User }
+
+func (t *UserID) String() string {
+	return Join(UsersPrefix, t.UserID)
+}
+
+func (t *UserID) ToMap() map[string]any {
+	return map[string]any{
+		"user_id": t.UserID,
+	}
+}
+
+func (t *UserID) FromMap(m map[string]any) error {
+	t.UserID = m["user_id"].(string)
+	return nil
+}
+
+func (t *UserID) Validate() error {
+	if t.UserID == "" {
+		return ErrInvalidID
+	}
+	return nil
+}
+
+func parseUserID(parts []string) (*UserID, error) {
+	var id UserID
+	if len(parts) != 2 || parts[0] != UsersPrefix {
+		return nil, ErrInvalidID
+	}
+
+	id.UserID = parts[1]
+	return &id, nil
+}
+
+func (t *UserID) UnmarshalJSON(b []byte) error {
+	var data string
+	if err := jsoniter.Unmarshal(b, &data); err != nil {
+		return err
+	}
+	knownID, err := Parse(data)
+	if err != nil {
+		return err
+	}
+	t.UserID = knownID.(*UserID).UserID
+	return nil
+}
+
+func (t *UserID) MarshalJSON() ([]byte, error) {
+	return []byte(t.String()), nil
+}
diff --git a/perxis-proto b/perxis-proto
index ecd756865a06583ce52bff6265c0fd5e159d93b9..63410745d6008eaa9d9b00626b5f5b6891ac9189 160000
--- a/perxis-proto
+++ b/perxis-proto
@@ -1 +1 @@
-Subproject commit ecd756865a06583ce52bff6265c0fd5e159d93b9
+Subproject commit 63410745d6008eaa9d9b00626b5f5b6891ac9189