{
  "swagger": "2.0",
  "info": {
    "title": "Group service",
    "version": "0.1507.0",
    "description": "Send your API key as a bearer token: `Authorization: Bearer <api key>`."
  },
  "tags": [
    {
      "name": "GroupService",
      "x-displayName": "Group service",
      "description": "Service for managing groups within an organization",
      "x-errors": [
        {
          "status": 403,
          "errorCode": "PERMISSION_DENIED",
          "errorName": "NominalAuthenticationGroupsV1:GroupErrorsUnauthorized",
          "message": "Not authorized for group"
        },
        {
          "status": 404,
          "errorCode": "NOT_FOUND",
          "errorName": "NominalAuthenticationGroupsV1:GroupErrorsNotFound",
          "message": "Group not found"
        },
        {
          "status": 400,
          "errorCode": "INVALID_ARGUMENT",
          "errorName": "NominalAuthenticationGroupsV1:GroupErrorsRequestedPageSizeTooLarge",
          "message": "Requested page size is too large"
        },
        {
          "status": 409,
          "errorCode": "CONFLICT",
          "errorName": "NominalAuthenticationGroupsV1:GroupErrorsIdConflict",
          "message": "Group with given group_id already exists in organization"
        },
        {
          "status": 400,
          "errorCode": "INVALID_ARGUMENT",
          "errorName": "NominalAuthenticationGroupsV1:GroupErrorsIdentityMismatch",
          "message": "Request identity field does not match the existing group"
        },
        {
          "status": 400,
          "errorCode": "INVALID_ARGUMENT",
          "errorName": "NominalAuthenticationGroupsV1:GroupErrorsMissingRequiredField",
          "message": "Required field missing on request"
        },
        {
          "status": 400,
          "errorCode": "INVALID_ARGUMENT",
          "errorName": "NominalAuthenticationGroupsV1:GroupErrorsUserNotInOrg",
          "message": "User is not a member of the group organization"
        },
        {
          "status": 400,
          "errorCode": "INVALID_ARGUMENT",
          "errorName": "NominalAuthenticationGroupsV1:GroupErrorsMembershipNotManagedManually",
          "message": "Group membership is not managed manually"
        },
        {
          "status": 400,
          "errorCode": "INVALID_ARGUMENT",
          "errorName": "NominalAuthenticationGroupsV1:GroupErrorsInvalidRid",
          "message": "RID field is invalid"
        }
      ]
    }
  ],
  "consumes": [
    "application/json"
  ],
  "produces": [
    "application/json"
  ],
  "paths": {
    "/authentication/groups/v1": {
      "post": {
        "description": "Org-scoped upsert.\n\nCreates 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.\n\nUpdates 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.\n\nThrows 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.",
        "operationId": "GroupService_CreateOrUpdateGroup",
        "responses": {
          "200": {
            "schema": {
              "$ref": "#/definitions/Group"
            },
            "x-example": {
              "rid": "<rid>",
              "orgRid": "<org rid>",
              "groupId": "<group id>",
              "displayName": "<display name>",
              "description": "<description>",
              "userRids": [
                "<user rids>"
              ],
              "createdAt": "2026-01-01T00:00:00Z",
              "updatedAt": "2026-01-01T00:00:00Z",
              "symbol": {
                "icon": "<icon>"
              },
              "membershipManagedManually": true
            }
          }
        },
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/CreateOrUpdateGroupRequest"
            },
            "x-example": {
              "group": {
                "rid": "<rid>",
                "orgRid": "<org rid>",
                "groupId": "<group id>",
                "displayName": "<display name>",
                "description": "<description>",
                "symbol": {
                  "icon": "<icon>"
                },
                "membershipManagedManually": true
              }
            }
          }
        ],
        "tags": [
          "GroupService"
        ],
        "summary": "Create or update group"
      }
    },
    "/authentication/groups/v1/batch-get": {
      "post": {
        "operationId": "GroupService_GetGroups",
        "responses": {
          "200": {
            "schema": {
              "type": "object",
              "additionalProperties": {
                "$ref": "#/definitions/Group"
              }
            },
            "x-example": {
              "<key>": {
                "rid": "<rid>",
                "orgRid": "<org rid>",
                "groupId": "<group id>",
                "displayName": "<display name>",
                "description": "<description>",
                "userRids": [
                  "<user rids>"
                ],
                "createdAt": "2026-01-01T00:00:00Z",
                "updatedAt": "2026-01-01T00:00:00Z",
                "symbol": {
                  "icon": "<icon>"
                },
                "membershipManagedManually": true
              }
            }
          }
        },
        "parameters": [
          {
            "name": "groupRids",
            "in": "body",
            "required": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "example": [
                "string"
              ]
            },
            "x-example": [
              "string"
            ]
          }
        ],
        "tags": [
          "GroupService"
        ],
        "summary": "Get groups",
        "description": "Get batch of groups within the user's organization"
      }
    },
    "/authentication/groups/v1/id/{groupId}": {
      "get": {
        "operationId": "GroupService_GetGroupById",
        "responses": {
          "200": {
            "schema": {
              "$ref": "#/definitions/Group"
            },
            "x-example": {
              "rid": "<rid>",
              "orgRid": "<org rid>",
              "groupId": "<group id>",
              "displayName": "<display name>",
              "description": "<description>",
              "userRids": [
                "<user rids>"
              ],
              "createdAt": "2026-01-01T00:00:00Z",
              "updatedAt": "2026-01-01T00:00:00Z",
              "symbol": {
                "icon": "<icon>"
              },
              "membershipManagedManually": true
            }
          }
        },
        "parameters": [
          {
            "name": "groupId",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ],
        "tags": [
          "GroupService"
        ],
        "summary": "Get group by ID",
        "description": "Gets a group by its ID within the user's organization"
      }
    },
    "/authentication/groups/v1/search": {
      "post": {
        "operationId": "GroupService_SearchGroups",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/SearchGroupsResponse"
            },
            "x-example": {
              "results": [
                {
                  "rid": "<rid>",
                  "orgRid": "<org rid>",
                  "groupId": "<group id>",
                  "displayName": "<display name>",
                  "description": "<description>",
                  "userRids": [
                    "<user rids>"
                  ],
                  "createdAt": "2026-01-01T00:00:00Z",
                  "updatedAt": "2026-01-01T00:00:00Z",
                  "symbol": {
                    "icon": "<icon>"
                  },
                  "membershipManagedManually": true
                }
              ],
              "nextPageToken": "<next page token>"
            }
          }
        },
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/SearchGroupsRequest"
            },
            "x-example": {
              "query": {
                "exactSubstringText": "<exact substring text>"
              }
            }
          }
        ],
        "tags": [
          "GroupService"
        ],
        "summary": "Search groups",
        "description": "Searches all groups in the user's organization using given query"
      }
    },
    "/authentication/groups/v1/{groupMembership.key.groupRid}/memberships": {
      "post": {
        "operationId": "GroupService_CreateGroupMembership",
        "responses": {
          "200": {
            "schema": {
              "$ref": "#/definitions/GroupMembership"
            },
            "x-example": {
              "key": {
                "groupRid": "<group rid>",
                "memberRid": "<member rid>"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "groupMembership.key.groupRid",
            "in": "path",
            "required": true,
            "type": "string",
            "maxLength": 256,
            "x-constraints": [
              "At most 256 characters."
            ]
          },
          {
            "name": "groupMembership",
            "in": "body",
            "required": true,
            "schema": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "object",
                  "properties": {
                    "memberRid": {
                      "type": "string",
                      "description": "The member of the group. Only user RIDs are accepted today."
                    }
                  },
                  "description": "Identity is a nested key so delete can address a membership without the full resource, which may gain fields later.",
                  "title": "Identity is a nested key so delete can address a membership without the full resource, which may gain fields later."
                }
              },
              "description": "A membership in a group.",
              "example": {
                "key": {
                  "memberRid": "<member rid>"
                }
              }
            },
            "x-example": {
              "key": {
                "memberRid": "<member rid>"
              }
            }
          }
        ],
        "tags": [
          "GroupService"
        ],
        "summary": "Create group membership",
        "description": "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."
      }
    },
    "/authentication/groups/v1/{groupRid}": {
      "get": {
        "operationId": "GroupService_GetGroup",
        "responses": {
          "200": {
            "schema": {
              "$ref": "#/definitions/Group"
            },
            "x-example": {
              "rid": "<rid>",
              "orgRid": "<org rid>",
              "groupId": "<group id>",
              "displayName": "<display name>",
              "description": "<description>",
              "userRids": [
                "<user rids>"
              ],
              "createdAt": "2026-01-01T00:00:00Z",
              "updatedAt": "2026-01-01T00:00:00Z",
              "symbol": {
                "icon": "<icon>"
              },
              "membershipManagedManually": true
            }
          }
        },
        "parameters": [
          {
            "name": "groupRid",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ],
        "tags": [
          "GroupService"
        ],
        "summary": "Get group",
        "description": "Get group within the user's organization"
      }
    },
    "/authentication/groups/v1/{groupRid}/metadata": {
      "put": {
        "operationId": "GroupService_UpdateGroupMetadata",
        "responses": {
          "200": {
            "schema": {
              "$ref": "#/definitions/Group"
            },
            "x-example": {
              "rid": "<rid>",
              "orgRid": "<org rid>",
              "groupId": "<group id>",
              "displayName": "<display name>",
              "description": "<description>",
              "userRids": [
                "<user rids>"
              ],
              "createdAt": "2026-01-01T00:00:00Z",
              "updatedAt": "2026-01-01T00:00:00Z",
              "symbol": {
                "icon": "<icon>"
              },
              "membershipManagedManually": true
            }
          }
        },
        "parameters": [
          {
            "name": "groupRid",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "request",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/UpdateGroupMetadataRequest"
            },
            "x-example": {
              "displayName": "<display name>",
              "description": "<description>",
              "symbol": {
                "value": {
                  "icon": "<icon>"
                }
              }
            }
          }
        ],
        "tags": [
          "GroupService"
        ],
        "summary": "Update group metadata",
        "description": "Updates the metadata of a group"
      }
    },
    "/authentication/groups/v1/{key.groupRid}/memberships/{key.memberRid}": {
      "delete": {
        "operationId": "GroupService_DeleteGroupMembership",
        "responses": {
          "200": {
            "description": "A successful response.",
            "schema": {
              "$ref": "#/definitions/DeleteGroupMembershipResponse"
            },
            "x-example": {}
          }
        },
        "parameters": [
          {
            "name": "key.groupRid",
            "in": "path",
            "required": true,
            "type": "string",
            "maxLength": 256,
            "x-constraints": [
              "At most 256 characters."
            ]
          },
          {
            "name": "key.memberRid",
            "description": "The member of the group. Only user RIDs are accepted today.",
            "in": "path",
            "required": true,
            "type": "string",
            "maxLength": 256,
            "x-constraints": [
              "At most 256 characters."
            ]
          }
        ],
        "tags": [
          "GroupService"
        ],
        "summary": "Delete group membership",
        "description": "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."
      }
    }
  },
  "definitions": {
    "CreateOrUpdateGroupRequest": {
      "type": "object",
      "properties": {
        "group": {
          "$ref": "#/definitions/Group"
        }
      },
      "required": [
        "group"
      ],
      "example": {
        "group": {
          "rid": "<rid>",
          "orgRid": "<org rid>",
          "groupId": "<group id>",
          "displayName": "<display name>",
          "description": "<description>",
          "symbol": {
            "icon": "<icon>"
          },
          "membershipManagedManually": true
        }
      }
    },
    "DeleteGroupMembershipResponse": {
      "type": "object"
    },
    "Group": {
      "type": "object",
      "properties": {
        "rid": {
          "type": "string"
        },
        "orgRid": {
          "type": "string",
          "description": "Required on create requests; immutable on updates."
        },
        "groupId": {
          "type": "string",
          "description": "Group ID, unique per org. If sourced from the customer's IdP, will match IdP group name. Required on create requests; immutable on updates."
        },
        "displayName": {
          "type": "string"
        },
        "description": {
          "type": "string"
        },
        "userRids": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "The list of users in this group.",
          "readOnly": true
        },
        "createdAt": {
          "type": "string",
          "format": "date-time",
          "readOnly": true
        },
        "updatedAt": {
          "type": "string",
          "format": "date-time",
          "readOnly": true
        },
        "symbol": {
          "$ref": "#/definitions/Symbol",
          "title": "Symbol to identify groups"
        },
        "membershipManagedManually": {
          "type": "boolean",
          "description": "Whether group membership is managed manually in Nominal. Defaults to false."
        }
      },
      "title": "A group represents a collection of users within an organization"
    },
    "GroupMembership": {
      "type": "object",
      "properties": {
        "key": {
          "$ref": "#/definitions/GroupMembershipKey",
          "description": "Identity is a nested key so delete can address a membership without the full resource, which may gain fields later."
        }
      },
      "description": "A membership in a group.",
      "required": [
        "key"
      ]
    },
    "GroupMembershipKey": {
      "type": "object",
      "properties": {
        "groupRid": {
          "type": "string",
          "maxLength": 256,
          "x-constraints": [
            "At most 256 characters."
          ]
        },
        "memberRid": {
          "type": "string",
          "description": "The member of the group. Only user RIDs are accepted today.",
          "maxLength": 256,
          "x-constraints": [
            "At most 256 characters."
          ]
        }
      },
      "description": "Client-known identity of a group membership.",
      "required": [
        "groupRid",
        "memberRid"
      ]
    },
    "SearchGroupsQuery": {
      "type": "object",
      "properties": {
        "exactSubstringText": {
          "type": "string"
        },
        "and": {
          "$ref": "#/definitions/SearchQueryAnd",
          "title": "and is implemented to allow for list (use SearchQueryAnd([]))"
        },
        "or": {
          "$ref": "#/definitions/SearchQueryOr"
        },
        "memberUsers": {
          "$ref": "#/definitions/SearchQueryMemberUsers"
        }
      },
      "x-oneofs": [
        {
          "fields": [
            "exactSubstringText",
            "and",
            "or",
            "memberUsers"
          ],
          "required": false
        }
      ],
      "description": "Set at most one of the fields below.",
      "example": {
        "exactSubstringText": "string"
      }
    },
    "SearchGroupsRequest": {
      "type": "object",
      "properties": {
        "pageSize": {
          "type": "integer",
          "format": "int32",
          "description": "Page sizes greater than 1_000 will be rejected. Default pageSize is 100."
        },
        "nextPageToken": {
          "type": "string"
        },
        "query": {
          "$ref": "#/definitions/SearchGroupsQuery"
        }
      },
      "required": [
        "query"
      ],
      "example": {
        "query": {
          "exactSubstringText": "<exact substring text>"
        }
      }
    },
    "SearchGroupsResponse": {
      "type": "object",
      "properties": {
        "results": {
          "type": "array",
          "items": {
            "type": "object",
            "$ref": "#/definitions/Group"
          }
        },
        "nextPageToken": {
          "type": "string"
        }
      }
    },
    "SearchQueryAnd": {
      "type": "object",
      "properties": {
        "queries": {
          "type": "array",
          "items": {
            "type": "object",
            "$ref": "#/definitions/SearchGroupsQuery"
          }
        }
      }
    },
    "SearchQueryMemberUsers": {
      "type": "object",
      "properties": {
        "userRids": {
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 256
          },
          "minItems": 1,
          "maxItems": 1000,
          "x-constraints": [
            "1 to 1,000 items.",
            "Each item: at most 256 characters."
          ]
        }
      },
      "description": "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."
    },
    "SearchQueryOr": {
      "type": "object",
      "properties": {
        "queries": {
          "type": "array",
          "items": {
            "type": "object",
            "$ref": "#/definitions/SearchGroupsQuery"
          }
        }
      }
    },
    "UpdateGroupMetadataRequest": {
      "type": "object",
      "properties": {
        "displayName": {
          "type": "string"
        },
        "description": {
          "type": "string"
        },
        "symbol": {
          "$ref": "#/definitions/UpdateGroupSymbolWrapper"
        }
      },
      "example": {
        "displayName": "<display name>",
        "description": "<description>",
        "symbol": {
          "value": {
            "icon": "<icon>"
          }
        }
      }
    },
    "UpdateGroupSymbolWrapper": {
      "type": "object",
      "properties": {
        "value": {
          "$ref": "#/definitions/Symbol"
        }
      }
    },
    "Symbol": {
      "type": "object",
      "properties": {
        "icon": {
          "type": "string",
          "title": "Icon name (e.g. castle)"
        },
        "emoji": {
          "type": "string",
          "title": "Emoji name (e.g. :castle:)"
        },
        "image": {
          "type": "string",
          "title": "Image url (e.g. https://example.com/image.png)"
        }
      },
      "x-oneofs": [
        {
          "fields": [
            "icon",
            "emoji",
            "image"
          ],
          "required": true
        }
      ],
      "description": "Set exactly one of the fields below.",
      "example": {
        "icon": "string"
      }
    }
  },
  "securityDefinitions": {
    "ApiKey": {
      "type": "apiKey",
      "in": "header",
      "name": "Authorization",
      "description": "Your API key as a bearer token: `Authorization: Bearer <api key>`."
    }
  },
  "security": [
    {
      "ApiKey": []
    }
  ]
}
