> For the complete documentation index, see [llms.txt](https://tools.docs.sendbird.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tools.docs.sendbird.com/plugins/client/add-voice-and-video-calls.md).

# Add voice and video calls

Add 1-to-1 voice and video calls with Sendbird Calls to an existing app, or create a new app with calls. The Sendbird Client plugin does both. Calls uses its own SDK, separate from the Chat SDK, and the same plugin sets up both.

## Before you begin

* Install the Sendbird Client plugin version 0.1.54 or later, and open your app project in Claude Code or Codex. See [How to use the plugin](/plugins/client.md#how-to-use-the-plugin). If you already have the plugin, [check its version](/plugins/client.md#check-the-installed-version).
* Use a Sendbird app with Calls turned on. If you create the app with Sendbird CLI, pass `--is-calls-enabled true` to `app create`. For an existing app, follow [Turn on Calls for an app](/readme/calls/turn-on-calls.md).
* Have your Sendbird App ID ready. The plugin can also find it, for example from the Sendbird app you selected in Sendbird CLI. See [Give the plugin your App ID](/plugins/client.md#give-the-plugin-your-app-id).
* Calls follows the same sign-in rule as Chat. During development, if your app allows users to connect without a token, users sign in to Calls with a user ID only. A user who has been issued a token can't sign in without it: Calls rejects the sign-in with error `9400302`, and Chat with `400302`. For the conditions, see [Add Chat](/plugins/client/add-chat.md#before-you-begin).
* For incoming calls on iOS, Android, or React Native, plan to set up push. On these platforms, a new incoming call arrives only through push. React needs no push setup. See [Set up incoming calls](#set-up-incoming-calls).

## Add calls to an existing app

### Ask like this

In the session, type:

```
Let the signed-in user place 1:1 voice and video calls to another user and accept or decline incoming calls with Sendbird Calls.
```

To start the same setup with a command, enter `/sendbird-client:setup --product calls`. In Codex, start the command with `$` instead of `/`.

### What the plugin asks

Your App ID, only when it can't find one. See [Give the plugin your App ID](/plugins/client.md#give-the-plugin-your-app-id).

### What you get

* The Calls SDK, installed with the package manager your project already uses.
* Camera and microphone permissions. The app asks the user for them before a call.
  * iOS: The plugin adds `NSMicrophoneUsageDescription` and, for video calls, `NSCameraUsageDescription` to `Info.plist`.
  * Android: `RECORD_AUDIO`, `CAMERA`, and, on Android 12 and later, `BLUETOOTH_CONNECT`. The Calls SDK already declares them in its own manifest, so the plugin adds only the code that asks the user.
  * React: The browser asks the first time a call starts.
* Calls sign-in where your app finishes signing in, and Calls sign-out when the user signs out.
* An incoming-call listener and voice and video dialing.
* Accept, decline, and end actions.
* On React, a call panel with these parts:
  * A field for the other user's ID
  * **Voice call**, **Video call**, **Accept**, **Decline**, and **End** buttons
  * A video area
* A list of anything the plugin couldn't do, as steps left for you.

## Create a new app with calls

The plugin creates the code project, not the Sendbird app.

Open an empty folder in Claude Code or Codex and type:

```
Create a new chat app with voice and video calls in this folder.
```

To start the same setup with a command, enter `/sendbird-client:setup new-app --product calls`. In Codex, start the command with `$` instead of `/`.

Calls always comes with Chat. A request for calls alone creates an app with both Chat and Calls, and the plugin tells you so. The plugin asks the same questions as for a new chat app. See [Create a new chat app](/plugins/client/add-chat.md#create-a-new-chat-app). The app gets both 1:1 voice and 1:1 video calls unless your request names only one.

When the user signs in, the app signs in to Chat and then to Calls, and when the user signs out, it signs out of both. Calls needs its own sign-in even though the app also uses Chat.

## Set up incoming calls

On iOS, Android, and React Native, a new incoming call arrives only through push, even while the app is open. This is because the Calls SDK connects to the server only during a call. Placing a call works without push. On React, the app receives calls while it's connected, and there's no push for incoming calls.

| Platform     | What you set up                                                                                                                                                                             |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| iOS          | Register an APNs VoIP certificate on Sendbird Dashboard. The plugin then adds PushKit and CallKit code.                                                                                     |
| Android      | Create a Firebase project and register its FCM credentials on Sendbird Dashboard. The plugin then adds the code that registers the FCM token and shows call notifications.                  |
| React Native | Prepare push by following [Make your first call](https://sendbird.com/docs/calls/sdk/v1/react-native/getting-started/make-first-call) in the Sendbird Calls documentation for React Native. |
| React        | Nothing.                                                                                                                                                                                    |

After you register the push credentials, ask:

```
Set up incoming calls for this app with Sendbird Calls.
```

To start the same setup with a command, enter `/sendbird-client:calls-incoming`.

## Run and test a call

Run the app with your platform's tools, the same way as in [Run the app](/plugins/client/add-chat.md#run-the-app). Then place a call between two users. The buttons below are from the React call panel; on other platforms, use the call controls the plugin added.

1. Sign in as two different users, one on each side. On React, use two browser windows. On iOS, Android, or React Native, use two devices.
2. On one side, enter the other user's ID and select **Video call** or **Voice call**.
3. On the other side, select **Accept**.
4. Select **End** to finish the call.

When you test calls:

* On iOS, use a real device. The simulator can't play call audio.
* On React, use HTTPS or localhost, because browsers allow the camera and microphone only there.
* For incoming calls on iOS, Android, or React Native, finish the [push setup](#set-up-incoming-calls) first.

## Check the changes

Review the changed files and finish the steps the plugin lists. Then ask:

```
Check that my Sendbird setup is correct.
```

In the changes, check:

* The Calls sign-in and sign-out code
* The incoming-call listener
* The call controls
* The camera and microphone permissions

## What you do yourself

If your app requires access or session tokens, your server issues them. The plugin writes the client code that uses the tokens. Give the plugin your server's token endpoint, as described in [Connect users with server-issued tokens](/plugins/client/add-chat.md#connect-users-with-server-issued-tokens). Don't paste real tokens or server keys into the conversation.

## Keep building

* To add chat to the same app, follow [Add Chat](/plugins/client/add-chat.md).
* To fix incoming calls that don't ring or in-call audio and video problems, ask the plugin, or enter `/sendbird-client:calls-incoming`.
* For more requests, see [Run a skill with a command](/plugins/client.md#run-a-skill-with-a-command).
