# Joining & Creating Calls

This guide covers creating, joining, leaving, and ending calls, including ring calls.

## Best Practices

- Handle `call.join()` errors with try/catch - network issues can cause failures.
- Always call `call.leave()` when unmounting to prevent memory leaks.
- Use `call.getOrCreate()` when you want to create if not exists, or just load if it does.
- Set `create: true` in `call.join()` only when you want to create the call on join.
- For recurring meetings, reuse the same call ID (e.g., your appointment ID).

## Call

`Call` is the main building block of the SDK, abstracting user actions, join flows, and call state.

### Create call

Specify `callType` and `callId` to create a call. The [Call Type](/video/docs/react/guides/configuring-call-types/) controls permissions and features. Call IDs can be reused for recurring meetings.

```ts
const callType = "default";
const callId = "test-call";

const call = client.call(callType, callId);
await call.getOrCreate();

// or create it with options:
await call.getOrCreate({
  data: {
    /* call creation options */
  },
});
```

See all options at the [Call creation options section](#call-creation-options).

### Join call

```ts
const callType = "default";
const callId = "test-call";

const call = client.call(callType, callId);
await call.join();
```

### Create and join a call

Set `create: true` to create a new call if it doesn't exist. Set it to `false` to only join an existing call.

```ts
await call.join({
  create: true,
  data: {
    /* call creation options */
  },
});
```

### Join with mic and camera on or off

Configure devices before joining (typically in a lobby view):

```ts
const call = client.call("default", "test-call");

// enable mic and camera
await call.camera.enable();
await call.microphone.enable();

// alternatively, you can disable them
await call.camera.disable();
await call.microphone.disable();

// and then join the call
await call.join();
```

### Leave call

```ts
await call.leave();
```

### End call

Ending terminates the call for everyone. Requires a [special permission](/video/docs/react/guides/permissions-and-moderation/).

```ts
await call.endCall();
```

Only users with `OwnCapability.JOIN_ENDED_CALL` can join an ended call.

### Load call

```ts
const call = client.call(type, id);
await call.get(); // just load

await call.getOrCreate(); // create if not present and load it
```

These operations initialize `call.state` and subscribe to real-time updates.

More: [Call & Participant State](/video/docs/react/guides/call-and-participant-state/)

### Update call

```ts
import { RecordSettingsRequestModeEnum } from "@stream-io/video-react-sdk";

await call.update({
  custom: { color: "green" },
  settings_override: {
    recording: {
      mode: RecordSettingsRequestModeEnum.DISABLED,
    },
  },
});
```

## Call creation options

| Option     | Description                                                                                                                                                     | Default |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| `members`  | A list of members to add to this call. You can specify the role and custom data on these members                                                                | -       |
| `custom`   | Any custom data you want to store                                                                                                                               | -       |
| `settings` | You can overwrite certain call settings for this specific call. This overwrites the call type standard settings                                                 | -       |
| `startsAt` | When the call will start. Used for calls scheduled in the future, livestreams, audio rooms etc                                                                  | -       |
| `team`     | Restrict the access to this call to a specific team                                                                                                             | -       |
| `ring`     | If you want the call to ring for each member                                                                                                                    | `false` |
| `notify`   | If you want the call to notify each member by sending push notification.                                                                                        | `false` |
| `video`    | When ringing, the notification will indicate whether it’s a video call or an audio-only call, depending on whether you set the video parameter to true or false | -       |

### Set call members

```ts
const call = client.call(type, id);
await call.getOrCreate({
  data: {
    members: [{ user_id: "alice", role: "admin" }, { user_id: "bob" }],
  },
});
```

### Update call members

```ts
await call.updateCallMembers({
  update_members: [{ user_id: "charlie", role: "admin" }],
  remove_members: ["alice"],
});
```

### Multi-tenant & teams

Users in a team must specify the same team when creating calls:

```ts
const call = client.call(type, id);
await call.getOrCreate({
  data: { team: "red" },
});
```

Ensure call IDs are unique - use UUIDs or prefix with team name.

### Custom call data

```ts
await call.getOrCreate({
  data: {
    custom: { color: "blue" },
  },
});
```

### Settings override

Override call type settings for a specific call:

```ts
// at creation time
await call.getOrCreate({
  data: {
    settings_override: {
      audio: { mic_default_on: false },
      video: { camera_default_on: false },
    },
  },
});

// or later
await call.update({
  settings_override: {
    video: { camera_default_on: true },
  },
});
```

### Backstage setup

The backstage feature lets you and your co-hosts set up before going live. Only after `call.goLive()` will regular users be allowed to join.

You can also specify `join_ahead_time_seconds` to allow users to join before the call goes live:

```ts
await call.getOrCreate({
  data: {
    starts_at: new Date(Date.now() + 500 * 1000), // 500 seconds from now
    settings_override: {
      backstage: {
        enabled: true,
        join_ahead_time_seconds: 300,
      },
    },
  },
});
```

In this example, the call starts 500 seconds from now with backstage enabled and `join_ahead_time_seconds` of 300. Regular users can join 200 seconds from now (500 - 300).

## Restricting access

Control access by configuring [Call Type](/video/docs/react/guides/configuring-call-types/) permissions.

### Step 1: Set up the roles and permissions

On the [dashboard](https://dashboard.getstream.io/), navigate to **Video & Audio -> Roles & Permissions** and select the appropriate role and scope.

By default, all users have the `user` role. Start by removing the `JoinCall` permission from the `user` role for your call type scope. This prevents regular users from joining calls of this type.

![Revoke JoinCall for user role](@video/ios/_assets/user-revoke-joincall.png)

Next, ensure the `call_member` role has the `JoinCall` permission. This allows users with the `call_member` role to join calls of this type.

![Grant JoinCall for call_member role](@video/ios/_assets/call_member-grant-joincall.png)

### Step 2: Set up the call

```ts
const call = client.call("my-call-type", "my-call-id");
await call.getOrCreate({
  data: {
    members: [
      // please note the `role` property
      { user_id: "alice", role: "call_member" },
      { user_id: "bob", role: "call_member" },
    ],
  },
});

// Grant access to more users
await call.updateCallMembers({
  update_members: [{ user_id: "charlie", role: "call_member" }],
});

// Remove access
await call.updateCallMembers({
  remove_members: ["charlie"],
});
```


---

This page was last updated at 2026-05-13T13:39:05.765Z.

For the most recent version of this documentation, visit [https://getstream.io/video/docs/react/guides/joining-and-creating-calls/](https://getstream.io/video/docs/react/guides/joining-and-creating-calls/).