> ## Documentation Index
> Fetch the complete documentation index at: https://learn.social.plus/llms.txt
> Use this file to discover all available pages before exploring further.

# Start Broadcasting

> Fetch room broadcaster credentials, hand them to your media stack, observe lifecycle changes, and stop the room.

Starting a broadcast has two parts: the social.plus SDK returns room broadcaster credentials, and your app-owned media stack uses those credentials to connect and publish audio/video. Keep those responsibilities separate in your implementation and docs.

<Note>
  This page covers SDK room broadcasting APIs. Camera, microphone, LiveKit client setup, permissions, and media publishing UI are app-owned concerns after the SDK returns broadcaster data.
</Note>

## Platform Surface

| Platform   | Get broadcaster data                                              | Observe lifecycle                                                                              | Stop broadcast                            | Notes                                                                                            |
| ---------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------ |
| TypeScript | `RoomRepository.getBroadcasterData(roomId)`                       | `RoomRepository.onRoomStartBroadcasting(...)`, `onRoomEndBroadcasting(...)`, or `getRoom(...)` | `RoomRepository.stopRoom(roomId)`         | `Amity.BroadcasterData` can include `coHostToken`, `coHostUrl`, and `directStreamUrl`.           |
| iOS        | `AmityRoomRepository().generateRoomToken(withId:)`                | `AmityRoomRepository().getRoom(withId:)` live object                                           | `AmityRoomRepository().stopRoom(withId:)` | Token response is a dictionary from `/api/v1/rooms/{roomId}/token`; read known keys defensively. |
| Android    | `AmityVideoClient.newRoomRepository().getBroadcasterData(roomId)` | `getRoom(roomId)` plus room topic subscription when real-time updates are needed               | `stopRoom(roomId)`                        | Returns `AmityRoomBroadcastData.CoHosts` or `AmityRoomBroadcastData.DirectStreaming`.            |
| Flutter    | No current public room broadcaster API found in this audit        | Not available                                                                                  | Not available                             | The Flutter SDK source exposes older stream read APIs, not the room broadcasting repository.     |

## Parameters

| Concept                   | Platforms                | TypeScript                   | iOS                               | Android                                                       | Notes                                                               |
| ------------------------- | ------------------------ | ---------------------------- | --------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------- |
| Fetch credentials         | TypeScript, iOS, Android | `getBroadcasterData(roomId)` | `generateRoomToken(withId:)`      | `getBroadcasterData(roomId)`                                  | Requires an existing room ID.                                       |
| Co-host URL               | TypeScript, iOS, Android | `coHostUrl?: string`         | `tokenPayload["coHostUrl"]`       | `AmityRoomBroadcastData.CoHosts.getCoHostUrl()`               | Use as the media connection URL for co-host rooms.                  |
| Co-host token             | TypeScript, iOS, Android | `coHostToken?: string`       | `tokenPayload["coHostToken"]`     | `AmityRoomBroadcastData.CoHosts.getCoHostToken()`             | Use as the media access token for co-host rooms.                    |
| Direct stream URL         | TypeScript, iOS, Android | `directStreamUrl?: string`   | `tokenPayload["directStreamUrl"]` | `AmityRoomBroadcastData.DirectStreaming.getDirectStreamUrl()` | Use only for direct-streaming publisher flows.                      |
| Flutter room broadcasting | Flutter                  | Not applicable               | Not applicable                    | Not applicable                                                | No current public Flutter room broadcaster API found in this audit. |

### Lifecycle Parameters

| Concept     | Platforms                | TypeScript                                   | iOS                                                                       | Android                                                                           |
| ----------- | ------------------------ | -------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| Start event | TypeScript, iOS, Android | `onRoomStartBroadcasting(callback)`          | Observe `AmityRoom.status` via `getRoom(withId:)`                         | Observe `getRoom(roomId)` and subscribe to `AmityRoomEvents.STREAMER` when needed |
| End event   | TypeScript, iOS, Android | `onRoomEndBroadcasting(callback)`            | Observe `AmityRoom.status` via `getRoom(withId:)`                         | Observe `getRoom(roomId)` and subscribe to `AmityRoomEvents.STREAMER` when needed |
| Stop room   | TypeScript, iOS, Android | `stopRoom(roomId)`                           | `stopRoom(withId:)`                                                       | `stopRoom(roomId)`                                                                |
| Cleanup     | TypeScript, iOS, Android | Call returned `Amity.Unsubscriber` functions | Retain and release the `AmityNotificationToken` with the screen lifecycle | Dispose Rx subscriptions and unsubscribe room topics when no longer needed        |

## Get Broadcaster Data

Call this after the room exists and before connecting your app-owned media client. Co-host rooms use `coHostUrl` and `coHostToken`; direct-streaming rooms use `directStreamUrl`.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { RoomRepository } from "@amityco/ts-sdk";

  async function getBroadcastCredentials(roomId: string) {
    const credentials = await RoomRepository.getBroadcasterData(roomId);

    if (credentials.coHostUrl && credentials.coHostToken) {
      return {
        mode: "coHosts" as const,
        url: credentials.coHostUrl,
        token: credentials.coHostToken,
      };
    }

    if (credentials.directStreamUrl) {
      return {
        mode: "directStreaming" as const,
        directStreamUrl: credentials.directStreamUrl,
      };
    }

    throw new Error("No broadcaster credentials returned for this room.");
  }
  ```

  ```kotlin Android theme={null}
  import com.amity.socialcloud.sdk.api.video.AmityVideoClient
  import com.amity.socialcloud.sdk.model.video.room.AmityRoomBroadcastData

  val disposable = AmityVideoClient.newRoomRepository()
      .getBroadcasterData(roomId)
      .subscribe(
          { broadcastData ->
              when (broadcastData) {
                  is AmityRoomBroadcastData.CoHosts -> {
                      showSuccessMessage(broadcastData.getCoHostUrl())
                      showSuccessMessage(broadcastData.getCoHostToken())
                  }
                  is AmityRoomBroadcastData.DirectStreaming -> {
                      showSuccessMessage(broadcastData.getDirectStreamUrl())
                  }
              }
          },
          { error -> handleGeneralError(error) }
      )
  ```

  ```swift iOS theme={null}
  let tokenPayload = try await AmityRoomRepository()
      .generateRoomToken(withId: roomId)

  if let coHostUrl = tokenPayload?["coHostUrl"] as? String,
     let coHostToken = tokenPayload?["coHostToken"] as? String {
      showSuccessMessage(coHostUrl)
      showSuccessMessage(coHostToken)
  } else if let directStreamUrl = tokenPayload?["directStreamUrl"] as? String {
      showSuccessMessage(directStreamUrl)
  }
  ```
</CodeGroup>

## Hand Credentials to Your Media Client

The SDK does not publish camera or microphone tracks. Use the returned URL/token with your media client, then let that client own connection, preview, mute, retry, and device-selection behavior.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { RoomRepository } from "@amityco/ts-sdk";

  type ExternalMediaClient = {
    connect: (url: string, token: string) => Promise<void>;
    publishCamera: () => Promise<void>;
  };

  async function startCoHostBroadcast(
    roomId: string,
    mediaClient: ExternalMediaClient,
  ) {
    const data = await RoomRepository.getBroadcasterData(roomId);

    if (!data.coHostUrl || !data.coHostToken) {
      throw new Error("This room did not return co-host broadcaster credentials.");
    }

    await mediaClient.connect(data.coHostUrl, data.coHostToken);
    await mediaClient.publishCamera();
  }
  ```

  ```kotlin Android theme={null}
  import com.amity.socialcloud.sdk.api.video.AmityVideoClient
  import com.amity.socialcloud.sdk.model.video.room.AmityRoomBroadcastData
  import io.reactivex.rxjava3.core.Completable

  fun connectExternalMedia(url: String, token: String): Completable {
      showSuccessMessage(url)
      showSuccessMessage(token)
      return Completable.complete()
  }

  fun publishExternalCamera(): Completable = Completable.complete()

  val disposable = AmityVideoClient.newRoomRepository()
      .getBroadcasterData(roomId)
      .flatMapCompletable { broadcastData ->
          when (broadcastData) {
              is AmityRoomBroadcastData.CoHosts -> {
                  connectExternalMedia(
                      url = broadcastData.getCoHostUrl(),
                      token = broadcastData.getCoHostToken()
                  ).andThen(publishExternalCamera())
              }
              is AmityRoomBroadcastData.DirectStreaming -> {
                  Completable.error(
                      IllegalStateException("Use directStreamUrl with your RTMP publisher.")
                  )
              }
          }
      }
      .subscribe(
          { showSuccessMessage(roomId) },
          { error -> handleGeneralError(error) }
      )
  ```

  ```swift iOS theme={null}
  func connectExternalMedia(url: String, token: String) async throws {
      showSuccessMessage(url)
      showSuccessMessage(token)
  }

  func publishExternalCamera() async throws {
      showSuccessMessage("camera")
  }

  let tokenPayload = try await AmityRoomRepository()
      .generateRoomToken(withId: roomId)

  guard let coHostUrl = tokenPayload?["coHostUrl"] as? String,
        let coHostToken = tokenPayload?["coHostToken"] as? String else {
      throw NSError(domain: "Broadcast", code: 0)
  }

  try await connectExternalMedia(url: coHostUrl, token: coHostToken)
  try await publishExternalCamera()
  ```
</CodeGroup>

## Observe Broadcast Lifecycle

Use lifecycle updates to keep host UI, viewer entry points, and moderation tools aligned with the room status.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { RoomRepository } from "@amityco/ts-sdk";

  function observeBroadcastLifecycle(roomId: string): Amity.Unsubscriber {
    const stopStarted = RoomRepository.onRoomStartBroadcasting(room => {
      if (room.roomId === roomId) {
        showSuccessMessage(room.status);
      }
    });

    const stopEnded = RoomRepository.onRoomEndBroadcasting(room => {
      if (room.roomId === roomId) {
        showSuccessMessage(room.status);
      }
    });

    return () => {
      stopStarted();
      stopEnded();
    };
  }
  ```

  ```kotlin Android theme={null}
  import com.amity.socialcloud.sdk.api.video.AmityVideoClient
  import com.amity.socialcloud.sdk.model.core.events.AmityRoomEvents

  val roomRepository = AmityVideoClient.newRoomRepository()

  val roomDisposable = roomRepository
      .getRoom(roomId)
      .subscribe(
          { room -> showSuccessMessage(room.getStatus()) },
          { error -> handleGeneralError(error) }
      )

  val topicDisposable = roomRepository
      .getRoom(roomId)
      .firstOrError()
      .flatMapCompletable { room ->
          room.subscription(AmityRoomEvents.STREAMER).subscribeTopic()
      }
      .subscribe(
          { showSuccessMessage(roomId) },
          { error -> handleGeneralError(error) }
      )
  ```

  ```swift iOS theme={null}
  var roomObservationToken: AmityNotificationToken?
  let roomObject = AmityRoomRepository().getRoom(withId: roomId)

  roomObservationToken = roomObject.observe { liveObject, error in
      if let error {
          handleGeneralError(error)
          return
      }

      if let room = liveObject.snapshot {
          showSuccessMessage(room.status.rawValue)
      }
  }

  showSuccessMessage(roomObservationToken != nil)
  ```
</CodeGroup>

## Stop the Broadcast

Stop the room when the host ends the session. Disconnect your media client separately, then call the SDK stop API so social.plus room state and viewer surfaces can move out of the live state.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { RoomRepository } from "@amityco/ts-sdk";

  async function stopBroadcast(roomId: string) {
    const { data: stoppedRoom } = await RoomRepository.stopRoom(roomId);
    showSuccessMessage(stoppedRoom.status);
  }
  ```

  ```kotlin Android theme={null}
  import com.amity.socialcloud.sdk.api.video.AmityVideoClient

  val disposable = AmityVideoClient.newRoomRepository()
      .stopRoom(roomId)
      .subscribe(
          { showSuccessMessage(roomId) },
          { error -> handleGeneralError(error) }
      )
  ```

  ```swift iOS theme={null}
  let stoppedRoom = try await AmityRoomRepository()
      .stopRoom(withId: roomId)

  showSuccessMessage(stoppedRoom.status.rawValue)
  ```
</CodeGroup>

<Warning>
  Stopping a room ends the current broadcast session. Do not build a "restart the same room" flow unless your product and backend contract explicitly support it.
</Warning>

## Media Boundary

| Area         | Owned by social.plus SDK                             | Owned by your app/media stack                                 |
| ------------ | ---------------------------------------------------- | ------------------------------------------------------------- |
| Room record  | Create, observe, update, stop, delete                | Product routing and host controls                             |
| Credentials  | Return room broadcaster fields                       | Store only in memory and hand to the media client             |
| Publishing   | Not handled by social.plus SDK room APIs             | Camera, microphone, preview, mute, reconnect, and permissions |
| Viewer state | Room status, live playback fields, recorded metadata | Player UI and playback SDK behavior                           |

## Related Topics

<CardGroup cols={3}>
  <Card title="Create Room" icon="plus" href="./create-room">
    Create the room before fetching broadcaster credentials.
  </Card>

  <Card title="Co-Host Management" icon="users" href="./co-host-management">
    Invite, observe, and manage co-hosts before or during the broadcast.
  </Card>

  <Card title="Live Viewing" icon="radio" href="./live-viewing">
    Show viewers how to watch active room broadcasts.
  </Card>
</CardGroup>
