---
title: "Change an audio stream's routing role"
method: PATCH
path: "/legs/{id}/streams/{streamId}"
tags: ["Legs"]
---

# Change an audio stream's routing role

`PATCH /legs/{id}/streams/{streamId}`

Updates the stream's role in place and, when the stream is mixed into a room, recomputes that room's matrix-derived allow-sets atomically (single mixer-mutex acquisition), so no audio bleeds through mid-change. Emits `leg.stream_role_changed` and `room.routing_changed` with `reason: leg_stream_role_changed`.

Only the role is mutable here. The SDP-level attributes — direction, lang, content, label — are fixed when the stream is negotiated; change them by removing the stream and adding a new one.

## Request body

- UpdateLegStreamRequest
  - `role` string — New routing role for this stream inside its room. The room's routing matrix decides who hears it. Pass an empty string to clear the role (full mesh). Omit to leave it untouched. Applied atomically — the room's allow-sets are recomputed in a single mixer-mutex acquisition, so no audio bleeds through mid-change.

## Response `200`

Updated stream

- LegStreamView
  - `id` string, required — Stream identifier, stable for the life of the dialog. The primary stream is always "0".
  - `mid` string — The stream's SDP a=mid token (RFC 5888), used to correlate it across offer/answer.
  - `index` integer, required — Position of the stream's m= line in the SDP. Fixed for the life of the dialog (RFC 3264 §8).
  - `primary` boolean, required — True for the call's main bidirectional audio stream, which cannot be removed or attached to a room independently.
  - `state` 'pending' | 'active' | 'removed', required — Negotiation state.
  - `direction` 'sendrecv' | 'sendonly' | 'recvonly' | 'inactive', required — Negotiated media direction from this server's point of view.
  - `desired_direction` 'sendrecv' | 'sendonly' | 'recvonly' | 'inactive' — Direction requested by the application. Survives hold/unhold, unlike the negotiated direction.
  - `codec` string — Codec negotiated for this stream. Streams on one leg may use different codecs.
  - `sample_rate` integer — Native sample rate of the stream's codec, in Hz.
  - `local_port` integer — Local RTP port. Each stream binds its own port; a shared transport is undefined without BUNDLE (RFC 9143).
  - `remote_addr` string — Remote RTP address media is currently sent to.
  - `label` string — The stream's a=label value (RFC 4574), for correlating it with external metadata.
  - `content` string — The stream's a=content value (RFC 4796), e.g. "main" for original audio and "alt" for a translated feed.
  - `lang` string — The stream's a=lang value (RFC 8866): a BCP 47 language tag such as "en" or "es-ES".
  - `room_id` string — Room this stream's audio is mixed into. A secondary stream may sit in a different room than its leg.
  - `role` string — Routing role of this stream within its room. Streams carry their own role, independent of their leg's.

## Other responses

- `400` — Invalid JSON, not a SIP leg, or the primary stream (its role follows the leg)
- `404` — Leg or stream not found

---

[API](https://skmtc.net/voiceblender/apis/voiceblender-api.md) · [All operations](https://skmtc.net/voiceblender/apis/voiceblender-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/voiceblender/voiceblender-api/versions/ad30827d3b57/schema)
