HTTP Integration API Reference

A local HTTP API for reading and writing VRCPersona's data from external tools (OBS, overlays, bots, and the like).

License-only feature

Setup

  1. Open Settings → HTTP Integration tab
  2. Enable the control server and the data server individually
  3. Change the ports if needed (default: control 11020 / data 11021)
  4. Copy the authentication token from “Authentication & Access Control” and set it in your external tool

Both servers bind only to 127.0.0.1 (local access only). Bearer token authentication is required on all requests (see “Authentication” below).


Authentication

All requests to the local HTTP servers require authentication with a Bearer token. Requests without a token, or with a token that doesn't match, are rejected with 401 Unauthorized.

  • You can view, copy, and regenerate the token in the app under Settings → HTTP Integration → Authentication & Access Control
  • Add an Authorization: Bearer <token> header to requests from your external tool
  • Do not share the token with third parties. If you regenerate it, you'll also need to update the settings on the external tool side

Example of putting the copied token in an environment variable:

TOKEN="<token copied from the settings screen>"

curl http://127.0.0.1:11021/api/groups \
  -H "Authorization: Bearer $TOKEN"

Note: For brevity, the curl examples below omit the Authorization header. In practice, add it to every request.

Using it from a page in a browser

Only when calling from a web page in a browser (JavaScript) do you need to add that page's origin (e.g. http://localhost:3000) to “Allowed Origins” in the settings. Non-browser external tools (OBS, bots, scripts, etc.) don't need to register an origin and work with the token alone.


Data server (read)

Default port: 11021

External tools retrieve VRCPersona's data with GET. The data is refreshed every 5 seconds.

User-Agent header (required)

Requests to the data server's retrieval endpoints (/api/groups / /api/friends / /api/notifications) require a non-empty User-Agent header. Requests with no header, or an empty one, are rejected with 400 Bad Request (/api/health is exempt, as it's for connectivity checks). The control server (11020) does not require a User-Agent.

The User-Agent you specify is passed on to VRChat in the form VRCPersona/<version> (via HTTP API; <your UA>) … whenever VRCPersona queries the VRChat API on your behalf. This is meant to make clear to VRChat which external tool is accessing it via VRCPersona. Please set a value that identifies the tool name and version (e.g. MyOverlay/1.0).

Many HTTP clients, including curl, send a User-Agent by default, so they work without any extra configuration. Only add it explicitly when your client doesn't send a User-Agent, such as the browser's fetch or a custom client.

curl http://127.0.0.1:11021/api/friends \
  -H "Authorization: Bearer $TOKEN" \
  -H "User-Agent: MyOverlay/1.0"

GET /api/groups

Retrieves the list of groups (server groups + local groups).

curl http://127.0.0.1:11021/api/groups

Example response:

[
  {
    "id": "d7569eaf-9673-4d5b-922e-5d868a048593",
    "name": "Favorites",
    "color": "#1e65d3",
    "icon": null,
    "storage": "server",
    "statusColor": "blue",
    "statusMessage": "Everyone welcome",
    "memberIds": ["usr_xxx", "usr_yyy"]
  },
  {
    "id": "local_0211fdf8-dbea-4f3b-a5b3-caca9d2c1d80",
    "name": "Local Group",
    "color": "#c43fe0",
    "icon": null,
    "storage": "local",
    "statusColor": null,
    "statusMessage": null,
    "memberIds": ["usr_aaa", "usr_bbb"]
  }
]
FieldTypeDescription
idstringGroup ID
namestringGroup name
colorstring | nullGroup color (HEX)
iconstring | nullGroup icon
storagestring | null"server" or "local"
statusColorstring | nullThe status color you set (blue / green / orange / red / null)
statusMessagestring | nullThe status message you set
memberIdsstring[]List of members' VRChat User IDs

GET /api/friends

Retrieves the list of friends.

curl http://127.0.0.1:11021/api/friends

Example response:

[
  {
    "id": "usr_xxx",
    "displayName": "Username",
    "status": "green",
    "statusDescription": "Hanging out",
    "location": "wrld_xxx:12345~friends(usr_yyy)~region(jp)",
    "groups": ["d7569eaf-..."],
    "priority": 3.1,
    "platform": "standalonewindows",
    "avatarThumbnail": "https://...",
    "userIcon": null,
    "minutesAtLocation": 42,
    "instanceInfo": {
      "worldId": "wrld_xxx",
      "worldName": "My World",
      "instanceId": "12345~friends(usr_yyy)~region(jp)",
      "userCount": 8,
      "capacity": 32,
      "instanceType": "Friends",
      "region": "jp"
    }
  }
]
FieldTypeDescription
idstringVRChat User ID
displayNamestringDisplay name
statusstringVRChat status (blue / green / orange / red)
statusDescriptionstring | nullStatus message
locationstring | nullCurrent location (wrld_xxx:instanceId format)
groupsstring[]List of group IDs the friend belongs to
prioritynumberSort priority (lower comes first)
platformstring | nullPlatform (standalonewindows / android / web, etc.)
avatarThumbnailstring | nullAvatar thumbnail URL
userIconstring | nullUser icon URL (automatically populated when retrieved with ?include=raw)
minutesAtLocationnumber | nullMinutes spent in the current instance
instanceInfoobject | nullInstance details (see below)

instanceInfo

FieldTypeDescription
worldIdstringWorld ID
worldNamestringWorld name
instanceIdstringInstance ID
userCountnumber | nullCurrent headcount
capacitynumber | nullCapacity
instanceTypestringInstance type (Public / Friends / Friends+, etc.)
regionstring | nullRegion (jp / us / eu, etc.)

Optional information (?include= query parameter)

Specifying comma-separated values with ?include= attaches additional information to each friend.

curl "http://127.0.0.1:11021/api/friends?include=groups"
curl "http://127.0.0.1:11021/api/friends?include=raw,groups,me"
curl "http://127.0.0.1:11021/api/friends?include=groups&fresh=true"
include valueAdded fieldDescription
rawrawUserThe full JSON of the VRChat user details (bio, tags, dateJoined, etc.)
groupsvrchatGroupIdsAn array of VRChat group IDs the user belongs to
worldsuserWorldIdsAn array of world IDs the user has created
mutualsmutualFriendsList of mutual friends
meAdds yourself to the start of the arrayIncludes an "isMe": true flag along with the basic fields (displayName, etc.)

?fresh=true: Ignores the cache and fetches the latest information from the VRChat API. When omitted, the cache is used (valid for 7 days).

Note: Specifying include triggers a VRChat API query for each friend, so the response may take some time. On a cache hit, the response is immediate.

GET /api/notifications

Retrieves the list of VRChat notifications.

curl http://127.0.0.1:11021/api/notifications

Example response:

[
  {
    "id": "not_xxx",
    "type": "invite",
    "senderUserId": "usr_xxx",
    "senderUsername": "Username",
    "message": "",
    "details": {
      "worldId": "wrld_xxx",
      "worldName": "World name",
      "inviteMessage": "Come hang out!"
    },
    "created_at": "2026-03-20T17:52:28.263Z",
    "seen": false
  }
]

GET /api/health

Check that the server is running.

curl http://127.0.0.1:11021/api/health

Response:

{"status": "ok"}

Control server (write)

Default port: 11020

Change VRCPersona's group status from an external tool.

PUT /api/groups/:groupId/status

Changes a group's status (color and message).

curl -X PUT http://127.0.0.1:11020/api/groups/{groupId}/status \
  -H "Content-Type: application/json" \
  -d '{"status_color":"blue","status_message":"Streaming"}'

Request body:

FieldTypeDescription
status_colorstring | null"blue" / "green" / "orange" / "red" / null (clear)
status_messagestring | nullStatus message (optional)

Response: 202 Accepted

{"ok": true}

The change is applied asynchronously to the VRCPersona server.

Example of clearing the status:

curl -X PUT http://127.0.0.1:11020/api/groups/{groupId}/status \
  -H "Content-Type: application/json" \
  -d '{"status_color":null,"status_message":null}'

GET /api/health

curl http://127.0.0.1:11020/api/health

Notes

  • An authentication token is required on all requests (401 if unset or mismatched). Manage the token in the app under “Settings → HTTP Integration → Authentication & Access Control.”
  • The data server's retrieval endpoints (/api/groups, /api/friends, /api/notifications) also require a User-Agent header (400 if empty or absent; /api/health and the control server are exempt). The value you specify is passed on as the origin when querying VRChat (details)
  • For groupId, use the id field obtained from the data server's GET /api/groups
  • Status changes only work for server groups (local groups don't support status sharing)
  • The data server's data is refreshed every 5 seconds, so there can be up to a 5-second delay
  • When the paid plan becomes inactive, the servers stop automatically
  • If a port conflicts with another app, it can be changed in the settings screen (1024–65535)