Websocket API

Error Codes

OK = 200
UNKNOWN_ERROR = 250

MISSING_ACTOR_ID = 500
MISSING_OBJECT_ID = 501
MISSING_TARGET_ID = 502
MISSING_OBJECT_URL = 503
MISSING_TARGET_DISPLAY_NAME = 504
MISSING_ACTOR_URL = 505
MISSING_OBJECT_CONTENT = 506
MISSING_OBJECT = 507
MISSING_OBJECT_ATTACHMENTS = 508
MISSING_ATTACHMENT_TYPE = 509
MISSING_ATTACHMENT_CONTENT = 510
MISSING_VERB = 511

INVALID_TARGET_TYPE = 600
INVALID_ACL_TYPE = 601
INVALID_ACL_ACTION = 602
INVALID_ACL_VALUE = 603
INVALID_STATUS = 604
INVALID_OBJECT_TYPE = 605
INVALID_BAN_DURATION = 606
INVALID_VERB = 607

EMPTY_MESSAGE = 700
NOT_BASE64 = 701
USER_NOT_IN_ROOM = 702
USER_IS_BANNED = 703
ROOM_ALREADY_EXISTS = 704
NOT_ALLOWED = 705
VALIDATION_ERROR = 706
ROOM_FULL = 707
NOT_ONLINE = 708
TOO_MANY_PRIVATE_ROOMS = 709
ROOM_NAME_TOO_LONG = 710
ROOM_NAME_TOO_SHORT = 711
INVALID_TOKEN = 712
INVALID_LOGIN = 713
MSG_TOO_LONG = 714
MULTIPLE_ROOMS_WITH_NAME = 715
TOO_MANY_ATTACHMENTS = 716
NOT_ENABLED = 717
ROOM_NAME_RESTRICTED = 718
USER_MUTED = 719

NOT_ALLOWED_TO_WHISPER_CHANNEL = 720
NOT_ALLOWED_TO_WHISPER_NOT_A_CONTACT = 721
NOT_ALLOWED_TO_WHISPER_TURNED_OFF = 722
NOT_ALLOWED_TO_WHISPER_SELF = 723
NOT_ALLOWED_TO_WHISPER_GENERIC_ERROR = 724
REMOTE_ERROR = 725

NO_SUCH_USER = 800
NO_SUCH_CHANNEL = 801
NO_SUCH_ROOM = 802
NO_ADMIN_ROOM_FOUND = 803
NO_USER_IN_SESSION = 804
NO_ADMIN_ONLINE = 805

connect

Responds with event name gn_connect.

Request contains no data.

Response data if successful:

{
    "status_code": 200
}

login

Responds with event name gn_login.

Request contains:

{
    "verb": "login",
    "actor": {
        "id": "<user ID>",
        "displayName": "<user name>",
        "attachments": [
            {
                "objectType": "token",
                "content": "<user token>"
            }
        ]
    }
}

Response data if successful:

{
    "status_code": 200,
    "data": {
        "id": "<server-generated UUID>",
        "published": "<server-generated timestamp, RFC3339 format>",
        "actor": {
            "id": "<user id>",
            "displayName": "<user name in base64>",
            "attachments": [
                {
                    "objectType": "room_role",
                    "id": "<room UUID>",
                    "content": "moderator,owner"
                },
                {
                    "objectType": "room_role",
                    "id": "<room UUID>",
                    "content": "owner"
                },
                {
                    "objectType": "channel_role",
                    "id": "<channel UUID>",
                    "content": "admin,owner"
                },
                {
                    "objectType": "global_roles",
                    "content": "superuser,globalmod"
                }
            ]
        },
        "object": {
            "objectType": "history",
            "attachments": [{
                "author": {
                    "id": "<sender id>", 
                    "displayName": "<sender name in base64>"
                },
                "content": "<message in base64>",
                "id": "84421980-d84a-4f6f-9ad7-0357d15d99f8",
                "published": "2017-11-17T07:19:12Z",
                "summary": "9fa5b40a-f0a6-44ea-93c1-acf2947e5f09",
                "objectType": "history"
            }]
        },
        "verb": "login"
    }
}

The object attachments are non-acked messages sent to any private rooms (i.e. conversation based private messaging). The object.attachments[0].id is the message UUID, while the object.attachments[0].summary is the room UUID. Multiple attachments will be listed if more than one un-acked message was found during login.

For the user roles, there will be an ID on the attached object if the role is for a channel or for a room. If it's a global role there will be no ID on the object. Roles are comma separated if more than one role for a room/channel/global.

Possible roles are:

  • global superuser (globalmod)
  • channel owner (owner)
  • channel admin (admin)
  • room owner (owner)
  • room moderator (moderator)

The only difference between global moderator and super user is that the global moderators can't remove static rooms (ephemeral set to false in room list).

Invisible login

See Invisibility docs.

list_channels

Responds with event name gn_list_channels.

Request contains:

{
    "verb": "list"
}

Response data if successful:

{
    "status_code": 200,
    "data": {
        "object": {
            "objectType": "channels",
            "attachments": [
                {
                    "id": "<channel UUID>",
                    "displayName": "<channel name>",
                    "url": 8,
                    "content": "normal,another-tag",
                    "objectType": "static",
                    "attachments": [
                        {
                            "summary": "message",
                            "objectType": "membership",
                            "content": "1"
                        }
                    ]
                },
                {
                    "id": "<channel UUID>",
                    "displayName": "<channel name>",
                    "url": 20,
                    "content": "another-tag",
                    "objectType": "temporary",
                    "attachments": [
                        {
                            "summary": "join",
                            "objectType": "gender",
                            "content": "f"
                        }
                    ]
                }
            ]
        },
        "verb": "list"
    }
}

Each channel has a url field, which is the sort order defined in the admin interface, in ascending order (lower url means higher up in the list).

The objectType of a channel tells you if this channel only contains static rooms, only temporary rooms or a mix of both. Possible values are thus:

  • temporary
  • static
  • mix

If the channel has 0 rooms in it, the objectType will be mix.

The content field on each attachment specifies a comma separated list of tags for the channel.

Attachments for each channel describes the ACLs for that channel.

received

Acknowledge that one or more messages has been received. The status will change from sent to delivered.

Does not emit a response, only invokes the callback with the status_code and potentially and error_message. Note that if multiple messages are being acknowledged at the same time, they all have to be for the same room (target.id).

Request contains:

{
    "verb": "receive",
    "target": {
        "id": "<uuid of the room the messages are all in>"
    },
    "object": {
        "attachments": [
            {"id": "<message1 uuid>"},
            {"id": "<message2 uuid>"},
            {"id": "<message3 uuid>"}
        ]
    }
}

msg_status

Check ack status of a set of messages sent to a single user. Request:

{
    "verb": "check",
    "target": {
        "id": "<uuid of the user to check ack status for>" 
    },
    "object": {
        "attachments": [
            {"id": "<message1 uuid>"},
            {"id": "<message2 uuid>"},
            {"id": "<message3 uuid>"}
        ]
    }
}

If message guarantee is not enabled on the server the 717 (NOT_ENABLED) error code will be retured as part of the callback, and no gn_msg_status event will be sent back.

Response will be sent as the gn_msg_status event with the following content:

{
    "status_code": 200,
    "data": {
        "object": {
            "objectType": "statuses",
            "attachments": [
                {
                    "id": "<msg UUID 1>",
                    "content": "<ack status 1>"
                },
                {
                    "id": "<msg UUID 2>",
                    "content": "<ack status 2>"
                },
                {
                    "id": "<msg UUID 3>",
                    "content": "<ack status 3>"
                }
            ]
        },
        "target": {
            "id": "<user ID the ack status are for>"
        },
        "verb": "check",
        "id": "<server-generated UUID>",
        "published": "<server-generated timestamp, RFC3339 format>"
    }
}

The ack statuses are:

  • 0: not acknowledged (receiver has not acked it yet)
  • 1: received
  • 2: read

read

Acknowledge that one or more messages has been read. The status will change from sent/delivered to read.

Does not emit a response, only invokes the callback with the status_code and potentially and error_message. Note that if multiple messages are being acknowledged at the same time, they all have to be for the same room (target.id).

Request contains:

{
    "verb": "read",
    "target": {
        "id": "<uuid of the room the messages are all in>" 
    },   
    "object": {
        "attachments": [
            {"id": "<message1 uuid>"},
            {"id": "<message2 uuid>"},
            {"id": "<message3 uuid>"}
        ]    
    }    
}

If the target.id is specified, the request will be relayed to online users in that room. E.g., user A sends message X to the room, user B then sends a read event after receiving it; this read event will then be sent to user A with the event name gn_message_read.

list_rooms

Get a list of all rooms for a channel.

Responds with event name gn_list_rooms.

Request contains:

{
    "object": {
        "url": "<channel UUID>"
    },
    "verb": "list"
}

Response data if successful:

{
    "status_code": 200,
    "data": {
        "object": {
            "objectType": "rooms",
            "url": "<channel UUID>",
            "attachments": [
                {
                    "id": "<room UUID>",
                    "displayName": "<room name>",
                    "url": 8,
                    "summary": <number of users in this room (integer)>,
                    "objectType": "static",
                    "content": "moderator,owner",
                    "attachments": [
                        {
                            "summary": "join",
                            "objectType": "gender",
                            "content": "f"
                        }
                    ]
                },
                {
                    "id": "<room UUID>",
                    "displayName": "<room name>",
                    "url": 20,
                    "summary": <number of users in this room (integer)>,
                    "objectType": "temporary",
                    "content": "",
                    "attachments": [
                        {
                            "summary": "join",
                            "objectType": "gender",
                            "content": "f"
                        }
                    ]
                }
            ]
        },
        "verb": "list"
    }
}

The url field for object is the UUID of the channel that these rooms are for.

Each room has a url field, which is the sort order defined in the admin interface, in ascending order (lower url means higher up in the list).

The content field on the attachments describe what kind of role you have for that room. If no roles are set then content will be empty, otherwise it will be a comma separated list of roles. Possible roles for rooms are:

  • moderator
  • owner
  • globalmod
  • superuser

Global roles and roles for channels are returned in the gn_login event.

Attachments for each room describes the ACLs for that room.

The objectType for each room describes if the room is static or temporary. Static rooms are not removed automatically when empty, while temporary rooms are removed when the owner leaves (usually only for user created rooms).

update_user_info

If a user e.g. changes his/her avatar, the change can be broadcasted to users in the same rooms as this user is in. To e.g. let other users know this user is currently streaming video, the objectType is_streaming might be used:

{
    "object": {
        "attachments": [
            {
                "content": "MA==",
                "objectType": "is_streaming"
            }
        ],
        "objectType": "userInfo"
    },
    "verb": "update",
    "id": "<server-generated UUID>",
    "published": "<server-generated timestamp, RFC3339 format>"
}

The content of the attachments needs to be base64 encoded.

Updates are saved in redis and thus will be included in the user info returned in gn_join and gn_users_in_room.

Responds with event name gn_update_user_info. When the update is sent to other users it will be received as an event with name gn_user_info_updated.

Response data if successful:

{
    "status_code": 200
}

Or if missing data, e.g.:

{
    "status_code": 509,
    "message": "no objectType on attachment for object"
}

request_admin

When help is wanted in a room, a user can request for an admin to join and help out. Every channel has an Admin room, which only admins can see when listing rooms and only admins can join. When a request_admin event is sent to the server it will be delivered to the admin room for that channel and the admins in that room can decide what to do.

Important:

  • If no user with the global role superuser or globalmod is online, the 805 code will be returned ("no admin is online").

Responds with event name gn_request_admin.

Request contains:

{
    "target": {
        "id": "<room UUID to request help for>"
    },
    "object": {
        "content": "<base64 encoded message that will be delivered to the admin room>"
    },
    "verb": "help"
}

Response data if successful:

{
    "status_code": 200
}

If no admin is online, the response will be:

{
    "status_code": 805,
    "message": "no admin is online"
}

The object.content could be anything, e.g. a base64 encoded json message with link to backend, extra information, a reason text etc.

The event generated to be sent to the admin room is called gn_admin_requested (see Events for more information).

leave

Leave a room.

Responds with event name gn_leave.

Request contains:

{
    "target": {
        "id": "<room UUID>"
    },
    "verb": "leave"
}

Or, if you want to leave a room based on the room name:

{
    "verb": "join",
    "target": {
        "id": "<room name>",
        "objectType": "name"
    }
}

Response data if successful:

{
    "status_code": 200
}

users_in_room

List all users in a room.

Responds with event name gn_users_in_room.

Request contains:

{
    "target": {
        "id": "<room UUID>"
    },
    "verb": "list"
}

Response data if successful:

{
    "status_code": 200,
    "data": {
        "object": {
            "objectType": "users",
            "attachments": [
                {
                    "id": "<user UUID>",
                    "displayName": "<user name>",
                    "content": "globalmod,moderator"
                    "attachments": [
                        {
                            "content": "NDA=",
                            "objectType": "age"
                        },
                        {
                            "content": "aHR0cDovL3NvbWUtdXJsLnRsZC9mb28uanBn",
                            "objectType": "avatar"
                        }
                    ]
                },
                {
                    "id": "<user UUID>",
                    "displayName": "<user name>",
                    "content": "moderator"
                    "attachments": [
                        {
                            "content": "NDA=",
                            "objectType": "age"
                        },
                        {
                            "content": "aHR0cDovL3NvbWUtdXJsLnRsZC9mb28uanBn",
                            "objectType": "avatar"
                        }
                    ]
                }
            ]
        },
        "verb": "list"
    }
}

The content of the user attachment describes the roles this user has in this room, plus any global roles. Examples:

  • globalmod,moderator
  • moderator
  • superuser

If no specific roles, the value will be blank.

history

TODO: include user UUID as well as user name.

When joining a room the history will be included in the gn_join response event. If history is needed for a separate reason than the history event can be used. Can also be used to get history for a private chat with another user, if "target.id" is set to the user UUID instead of the room UUID.

Responds with event name gn_history.

Request contains:

{
    "target": {
        "id": "<room UUID>"
    },
    "updated": "<last read timestamp, if configured in server will return messages since this time>",
    "verb": "list"
}

Response data if successful:

{
    "status_code": 200,
    "data": {
        "object": {
            "objectType": "messages",
            "attachments": [
                {
                    "author": {
                        "id": "<the user id of the sender>",
                        "displayName": "<the user name of the sender>"
                    },
                    "id": "<message ID>",
                    "content": "<the message content>",
                    "published": "<the time it was sent, RFC3339>"
                },
                {
                    "author": {
                        "id": "<the user id of the sender>",
                        "displayName": "<the user name of the sender>"
                    },
                    "id": "<message ID>",
                    "content": "<the message content>",
                    "published": "<the time it was sent, RFC3339>"
                }
            ]
        },
        "target": {
            "id": "<room UUID>"
        },
        "verb": "history"
    }
}

status

Change the online status for this user.

Responds with gn_status.

Request contains:

{
    "verb": "<one of online/offline/invisible>"
}

Response data if successful:

{
    "status_code": 200
}

get_acl

Get the permissions (ACL) for a channel or room.

Responds with gn_get_acl.

Request contains:

    {
        "target": {
            "id": "<room UUID>",
            "objectType": "<room/channel>"
        },
        "verb": "get"
    }

Response data if successful:

{
    "status_code": 200,
    "data": {
        "target": {
            "id": "<room/channel UUID>",
            "objectType": "<room/channel>"
        },
        "object": {
            "objectType": "acl",
            "attachments": [
                {
                    "objectType": "<ACL type name>",
                    "content": "<ACL value>",
                    "summary": "<API action, e.g. join/kick/etc>"
                },
                {
                    "objectType": "<ACL type name>",
                    "content": "<ACL value>",
                    "summary": "<API action, e.g. join/kick/etc>"
                }
            ]
        },
        "verb": "get"
    }
}

set_acl

Update the permissions of a room/channel. If the "content" is blank, the ACL with that type for the specified action will be removed. Example "API actions" are "join", "create", "message", "kick". Example "permission types" are "age", "gender", "membership".

Responds with gn_set_acl.

Request contains:

{
    "target": {
        "id": "<room/channel UUID>",
        "objectType": "<room/channel>"
    },
    "object": {
        "objectType": "acl",
        "attachments": [
            {
                "objectType": "<ACL type name>",
                "content": "<ACL value>",
                "summary": "<API action, e.g. join/kick/etc>"
            },
            {
                "objectType": "<ACL type name>",
                "content": "<ACL value>",
                "summary": "<API action, e.g. join/kick/etc>"
            }
        ]
    },
    "verb": "set"
}

Response data if successful:

{
    "status_code": 200
}

create

Create a new room under a channel. The sender of the event will be set as the first owner of the new room.

Responds with gn_create.

Request contains:

{
    "target": {
        "displayName": "<name of the new room>"
    },
    "object": {
        "url": "<channel UUID>"
    },
    "verb": "create"
}

Response data if successful:

{
    "status_code": 200,
    "data": {
        "target": {
            "id": "<the generated UUID for this room>",
            "displayName": "<name of the new room>",
            "objectType": "temporary"
        },
        "object": {
            "url": "<channel UUID>"
        },
        "verb": "create"
    }
}

If the amount of private rooms already exceed 2, the error code 709 (TOO_MANY_PRIVATE_ROOMS) will be returned.

The target.objectType will always be temporary since all rooms created using the API are user created rooms, meaning they will be automatically removed when the owner leaves.

It is also possible to specify ACLs for a room while creating it, but adding object.attachments as in the set_acl event:

{
    "target": {
        "displayName": "<name of the new room>"
    },
    "object": {
        "url": "<channel UUID>",
        "objectType": "acl",
        "attachments": [
            {
                "objectType": "<ACL type name>",
                "content": "<ACL value>",
                "summary": "<API action, e.g. join/kick/etc>"
            },
            {
                "objectType": "<ACL type name>",
                "content": "<ACL value>",
                "summary": "<API action, e.g. join/kick/etc>"
            }
        ]
    },
    "verb": "create"
}

invite

Invite another user to a room the current user is already in.

Responds with gn_invite.

Request contains:

{
    "target": {
        "id": "<UUID of the user to invite>"
    },
    "actor": {
        "url": "<the room UUID the invitation is for>"
    },
    "verb": "invite"
}

Response data if successful:

{
    "status_code": 200
}

delete

Delete a message from a room (needs to be superuser, admin for channel, owner of channel, moderator of the room, owner of room or (configurable) sender of the message).

If object_type is set to room the object.id should be the UUID of a room. All messages in that room will then be deleted. If object_type is set to anything else, or not specified at all, then object.id is assumed to be the UUID of a single message.

Responds with gn_delete.

Request contains:

{
    "target": {
        "id": "<UUID of the room to delete from>"
    },
    "object": {
        "id": "<UUID of the message to delete OR the UUID of the room to clear>",
        "object_type": "<optional; if set to 'room' the object.id is assumed to be the room id>"
    },
    "verb": "delete"
}

kick

Kick a user from a room.

Responds with gn_kick.

Request contains:

{
    "actor": {
        "id": "<ID of the user who's kicking someone, leave black for admin ID>"
    },
    "target": {
        "id": "<ID of the room to kick from>"
    },
    "object": {
        "id": "<ID of the user to kick>",
        "content": "<optional reason field>"
    },
    "verb": "kick"
}

Response data if successful:

{
    "status_code": 200
}

ban

Ban a user from a room for a given amount of time.

Responds with gn_ban.

Request contains:

{
    "actor": {
        "id": "<ID of the user who's banning someone; leave empty for admin ID>"
    },
    "target": {
        "id": "<UUID of the room/channel to ban from>",
        "objectType": "<room/channel/global>"
    },
    "object": {
        "id": "<UUID of the user to ban>",
        "summary": "<ban duration, an integer suffixed with one of [d, h, m, s]>",
        "content": "<optional reason field, base64>"
    },
    "verb": "ban"
}

If banning a used in a room, set objectType to room and target.id to the uuid of the room. If banning from a channel, set objectType to channel and target.id to the uuid of the channel. If banning globally, set objectType to global and skip target.id.

Summary is the duration of the ban. It's a number with a suffix d, h, m or s, meaning days, hours, minutes and seconds. Only one suffix can be used. E.g.:

  • 5m (ban for five minutes),
  • 3600s (ban for 3600 seconds, same as 1h),
  • 365d (ban for one year).

It's not possible to permanently ban a user, but you can set a very large duration for the same effect. The only restriction is that the date when the ban ends (utcnow()+duration) must be before the year 10000 (date lib restriction).

Response data if successful:

{
    "status_code": 200
}

message

Send a message to a room UUID (can be the user UUID or an actual room UUID).

Responds with event name gn_message.

Request contains:

{
    "verb": "send",
    "target": {
        "id": "<room uuid>",
        "objectType": "<room/private>"
    },
    "object": {
        "content": "<the message, base64 encoded>",
    }
}

If request is for conversation-based private messaging, use objectType: 'private'. In this case, the other user(s) in this conversation (owners of the room) will initially have a NOT_ACKED status for the message. If they are online they will receive it and they can acknowledge the message. If they are offline they will receive it in gn_login then they come online (all non-acked messages for rooms they are owner for).

Response data if successful:

{
    "status_code": 200,
    "data": {
        "id": "c42ebf01-3d50-4f27-a345-4ed213be045d",
        "published": "2016-10-07T10:45:34Z",
        "actor": {
            "id": "<your user ID>",
            "displayName": "<your user name>"
        },
        "verb": "send",
        "target": {
            "id": "<room ID>",
            "displayName": "<room name>"
        },
        "object": {
            "content": "<the message>",
            "displayName": "<the channel name>",
            "url": "<the channel id>",
            "objectType": "<room/private>"
        }
    }
}

The response will send the same ActivityStreams as was in the request, with the addition of a server generated ID (uuid) and the published field set to the time the server received the request (in RFC3339 format).

If the user is muted

If a user is muted in a room, and tries to send a message in that room, a 703 error code will be returned, along with this example response (object.summary is the remaining seconds of the mute, and object.content is the reason for the mute (if any) in base64):

[
  "gn_message",
  {
    "status_code": 703,
    "data": {
      "verb": "mute",
      "object": {
        "content": "",
        "summary": "1608"
      },
      "target": {
        "objectType": "room",
        "id": "2e7d537e-bed5-47c5-a7f6-357075759e5d",
        "displayName": "ZGVmYXVsdA=="
      },
      "id": "97fa6454-e93a-4379-876d-c9e45d6f9b86",
      "published": "2023-08-17T06:46:18Z",
      "provider": {
        "id": "popp"
      }
    }
  }
]

Note: a mute is automatically removed when the timer runs out, and the user can then send messages again.

remove_room

Response with the event name gn_remove_room.

Request contains:

{
    "verb": "remove",
    "target": {
        "id": "<room ID>"
    }
}

Response data if successful:

{
    "status_code": 200,
    "data": {
        "target": {
            "id": "<room uuid>",
            "displayName": "<room name in base64>",
            "objectType": "room"
        },
        "id": "c42ebf01-3d50-4f27-a345-4ed213be045d",
        "published": "2016-10-07T10:45:34Z",
        "verb": "removed"
    }
}

report

No response.

Request contains:

{
    "verb": "report",
    "object": {
        "id": "<uuid of message>",
        "content": "<the actual message to report, base64>",
        "summary": "<optional reason text, base64>"
    },
    "target": {
        "id": "<user ID to report>"
    }
}

A report will be sent to both the admin room and as an external event published on the MQ.

join

Responds with the event name gn_join.

In the user attachments, the content fields tells you the room roles that the user has in this room (as a comma separated value), plus any global roles. Possible roles are:

  • superuser,
  • globalmod,
  • owner,
  • moderator,
  • admin.

Currently only the superuser and globalmod role is considered when the request_admin api is used.

Request contains:

{
    "verb": "join",
    "target": {
        "id": "<room ID>"
    }
}

Or, if you want to join a room based on the room name:

{
    "verb": "join",
    "target": {
        "id": "<room name>",
        "objectType": "name"
    }
}

Response data if successful:

{
    "status_code": 200,
    "data": {
        "object": {
            "objectType": "room",
            "attachments": [{
                "objectType": "acl",
                "attachments": []
            }, {
                "objectType": "history",
                "attachments": [{
                    "author": {
                        "id": "50327",
                        "displayName": "Zmbw==",
                        "attachments": [{
                            "objectType": "avatar",
                            "content": "aHR0cDovLzAuYmlsZC5wb3BwZW4ubGFiL20QzdCOUI5XzMwMF9zcS5qcGc="
                        }, {
                            "objectType": "app_avatar",
                            "content": "aHR0cDovLzAuaW1nLnBvcGNvcm4NTAwNDMyNy1GOUI4RjBDNjBBlCOV8zMDBfc3EuanBn"
                        }, {
                            "objectType": "app_avatar_safe",
                            "content": "aHR0cDovLzAuaW1nLnBvcGNvcTAwNDMyNy1GOUI4RjBDN3QjlCOV8zMDBfc3EuanBn"
                        }]
                    },
                    "summary": "7935a673-da64-4419-818b-e6e0d1864b61",
                    "id": "d02598a1-6ae6-4182-8f2c-c2e66d941303",
                    "content": "eyJjb2xvciI6IjiwiZ2nRlOiJkc2EifQ==",
                    "published": "2019-07-04T07:45:59Z"
                }]
            }, {
                "objectType": "owner",
                "attachments": [{
                    "id": "0",
                    "displayName": "QWW4="
                }]
            }, {
                "objectType": "user",
                "attachments": [{
                    "id": "5048",
                    "displayName": "am8yaw==",
                    "attachments": [{
                        "objectType": "region",
                        "content": "Qmlu"
                    }, {
                        "objectType": "avatar",
                        "content": "aHR0cDovLzAuYmOTgxRjhBNUM0NDA5MDQ2MDA4ODdERTJfMzAwX3NxLmpwZw=="
                    }, {
                        "objectType": "<etc. etc.>",
                        "content": "<some other content>"
                    }],
                    "content": "globalmod",
                    "objectType": "user"
                }]
            }]
        },
        "verb": "join",
        "target": {
            "id": "7935a673-da64-4419-818b-e6e0d1864b61",
            "displayName": "TG9k="
        },
        "id": "6a61c7a5-0ae0-4735-918d-18c9d137e43c",
        "published": "2019-07-05T06:18:30Z",
        "provider": {
            "id": "some-name"
        }
    }
}

Attachments for each user contains the user data, e.g. user name, age, city etc.

rename_room

Responds with the event name gn_rename_room. Every other connected user will receive a gn_room_renamed event.

Request example:

{
    "verb": "rename",
    "target": {
        "id": "<room ID>",
        "displayName": "<NEW room name in base64>",
        "objectType": "room"
    }
}

Response data if successful:

{
    "status_code": 200,
    "data": {
        "target": {
            "id": "<room uuid>",
            "displayName": "<room name in base64>",
            "objectType": "room"
        },
        "id": "c42ebf01-3d50-4f27-a345-4ed213be045d",
        "published": "2016-10-07T10:45:34Z",
        "verb": "renamed"
    }
}