# Groups

**URL:** https://heroiclabs.com/docs/nakama/concepts/groups/
**Summary:** Groups enable players to chat and play together as a team or community. These player groups can be public or private, requiring approval from group admin(s) to join.
**Keywords:** list groups, join groups, list user's groups, list group members, create group, update group, leave group, manage group, accept group members, approve group members, promote group member, demote group member, kick group member, ban group member, remove group, delete group, metadata
**Categories:** nakama, groups, concepts

---


# Groups

A group or clan brings together a bunch of users into a small community or team.

A group is made up of a superadmin, admins, and members. It can be public or private, which determines whether anyone can join the group. Private groups (`open=false`) are similar to how WhatsApp groups work, a user can only be added when they're invited to join by one of the group's admins.

A group also has a maximum member count. This is set to 100 by default if the group is created by the client, or can be overridden if the group is created by the code runtime.

<!-- more -->

A group user has four states:

| Code | Purpose      |                                                                                                                                                                       |
|
------|--------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 0    | Superadmin   | There must at least be 1 superadmin in any group. The superadmin has all the privileges of the admin and can additionally delete the group and promote admin members. |
| 1    | Admin        | There can be one of more admins. Admins can update groups as well as accept, kick, promote, demote, ban or add members.                                               |
| 2    | Member       | Regular group member. They cannot accept join requests from new users.                                                                                                |
| 3    | Join request | A new join request from a new user. This does not count towards the maximum group member count.                                                                       |

## List and filter groups

Groups can be listed using a number of optional filters: `name`, `lang_tag`, `open` and (number of) `members`. If all filters are omitted, the operation will list all existing groups.

The `name` filter is case insensitive and mutually exclusive to the remainder filters. It can be useful to help the user look for a specific group by name, and it supports the `%` wildcard for partial matches as a suffix. As an example, looking for a group that is prefixed with the "Persian" word would be written as `persian%` name filter.

The remainder filters can be combined or omitted in any way, for instance, we could use the `open` and `members` filters to list all open groups with at most the specified amount of members.

{{< code type="client" >}}
```javascript
const groups = await client.listGroups(session, "heroes%", 20); // fetch first 20 groups
console.info("List of groups:", groups);
```
{{< / code >}}

{{< code type="client" >}}
```csharp
// Filter for group names which start with "heroes"
const string nameFilter = "heroes%";
var result = await client.ListGroupsAsync(session, nameFilter, 20);
foreach (var g in result.Groups)
{
    System.Console.WriteLine("Group name '{0}' count '{1}'", g.Name, g.EdgeCount);
}
```
{{< / code >}}

{{< code type="client" >}}
```swift
// Filter for group names which start with "heroes"
let nameFilter = "heroes%"
var result = try await client.listGroups(session: session, name: nameFilter, limit: 20)
for g in result.groups {
    debugPrint("Group name", g.name, "count", g.edgeCount)
}
```
{{< / code >}}

{{< code type="client" >}}
```dart
// Filter for group names which start with "heroes"
const nameFilter = 'heroes%';
final result = await client.listGroups(
  session: session,
  name: nameFilter,
  limit: 20,
);
for (final g in result.groups) {
  print('Group name ${g.name}, count ${g.edgeCount}');
}```
{{< / code >}}

{{< code type="client" >}}
```cpp
auto successCallback = [this](NGroupListPtr list)
{
    for (auto& group : list->groups)
    {
        std::cout << "Group name '" << group.name << "' count " << group.edgeCount << std::endl;
    }
};

// Filter for group names which start with "heroes"
// fetch first 20 groups
client->listGroups(session, "heroes%", 20, "", successCallback);
```
{{< / code >}}

{{< code type="client" >}}
```java
// Filter for group names which start with "heroes"
String nameFilter = "heroes%";
GroupList groups = client.listGroups(session, nameFilter, 20).get();

for (Group group : groups.getGroupsList()) {
    System.out.format("Group name %s count %s", group.getName(), group.getEdgeCount());
}
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var list : NakamaAPI.ApiGroupList = yield(client.list_groups_async(session, "heroes*", 20), "completed")

if list.is_exception():
    print("An error occurred: %s" % list)
    return

for g in list.groups:
    var group = g as NakamaAPI.ApiGroup
    print("Group: name %s, id %s", [group.name, group.id])
```
{{< / code >}}

{{< code type="client" framework="godot4" >}}
```gdscript
var list : NakamaAPI.ApiGroupList = await client.list_groups_async(session, "heroes*", 20)

if list.is_exception():
    print("An error occurred: %s" % list)
    return

for g in list.groups:
    var group = g as NakamaAPI.ApiGroup
    print("Group: name %s, id %s", [group.name, group.id])
```
{{< / code >}}

{{< code type="client" >}}
```bash
curl "http://127.0.0.1:7350/v2/group?limit=20&name=heroes%25" \
  -H 'Authorization: Bearer <session token>'
```
{{< / code >}}

{{< code type="client" >}}
```shell
GET /v2/group?limit=20&name=heroes%&cursor=<cursor>
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local result = client.list_groups("heroes*", "<cursor>", 20)

for _,group in ipairs(result.groups) do
  pprint(groups)
end
```
{{< / code >}}

The message response for a list of groups contains a cursor. The cursor can be used to quickly retrieve the next set of results.

{{< code type="client" >}}
```bash
curl -X GET "http://127.0.0.1:7350/v2/group?limit=20&name=heroes%25&cursor=somecursor" \
  -H 'Authorization: Bearer <session token>'
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const groups = await client.listGroups(session, "heroes%", 20, cursor);
console.info("List of groups:", groups);
```
{{< / code >}}

{{< code type="client" >}}
```csharp
// Filter for group names which start with "heroes"
const string nameFilter = "heroes%";
var result = await client.ListGroupsAsync(session, nameFilter, 20);

// If there are more results get next page.
if (result.Cursor != null)
{
    result = await client.ListGroupsAsync(session, nameFilter, 20, result.Cursor);

    foreach (var g in result.Groups)
    {
        System.Console.WriteLine("Group name '{0}' count '{1}'", g.Name, g.EdgeCount);
    }
}
```
{{< / code >}}

{{< code type="client" >}}
```swift
// Filter for group names which start with "heroes"
let nameFilter = "heroes%"
var result = try await client.listGroups(session: session, name: nameFilter, limit: 20)

// If there are more results get next page.
if !result.cursor.isEmpty {
    result = try await client.listGroups(session: session, name: nameFilter, limit: 20, cursor: result.cursor)

    for g in result.groups {
        debugPrint("Group name", g.name, "count", g.edgeCount)
    }
}
```
{{< / code >}}

{{< code type="client" >}}
```dart
// Filter for group names which start with "heroes"
const nameFilter = 'heroes%';
var result = await client.listGroups(
  session: session,
  name: nameFilter,
  limit: 20,
);

// If there are more results get next page.
if (result.cursor != null) {
  result = await client.listGroups(session: session, name: nameFilter, limit: 20, cursor: result.cursor);

  for (final g in result.groups) {
    print('Group name ${g.name}, count ${g.edgeCount}');
  }
}
```
{{< / code >}}

{{< code type="client" >}}
```cpp
void YourClass::processGroupList(NGroupListPtr list)
{
    for (auto& group : list->groups)
    {
        std::cout << "Group name '" << group.name << "' count " << group.edgeCount << std::endl;
    }

    if (!list->cursor.empty())
    {
        // request next page
        requestHeroes(list->cursor);
    }
}

void YourClass::requestHeroes(const string& cursor)
{
    // Filter for group names which start with "heroes"
    // fetch first 20 groups
    client->listGroups(session, "heroes%", 20, cursor, std::bind(&YourClass::processGroupList, this, std::placeholders::_1));
}
```
{{< / code >}}

{{< code type="client" >}}
```java
// Filter for group names which start with "heroes"
String nameFilter = "heroes%";
GroupList groups = client.listGroups(session, nameFilter, 20).get();

if (groups.getCursor() != null) {
    groups = client.listGroups(session, nameFilter, 20, groups.getCursor()).get();

    for (Group group : groups.getGroupsList()) {
        System.out.format("Group name %s count %s", group.getName(), group.getEdgeCount());
    }
}
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var list : NakamaAPI.ApiGroupList = yield(client.list_groups_async(session, "heroes*", 20), "completed")

if list.is_exception():
    print("An error occurred: %s" % list)
    return

for g in list.groups:
    var group = g as NakamaAPI.ApiGroup
    print("Group: name %s, id %s", [group.name, group.id])

var cursor = list.cursor

while cursor: # While there are more results get next page.
    list = yield(client.list_groups_async(session, "heroes*", 20, cursor), "completed")

    if list.is_exception():
        print("An error occurred: %s" % list)
        return

    for g in list.groups:
        var group = g as NakamaAPI.ApiGroup
        print("Group: name %s, id %s", [group.name, group.id])

    cursor = list.cursor
```
{{< / code >}}

{{< code type="client" framework="godot4" >}}
```gdscript
var list : NakamaAPI.ApiGroupList = await client.list_groups_async(session, "heroes*", 20)

if list.is_exception():
    print("An error occurred: %s" % list)
    return

for g in list.groups:
    var group = g as NakamaAPI.ApiGroup
    print("Group: name %s, id %s", [group.name, group.id])

var cursor = list.cursor

while cursor: # While there are more results get next page.
    list = await client.list_groups_async(session, "heroes*", 20, cursor)

    if list.is_exception():
        print("An error occurred: %s" % list)
        return

    for g in list.groups:
        var group = g as NakamaAPI.ApiGroup
        print("Group: name %s, id %s", [group.name, group.id])

    cursor = list.cursor
```
{{< / code >}}

{{< code type="client" >}}
```shell
GET /v2/group?limit=20&name=heroes%&cursor=somecursor
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local cursor = nil

repeat
  local result = client.list_groups("heroes*", cursor, 20)

  if result.error then
    print(result.message)
    break
  end

  cursor = result.cursor

  for _,group in ipairs(result.groups) do
    pprint(groups)
  end
until not cursor
```
{{< / code >}}

## Join groups

When a user has found a group to join they can request to become a member. A public group can be joined without any need for permission while a private group requires a [superadmin or an admin to accept](#accept-new-members) the user.

When a user joins or leaves a group event messages are added to chat history. This makes it easy for members to see what's changed in the group.

A user who's part of a group can join [group chat](../chat/#groups) and access it's [message history](../chat/#message-history).

{{< code type="client" >}}
```bash
curl -X POST "http://127.0.0.1:7350/v2/group/<group id>/join" \
  -H 'Authorization: Bearer <session token>'
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const group_id = "<group id>";
await client.joinGroup(session, group_id);
console.info("Sent group join request", group_id);
```
{{< / code >}}

{{< code type="client" >}}
```csharp
const string groupId = "<group id>";
await client.JoinGroupAsync(session, groupId);
System.Console.WriteLine("Sent group join request '{0}'", groupId);
```
{{< / code >}}

{{< code type="client" >}}
```swift
let groupId = "<group id>"
try await client.joinGroup(session: session, groupId: groupId)
debugPrint("Sent group join request", groupId)
```
{{< / code >}}

{{< code type="client" >}}
```dart
const groupId = '<group id>';
await client.joinGroup(
  session: session,
  groupId: groupId,
);
print('Sent group join request ${groupId}');
```
{{< / code >}}

{{< code type="client" >}}
```cpp
auto successCallback = []()
{
    std::cout << "Sent group join request" << std::endl;
};

string group_id = "<group id>";
client->joinGroup(session, group_id, successCallback);
```
{{< / code >}}

{{< code type="client" >}}
```java
String groupid = "<group id>";
client.joinGroup(session, groupid).get();
System.out.format("Sent group join request %s", groupid);
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var group_id = "<group id>"
var join : NakamaAsyncResult = yield(client.join_group_async(session, group_id), "completed")

if join.is_exception():
    print("An error occurred: %s" % join)
    return

print("Sent group join request %s" % group_id)
```
{{< / code >}}

{{< code type="client" framework="godot4" >}}
```gdscript
var group_id = "<group id>"
var join : NakamaAsyncResult = await client.join_group_async(session, group_id)

if join.is_exception():
    print("An error occurred: %s" % join)
    return

print("Sent group join request %s" % group_id)
```
{{< / code >}}

{{< code type="client" >}}
```shell
POST /v2/group/<group id>/join
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local group_id = "<group_id>"
local result = client.join_group(group_id)

if result.error then
  print(result.message)
  return
end

print("Sent group join request", group_id)
```
{{< / code >}}

The user will receive an [in-app notification](../notifications/) when they've been added to the group. In a private group an admin or superadmin will receive a notification when a user has requested to join.

## List a user's groups

Each user can list groups they've joined as a member or an admin or a superadmin. The list also contains groups which they've requested to join but not been accepted into yet.

{{< code type="client" >}}
```bash
curl -X GET "http://127.0.0.1:7350/v2/user/<user id>/group" \
  -H 'Authorization: Bearer <session token>'
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const userId = "<user id>";
const groups = await client.listUserGroups(session, userid);

groups.user_groups.forEach(function(userGroup){
  console.log("Group: name '%o' id '%o'.", userGroup.group.name, userGroup.group.id);
  // group.State is one of: SuperAdmin, Admin, Member, or Join.
  console.log("Group's state is %o.", userGroup.state);
});
```
{{< / code >}}

{{< code type="client" >}}
```csharp
const string userId = "<user id>";
var result = await client.ListUserGroupsAsync(session, userId);

foreach (var ug in result.UserGroups)
{
    var g = ug.Group;
    System.Console.WriteLine("Group '{0}' role '{1}'", g.Id, ug.State);
}
```
{{< / code >}}

{{< code type="client" >}}
```swift
let userId = "<user id>"
var result = try await client.listUserGroups(session: session, userId: userId)

for ug in result.userGroups {
    debugPrint("Group: name", ug.group.name, "role", ug.state)
}
```
{{< / code >}}

{{< code type="client" >}}
```dart
const userId = '<user id>';
final result = await client.listUserGroups(
  session: session,
  userId: userId,
);

for (final ug in result.userGroups) {
  print('Group: name ${ug.group.name}, role ${ug.state}');
}
```
{{< / code >}}

{{< code type="client" >}}
```cpp
auto successCallback = [](NUserGroupListPtr list)
{
    for (auto& userGroup : list->userGroups)
    {
        std::cout << "Group name " << userGroup.group.name << std::endl;
    }
};

string userId = "<user id>";
client->listUserGroups(session, userId, {}, {}, {}, successCallback);
```
{{< / code >}}

{{< code type="client" >}}
```java
String userid = "<user id>";
UserGroupList userGroups = client.listUserGroups(session, userid).get();

for (UserGroupList.UserGroup userGroup : userGroups.getUserGroupsList()) {
    System.out.format("Group name %s role %d", userGroup.getGroup().getName(), userGroup.getState());
}
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var user_id = "<user id>"
var result : NakamaAPI.ApiUserGroupList = yield(client.list_user_groups_async(session, user_id), "completed")

if result.is_exception():
    print("An error occurred: %s" % result)
    return

for ug in result.user_groups:
    var g = ug.group as NakamaAPI.ApiGroup
    print("Group %s role %s", g.id, ug.state)
```
{{< / code >}}

{{< code type="client" framework="godot4" >}}
```gdscript
var user_id = "<user id>"
var result : NakamaAPI.ApiUserGroupList = await client.list_user_groups_async(session, user_id)

if result.is_exception():
    print("An error occurred: %s" % result)
    return

for ug in result.user_groups:
    var g = ug.group as NakamaAPI.ApiGroup
    print("Group %s role %s", g.id, ug.state)
```
{{< / code >}}

{{< code type="client" >}}
```shell
GET /v2/user/<user id>/group
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local user_id = "<user>"
local result = client.list_user_groups(user_id)

if result.error then
  print(result.message)
  return
end

for _,user_group in ipairs(result.user_groups) do
  pprint(user_group)
end
```
{{< / code >}}

## List group members

A user can list all members who're part of their group. These include other users who've requested to join the private group but not been accepted into yet.

{{< code type="client" >}}
```bash
curl -X GET "http://127.0.0.1:7350/v2/group/<group id>/user" \
  -H 'Authorization: Bearer <session token>'
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const group_id = "<group id>";
const users = await client.listGroupUsers(session, group_id);
console.info("Users in group:", users);
```
{{< / code >}}

{{< code type="client" >}}
```csharp
const string groupId = "<group id>";
var result = await client.ListGroupUsersAsync(session, groupId);
foreach (var ug in result.UserGroups)
{
    var g = ug.Group;
    System.Console.WriteLine("group '{0}' role '{1}'", g.Id, ug.State);
}
```
{{< / code >}}

{{< code type="client" >}}
```swift
let groupId = "<group id>"
let result = try await client.listGroupUsers(session: session, groupId: groupId, limit: 20)
for ug in result.groupUsers {
    let g = ug.user
    print("group '\(g.id)' role '\(ug.state)'")
}
```
{{< / code >}}

{{< code type="client" >}}
```dart
const groupId = '<group id>';
final result = await client.listGroupUsers(
  session: session,
  groupId: groupId,
);
for (final ug in result.groupUsers) {
  final g = ug.user;
  print('group ${g.id} role ${ug.state}');
}
```
{{< / code >}}

{{< code type="client" >}}
```cpp
auto successCallback = [](NGroupUserListPtr list)
{
    std::cout << "Users in group: " << list->groupUsers << std::endl;
};

string group_id = "<group id>";
client->listGroupUsers(session, group_id, {}, {}, {}, successCallback);
```
{{< / code >}}

{{< code type="client" >}}
```java
String groupid = "<group id>";
GroupUserList groupUsers = client.listGroupUsers(session, groupid).get();

for (GroupUserList.GroupUser groupUser : groupUsers.getGroupUsersList()) {
    System.out.format("Username %s role %d", groupUser.getUser().getUsername(), groupUser.getState());
}
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var group_id = "<group id>"
var member_list : NakamaAPI.ApiGroupUserList = yield(client.list_group_users_async(session, group_id), "completed")

if member_list.is_exception():
    print("An error occurred: %s" % member_list)
    return

for ug in member_list.group_users:
    var u = ug.user as NakamaAPI.ApiUser
    print("User %s role %s" % [u.id, ug.state])
```
{{< / code >}}

{{< code type="client" framework="godot4" >}}
```gdscript
var group_id = "<group id>"
var member_list : NakamaAPI.ApiGroupUserList = await client.list_group_users_async(session, group_id)

if member_list.is_exception():
    print("An error occurred: %s" % member_list)
    return

for ug in member_list.group_users:
    var u = ug.user as NakamaAPI.ApiUser
    print("User %s role %s" % [u.id, ug.state])
```
{{< / code >}}

{{< code type="client" >}}
```shell
GET /v2/group/<group id>/user
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local group_id = "<group_id>"
local result = client.list_group_users(group_id)

if result.error then
  print(result.message)
  return
end

for _,user in ipairs(result.group_users) do
  pprint(user)
end
```
{{< / code >}}

## Create a group

A group can be created with a name and other optional fields. These optional fields are used when a user [lists and filter groups](#list-and-filter-groups). The user who creates the group becomes the owner and a superadmin for it.

{{< code type="client" >}}
```bash
curl -X POST "http://127.0.0.1:7350/v2/group" \
  -H 'Authorization: Bearer <session token>' \
  -d '{
    "name": "pizza-lovers",
    "description": "pizza lovers, pineapple haters",
    "lang_tag": "en_US",
    "open": true
  }'
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const group_name = "pizza-lovers";
const description = "pizza lovers, pineapple haters";

const group = await client.createGroup(session, {
  name: group_name,
  description: description,
  lang_tag: "en_US",
  open: true
});

console.info("New group:", group);
```
{{< / code >}}

{{< code type="client" >}}
```csharp
const string name = "pizza-lovers";
const string desc = "pizza lovers, pineapple haters";
var group = await client.CreateGroupAsync(session, name, desc);
System.Console.WriteLine("New group: {0}", group);
```
{{< / code >}}

{{< code type="client" >}}
```swift
let name = "pizza-lovers"
let desc = "pizza lovers, pineapple haters"
let group = try await client.createGroup(session: session, name: name, description: desc)
debugPrint("New group:", group)
```
{{< / code >}}

{{< code type="client" >}}
```dart
const name = 'pizza-lovers';
const desc = 'pizza lovers, pineapple haters';
final group = await client.createGroup(
  session: session,
  name: name,
  description: desc,
);
print('New group: ${group}');
```
{{< / code >}}

{{< code type="client" >}}
```cpp
auto successCallback = [](const NGroup& group)
{
    std::cout << "New group ID: " << group.id << std::endl;
};

string group_name = "pizza-lovers";
string description = "pizza lovers, pineapple haters";
client->createGroup(session, group_name, description, "", "en_US", true, {}, successCallback);
```
{{< / code >}}

{{< code type="client" >}}
```java
String name = "pizza-lovers";
String desc = "pizza lovers, pineapple haters";
Group group = client.createGroup(session, name, desc).get();
System.out.format("New group %s", group.getId());
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var group_name = "pizza-lovers"
var group_desc = "pizza lovers, pineapple haters"
var group : NakamaAPI.ApiGroup = yield(client.create_group_async(session, group_name, group_desc), "completed")

if group.is_exception():
    print("An error occurred: %s" % group)
    return

print("New group: %s" % group)
```
{{< / code >}}

{{< code type="client" framework="godot4" >}}
```gdscript
var group_name = "pizza-lovers"
var group_desc = "pizza lovers, pineapple haters"
var group : NakamaAPI.ApiGroup = await client.create_group_async(session, group_name, group_desc)

if group.is_exception():
    print("An error occurred: %s" % group)
    return

print("New group: %s" % group)
```
{{< / code >}}

{{< code type="client" >}}
```shell
POST /v2/group
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
  "name": "pizza-lovers",
  "description": "pizza lovers, pineapple haters",
  "lang_tag": "en_US",
  "open": true
}
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local avatar_url = ""
local description = "pizza lovers, pineapple haters"
local lang_tag = "en-gb"
local max_count = 10
local name = "pizza-lovers"
local open = true

local result = client.create_group(avatar_url, description, lang_tag, max_count, name, open)

if result.error then
  print(result.message)
  return
end
```
{{< / code >}}

You can also create a group with server-side code. This can be useful when the group must be created together with some other record or feature.

{{< code type="server" >}}
```lua
local nk = require("nakama")

local metadata = { -- Add whatever custom fields you want.
  my_custom_field = "some value"
}

local user_id = "<user id>"
local name = "pizza-lovers"
local creator_id = user_id
local lang = "en_US"
local description = "pizza lovers, pineapple haters"
local avatar_url = "url://somelink"
local open = true
local maxMemberCount = 100

local success, err = pcall(nk.group_create, user_id, name, creator_id, lang, description, avatar_url, open, metadata, maxMemberCount)

if (not success) then
  nk.logger_error(("Could not create group: %q"):format(err))
end
```
{{< / code >}}

{{< code type="server" >}}
```go
metadata := map[string]interface{}{
    "my_custom_field": "some value", // Add whatever custom fields you want.
}

userID := "<user id>"
creatorID := userID
name := "pizza-lovers"
description := "pizza lovers, pineapple haters"
langTag := "en"
open := true
avatarURL := "url://somelink"
maxCount := 100

if _, err := nk.GroupCreate(ctx, userID, name, creatorID, langTag, description, avatarURL, open, metadata, maxCount); err != nil {
    logger.Error("Could not create group: %s", err.Error())
}
```
{{< / code >}}

{{< code type="server" >}}
```typescript
let userId = 'dcb891ea-a311-4681-9213-6741351c9994';
let creatorId = 'dcb891ea-a311-4681-9213-6741351c9994';
let name = 'Some unique group name';
let description = 'My awesome group.';
let lang = 'en';
let open = true;
let avatarURL = 'url://somelink';
let metadata = { custom_field: 'some_value' };
let maxCount = 100;

let group = {} as nkruntime.Group;

try {
    group = nk.groupCreate(userId, name, creatorId, lang, description, avatarURL, open, metadata, maxCount);
} catch (error) {
    // Handle error
}
```
{{< / code >}}

### Group metadata

You can store additional fields for a group in `group.metadata`. This is useful to share data you want to be publicly available to users, and providing additional details usable for listing and filtering groups.

Metadata can also be used to [create new functionality](./best-practices/#use-metadata-to-layer-new-functionality) on top of existing groups features.

Metadata is limited to 16KB per group and can only be set via the [script runtime](../../server-framework/).

The following example shows how you might use group metadata to extend Nakama's group member permissions with added roles (as described in the [best practices guide](./best-practices/#use-metadata-to-layer-new-functionality)). Specifically, we will introduce the concept of a **bouncer** role which a group member must have in order to kick another member from the group.

{{< code type="server" >}}
```typescript
// Assuming a group metadata structure as follows
const metadata = {
  roles: {
    '000d8152-3258-457b-905b-05a9223c5c8c': ['bouncer'],
    '2c0c8e80-fcbc-4b61-901a-dace129f45f5': ['bouncer', 'vip'],
  }
}

// Add a before hook to only allow bouncers to kick group users
let BeforeKickGroupUser: nkruntime.BeforeHookFunction<KickGroupUsersRequest> = function (ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, data: nkruntime.KickGroupUsersRequest): nkruntime.KickGroupUsersRequest | void {
  const groups = nk.groupsGetId([data.groupId]);
  if (groups.length == 0) {
    logger.warn('Invalid group Id');
    return null;
  }
  
  // Only continue with the Kick request if the actioning user has the bouncer role
  const roles = groups[0].metadata.roles;
  if (roles && roles[ctx.userId] && roles[ctx.userId].includes('bouncer')) {
    return data;
  }

  logger.warn("you must be a bouncer to kick group members")
  return null;
};

// Register inside InitModule
initializer.registerBeforeKickGroupUsers(BeforeKickGroupUser);
```
{{< / code >}}

{{< code type="server" >}}
```go
metadata := map[string]interface{}{
  "roles": map[string][]string{
    "000d8152-3258-457b-905b-05a9223c5c8c": {"bouncer"},
    "2c0c8e80-fcbc-4b61-901a-dace129f45f5": {"bouncer", "vip"},
  },
}

// Add a before hook to only allow bouncers to kick group users
if err := initializer.RegisterBeforeKickGroupUsers(func (ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, in *api.KickGroupUsersRequest) (*api.KickGroupUsersRequest, error) {
  userId, ok := ctx.Value(runtime.RUNTIME_CTX_USER_ID).(string)
  if !ok {
    logger.Error("invalid user")
    return nil, runtime.NewError("invalid user", 13)
  }

  groups, err := nk.GroupsGetId(ctx, []string { in.GroupId })
  if err != nil || len(groups) == 0 {
    logger.Error("group not found")
    return nil, runtime.NewError("group not found", 5)
  }

  // Only continue with the Kick request if the actioning user has the bouncer role
  var metadata map[string]interface{}
  if err := json.Unmarshal([]byte(groups[0].GetMetadata()), &metadata); err != nil {
    logger.Error("error deserializing metadata")
    return nil, runtime.NewError("error deserializing metadata", 13)
  }

  for _, role := range metadata.Roles[userId] {
    if role == "bouncer" {
      return in, nil
    }
  }

  return nil, runtime.NewError("you must be a bouncer to kick group members", 7)
}); err != nil {
  logger.Error("unable to register before kick group users hook: %v", err)
  return err
}
```
{{< / code >}}


{{< code type="server" >}}
```lua
-- Assuming a group metadata structure as follows
local metadata = {
  ["roles"] = {
    ["000d8152-3258-457b-905b-05a9223c5c8c"] = { "bouncer" },
    ["2c0c8e80-fcbc-4b61-901a-dace129f45f5"] = { "bouncer", "vip" },
  }
}

-- Add a before hook to only allow bouncers to kick group users
local function before_kick_group_users(context, payload)
    local groups = nk.groups_get_id({ payload.group_id })
    if #groups == 0 then
        nk.logger_error("group not found")
        return nil
    end

    local roles = groups[1].metadata["roles"]
    if roles == nil or roles[context.user_id] == nil then
        nk.logger_error("no roles configured for user")
        return nil
    end

    for _, role in ipairs(roles[context.user_id]) do
        if role == "bouncer" then
            return payload
        end
    end

    nk.logger_error("you must be a bouncer to kick group members")
    return nil
end

nk.register_req_before(before_kick_group_users, "KickGroupUsers")
```
{{< / code >}}

## Update a group

When a group has been created it's admins can update optional fields.

{{< code type="client" >}}
```bash
curl -X PUT "http://127.0.0.1:7350/v2/group/<group id>" \
  -H 'Authorization: Bearer <session token>' \
  -d '{
    "description": "Better than Marvel Heroes!",
  }'
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const group_id = "<group id>";
const description = "Better than Marvel Heroes!";
const group = await client.updateGroup(session, group_id, { description: description });
console.info("Updated group:", group);
```
{{< / code >}}

{{< code type="client" >}}
```csharp
const string groupId = "<group id>";
const string desc = "Better than Marvel Heroes!";
var group = await client.UpdateGroupAsync(session, groupId, null, desc);
System.Console.WriteLine("Updated group: {0}", group);
```
{{< / code >}}

{{< code type="client" >}}
```swift
let groupId = "<group id>"
let description = "Better than Marvel Heroes!"
try await client.updateGroup(session: session, groupId: groupId, description: description)
debugPrint("Updated group:", groupId)
```
{{< / code >}}

{{< code type="client" >}}
```dart
const groupId = '<group id>';
const description = 'Better than Marvel Heroes!';
await client.updateGroup(
  session: session,
  groupId: groupId,
  description: description,
);
print('Updated group: ${groupId}');
```
{{< / code >}}

{{< code type="client" >}}
```cpp
auto successCallback = []()
{
    std::cout << "Updated group" << std::endl;
};

string group_id = "<group id>";
string description = "Better than Marvel Heroes!";
client->updateGroup(session, group_id, opt::nullopt, description, opt::nullopt, opt::nullopt, opt::nullopt, successCallback);
```
{{< / code >}}

{{< code type="client" >}}
```java
String groupid = "<group id>";
String desc = "Better than Marvel Heroes!";
client.updateGroup(session, groupid, null, desc).get();
System.out.format("Updated group %s", groupid);
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var group_id = "<group id>"
var description = "Better than Marvel Heroes!"
var update : NakamaAsyncResult = yield(client.update_group_async(session, group_id, null, description), "completed")

if update.is_exception():
    print("An error occurred: %s" % update)
    return

print("Updated group")
```
{{< / code >}}

{{< code type="client" framework="godot4" >}}
```gdscript
var group_id = "<group id>"
var description = "Better than Marvel Heroes!"
var update : NakamaAsyncResult = await client.update_group_async(session, group_id, null, description)

if update.is_exception():
    print("An error occurred: %s" % update)
    return

print("Updated group")
```
{{< / code >}}

{{< code type="client" >}}
```shell
PUT /v2/group/<group id>
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
  "description": "I was only kidding. Basil sauce ftw!",
}
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local group_id = "<group id>"
local avatar_url = "http://example.com/example.jpg"

local result = client.update_group(group_id, avatar_url)

if result.error then
  print(result.message)
end
```
{{< / code >}}

## Updating group size

Updating a group's size is an action that can only be performed authoritatively via the server.

{{< code type="server" >}}
```lua
local nk = require("nakama")

local new_max_size = 50
local success, err = pcall(nk.group_update("<GroupId>", nil, "", "", "", "", "", nil, nil, new_max_size))

if (not success) then
  nk.logger_error(("Could not update group: %q"):format(err))
end
```
{{< / code >}}

{{< code type="server" >}}
```go
newMaxSize := 50
open := true

if err := nk.GroupUpdate(ctx, "<GroupId>", "", "", "", "", "", open, nil, newMaxSize); err != nil {
    return err
}
```
{{< / code >}}

{{< code type="server" >}}
```typescript
let newMaxSize = 50
nk.groupUpdate("<GroupId>", "", null, null, null, null, null, null, null, newMaxSize);
```
{{< / code >}}

## Leave a group

A user can leave a group and will no longer be able to join [group chat](../chat/#groups) or read [message history](../chat/#message-history). If the user is a superadmin they will only be able to leave when at least one other superadmin exists in the group.

Any user who leaves the group will generate an event message in group chat which other members can read.

{{< code type="client" >}}
```bash
curl -X POST "http://127.0.0.1:7350/v2/group/<group id>/leave" \
  -H 'Authorization: Bearer <session token>'
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const group_id = "<group id>";
await client.leaveGroup(session, group_id);
```
{{< / code >}}

{{< code type="client" >}}
```csharp
const string groupId = "<group id>";
await client.LeaveGroupAsync(session, groupId);
```
{{< / code >}}

{{< code type="client" >}}
```swift
let groupId = "<group id>"
try await client.leaveGroup(session: session, groupId: groupId)
```
{{< / code >}}

{{< code type="client" >}}
```dart
const groupId = '<group id>';
await client.leaveGroup(
  session: session,
  groupId: groupId,
);
```
{{< / code >}}

{{< code type="client" >}}
```cpp
string groupId = "<group id>";
client->leaveGroup(session, groupId);
```
{{< / code >}}

{{< code type="client" >}}
```java
String groupId = "<group id>";
client.leaveGroup(session, groupId).get();
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var group_id = "<group id>"
var leave : NakamaAsyncResult = yield(client.leave_group_async(session, group_id), "completed")

if leave.is_exception():
    print("An error occurred: %s" % leave)
    return

print("Group left")
```
{{< / code >}}

{{< code type="client" framework="godot4" >}}
```gdscript
var group_id = "<group id>"
var leave : NakamaAsyncResult = await client.leave_group_async(session, group_id)

if leave.is_exception():
    print("An error occurred: %s" % leave)
    return

print("Group left")
```
{{< / code >}}

{{< code type="client" >}}
```shell
POST /v2/group/<group id>/leave
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local group_id = "<group id>"
local result = client.leave_group(group_id)

if result.error then
  print(result.message)
end
```
{{< / code >}}

## Manage groups

Each group is managed by one or more superadmins or admins. These users are members with permission to make changes to optional fields, accept or reject new members, remove members or other admins, and promote other members as admins.

{{< note "important" >}}
A group must have at least one superadmin. The last superadmin has to [promote](../groups/#promote-a-member) another member before they can [leave](../groups/#leave-a-group).
{{< / note >}}

### Accept new members

When a user joins a private group it will create a join request until an admin accepts or rejects the user. The superadmin or admin can accept the user into the group.

{{< code type="client" >}}
```bash
curl -X POST "http://127.0.0.1:7350/v2/group/<group id>/add?user_ids=<user id>" \
    -H 'Authorization: Bearer <session token>'
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const group_id = "<group id>";
const user_id = "<user id>";
await client.addGroupUsers(session, group_id, [user_id]);
```
{{< / code >}}

{{< code type="client" >}}
```csharp
const string groupId = "<group id>";
var userIds = new[] {"<user id>"};
await client.AddGroupUsersAsync(session, groupId, userIds);
```
{{< / code >}}

{{< code type="client" >}}
```swift
let groupId = "<group id>"
let userIds = ["<user id>"]
try await client.addGroupUsers(session: session, groupId: groupId, ids: userIds)
```
{{< / code >}}

{{< code type="client" >}}
```dart
const groupId = '<group id>';
final userIds = ['<user id>'];
await client.addGroupUsers(
  session: session,
  groupId: groupId,
  userIds: userIds,
);
```
{{< / code >}}

{{< code type="client" >}}
```cpp
auto successCallback = []()
{
    std::cout << "added user to group" << std::endl;
};

string group_id = "<group id>";
string user_id = "<user id>";
client->addGroupUsers(session, group_id, { user_id }, successCallback);
```
{{< / code >}}

{{< code type="client" >}}
```java
String groupid = "<group id>";
String[] userIds = new String[] {"<user id>"};
client.addGroupUsers(session, groupid, userIds).get();
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var group_id = "<group id>"
var user_ids = ["<user id>"]
var accept : NakamaAsyncResult = yield(client.add_group_users_async(session, group_id, user_ids), "completed")

if accept.is_exception():
    print("An error occurred: %s" % accept)
    return

print("User added")
```
{{< / code >}}

{{< code type="client" framework="godot4" >}}
```gdscript
var group_id = "<group id>"
var user_ids = ["<user id>"]
var accept : NakamaAsyncResult = await client.add_group_users_async(session, group_id, user_ids)

if accept.is_exception():
    print("An error occurred: %s" % accept)
    return

print("User added")
```
{{< / code >}}

{{< code type="client" >}}
```shell
POST /v2/group/<group id>/add?user_ids=<user id>
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local group_id = "<group id>"
local user_ids = { "<user id>" }
local result = client.add_group_users(group_id, user_ids)

if result.error then
  print(result.message)
end
```
{{< / code >}}

The user will receive an [in-app notification](../notifications/) when they've been added to the group. In a private group an admin will receive a notification about the join request.

To reject the user from joining the group you should [kick them](#kick-a-member).

### Promote a member

An admin can promote another member of the group as an admin. This grants the member the same privileges to [manage the group](#manage-groups). A group can have one or more admins.

{{< code type="client" >}}
```bash
curl -X POST "http://127.0.0.1:7350/v2/group/<group id>/promote?user_ids=<user id>" \
    -H 'Authorization: Bearer <session token>'
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const group_id = "<group id>";
const user_id = "<user id>";
await client.promoteGroupUsers(session, group_id, [user_id]);
```
{{< / code >}}

{{< code type="client" >}}
```csharp
const string groupId = "<group id>";
var userIds = new[] {"<user id>"};
await client.PromoteGroupUsersAsync(session, groupId, userIds);
```
{{< / code >}}

{{< code type="client" >}}
```swift
let groupId = "<group id>"
let userIds = ["<user id>"]
try await client.promoteGroupUsers(session: session, groupId: groupId, ids: userIds)
```
{{< / code >}}

{{< code type="client" >}}
```dart
const groupId = '<group id>';
final userIds = ['<user id>'];
await client.promoteGroupUsers(
  session: session,
  groupId: groupId,
  userIds: userIds,
);
```
{{< / code >}}

{{< code type="client" >}}
```cpp
auto successCallback = []()
{
    std::cout << "user has been promoted" << std::endl;
};

string group_id = "<group id>";
string user_id = "<user id>";
client->promoteGroupUsers(session, group_id, { user_id }, successCallback);
```
{{< / code >}}

{{< code type="client" >}}
```java
String groupid = "<group id>";
String[] userIds = new String[] {"<user id>"};
client.promoteGroupUsers(session, groupid, userIds).get();
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var group_id = "<group id>"
var user_ids = ["<user id>"]
var promote : NakamaAsyncResult = yield(client.promote_group_users_async(session, group_id, user_ids), "completed")

if promote.is_exception():
    print("An error occurred: %s" % promote)
    return

print("User promoted")
```
{{< / code >}}

{{< code type="client" framework="godot4" >}}
```gdscript
var group_id = "<group id>"
var user_ids = ["<user id>"]
var promote : NakamaAsyncResult = await client.promote_group_users_async(session, group_id, user_ids)

if promote.is_exception():
    print("An error occurred: %s" % promote)
    return

print("User promoted")
```
{{< / code >}}

{{< code type="client" >}}
```shell
POST /v2/group/<group id>/promote?user_ids=<user id>
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local group_id = "<group id>"
local user_ids = { "<user id>" }
local result = client.promote_group_users(group_id, user_ids)

if result.error then
  print(result.message)
end
```
{{< / code >}}

### Demote a member

An admin can demote another member of the group down a role. This revokes the member of his current privileges to and assigns member the privileges available in the demoted role. Members who are already at the lowest role in their group will not be affected by a demotion.

{{< code type="client" >}}
```cpp
auto successCallback = []()
{
    std::cout << "user has been demoted" << std::endl;
};

string group_id = "<group id>";
string user_id = "<user id>";
client->demoteGroupUsers(session, group_id, { user_id }, successCallback);
```
{{< / code >}}

{{< code type="client" >}}
```bash
curl -X POST "http://127.0.0.1:7350/v2/group/<group id>/demote?user_ids=<user id>" \
    -H 'Authorization: Bearer <session token>'
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const group_id = "<group id>";
const user_id = "<user id>";
await client.demoteGroupUsers(session, group_id, [user_id]);
```
{{< / code >}}

{{< code type="client" >}}
```csharp
const string groupId = "<group id>";
var userIds = new[] {"<user id>"};
await client.DemoteGroupUsersAsync(session, groupId, userIds);
```
{{< / code >}}

{{< code type="client" >}}
```swift
let groupId = "<group id>"
let userIds = ["<user id>"]
try await client.demoteGroupUsers(session: session, groupId: groupId, ids: userIds)
```
{{< / code >}}

{{< code type="client" >}}
```dart
const groupId = '<group id>';
final userIds = ['<user id>'];
await client.demoteGroupUsers(
  session: session,
  groupId: groupId,
  userIds: userIds,
);
```
{{< / code >}}

{{< code type="client" >}}
```java
String groupid = "<group id>";
String[] userIds = new String[] {"<user id>"};
client.demoteGroupUsers(session, groupid, userIds).get();
```
{{< / code >}}

{{< code type="client" >}}
```shell
POST /v2/group/<group id>/demote?user_ids=<user id>
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local group_id = "<group id>"
local user_ids = { "<user id>" }
local result = client.demote_group_users(group_id, user_ids)

if result.error then
  print(result.message)
end
```
{{< / code >}}

### Kick a member

An admin or superadmin can kick a member from the group. The user is removed but can re-join again later unless the user is [banned](#ban-a-group-member) or the group is private in which case an admin must accept the re-join request. This can also be used to remove pending requests for groups that require acceptance.

If a user is removed from a group it does not prevent them from joining other groups. Sometimes a bad user needs to be kicked from the group and banned from re-joining either [the group](#ban-a-group-member) or the [whole server](../friends/#ban-a-user). This will prevent the user from being able to connect to the server and interact at all.

{{< code type="client" >}}
```bash
curl -X POST "http://127.0.0.1:7350/v2/group/<group id>/kick?user_ids=<user id>" \
    -H 'Authorization: Bearer <session token>'
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const group_id = "<group id>";
const user_id = "<user id>";
await client.kickGroupUsers(session, group_id, [user_id]);
```
{{< / code >}}

{{< code type="client" >}}
```csharp
const string groupId = "<group id>";
var userIds = new[] {"<user id>"};
await client.KickGroupUsersAsync(session, groupId, userIds);
```
{{< / code >}}

{{< code type="client" >}}
```swift
let groupId = "<group id>"
let userIds = ["<user id>"]
try await client.kickGroupUsers(session: session, groupId: groupId, ids: userIds)
```
{{< / code >}}

{{< code type="client" >}}
```dart
const groupId = '<group id>';
final userIds = ['<user id>'];
await client.kickGroupUsers(
  session: session,
  groupId: groupId,
  userIds: userIds,
);
```
{{< / code >}}

{{< code type="client" >}}
```cpp
auto successCallback = []()
{
    std::cout << "user has been kicked" << std::endl;
};

string group_id = "<group id>";
string user_id = "<user id>";
client->kickGroupUsers(session, group_id, { user_id }, successCallback);
```
{{< / code >}}

{{< code type="client" >}}
```java
String groupid = "<group id>";
String[] userIds = new String[] {"<user id>"};
client.kickGroupUsers(session, groupid, userIds).get();
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var group_id = "<group id>"
var user_ids = ["<user id>"]
var kick : NakamaAsyncResult = yield(client.kick_group_users_async(session, group_id, user_ids), "completed")

if kick.is_exception():
    print("An error occurred: %s" % kick)
    return

print("User kicked")
```
{{< / code >}}

{{< code type="client" framework="godot4" >}}
```gdscript
var group_id = "<group id>"
var user_ids = ["<user id>"]
var kick : NakamaAsyncResult = await client.kick_group_users_async(session, group_id, user_ids)

if kick.is_exception():
    print("An error occurred: %s" % kick)
    return

print("User kicked")
```
{{< / code >}}

{{< code type="client" >}}
```shell
POST /v2/group/<group id>/kick?user_ids=<user id>
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local group_id = "<group id>"
local user_ids = { "<user id>" }
local result = client.kick_group_users(group_id, user_ids)

if result.error then
  print(result.message)
end
```
{{< / code >}}

### Ban a group member

An admin or superadmin can ban a member from the group. The user is kicked from the group and prevented from re-joining or even requesting to re-join.

The user can be unbanned either via the Nakama Console or a runtime code function.

{{< code type="client" >}}
```cpp
auto successCallback = []()
{
    std::cout << "user has been banned" << std::endl;
};

string group_id = "<group id>";
string user_id = "<user id>";
client->banGroupUsers(session, group_id, { user_id }, successCallback);
```
{{< / code >}}

{{< code type="client" >}}
```bash
curl -X POST "http://127.0.0.1:7350/v2/group/<group id>/ban?user_ids=<user id>" \
    -H 'Authorization: Bearer <session token>'
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const group_id = "<group id>";
const user_id = "<user id>";
await client.banGroupUsers(session, group_id, [user_id]);
```
{{< / code >}}

{{< code type="client" >}}
```csharp
const string groupId = "<group id>";
var userIds = new[] {"<user id>"};
await client.BanGroupUsersAsync(session, groupId, userIds);
```
{{< / code >}}

{{< code type="client" >}}
```swift
let groupId = "<group id>"
let userIds = ["<user id>"]
try await client.banGroupUsers(session: session, groupId: groupId, ids: userIds)
```
{{< / code >}}

{{< code type="client" >}}
```dart
const groupId = '<group id>';
final userIds = ['<user id>'];
await client.banGroupUsers(
  session: session,
  groupId: groupId,
  userIds: userIds,
);
```
{{< / code >}}

{{< code type="client" >}}
```java
String groupid = "<group id>";
String[] userIds = new String[] {"<user id>"};
client.banGroupUsers(session, groupid, userIds).get();
```
{{< / code >}}

{{< code type="client" >}}
```shell
POST /v2/group/<group id>/ban?user_ids=<user id>
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local group_id = "<group id>"
local user_ids = { "<user id>" }
local result = client.ban_group_users(group_id, user_ids)

if result.error then
  print(result.message)
end
```
{{< / code >}}

## Remove a group

A group can only be removed by one of the superadmins which will disband all members. When a group is removed it's name can be re-used to [create a new group](#create-a-group).

{{< code type="client" >}}
```bash
curl -X DELETE "http://127.0.0.1:7350/v2/group/<group id>" \
  -H 'Authorization: Bearer <session token>'
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const group_id = "<group id>";
await client.deleteGroup(session, group_id);
```
{{< / code >}}

{{< code type="client" >}}
```csharp
const string groupId = "<group id>";
await client.DeleteGroupAsync(session, groupId);
```
{{< / code >}}

{{< code type="client" >}}
```swift
let groupId = "<group id>"
try await client.deleteGroup(session: session, groupId: groupId)
```
{{< / code >}}

{{< code type="client" >}}
```dart
const groupId = '<group id>';
await client.deleteGroup(
  session: session,
  groupId: groupId,
);
```
{{< / code >}}

{{< code type="client" >}}
```cpp
auto successCallback = []()
{
    std::cout << "group deleted" << std::endl;
};

string group_id = "<group id>";
client->deleteGroup(session, group_id, successCallback);
```
{{< / code >}}

{{< code type="client" >}}
```java
String groupid = "<group id>";
client.deleteGroup(session, groupid).get();
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var group_id = "<group id>"
var remove : NakamaAsyncResult = yield(client.delete_group_async(session, group_id), "completed")

if remove.is_exception():
    print("An error occurred: %s" % remove)
    return

print("Group removed")
```
{{< / code >}}

{{< code type="client" framework="godot4" >}}
```gdscript
var group_id = "<group id>"
var remove : NakamaAsyncResult = await client.delete_group_async(session, group_id)

if remove.is_exception():
    print("An error occurred: %s" % remove)
    return

print("Group removed")
```
{{< / code >}}

{{< code type="client" >}}
```shell
DELETE /v2/group/<group id>
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local group_id = "<group id>"
local result = client.delete_group(group_id)

if result.error then
  print(result.message)
end
```
{{< / code >}}

## Additional Information

- [Nakama Groups sample project](../../../sample-projects/unity/nakama-groups)