The VRChat Websocket API (a.k.a. "Pipeline") is used receiving updates regarding the API, such as receiving an invite request.
The WebSocket is receive-only, meaning that you can only listen for messages. Sending messages is undefined behavior.
Connecting to the VRChat webhook server is done via the URL:
wss://pipeline.vrchat.cloud/?authToken=authcookie_...
The auth-token query parameter is the authorization cookie you receive when logging into VRChat.
A proper User-Agent is also required to connect to the websocket server.
It is possible to be connected from multiple locations at the same time. All clients will receive the exact same messages.
ℹ️ Most messages are double-encoded!
All notification content values will be documented in JSON object form. The "content" frield is a stringified version of the JSON object, and needs to be unpacked separately. (with the exception of see-notification and hide-notification events, which take a notification ID instead.)
Several JSON string values present in both Websocket API messages and HTTP API responses appear to be contained in predictably consistent sets.
Throughout both APIs, the empty string "" is returned in places (including the immediately) where it would seem otherwise reasonable to have a null or undefined value.
In this part of the documentation, the following ":identifier"s will be used to describe possible enumeration-ish values:
":locationString"
"" Pseudo-null value
"offline" Implies a user currently is not either running the VRChat client or connected to the Pipeline (e.g., browser tab open)
"traveling" Indicates a user's client is travelling between instances (e.g., downloading world, synchronizing world state)
Also can be "traveling:traveling
"private" Indicates a user's location is not visible to the currently logged-in user. (e.g., Ask Me/Do Not Disturb status, Invite/Invite+/Group instance)
The "response-notification" event is used to respond to a previously sent event.
It carries an ID of the response notification "responseId", whereby the data is required to be fetched from the API.
It also carries "notificationId" to indicate which notification this is a response to.
A "notification-v2" event carries a NotificationV2 object.
{ "type": "notification-v2", "content": { "id": ":notificationId", "version": 2, // Appears to be a static marker "type": ":notificationV2TypeEnum", // One of: "group.announcement", ??? "category": ":notificationV2CategoryEnum", // One of: "social.group", ??? "isSystem": <boolean>, "ignoreDND": <boolean>, "senderUserId": ":userId", // NOTE: WILL BE NULL if the notification didn't originate from a user "senderUsername": ":username", // NOTE: same as "senderUserId" "receiverUserId": ":userId", "relatedNotificationsId": ":?Id", // gpos_00000000-0000-0000-0000-000000000000 "title": ":string", "message": ":string", "imageUrl": ":assetUrl", "link": ":notificationLinkUri", // The scheme is the type of the linked object, and the path is the id of that object, e.g.: "group:grp_00000000-0000-0000-0000-000000000000" "linkText": ":string", "responses": [ { "type": ":notificationV2ResponseTypeEnum", // One of: "delete", "unsubscribe", ??? "data": ":string", // Auxiliary data // If the type is "delete", then this will be empty // If the type is "unsubscribe", then this will be the id of the sending object, a comma, and the id of the (recieving) user // e.g.: grp_00000000-0000-0000-000000000000,usr_00000000-0000-0000-000000000000 "icon": ":notificationV2ResponseIconEnum", // One of: "check", "bell-slash", ??? "text": ":string" } ], "expiresAt": "dateTimeString", // yyyy-mm-ddThh:mm:ss.sssZ "expiryAfterSeen": <number>, // Typically 900 "requireSeen": <boolean>, "seen": <boolean>, "canDelete": <boolean>, "createdAt": ":dateTimeString", "updatedAt": ":dateTimeString" }}
A "notification-v2-update" event is sent when the client should update a notification, overwriting at least one property.
{ "type": "notification-v2-update", "content": { "id": ":notificationId", "version": 2, "updates": { // These properties are to be overwrite the properties found in the content of a `notification-v2` event } }}
A "friend-online" event is sent when one of the user's friends has gone online in-game. Note that the "world" field will be an empty object when the friend is on orange/red, or is in a private world.
{ "type": "friend-online", "content": { "userId": ":userId", "platform": ":platformString", "location": ":locationString", "canRequestInvite": <boolean>, "user": { // <User Object>, See return data of User API: // https://vrchatapi.github.io/docs/api/#get-/users/-userId- } }}
A "friend-active" event is sent when one of the user's friends is active on the website.
{ "type": "friend-active", "content": { "userid": ":userId", "platform": ":platformString", // Appears only to be "web" from these events "user": { // <User Object>, See return data of User API: // https://vrchatapi.github.io/docs/api/#get-/users/-userId- } }}
A "friend-location" event is sent when one of the user's friends has changed instances. Note that the worldId field will be "private" when the friend is on orange/red, or is in a private world.
{ "type": "friend-location", "content": { "userId": ":userId", "location": ":locationString", "travelingToLocation": ":locationString", // normally empty "", but when the above "location" is "traveling", this contains the imminent destination "worldId": ":worldId", "canRequestInvite": <boolean>, "user": { // <User Object>, See return data of User API: // https://vrchatapi.github.io/docs/api/#get-/users/-userId- } }}
A "user-update" event is sent when something regarding the user has been updated. Note that the "user" object is not a LimitedUser object, even though it has similarities. It's missing developerType, friendKey, isFriend, last_platform and location. It also has extra currentAvatar and currentAvatarAssetUrl fields.
A "user-location" event is sent when the user has changed instances.
{ "type": "user-location", "content": { "userId": ":userId", "user": { // <User Object>, See return data of User API: // https://vrchatapi.github.io/docs/api/#get-/users/-userId- }, "location": ":locationString", "instance": ":instanceId", // This is locationString without the World ID part. "travelingToLocation": ":locationString" }}
A "instance-queue-ready" event is sent when the user is at the front of the queue.
{ "type": "instance-queue-ready", "content": { "instanceLocation": ":locationString", "expiry": ":dateTimeString" // Time at which priority will be lost }}
A "group-member-updated" event is sent when something regarding the user's group membership changes. Note that the optional values in the member are as if "fetching a specific user", per the schema description.
{ "type": "group-member-updated", "content": { "member": { // <GroupLimitedMember Object>, See return data of Groups API: // https://vrchatapi.github.io/docs/api/#get-/groups/-groupId-/members/-userId- } }}
A "group-role-updated" event is sent when something regarding one of the user's group's roles changes.
{ "type": "group-role-updated", "content": { "role": { // <GroupRole Object>, See return data of Groups API: // https://vrchatapi.github.io/docs/api/#get-/groups/-groupId-/roles } }}