# Group service (gRPC)

Service for managing groups within an organization

## Endpoints

### Create group membership

gRPC `/nominal.authentication.groups.v1.GroupService/CreateGroupMembership`

Adds a member to a manually managed group. Re-adding an existing member is a no-op. Throws HTTP 400 / gRPC 3 if group_rid or member_rid is missing, or member_rid is not a supported RID type. Throws HTTP 403 / gRPC 7 if the caller is not authorized to update the group, including when the group does not exist. The permission check runs before the group lookup so unknown groups are not disclosed. Throws HTTP 400 / gRPC 3 if the target user is not in the group’s organization. Throws HTTP 400 / gRPC 3 if the group’s membership is not managed manually.

Request: `CreateGroupMembershipRequest`

`group_membership` [`GroupMembership`](https://dev.nominal.io/api/grpc/group-service.md#groupmembership) required
:   ​

    Fields of `GroupMembership`

    `key` [`GroupMembershipKey`](https://dev.nominal.io/api/grpc/group-service.md#groupmembershipkey) required
    :   Identity is a nested key so delete can address a membership without the full resource, which may gain fields later.

        Fields of `GroupMembershipKey`

        `group_rid` string required
        :   At most 256 characters.

        `member_rid` string required
        :   The member of the group. Only user RIDs are accepted today. At most 256 characters.

Response: `CreateGroupMembershipResponse`

`group_membership` [`GroupMembership`](https://dev.nominal.io/api/grpc/group-service.md#groupmembership)
:   ​

    Fields of `GroupMembership`

    `key` [`GroupMembershipKey`](https://dev.nominal.io/api/grpc/group-service.md#groupmembershipkey)
    :   Identity is a nested key so delete can address a membership without the full resource, which may gain fields later.

        Fields of `GroupMembershipKey`

        `group_rid` string
        :   At most 256 characters.

        `member_rid` string
        :   The member of the group. Only user RIDs are accepted today. At most 256 characters.

Request

**Python**

```python
import grpc
from nominal.protos.authentication.groups.v1 import groups_pb2, groups_pb2_grpc

channel = grpc.secure_channel("api.gov.nominal.io:443", grpc.ssl_channel_credentials())
group_service = groups_pb2_grpc.GroupServiceStub(channel)
auth = [("authorization", "Bearer <api key>")]

request = groups_pb2.CreateGroupMembershipRequest(
    group_membership={"key": {"group_rid": "<group rid>", "member_rid": "<member rid>"}},
)
response = group_service.CreateGroupMembership(request, metadata=auth)
print(response)
```

**Go**

```go
package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"

	groupsv1 "github.com/nominal-io/nominal-api-protos-go/nominal/protos/authentication/groups/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"
)

func main() {
	conn, err := grpc.NewClient("api.gov.nominal.io:443",
		grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})))
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	groupService := groupsv1.NewGroupServiceClient(conn)
	ctx := metadata.AppendToOutgoingContext(context.Background(), "authorization", "Bearer <api key>")

	request := &groupsv1.CreateGroupMembershipRequest{
		GroupMembership: &groupsv1.GroupMembership{
			Key: &groupsv1.GroupMembershipKey{
				GroupRid:  "<group rid>",
				MemberRid: "<member rid>",
			},
		},
	}
	response, err := groupService.CreateGroupMembership(ctx, request)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(response)
}
```

Response

```json
{
  "group_membership": {
    "key": {
      "group_rid": "<group rid>",
      "member_rid": "<member rid>"
    }
  }
}
```

### Create or update group

gRPC `/nominal.authentication.groups.v1.GroupService/CreateOrUpdateGroup`

Org-scoped upsert.

Creates a new group if Group.rid is empty in the request (server assigns the rid). Both Group.org_rid and Group.group_id are required on create, and the new group_id must be unique within the organization.

Updates an existing group if Group.rid is present in the request. Group.org_rid and Group.group_id are immutable: when set on the update request they must match the existing values, but they may be omitted to leave them unchanged. Fields in the group message marked as OUTPUT_ONLY are ignored when present in the input request. All other mutable fields are fully overwritten, and will be cleared if not set in the request.

Throws HTTP 400 / gRPC 3 if a supplied immutable field does not match the existing group. Throws HTTP 404 / gRPC 5 if the group identified by Group.rid for an update does not exist. Throws HTTP 409 / gRPC 6 if a create would result in duplicate group_ids within the organization. Throws HTTP 403 / gRPC 7 if the caller is not authorized.

Request: `CreateOrUpdateGroupRequest`

`group` [`Group`](https://dev.nominal.io/api/grpc/group-service.md#group) required
:   ​

    Fields of `Group`

    `rid` string
    :   ​

    `org_rid` string
    :   Required on create requests; immutable on updates.

    `group_id` string
    :   Group ID, unique per org. If sourced from the customer’s IdP, will match IdP group name. Required on create requests; immutable on updates.

    `display_name` string
    :   ​

    `description` string
    :   ​

    `user_rids` array of string output only
    :   The list of users in this group.

    `created_at` string (date-time) output only
    :   ​

    `updated_at` string (date-time) output only
    :   ​

    `symbol` [`Symbol`](https://dev.nominal.io/api/grpc/group-service.md#symbol)
    :   Symbol to identify groups

        Fields of `Symbol`

        one of `icon`, `emoji`, `image` required
        :   ​

            **icon**

            `icon` string
            :   Icon name (e.g. castle)

            **emoji**

            `emoji` string
            :   Emoji name (e.g. :castle:)

            **image**

            `image` string
            :   Image url (e.g. https://example.com/image.png)

    `membership_managed_manually` boolean
    :   Whether group membership is managed manually in Nominal. Defaults to false.

Response: `CreateOrUpdateGroupResponse`

`group` [`Group`](https://dev.nominal.io/api/grpc/group-service.md#group)
:   ​

    Fields of `Group`

    `rid` string
    :   ​

    `org_rid` string
    :   Required on create requests; immutable on updates.

    `group_id` string
    :   Group ID, unique per org. If sourced from the customer’s IdP, will match IdP group name. Required on create requests; immutable on updates.

    `display_name` string
    :   ​

    `description` string
    :   ​

    `user_rids` array of string
    :   The list of users in this group.

    `created_at` string (date-time)
    :   ​

    `updated_at` string (date-time)
    :   ​

    `symbol` [`Symbol`](https://dev.nominal.io/api/grpc/group-service.md#symbol)
    :   Symbol to identify groups

        Fields of `Symbol`

        one of `icon`, `emoji`, `image`
        :   ​

            **icon**

            `icon` string
            :   Icon name (e.g. castle)

            **emoji**

            `emoji` string
            :   Emoji name (e.g. :castle:)

            **image**

            `image` string
            :   Image url (e.g. https://example.com/image.png)

    `membership_managed_manually` boolean
    :   Whether group membership is managed manually in Nominal. Defaults to false.

Request

**Python**

```python
import grpc
from nominal.protos.authentication.groups.v1 import groups_pb2, groups_pb2_grpc

channel = grpc.secure_channel("api.gov.nominal.io:443", grpc.ssl_channel_credentials())
group_service = groups_pb2_grpc.GroupServiceStub(channel)
auth = [("authorization", "Bearer <api key>")]

request = groups_pb2.CreateOrUpdateGroupRequest(
    group={
        "rid": "<rid>",
        "org_rid": "<org rid>",
        "group_id": "<group id>",
        "display_name": "<display name>",
        "description": "<description>",
        "symbol": {"icon": "<icon>"},
        "membership_managed_manually": True,
    },
)
response = group_service.CreateOrUpdateGroup(request, metadata=auth)
print(response)
```

**Go**

```go
package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"

	groupsv1 "github.com/nominal-io/nominal-api-protos-go/nominal/protos/authentication/groups/v1"
	elementsv1 "github.com/nominal-io/nominal-api-protos-go/nominal/protos/scout/elements/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"
)

func main() {
	conn, err := grpc.NewClient("api.gov.nominal.io:443",
		grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})))
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	groupService := groupsv1.NewGroupServiceClient(conn)
	ctx := metadata.AppendToOutgoingContext(context.Background(), "authorization", "Bearer <api key>")

	request := &groupsv1.CreateOrUpdateGroupRequest{
		Group: &groupsv1.Group{
			Rid:         "<rid>",
			OrgRid:      "<org rid>",
			GroupId:     "<group id>",
			DisplayName: "<display name>",
			Description: "<description>",
			Symbol: &elementsv1.Symbol{
				Symbol: &elementsv1.Symbol_Icon{Icon: "<icon>"},
			},
			MembershipManagedManually: true,
		},
	}
	response, err := groupService.CreateOrUpdateGroup(ctx, request)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(response)
}
```

Response

```json
{
  "group": {
    "rid": "<rid>",
    "org_rid": "<org rid>",
    "group_id": "<group id>",
    "display_name": "<display name>",
    "description": "<description>",
    "user_rids": [
      "<user rids>"
    ],
    "created_at": "2026-01-01T00:00:00Z",
    "updated_at": "2026-01-01T00:00:00Z",
    "symbol": {
      "icon": "<icon>"
    },
    "membership_managed_manually": true
  }
}
```

### Delete group membership

gRPC `/nominal.authentication.groups.v1.GroupService/DeleteGroupMembership`

Removes a member from a manually managed group. Removing a non-member is a no-op. Throws HTTP 400 / gRPC 3 if group_rid or member_rid is missing, or member_rid is not a supported RID type. Throws HTTP 403 / gRPC 7 if the caller is not authorized to update the group, including when the group does not exist. The permission check runs before the group lookup so unknown groups are not disclosed. Throws HTTP 400 / gRPC 3 if the group’s membership is not managed manually.

Request: `DeleteGroupMembershipRequest`

`key` [`GroupMembershipKey`](https://dev.nominal.io/api/grpc/group-service.md#groupmembershipkey) required
:   ​

    Fields of `GroupMembershipKey`

    `group_rid` string required
    :   At most 256 characters.

    `member_rid` string required
    :   The member of the group. Only user RIDs are accepted today. At most 256 characters.

Response: `DeleteGroupMembershipResponse`

A message with no fields.

Request

**Python**

```python
import grpc
from nominal.protos.authentication.groups.v1 import groups_pb2, groups_pb2_grpc

channel = grpc.secure_channel("api.gov.nominal.io:443", grpc.ssl_channel_credentials())
group_service = groups_pb2_grpc.GroupServiceStub(channel)
auth = [("authorization", "Bearer <api key>")]

request = groups_pb2.DeleteGroupMembershipRequest(
    key={"group_rid": "<group rid>", "member_rid": "<member rid>"},
)
response = group_service.DeleteGroupMembership(request, metadata=auth)
print(response)
```

**Go**

```go
package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"

	groupsv1 "github.com/nominal-io/nominal-api-protos-go/nominal/protos/authentication/groups/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"
)

func main() {
	conn, err := grpc.NewClient("api.gov.nominal.io:443",
		grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})))
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	groupService := groupsv1.NewGroupServiceClient(conn)
	ctx := metadata.AppendToOutgoingContext(context.Background(), "authorization", "Bearer <api key>")

	request := &groupsv1.DeleteGroupMembershipRequest{
		Key: &groupsv1.GroupMembershipKey{
			GroupRid:  "<group rid>",
			MemberRid: "<member rid>",
		},
	}
	response, err := groupService.DeleteGroupMembership(ctx, request)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(response)
}
```

Response

```json
{}
```

### Get group

gRPC `/nominal.authentication.groups.v1.GroupService/GetGroup`

Get group within the user’s organization

Request: `GetGroupRequest`

`group_rid` string required
:   ​

Response: `GetGroupResponse`

`group` [`Group`](https://dev.nominal.io/api/grpc/group-service.md#group)
:   ​

    Fields of `Group`

    `rid` string
    :   ​

    `org_rid` string
    :   Required on create requests; immutable on updates.

    `group_id` string
    :   Group ID, unique per org. If sourced from the customer’s IdP, will match IdP group name. Required on create requests; immutable on updates.

    `display_name` string
    :   ​

    `description` string
    :   ​

    `user_rids` array of string
    :   The list of users in this group.

    `created_at` string (date-time)
    :   ​

    `updated_at` string (date-time)
    :   ​

    `symbol` [`Symbol`](https://dev.nominal.io/api/grpc/group-service.md#symbol)
    :   Symbol to identify groups

        Fields of `Symbol`

        one of `icon`, `emoji`, `image`
        :   ​

            **icon**

            `icon` string
            :   Icon name (e.g. castle)

            **emoji**

            `emoji` string
            :   Emoji name (e.g. :castle:)

            **image**

            `image` string
            :   Image url (e.g. https://example.com/image.png)

    `membership_managed_manually` boolean
    :   Whether group membership is managed manually in Nominal. Defaults to false.

Request

**Python**

```python
import grpc
from nominal.protos.authentication.groups.v1 import groups_pb2, groups_pb2_grpc

channel = grpc.secure_channel("api.gov.nominal.io:443", grpc.ssl_channel_credentials())
group_service = groups_pb2_grpc.GroupServiceStub(channel)
auth = [("authorization", "Bearer <api key>")]

request = groups_pb2.GetGroupRequest(group_rid="<group rid>")
response = group_service.GetGroup(request, metadata=auth)
print(response)
```

**Go**

```go
package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"

	groupsv1 "github.com/nominal-io/nominal-api-protos-go/nominal/protos/authentication/groups/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"
)

func main() {
	conn, err := grpc.NewClient("api.gov.nominal.io:443",
		grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})))
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	groupService := groupsv1.NewGroupServiceClient(conn)
	ctx := metadata.AppendToOutgoingContext(context.Background(), "authorization", "Bearer <api key>")

	request := &groupsv1.GetGroupRequest{
		GroupRid: "<group rid>",
	}
	response, err := groupService.GetGroup(ctx, request)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(response)
}
```

Response

```json
{
  "group": {
    "rid": "<rid>",
    "org_rid": "<org rid>",
    "group_id": "<group id>",
    "display_name": "<display name>",
    "description": "<description>",
    "user_rids": [
      "<user rids>"
    ],
    "created_at": "2026-01-01T00:00:00Z",
    "updated_at": "2026-01-01T00:00:00Z",
    "symbol": {
      "icon": "<icon>"
    },
    "membership_managed_manually": true
  }
}
```

### Get group by ID

gRPC `/nominal.authentication.groups.v1.GroupService/GetGroupById`

Gets a group by its ID within the user’s organization

Request: `GetGroupByIdRequest`

`group_id` string required
:   ​

Response: `GetGroupByIdResponse`

`group` [`Group`](https://dev.nominal.io/api/grpc/group-service.md#group)
:   ​

    Fields of `Group`

    `rid` string
    :   ​

    `org_rid` string
    :   Required on create requests; immutable on updates.

    `group_id` string
    :   Group ID, unique per org. If sourced from the customer’s IdP, will match IdP group name. Required on create requests; immutable on updates.

    `display_name` string
    :   ​

    `description` string
    :   ​

    `user_rids` array of string
    :   The list of users in this group.

    `created_at` string (date-time)
    :   ​

    `updated_at` string (date-time)
    :   ​

    `symbol` [`Symbol`](https://dev.nominal.io/api/grpc/group-service.md#symbol)
    :   Symbol to identify groups

        Fields of `Symbol`

        one of `icon`, `emoji`, `image`
        :   ​

            **icon**

            `icon` string
            :   Icon name (e.g. castle)

            **emoji**

            `emoji` string
            :   Emoji name (e.g. :castle:)

            **image**

            `image` string
            :   Image url (e.g. https://example.com/image.png)

    `membership_managed_manually` boolean
    :   Whether group membership is managed manually in Nominal. Defaults to false.

Request

**Python**

```python
import grpc
from nominal.protos.authentication.groups.v1 import groups_pb2, groups_pb2_grpc

channel = grpc.secure_channel("api.gov.nominal.io:443", grpc.ssl_channel_credentials())
group_service = groups_pb2_grpc.GroupServiceStub(channel)
auth = [("authorization", "Bearer <api key>")]

request = groups_pb2.GetGroupByIdRequest(group_id="<group id>")
response = group_service.GetGroupById(request, metadata=auth)
print(response)
```

**Go**

```go
package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"

	groupsv1 "github.com/nominal-io/nominal-api-protos-go/nominal/protos/authentication/groups/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"
)

func main() {
	conn, err := grpc.NewClient("api.gov.nominal.io:443",
		grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})))
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	groupService := groupsv1.NewGroupServiceClient(conn)
	ctx := metadata.AppendToOutgoingContext(context.Background(), "authorization", "Bearer <api key>")

	request := &groupsv1.GetGroupByIdRequest{
		GroupId: "<group id>",
	}
	response, err := groupService.GetGroupById(ctx, request)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(response)
}
```

Response

```json
{
  "group": {
    "rid": "<rid>",
    "org_rid": "<org rid>",
    "group_id": "<group id>",
    "display_name": "<display name>",
    "description": "<description>",
    "user_rids": [
      "<user rids>"
    ],
    "created_at": "2026-01-01T00:00:00Z",
    "updated_at": "2026-01-01T00:00:00Z",
    "symbol": {
      "icon": "<icon>"
    },
    "membership_managed_manually": true
  }
}
```

### Get groups

gRPC `/nominal.authentication.groups.v1.GroupService/GetGroups`

Get batch of groups within the user’s organization

Request: `GetGroupsRequest`

`group_rids` array of string
:   ​

Response: `GetGroupsResponse`

`groups` map of string to [`Group`](https://dev.nominal.io/api/grpc/group-service.md#group)
:   ​

    Fields of `Group`

    `rid` string
    :   ​

    `org_rid` string
    :   Required on create requests; immutable on updates.

    `group_id` string
    :   Group ID, unique per org. If sourced from the customer’s IdP, will match IdP group name. Required on create requests; immutable on updates.

    `display_name` string
    :   ​

    `description` string
    :   ​

    `user_rids` array of string
    :   The list of users in this group.

    `created_at` string (date-time)
    :   ​

    `updated_at` string (date-time)
    :   ​

    `symbol` [`Symbol`](https://dev.nominal.io/api/grpc/group-service.md#symbol)
    :   Symbol to identify groups

        Fields of `Symbol`

        one of `icon`, `emoji`, `image`
        :   ​

            **icon**

            `icon` string
            :   Icon name (e.g. castle)

            **emoji**

            `emoji` string
            :   Emoji name (e.g. :castle:)

            **image**

            `image` string
            :   Image url (e.g. https://example.com/image.png)

    `membership_managed_manually` boolean
    :   Whether group membership is managed manually in Nominal. Defaults to false.

Request

**Python**

```python
import grpc
from nominal.protos.authentication.groups.v1 import groups_pb2, groups_pb2_grpc

channel = grpc.secure_channel("api.gov.nominal.io:443", grpc.ssl_channel_credentials())
group_service = groups_pb2_grpc.GroupServiceStub(channel)
auth = [("authorization", "Bearer <api key>")]

request = groups_pb2.GetGroupsRequest(group_rids=["<group rids>"])
response = group_service.GetGroups(request, metadata=auth)
print(response)
```

**Go**

```go
package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"

	groupsv1 "github.com/nominal-io/nominal-api-protos-go/nominal/protos/authentication/groups/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"
)

func main() {
	conn, err := grpc.NewClient("api.gov.nominal.io:443",
		grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})))
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	groupService := groupsv1.NewGroupServiceClient(conn)
	ctx := metadata.AppendToOutgoingContext(context.Background(), "authorization", "Bearer <api key>")

	request := &groupsv1.GetGroupsRequest{
		GroupRids: []string{
			"<group rids>",
		},
	}
	response, err := groupService.GetGroups(ctx, request)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(response)
}
```

Response

```json
{
  "groups": {
    "<key>": {
      "rid": "<rid>",
      "org_rid": "<org rid>",
      "group_id": "<group id>",
      "display_name": "<display name>",
      "description": "<description>",
      "user_rids": [
        "<user rids>"
      ],
      "created_at": "2026-01-01T00:00:00Z",
      "updated_at": "2026-01-01T00:00:00Z",
      "symbol": {
        "icon": "<icon>"
      },
      "membership_managed_manually": true
    }
  }
}
```

### Search groups

gRPC `/nominal.authentication.groups.v1.GroupService/SearchGroups`

Searches all groups in the user’s organization using given query

Request: `SearchGroupsRequest`

`page_size` integer
:   Page sizes greater than 1_000 will be rejected. Default pageSize is 100.

`next_page_token` string
:   ​

`query` [`SearchGroupsQuery`](https://dev.nominal.io/api/grpc/group-service.md#searchgroupsquery) required
:   ​

    Fields of `SearchGroupsQuery`

    one of `exact_substring_text`, `and`, `or`, `member_users`
    :   ​

        **exact_substring_text**

        `exact_substring_text` string
        :   ​

        **and**

        `and` [`SearchQueryAnd`](https://dev.nominal.io/api/grpc/group-service.md#searchqueryand)
        :   and is implemented to allow for list (use SearchQueryAnd([]))

            Fields of `SearchQueryAnd`

            `queries` array of [`SearchGroupsQuery`](https://dev.nominal.io/api/grpc/group-service.md#searchgroupsquery)
            :   ​

        **or**

        `or` [`SearchQueryOr`](https://dev.nominal.io/api/grpc/group-service.md#searchqueryor)
        :   ​

            Fields of `SearchQueryOr`

            `queries` array of [`SearchGroupsQuery`](https://dev.nominal.io/api/grpc/group-service.md#searchgroupsquery)
            :   ​

        **member_users**

        `member_users` [`SearchQueryMemberUsers`](https://dev.nominal.io/api/grpc/group-service.md#searchquerymemberusers)
        :   ​

            Fields of `SearchQueryMemberUsers`

            `user_rids` array of string
            :   1 to 1,000 items. Each item: at most 256 characters.

Response: `SearchGroupsResponse`

`results` array of [`Group`](https://dev.nominal.io/api/grpc/group-service.md#group)
:   ​

    Fields of `Group`

    `rid` string
    :   ​

    `org_rid` string
    :   Required on create requests; immutable on updates.

    `group_id` string
    :   Group ID, unique per org. If sourced from the customer’s IdP, will match IdP group name. Required on create requests; immutable on updates.

    `display_name` string
    :   ​

    `description` string
    :   ​

    `user_rids` array of string
    :   The list of users in this group.

    `created_at` string (date-time)
    :   ​

    `updated_at` string (date-time)
    :   ​

    `symbol` [`Symbol`](https://dev.nominal.io/api/grpc/group-service.md#symbol)
    :   Symbol to identify groups

        Fields of `Symbol`

        one of `icon`, `emoji`, `image`
        :   ​

            **icon**

            `icon` string
            :   Icon name (e.g. castle)

            **emoji**

            `emoji` string
            :   Emoji name (e.g. :castle:)

            **image**

            `image` string
            :   Image url (e.g. https://example.com/image.png)

    `membership_managed_manually` boolean
    :   Whether group membership is managed manually in Nominal. Defaults to false.

`next_page_token` string
:   ​

Request

**Python**

```python
import grpc
from nominal.protos.authentication.groups.v1 import groups_pb2, groups_pb2_grpc

channel = grpc.secure_channel("api.gov.nominal.io:443", grpc.ssl_channel_credentials())
group_service = groups_pb2_grpc.GroupServiceStub(channel)
auth = [("authorization", "Bearer <api key>")]

request = groups_pb2.SearchGroupsRequest(
    query={"exact_substring_text": "<exact substring text>"},
)
response = group_service.SearchGroups(request, metadata=auth)
print(response)
```

**Go**

```go
package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"

	groupsv1 "github.com/nominal-io/nominal-api-protos-go/nominal/protos/authentication/groups/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"
)

func main() {
	conn, err := grpc.NewClient("api.gov.nominal.io:443",
		grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})))
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	groupService := groupsv1.NewGroupServiceClient(conn)
	ctx := metadata.AppendToOutgoingContext(context.Background(), "authorization", "Bearer <api key>")

	request := &groupsv1.SearchGroupsRequest{
		Query: &groupsv1.SearchGroupsQuery{
			Query: &groupsv1.SearchGroupsQuery_ExactSubstringText{ExactSubstringText: "<exact substring text>"},
		},
	}
	response, err := groupService.SearchGroups(ctx, request)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(response)
}
```

Response

```json
{
  "results": [
    {
      "rid": "<rid>",
      "org_rid": "<org rid>",
      "group_id": "<group id>",
      "display_name": "<display name>",
      "description": "<description>",
      "user_rids": [
        "<user rids>"
      ],
      "created_at": "2026-01-01T00:00:00Z",
      "updated_at": "2026-01-01T00:00:00Z",
      "symbol": {
        "icon": "<icon>"
      },
      "membership_managed_manually": true
    }
  ],
  "next_page_token": "<next page token>"
}
```

### Update group metadata

gRPC `/nominal.authentication.groups.v1.GroupService/UpdateGroupMetadata`

Updates the metadata of a group

Request: `UpdateGroupMetadataRequestWrapper`

`group_rid` string required
:   ​

`request` [`UpdateGroupMetadataRequest`](https://dev.nominal.io/api/grpc/group-service.md#updategroupmetadatarequest) required
:   ​

    Fields of `UpdateGroupMetadataRequest`

    `display_name` string
    :   ​

    `description` string
    :   ​

    `symbol` [`UpdateGroupSymbolWrapper`](https://dev.nominal.io/api/grpc/group-service.md#updategroupsymbolwrapper)
    :   ​

        Fields of `UpdateGroupSymbolWrapper`

        `value` [`Symbol`](https://dev.nominal.io/api/grpc/group-service.md#symbol)
        :   ​

            Fields of `Symbol`

            one of `icon`, `emoji`, `image` required
            :   ​

                **icon**

                `icon` string
                :   Icon name (e.g. castle)

                **emoji**

                `emoji` string
                :   Emoji name (e.g. :castle:)

                **image**

                `image` string
                :   Image url (e.g. https://example.com/image.png)

Response: `UpdateGroupMetadataResponse`

`group` [`Group`](https://dev.nominal.io/api/grpc/group-service.md#group)
:   ​

    Fields of `Group`

    `rid` string
    :   ​

    `org_rid` string
    :   Required on create requests; immutable on updates.

    `group_id` string
    :   Group ID, unique per org. If sourced from the customer’s IdP, will match IdP group name. Required on create requests; immutable on updates.

    `display_name` string
    :   ​

    `description` string
    :   ​

    `user_rids` array of string
    :   The list of users in this group.

    `created_at` string (date-time)
    :   ​

    `updated_at` string (date-time)
    :   ​

    `symbol` [`Symbol`](https://dev.nominal.io/api/grpc/group-service.md#symbol)
    :   Symbol to identify groups

        Fields of `Symbol`

        one of `icon`, `emoji`, `image`
        :   ​

            **icon**

            `icon` string
            :   Icon name (e.g. castle)

            **emoji**

            `emoji` string
            :   Emoji name (e.g. :castle:)

            **image**

            `image` string
            :   Image url (e.g. https://example.com/image.png)

    `membership_managed_manually` boolean
    :   Whether group membership is managed manually in Nominal. Defaults to false.

Request

**Python**

```python
import grpc
from nominal.protos.authentication.groups.v1 import groups_pb2, groups_pb2_grpc

channel = grpc.secure_channel("api.gov.nominal.io:443", grpc.ssl_channel_credentials())
group_service = groups_pb2_grpc.GroupServiceStub(channel)
auth = [("authorization", "Bearer <api key>")]

request = groups_pb2.UpdateGroupMetadataRequestWrapper(
    group_rid="<group rid>",
    request={
        "display_name": "<display name>",
        "description": "<description>",
        "symbol": {"value": {"icon": "<icon>"}},
    },
)
response = group_service.UpdateGroupMetadata(request, metadata=auth)
print(response)
```

**Go**

```go
package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"

	groupsv1 "github.com/nominal-io/nominal-api-protos-go/nominal/protos/authentication/groups/v1"
	elementsv1 "github.com/nominal-io/nominal-api-protos-go/nominal/protos/scout/elements/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"
	"google.golang.org/protobuf/proto"
)

func main() {
	conn, err := grpc.NewClient("api.gov.nominal.io:443",
		grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})))
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	groupService := groupsv1.NewGroupServiceClient(conn)
	ctx := metadata.AppendToOutgoingContext(context.Background(), "authorization", "Bearer <api key>")

	request := &groupsv1.UpdateGroupMetadataRequestWrapper{
		GroupRid: "<group rid>",
		Request: &groupsv1.UpdateGroupMetadataRequest{
			DisplayName: proto.String("<display name>"),
			Description: proto.String("<description>"),
			Symbol: &groupsv1.UpdateGroupMetadataRequest_UpdateGroupSymbolWrapper{
				Value: &elementsv1.Symbol{
					Symbol: &elementsv1.Symbol_Icon{Icon: "<icon>"},
				},
			},
		},
	}
	response, err := groupService.UpdateGroupMetadata(ctx, request)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(response)
}
```

Response

```json
{
  "group": {
    "rid": "<rid>",
    "org_rid": "<org rid>",
    "group_id": "<group id>",
    "display_name": "<display name>",
    "description": "<description>",
    "user_rids": [
      "<user rids>"
    ],
    "created_at": "2026-01-01T00:00:00Z",
    "updated_at": "2026-01-01T00:00:00Z",
    "symbol": {
      "icon": "<icon>"
    },
    "membership_managed_manually": true
  }
}
```

## Errors

[Errors](https://dev.nominal.io/api/grpc.md#errors) shows how these errors are sent.

| Code | Reason | Message |
| --- | --- | --- |
| `INVALID_ARGUMENT` | `GROUP_ERRORS_IDENTITY_MISMATCH` | Request identity field does not match the existing group |
| `INVALID_ARGUMENT` | `GROUP_ERRORS_INVALID_RID` | RID field is invalid |
| `INVALID_ARGUMENT` | `GROUP_ERRORS_MEMBERSHIP_NOT_MANAGED_MANUALLY` | Group membership is not managed manually |
| `INVALID_ARGUMENT` | `GROUP_ERRORS_MISSING_REQUIRED_FIELD` | Required field missing on request |
| `INVALID_ARGUMENT` | `GROUP_ERRORS_REQUESTED_PAGE_SIZE_TOO_LARGE` | Requested page size is too large |
| `INVALID_ARGUMENT` | `GROUP_ERRORS_USER_NOT_IN_ORG` | User is not a member of the group organization |
| `NOT_FOUND` | `GROUP_ERRORS_NOT_FOUND` | Group not found |
| `ALREADY_EXISTS` | `GROUP_ERRORS_ID_CONFLICT` | Group with given group_id already exists in organization |
| `PERMISSION_DENIED` | `GROUP_ERRORS_UNAUTHORIZED` | Not authorized for group |

## Types

### Group

A group represents a collection of users within an organization

`rid` string
:   ​

`org_rid` string
:   Required on create requests; immutable on updates.

`group_id` string
:   Group ID, unique per org. If sourced from the customer’s IdP, will match IdP group name. Required on create requests; immutable on updates.

`display_name` string
:   ​

`description` string
:   ​

`user_rids` array of string output only
:   The list of users in this group.

`created_at` string (date-time) output only
:   ​

`updated_at` string (date-time) output only
:   ​

`symbol` [`Symbol`](https://dev.nominal.io/api/grpc/group-service.md#symbol)
:   Symbol to identify groups

`membership_managed_manually` boolean
:   Whether group membership is managed manually in Nominal. Defaults to false.

### GroupMembership

A membership in a group.

`key` [`GroupMembershipKey`](https://dev.nominal.io/api/grpc/group-service.md#groupmembershipkey) required
:   Identity is a nested key so delete can address a membership without the full resource, which may gain fields later.

### GroupMembershipKey

Client-known identity of a group membership.

`group_rid` string required
:   At most 256 characters.

`member_rid` string required
:   The member of the group. Only user RIDs are accepted today. At most 256 characters.

### SearchGroupsQuery

Set at most one of the fields below.

one of `exact_substring_text`, `and`, `or`, `member_users`
:   ​

    **exact_substring_text**

    `exact_substring_text` string
    :   ​

    **and**

    `and` [`SearchQueryAnd`](https://dev.nominal.io/api/grpc/group-service.md#searchqueryand)
    :   and is implemented to allow for list (use SearchQueryAnd([]))

    **or**

    `or` [`SearchQueryOr`](https://dev.nominal.io/api/grpc/group-service.md#searchqueryor)
    :   ​

    **member_users**

    `member_users` [`SearchQueryMemberUsers`](https://dev.nominal.io/api/grpc/group-service.md#searchquerymemberusers)
    :   ​

### SearchQueryAnd

`queries` array of [`SearchGroupsQuery`](https://dev.nominal.io/api/grpc/group-service.md#searchgroupsquery)
:   ​

### SearchQueryMemberUsers

Matches the groups that any of the users is a direct member of, whether that membership is synced from the identity provider or managed manually.

`user_rids` array of string
:   1 to 1,000 items. Each item: at most 256 characters.

### SearchQueryOr

`queries` array of [`SearchGroupsQuery`](https://dev.nominal.io/api/grpc/group-service.md#searchgroupsquery)
:   ​

### Symbol

Set exactly one of the fields below.

one of `icon`, `emoji`, `image` required
:   ​

    **icon**

    `icon` string
    :   Icon name (e.g. castle)

    **emoji**

    `emoji` string
    :   Emoji name (e.g. :castle:)

    **image**

    `image` string
    :   Image url (e.g. https://example.com/image.png)

### UpdateGroupMetadataRequest

`display_name` string
:   ​

`description` string
:   ​

`symbol` [`UpdateGroupSymbolWrapper`](https://dev.nominal.io/api/grpc/group-service.md#updategroupsymbolwrapper)
:   ​

### UpdateGroupSymbolWrapper

`value` [`Symbol`](https://dev.nominal.io/api/grpc/group-service.md#symbol)
:   ​

Page design based on [Scalar](https://scalar.com), MIT License.
