Admin voice
A voice region is a group of LiveKit servers, and Fluxer places each new voice session in a region and then on a server inside it. Each API node reloads the configuration shortly after a write, and a write never moves or disconnects a session that is already connected.
Placement eligibility
Section titled “Placement eligibility”Regions and servers have the same eligibility fields, and a session can use a server only when it is active and both the server and its region admit the session.
| Field | Rule |
|---|---|
| allowed_user_ids | When the list is not empty, only the accounts in it are admitted |
| allowed_guild_ids | Admits only these guilds, or lets them skip vip_only and required_guild_features when those are set |
| vip_only | The guild needs the VIP_VOICE guild feature |
| required_guild_features | The guild needs at least one of the listed features |
When allowed_guild_ids, vip_only or required_guild_features is set, calls in direct messages and group direct messages can't use that region or server.
Admin voice region object
Section titled “Admin voice region object”You pick the id when you create the region, and you can't change it later. A channel's rtc_region is this ID.
Structure
Section titled “Structure”| Field | Type | Description |
|---|---|---|
| id | string | The ID of the region (1-64 characters) |
| name | string | The name shown for the region in a client (1-100 characters) |
| emoji | string | The emoji shown for the region (1-64 characters) |
| latitude | number | The latitude placement measures distance from |
| longitude | number | The longitude placement measures distance from |
| is_default1 | boolean | Whether automatic placement falls back to this region |
| vip_only | boolean | Whether the guild needs VIP_VOICE |
| required_guild_features | array[string] | Guild features that admit a guild (max 100) |
| allowed_guild_ids | array[snowflake] | The guilds admitted without other guild checks (max 1000) |
| allowed_user_ids | array[snowflake] | The only accounts allowed to use the region, when not empty (max 1000) |
| created_at | ?ISO8601 timestamp | When the region was created |
| updated_at | ?ISO8601 timestamp | When the region last changed |
| servers? | array[Admin voice server object] | The servers in the region, when include_servers is true |
1 To change the default, turn the flag off on the old default region too.
Example
Section titled “Example”{ "id": "europe-north", "name": "Northern Europe", "emoji": "🇸🇪", "latitude": 59.33, "longitude": 18.06, "is_default": true, "vip_only": false, "required_guild_features": [], "allowed_guild_ids": [], "allowed_user_ids": [], "created_at": "2026-02-11T08:14:00.000Z", "updated_at": "2026-08-02T17:45:31.000Z"}Admin voice server object
Section titled “Admin voice server object”A voice server is one LiveKit deployment in one region, identified by its region_id and server_id. To move a server to another region, delete it and create it again. You send api_key and api_secret when you create or update a server, and no read returns them.
Structure
Section titled “Structure”| Field | Type | Description |
|---|---|---|
| region_id | string | The ID of the region the server belongs to (1-64 characters) |
| server_id | string | The ID of the server, unique inside its region (1-64 characters) |
| endpoint | string | The LiveKit ws:// or wss:// URL that clients connect to |
| latitude | ?number | The latitude used for this server, or null to use the region's |
| longitude | ?number | The longitude used for this server, or null to use the region's |
| is_active | boolean | Whether new sessions can be placed on the server |
| soft_connection_limit | ?integer | See soft connection limits, or null for none (1-2147483647) |
| notes | ?string | Admin notes about the server, or null for none |
| vip_only | boolean | Whether the guild needs VIP_VOICE |
| required_guild_features | array[string] | Guild features that admit a guild (max 100) |
| allowed_guild_ids | array[snowflake] | The guilds admitted without other guild checks (max 1000) |
| allowed_user_ids | array[snowflake] | The only accounts allowed to use the server, when not empty (max 1000) |
| internal_endpoint | ?string | The http:// or https:// URL the API uses for the LiveKit control API and health checks, or null to use endpoint |
| draining | boolean | Whether new calls go to other servers while calls already on this one keep going |
| public_ipv4 | ?string | The public IPv4 address LiveKit gives to clients, or null when none is known |
| public_ipv6 | ?string | The public IPv6 address LiveKit gives to clients, or null when none is known |
| health | health object | The server's health as the API node that answered last saw it |
| duplicate_api_key | boolean | Whether another server uses the same api_key |
| created_at | ?ISO8601 timestamp | When the server was created |
| updated_at | ?ISO8601 timestamp | When the server last changed |
Voice server health object
Section titled “Voice server health object”| Field | Type | Description |
|---|---|---|
| status | string | up, down, or unknown before the first check |
| checked_at | ?ISO8601 timestamp | When the last check ran, or null before the first |
| error | ?string | The status code or short transport error of the last failed check |
Every API node checks every active server every FLUXER_VOICE_HEALTH_INTERVAL_MS, 15 seconds by default, with GET on internal_endpoint or endpoint. Two failed checks in a row mark a server down, and one passing check marks it up. Placement sends no new calls to a server that is down, unless every server it could pick is down. A call pinned to a server that is down moves on its next join. A check of a public endpoint that resolves to a private address doesn't run, and the status stays unknown.
Draining
Section titled “Draining”A server with draining set gets no new calls. Calls already on it keep going, and people who join those calls still land on it. rexctl sets it while it upgrades or removes a server. Unlike is_active false, draining never splits a call in two.
Soft connection limits
Section titled “Soft connection limits”soft_connection_limit is the connection count at which placement starts to prefer the region's other servers, and it never refuses a session. The count comes from Get voice state counts. Each API node refreshes it every 15 seconds, and a burst of joins between refreshes can go past the limit. The limit is checked when a channel's first session is placed, and later joins to that channel use the same server.
List voice regions
Section titled “List voice regions”GET/v1/admin/voice/regionsReturns every Admin voice region object, sorted by name. Requires voice:region:list.
Query parameters
Section titled “Query parameters”| Field | Type | Description |
|---|---|---|
| include_servers? | boolean | Whether to include each region's servers (default false) |
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| regions | array[Admin voice region object] | Every configured region |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | The regions were returned |
The rate limit is 200 requests per minute, on the admin:lookup bucket.
Get voice region
Section titled “Get voice region”GET/v1/admin/voice/regions/{region_id}Returns one Admin voice region object. Requires voice:region:list.
Query parameters
Section titled “Query parameters”| Field | Type | Description |
|---|---|---|
| include_servers? | boolean | Whether to include the region's servers (default true) |
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| region | ?Admin voice region object | The region, or null |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | The region was looked up, and region is null when none has the ID |
The rate limit is 200 requests per minute, on the admin:lookup bucket.
Create voice region
Section titled “Create voice region”POST/v1/admin/voice/regionsCreates a region, and an id already in use overwrites that region. Requires voice:region:create.
JSON body
Section titled “JSON body”| Field | Type | Description |
|---|---|---|
| id | string | The ID of the region (1-64 characters) |
| name | string | The name shown for the region in a client (1-100 characters) |
| emoji | string | The emoji shown for the region (1-64 characters) |
| latitude | number | The latitude placement measures distance from |
| longitude | number | The longitude placement measures distance from |
| is_default? | boolean | Whether automatic placement falls back to this region (default false) |
| vip_only? | boolean | Whether the guild needs VIP_VOICE (default false) |
| required_guild_features? | array[string] | Guild features that admit a guild (max 100) |
| allowed_guild_ids? | array[snowflake] | The guilds admitted without other guild checks (max 1000) |
| allowed_user_ids? | array[snowflake] | The only accounts allowed to use the region, when not empty (max 1000) |
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| region | Admin voice region object | The created region |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | The region was created |
The rate limit is 100 requests per minute, on the admin:guild:modify bucket.
Update voice region
Section titled “Update voice region”PATCH/v1/admin/voice/regions/{region_id}Updates a region, and an array you send replaces the stored one. Requires voice:region:update.
JSON body
Section titled “JSON body”| Field | Type | Description |
|---|---|---|
| name? | string | The name shown for the region in a client (1-100 characters) |
| emoji? | string | The emoji shown for the region (1-64 characters) |
| latitude? | number | The latitude placement measures distance from |
| longitude? | number | The longitude placement measures distance from |
| is_default? | boolean | Whether automatic placement falls back to this region |
| vip_only? | boolean | Whether the guild needs VIP_VOICE |
| required_guild_features? | array[string] | Guild features that admit a guild (max 100) |
| allowed_guild_ids? | array[snowflake] | The guilds admitted without other guild checks (max 1000) |
| allowed_user_ids? | array[snowflake] | The only accounts allowed to use the region, when not empty (max 1000) |
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| region | Admin voice region object | The updated region |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | The region was updated |
| 404 | error response | UNKNOWN_VOICE_REGION when no region has the ID |
The rate limit is 100 requests per minute, on the admin:guild:modify bucket.
Delete voice region
Section titled “Delete voice region”DELETE/v1/admin/voice/regions/{region_id}Deletes a region and every server in it. Sessions on those servers stay connected, but Fluxer can't mute or disconnect them. Requires voice:region:delete.
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| success | boolean | Always true |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | The region was deleted |
| 404 | error response | UNKNOWN_VOICE_REGION when no region has the ID |
The rate limit is 100 requests per minute, on the admin:guild:modify bucket.
List voice servers
Section titled “List voice servers”GET/v1/admin/voice/regions/{region_id}/serversReturns every Admin voice server object in a region. Requires voice:server:list.
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| servers | array[Admin voice server object] | The servers in the region, empty when the region doesn't exist |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | The servers were returned |
The rate limit is 200 requests per minute, on the admin:lookup bucket.
Get voice server
Section titled “Get voice server”GET/v1/admin/voice/regions/{region_id}/servers/{server_id}Returns one Admin voice server object. Requires voice:server:list.
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| server | ?Admin voice server object | The server, or null |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | The server was looked up, and server is null when none matches |
The rate limit is 200 requests per minute, on the admin:lookup bucket.
Create voice server
Section titled “Create voice server”POST/v1/admin/voice/regions/{region_id}/serversAdds a server to a region. Requires voice:server:create.
JSON body
Section titled “JSON body”| Field | Type | Description |
|---|---|---|
| server_id | string | The ID of the server, unique inside the region (1-64 characters) |
| endpoint | string | The LiveKit ws:// or wss:// URL that clients connect to |
| api_key | string | The LiveKit API key (1-256 characters) |
| api_secret | string | The LiveKit API secret (1-256 characters) |
| latitude?1 | ?number | The latitude used for this server, or null to use the region's |
| longitude?1 | ?number | The longitude used for this server, or null to use the region's |
| is_active? | boolean | Whether new sessions can be placed on the server (default true) |
| soft_connection_limit? | ?integer | See soft connection limits, or null for none (1-2147483647) |
| notes? | ?string | Admin notes about the server, trimmed, with an empty value stored as null (max 2000 characters) |
| vip_only? | boolean | Whether the guild needs VIP_VOICE (default false) |
| required_guild_features? | array[string] | Guild features that admit a guild (max 100) |
| allowed_guild_ids? | array[snowflake] | The guilds admitted without other guild checks (max 1000) |
| allowed_user_ids? | array[snowflake] | The only accounts allowed to use the server, when not empty (max 1000) |
| internal_endpoint? | ?string | The http:// or https:// URL the API uses for the control API and health checks (max 512 characters) |
| draining? | boolean | Whether new calls go to other servers (default false) |
| public_ipv4? | ?string | The public IPv4 address LiveKit gives to clients, for the admin panel |
| public_ipv6? | ?string | The public IPv6 address LiveKit gives to clients, for the admin panel |
1 Send both coordinates or neither
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| server | Admin voice server object | The created server |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | The server was created |
| 404 | error response | UNKNOWN_VOICE_REGION when no region has the ID |
| 409 | error response | VOICE_SERVER_ID_IN_USE for a taken ID, or VOICE_SERVER_API_KEY_IN_USE for a taken key |
The rate limit is 100 requests per minute, on the admin:guild:modify bucket.
Update voice server
Section titled “Update voice server”PATCH/v1/admin/voice/regions/{region_id}/servers/{server_id}Updates a server, and an array you send replaces the stored one. Requires voice:server:update.
JSON body
Section titled “JSON body”| Field | Type | Description |
|---|---|---|
| endpoint? | string | The LiveKit ws:// or wss:// URL that clients connect to |
| api_key? | string | The LiveKit API key (1-256 characters) |
| api_secret? | string | The LiveKit API secret (1-256 characters) |
| latitude?1 | ?number | The latitude used for this server, or null to use the region's |
| longitude?1 | ?number | The longitude used for this server, or null to use the region's |
| is_active? | boolean | Whether new sessions can be placed on the server |
| soft_connection_limit? | ?integer | See soft connection limits, or null for none (1-2147483647) |
| notes? | ?string | Admin notes about the server, trimmed, with null or an empty value clearing them (max 2000 characters) |
| vip_only? | boolean | Whether the guild needs VIP_VOICE |
| required_guild_features? | array[string] | Guild features that admit a guild (max 100) |
| allowed_guild_ids? | array[snowflake] | The guilds admitted without other guild checks (max 1000) |
| allowed_user_ids? | array[snowflake] | The only accounts allowed to use the server, when not empty (max 1000) |
| internal_endpoint? | ?string | The http:// or https:// URL the API uses for the control API and health checks, or null to use endpoint |
| draining? | boolean | Whether new calls go to other servers |
| public_ipv4? | ?string | The public IPv4 address LiveKit gives to clients, or null to clear it |
| public_ipv6? | ?string | The public IPv6 address LiveKit gives to clients, or null to clear it |
1 Send both coordinates or neither, and send both as null to use the region's coordinates
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| server | Admin voice server object | The updated server |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | The server was updated |
| 404 | error response | UNKNOWN_VOICE_SERVER when no server matches |
| 409 | error response | VOICE_SERVER_API_KEY_IN_USE when another server uses the new key |
The rate limit is 100 requests per minute, on the admin:guild:modify bucket.
Delete voice server
Section titled “Delete voice server”DELETE/v1/admin/voice/regions/{region_id}/servers/{server_id}Deletes a server and its stored credentials. Sessions on it stay connected, but Fluxer can't mute or disconnect them. Requires voice:server:delete.
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| success | boolean | Always true |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | The server was deleted |
| 404 | error response | UNKNOWN_VOICE_SERVER when no server matches |
The rate limit is 100 requests per minute, on the admin:guild:modify bucket.
Create voice server test token
Section titled “Create voice server test token”POST/v1/admin/voice/regions/{region_id}/servers/{server_id}/test-tokenSigns a LiveKit token for a hidden, audio only participant in a throwaway room whose name starts with rexctl-test-. The token works for 60 seconds. rexctl voice test uses it to test a server from outside its network, and Fluxer ignores webhooks for these rooms. Requires voice:server:update.
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| token | string | The LiveKit token |
| endpoint | string | The server's endpoint |
| room | string | The name of the throwaway room |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | The token was signed |
| 404 | error response | UNKNOWN_VOICE_SERVER when no server matches |
The rate limit is 100 requests per minute, on the admin:guild:modify bucket.
Voice join tokens
Section titled “Voice join tokens”A join token lets one machine register one voice server with Join a voice server. It looks like fvj_ followed by its ID, an underscore and a 43 character secret. Fluxer stores only a hash of the secret. A token works once, for 60 seconds to 24 hours, and the list keeps it for 7 days after it expires. Whoever runs the server can record the calls placed on it. A token trusts that machine with calls, and it gives no other access.
Voice join token object
Section titled “Voice join token object”| Field | Type | Description |
|---|---|---|
| token_id | snowflake | The ID of the token |
| status | string | pending, used, expired or revoked |
| created_at | ISO8601 timestamp | When the token was created |
| expires_at | ISO8601 timestamp | When the token stops working |
| used_at | ?ISO8601 timestamp | When a server joined with it |
| used_server_id | ?string | The server that joined with it |
| revoked_at | ?ISO8601 timestamp | When it was revoked |
| region_id | ?string | The region preset, or null to let Fluxer pick |
| region_name | ?string | The name of the new region the join creates, or null |
| server_id | ?string | The server ID preset, or null |
| hostname | ?string | The hostname the endpoint must use, or null |
| notes | ?string | The notes for the new server, or null |
Create voice join token
Section titled “Create voice join token”POST/v1/admin/voice/join-tokensRequires voice:join_token:create. Every preset wins over what the joining machine sends.
JSON body
Section titled “JSON body”| Field | Type | Description |
|---|---|---|
| region_id? | ?string | An existing region, or the ID of a new region with region_name (lowercase letters, digits and dashes, 1-64 characters) |
| region_name? | ?string | The name of a new region the join creates (1-100 characters) |
| region_emoji? | ?string | The emoji of the new region (1-64 characters) |
| region_latitude? | ?number | The latitude of the new region, with region_longitude (-90 to 90) |
| region_longitude? | ?number | The longitude of the new region, with region_latitude (-180 to 180) |
| server_id? | ?string | The server ID (lowercase letters, digits and dashes, 1-64 characters) |
| hostname? | ?string | The hostname the endpoint must use |
| internal_endpoint? | ?string | The URL the API uses for the control API and health checks of the new server |
| soft_connection_limit? | ?integer | The soft connection limit of the new server |
| notes? | ?string | The notes of the new server (max 2000 characters) |
| vip_only? | boolean | Restricts the new server to VIP_VOICE guilds (default false) |
| required_guild_features? | array[string] | Guild features that admit a guild to the new server (max 100) |
| allowed_guild_ids? | array[snowflake] | Guilds admitted to the new server (max 1000) |
| allowed_user_ids? | array[snowflake] | The only accounts allowed on the new server (max 1000) |
| expires_in_seconds? | integer | Seconds until the token stops working (60-86400, default 3600) |
A new region from region_name gets region_latitude and region_longitude. Without them, it gets the location of the server that joins.
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| token_id | snowflake | The ID of the token |
| token | string | The token, which no other response returns |
| expires_at | ISO8601 timestamp | When the token stops working |
| command | string | The install command to run on the voice machine |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | The token was created |
| 400 | error response | region_latitude or region_longitude comes without the other, or without region_name |
| 404 | error response | UNKNOWN_VOICE_REGION when no region has the ID in region_id and region_name is empty |
The rate limit is 100 requests per minute, on the admin:guild:modify bucket.
List voice join tokens
Section titled “List voice join tokens”GET/v1/admin/voice/join-tokensLists the tokens of the last 7 days, newest first, without their secrets. Requires voice:join_token:list.
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| tokens | array[voice join token object] | The tokens |
The rate limit is on the admin:lookup bucket.
Revoke voice join token
Section titled “Revoke voice join token”DELETE/v1/admin/voice/join-tokens/{token_id}Stops a pending token from working. Requires voice:join_token:revoke.
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| success | boolean | Always true |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | {"success": true} | The token was revoked |
| 401 | error response | VOICE_JOIN_TOKEN_INVALID when the token is unknown, used, expired or revoked |
Join a voice server
Section titled “Join a voice server”These routes are for the voice machine, not for admins. They take Authorization: VoiceJoin followed by a join token, and the rate limit is 10 requests per minute for each address.
POST/v1/voice/join/previewShows what a join would choose, without using the token. The JSON body has public_ipv4, public_ipv6, region_id, cpu_count, memory_bytes and rexctl_version, all optional. Fluxer looks up one address in GeoIP, the first of public_ipv4, public_ipv6 and the address the request comes from. The response has the suggested server_id, server_id_pinned set to true when the token sets the server ID, the region with its id, name, distance_km, whether it's new and its source, the latitude and longitude from GeoIP rounded to 0.1 degrees, the hostname preset, a suggested_hostname, the soft_connection_limit preset, the webhook_url, the api_code_version, the release of the API, expires_at and the instance's turn_servers.
Fluxer picks the token's region first, then an existing region in region_id, then the existing region nearest to the machine when the instance has a GeoIP database, and the default region otherwise. It never creates a region unless the token has a new one. The source says which of these it used, as token, requested, nearest or default.
POST/v1/voice/joinRegisters the server and uses the token.
| Field | Type | Description |
|---|---|---|
| endpoint | string | The public wss:// URL of the server. A host that is an IP address must be IPv4. A server with only IPv6 needs a hostname |
| api_key | string | A LiveKit API key no other server uses (1-256 characters) |
| api_secret | string | Its secret (32-256 characters) |
| server_id? | string | The server ID, ignored when the token has one |
| region_id? | string | An existing region, ignored when the token has one |
| latitude?1 | number | The server's latitude |
| longitude?1 | number | The server's longitude |
| soft_connection_limit? | integer | Ignored when the token has one |
| public_ipv4? | string | The machine's public IPv4 address, for GeoIP and the admin panel |
| public_ipv6? | string | The machine's public IPv6 address, for GeoIP and the admin panel |
1 Send both coordinates or neither
Fluxer resolves the endpoint's host, refuses it when any address is private unless FLUXER_VOICE_JOIN_ALLOW_PRIVATE_ENDPOINTS is true, and connects to the addresses it checked, one after another until one answers. GET on the endpoint's path must answer OK within 10 seconds with a certificate Fluxer trusts, and the room service must accept the key and secret. Then Fluxer saves the server, active and not draining, and the token stops working. The response has server_id, region_id, endpoint, webhook_url, node_url and turn_servers.
turn_servers is the list in FLUXER_LIVEKIT_TURN_SERVERS on the instance, as objects with host, port, protocol and either username and credential or secret and ttl. It's empty when the setting is. rexctl puts it in the LiveKit configuration of the server.
| Status | Code | Condition |
|---|---|---|
| 400 | VOICE_JOIN_ENDPOINT_INVALID | The endpoint isn't wss://, uses an IPv6 address, a private address or another hostname than the token's |
| 400 | VOICE_JOIN_ENDPOINT_UNREACHABLE | The endpoint didn't answer OK, with the status or error in detail |
| 400 | VOICE_JOIN_KEY_MISMATCH | The server refused the key and secret |
| 401 | VOICE_JOIN_TOKEN_INVALID | The token is unknown, used, expired or revoked |
| 404 | UNKNOWN_VOICE_REGION | No region has the ID in region_id |
| 409 | VOICE_JOIN_TOKEN_BUSY | Another join with the token is running |
| 409 | VOICE_SERVER_ID_IN_USE | Another server has the ID |
| 409 | VOICE_SERVER_API_KEY_IN_USE | Another server uses the key |
A failed join leaves the token unused. The same machine can try again.
Voice node routes
Section titled “Voice node routes”A server that joined manages its own row with GET, PATCH and DELETE on /v1/voice/node. The request takes Authorization: Bearer followed by an HS256 JWT signed with the server's API secret, with these claims:
issset to the server's API key.audset tofluxer-voice-node.methodandpathof the request, withpathset to/voice/node.expin the future and at most 5 minutes ahead, andiatwhen present. Fluxer allows 60 seconds of clock difference either way.sha256set to the base64 SHA-256 of the body, or an empty string forGETandDELETE.
Fluxer refuses tokens with a video grant. LiveKit participant tokens and webhooks signed with the same secret don't work here.
GET/v1/voice/nodeReturns the server's region_id, server_id, endpoint, latitude, longitude, is_active, draining, soft_connection_limit, public_ipv4, public_ipv6, turn_servers, features and health. features lists what the API supports for voice servers. server_addresses means PATCH takes public_ipv4 and public_ipv6.
PATCH/v1/voice/nodeTakes endpoint, draining, latitude, longitude, soft_connection_limit, public_ipv4 and public_ipv6, and returns the server like GET. An empty string for public_ipv4 or public_ipv6 clears it. Fluxer checks a new endpoint the same way as a join. When the server has an internal endpoint, Fluxer also tries the new public endpoint and logs a warning when it doesn't answer, without refusing the change. A new endpoint must keep the token's hostname, and a server can't change a soft connection limit the token set. A server can't change its region, its restrictions, internal_endpoint or is_active. A server an admin turned off stays off.
DELETE/v1/voice/nodeRemoves the server and answers 204.
Every node route can fail with these errors.
| Status | Code | Condition |
|---|---|---|
| 401 | VOICE_NODE_UNKNOWN | No server has the key in iss, usually because an admin deleted it |
| 401 | VOICE_NODE_SIGNATURE_INVALID | The signature or a claim is wrong, or an admin changed the server's secret |
| 401 | VOICE_NODE_TOKEN_EXPIRED | The signature matches, and exp or iat is outside the allowed time. The clock of the voice machine is usually off |