Document Group API
Foorack opened this issue · 10 comments
Written by @ariesclark and @Miner28. Moving to an Issue to better track information across PR's.
Group API
Research Stage
List User Groups GET /users/<user-id>/groups
Response (example)
[
{
"id": "gmem_<uuid>",
"groupId": "grp_<uuid>",
"memberVisibility": "visible",
"mutualGroup": true,
"name": "...",
"shortCode": "...",
"discriminator": "0000",
"description": "...",
"iconUrl": "https://api.vrchat.cloud/api/1/file/file_<uuid>/1/file",
"bannerUrl": "https://assets.vrchat.com/www/groups/default_banner.png",
"privacy": "default",
"ownerId": "usr_<uuid>",
"memberCount": 1
}
]
Create Group POST /groups
Body (example)
{
"name": "...",
"shortCode": "...",
"description": "",
"joinState": "open",
"privacy": "default",
"roleTemplate": "managedFree",
"bannerId": null,
"iconId": null
}
name
must be at least 3 characters.
shortCode
must be at least 3 characters.
joinState
one of closed
, invite
, request
, open
.
privacy
one of default
, private
.
roleTemplate
one of default
, managedFree
, managedInvite
, managedRequest
.
Response
Group Object
Get Group GET groups/<group-id>
Query parameters
includeRoles
optional boolean, default false.
Response (example)
{
"id": "grp_<uuid>",
"name": "...",
"shortCode": "...",
"discriminator": "0000",
"description": "My exciting new group. It's pretty nifty!",
"iconUrl": "https://assets.vrchat.com/www/groups/default_icon.png",
"bannerUrl": "https://assets.vrchat.com/www/groups/default_banner.png",
"privacy": "default",
"ownerId": "<user-id>",
"links": [],
"languages": [],
"iconId": null,
"bannerId": null,
"memberCount": 1,
"memberCountSyncedAt": "2022-12-01T01:50:50.918Z",
"isVerified": false,
"joinState": "open",
"tags": [],
"galleries": [],
"createdAt": "2022-12-01T01:50:50.918Z",
"initialRoleIds": [
"grol_<uuid>"
],
"updatedAt": "2022-12-01T01:50:50.918Z",
"membershipStatus": "member",
"myMember": {
"id": "gmem_<uuid>",
"groupId": "grp_<uuid>",
"userId": "usr_<uuid>",
"isRepresenting": false,
"roleIds": [
"grol_<uuid>"
],
"joinedAt": "2022-12-01T01:50:50.918Z",
"membershipStatus": "member",
"visibility": "visible",
"isSubscribedToAnnouncements": true,
"createdAt": "2022-12-01T01:50:50.918Z",
"managerNotes": "",
"has2FA": false,
"permissions": [
"*"
]
}
}
Update Group PUT groups/<group-id>
Body
{
"name": "...",
"shortCode": "...",
"description": "...",
"joinState": "open",
"language": [],
"links": [],
"rules": null
}
Response
Group Object
Delete Group DELETE groups/<group-id>
Response
{
"success": {
"message": "Group deleted!",
"status_code": 200
}
}
List Group Galleries GET /groups/<group-id>/galleries/<group-gallery-id>
Query parameters
looks like the website sends a duplicate parameter galleryId
typical pagination: n
, offset
.
Response (example)
[
{
"id": "ggim_<uuid>",
"groupId": "grp_<uuid>",
"galleryId": "ggal_<uuid>",
"fileId": "file_<uuid>",
"imageUrl": "https://api.vrchat.cloud/api/1/file/file_<uuid>/1/file",
"createdAt": "2022-12-01T09:02:57.166Z",
"submittedByUserId": "usr_<uuid>",
"approved": true,
"approvedByUserId": "usr_<uuid>",
"approvedAt": "2022-12-01T09:02:57.166Z"
}
]
List Group Roles GET /groups/<group-id>/roles
Response (example)
[
{
"id": "grol_<uuid>",
"groupId": "grp_<uuid>",
"name": "Group Owner",
"description": "The owner of this group.",
"isSelfAssignable": false,
"permissions": [
"*"
],
"isManagementRole": true,
"requiresTwoFactor": false,
"requiresPurchase": false,
"order": 0,
"createdAt": "2022-12-01T09:55:53.794Z",
"updatedAt": "2022-12-01T09:55:53.794Z"
}
]
Create Group Role POST /groups/<group-id>/roles
Body (example)
{
"id": "...",
"name": "...",
"description": "",
"isSelfAssignable": true,
"permissions": []
}
Response
Group Role Object
Update Group Role PUT /groups/<group-id>/roles/<group-role-id>
Body
assuming same properties as creation.
Response
Array of existing role objects.
Delete Group Role DELETE /groups/<group-id>/roles/<group-role-id>
Body (example)
{
"id": "...",
"name": "...",
"description": "",
"isSelfAssignable": true,
"permissions": []
}
Response
Array of existing role objects.
List Group Invites GET /groups/<group-id>/invites
Query parameters
typical pagination: n
, offset
.
Response (example)
[
{
"id": "gmem_<uuid>",
"groupId": "grp_<uuid>",
"userId": "usr_<uuid>",
"isRepresenting": false,
"user": {
"id": "usr_<uuid>",
"displayName": "...",
"thumbnailUrl": "https://api.vrchat.cloud/api/1/file/file_<uuid>/1/file",
"iconUrl": ""
},
"roleIds": [],
"joinedAt": null,
"membershipStatus": "invited",
"visibility": "visible",
"isSubscribedToAnnouncements": true,
"createdAt": "2022-12-01T09:45:00.813Z",
"bannedAt": null,
"managerNotes": ""
}
]
Create Group Invite POST /groups/<group-id>/invites
Body (example)
{
"userId": "usr_<uuid>",
"confirmOverrideBlock": true
}
Delete Group Invite DELETE /groups/<group-id>/invites/<user-id>
Response
{
"success": {
"message": "... has been uninvited.",
"status_code": 200
}
}
List Group Requests GET /groups/<group-id>/requests
Query parameters
typical pagination: n
, offset
.
List Group Members GET /groups/<group-id>/members
Query parameters
typical pagination: n
, offset
.
Response (example)
[
{
"id": "gmem_<uuid>",
"groupId": "grp_<uuid>",
"userId": "usr_<uuid>"
"isRepresenting": false,
"user": {
"id": "usr_<uuid>",
"displayName": "...",
"thumbnailUrl": "https://api.vrchat.cloud/api/1/image/file_<uuid>",
"iconUrl": "https://api.vrchat.cloud/api/1/file/file_<uuid>/1"
},
"roleIds": [
"grol_<uuid>",
]
}
]
List Group Audit Log GET /groups/<group-id>/auditLogs
Query parameters
typical pagination: n
, offset
.
Response (example)
{
"results": [
{
"id": "gaud_<uuid>",
"created_at": "2022-12-01T09:31:16.297Z",
"groupId": "grp_<uuid>",
"actorId": "usr_<uuid>",
"actorDisplayName": "...",
"targetId": "grp_<uuid>",
"eventType": "group.create",
"description": "Group test created by <displayName>.",
"data": {
"name": "...",
"shortCode": "...",
"description": "...",
"rules": null,
"bannerId": null,
"iconId": "file_<uuid>",
"joinState": "open",
"privacy": "default",
"ownerId": "usr_<uuid>",
"iconVersion": 1,
"galleries": [
{
"_id": "ggal_<uuid>",
"_created_at": "2022-12-01T09:31:16.297Z",
"_updated_at": "2022-12-01T09:31:16.297Z",
"name": "Banner and Icon",
"description": "A gallery to store your group's banner and icon. You can edit this any way you like later.",
"membersOnly": true
}
]
}
}
],
"totalCount": 1,
"hasNext": false
}
List Group Permissions GET /groups/<group-id>/permissions
Response (example)
[
{
"name": "group-members-manage",
"displayName": "Manage Group Member Data",
"help": "Allows role to view all members and edit data about them.",
"isManagementPermission": true,
"allowedToAdd": true
}
]
Get Group Announcement GET /groups/<group-id>/announcement
Response (example)
Empty object when no announcement or
{
"id": "gpos_<uuid>",
"groupId": "grp_<uuid>",
"authorId": "usr_<uuid>",
"title": "a",
"text": "a",
"imageId": null,
"imageUrl": null,
"createdAt": "2022-12-01T09:12:48.487Z",
"updatedAt": "2022-12-01T09:12:48.487Z"
}
Set Group Announcement POST /groups/<group-id>/announcement
Body
{
"text": "...",
"title": "...",
"imageId": null,
"sendNotification": false
}
Response
Group Announcement Object
List Group Instances GET /groups/<group-id>/instances
Delete Join Request DELETE /groups/<group-id>/requests
Create Join Request POST /groups/<group-id>/join
List Group Role Templates GET /groups/roleTemplates
Feedback API? /users/<user-id>/feedback?contentId=<group-id>&metadata=true
POST /feedbacks/group/<group-id>
{
"type":"report",
"reasons":[
"inappropriate",
"abusive_disruptive",
"malicious",
"hateful"
],
"locations":[
"group_name",
"group_description",
"image_banner_gallery_icon",
"group_user_behaviour"
],
"description":"a"
}
RESPONSE
{
"id":"feedback_<uuid>",
"type":"report",
"reasons":[
"inappropriate",
"abusive_disruptive",
"malicious",
"hateful"
],
"locations":[
"group_name",
"group_description",
"image_banner_gallery_icon",
"group_user_behaviour"
],
"description":"Report comment",
"commenterId":"usr_<uuid>",
"commenterName":"<displayname>",
"contentId":"grp_<uuid>",
"contentType":"group",
"contentName":"test",
"tags":[
]
}
PUT /groups/<groupId>/requests/<userId>
{
"action":"accept" | "reject"
}
ACCEPT RESPONSE
{
"success": {
"message": "Successfully accepted <display-name> request.",
"status_code": 200
}
}
REJECT RESPONSE
{
"success": {
"message": "Successfully rejected <display-name> request.",
"status_code": 200
}
}
Name and shortcode follow standard sanitation functions:
{
"error": {
"message": "name did not pass sanitization‚ shortCode did not pass sanitization",
"status_code": 400
}
}
/groups/<group-id>/galleries
{name: "E1", description: "E2", membersOnly: false}
name - required
Keeping this open as groups/<groupId>/instances
is missing, but that is broken.
And /groups/strictsearch?query=abc
All GroupMember examples in the documentation have "membershipStatus": "member"
. The GroupMember examples displayed for the respective endpoints should be updated to display:
- When in response to a join request or representing a user who requested to join, but whose request hasn't yet been approved:
"membershipStatus": "requested"
. - When in response to requesting the list of invited members who haven't yet accepted their invites ("Sent" in the website):
"membershipStatus": "invited"
- When the user is banned (in addition to the ban date):
"membershipStatus": "banned"
(all of these have been tested.)
Maybe one more thing: They changed recently that you can create multiple announcements, so Endpoint /groups/{groupId}/announcement
is not used by VRC anymore (at least on website) instead they have new Posts Apis:
Read Posts
GET /groups/{groupId}/posts?n=10&offset=0&publicOnly=false
Reponse looks like:
{
"total": 2,
"posts": [
{
"id": "not_163d458f-f9a5-4ff8-9ce7-5879777997b4",
"groupId": "grp_03c380ba-812a-4b3a-ae65-f0da222fc103",
"authorId": "usr_362cdd9f-3b2d-4ff5-afa7-65a2d3f8435d",
"editorId": null,
"visibility": "public",
"roleIds": [],
"title": "asd",
"text": "asd",
"imageId": null,
"imageUrl": null,
"createdAt": "2024-03-31T14:07:53.188Z",
"updatedAt": "2024-03-31T14:07:53.188Z"
},
{
"id": "not_ec21137e-2271-4e8a-8216-f0a008033f1d",
"groupId": "grp_03c380ba-812a-4b3a-ae65-f0da222fc103",
"authorId": "usr_362cdd9f-3b2d-4ff5-afa7-65a2d3f8435d",
"editorId": null,
"visibility": "group",
"roleIds": [],
"title": "test",
"text": "test please ignore",
"imageId": null,
"imageUrl": null,
"createdAt": "2024-03-31T14:07:37.504Z",
"updatedAt": "2024-03-31T14:07:37.504Z"
}
]
}
To Create a new Post:
POST /groups/{groupId}/posts
Body:
{
"text":"test",
"title":"Yeah I am sry need one more test",
"imageId":null,
"sendNotification":true,
"roleIds":["grol_a1be5571-4a46-42c7-ae84-7a1e1262ee68","grol_e1ec0583-a87b-4b46-a0e0-86b4dfc1361b"],
"visibility":"group"
}
Response:
{
"id": "not_a9f5f5d8-c1c7-4d1c-a228-f1b6140d29b5",
"groupId": "grp_03c380ba-812a-4b3a-ae65-f0da222fc103",
"authorId": "usr_362cdd9f-3b2d-4ff5-afa7-65a2d3f8435d",
"editorId": null,
"visibility": "group",
"roleIds": [
"grol_a1be5571-4a46-42c7-ae84-7a1e1262ee68",
"grol_e1ec0583-a87b-4b46-a0e0-86b4dfc1361b"
],
"title": "Yeah I am sry need one more test",
"text": "test",
"imageId": null,
"imageUrl": null,
"createdAt": "2024-03-31T14:11:41.923Z",
"updatedAt": "2024-03-31T14:11:41.923Z"
}
Delete Post
DELETE /groups/{groupId}/posts/not_a9f5f5d8-c1c7-4d1c-a228-f1b6140d29b5
Reponse:
{
"success": {
"message":"Group post was deleted!",
"status_code":200
}
}
Fields:
- visibility can be "public" or "group" - can be filtered on get request with publicOnly=true
- roleIds can contain Ids only if visibility is "group" - therefore post is only shown inside group to defined roles
- don't know what editorId is on Reponses....