# Joining and Creating Calls

### Creating Calls

To create a call, we first call the `makeCall` function on the `StreamVideo` class and pass it the call type and ID.
The most common call type is `default`, which enables full audio and video transmission.
However, there are multiple call types (and even custom types) from which you can choose based on your use case.

| Call type   | Name        | Short overview                                                                                                |
| ----------- | ----------- | ------------------------------------------------------------------------------------------------------------- |
| default     | Default     | simple 1-1 calls for larger group video calling with sensible defaults                                        |
| audio_room  | Audio       | pre-configured for a workflow around requesting permissions in audio settings (speaking, etc.)                |
| livestream  | Livestream  | access to calls is granted to all authenticated users, useful in one-to-many settings (such as livestreaming) |
| development | Development | should only be used for testing, permissions are open and everything is enabled (use carefully)               |

You can read more about call types [here](/video/docs/flutter/guides/call-types/).

```dart
final call = StreamVideo.instance.makeCall(callType: StreamCallType.defaultType(), id: 'Your-call-ID');
await call.getOrCreate();
```

Calling `makeCall` returns a `Call` object for us to work with. However, it neither connects nor starts transmitting data automatically.
To create and join the call, we must then invoke `getOrCreate` on the returned object which creates the call if it doesn't exist and returns the existing call if it does.

For the call ID there are a few things to note:

- You can reuse the same call multiple times.
- If you have a unique id for the call we recommend passing that as the id.
- If you don't have a unique id you can leave it empty and we'll generate one for you.

As an example, if you're building a telemedicine app, calls will be connected to an appointment. Using your own appointment id as the **Call ID** makes it easy to find the call later.

### Managing Members and Ringing Calls

You can pass certain arguments to the `call.getOrCreate()` method used in the previous example:

1. `members`: Upon creation, we can supply a list of user IDs we would like to immediately add to the call.
2. `ringing`: If ringing is set to `true`, Stream will send a VoIP notification to the users on the call, triggering the platform call screen on iOS and Android.
3. `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.
4. `custom`: Any custom data associated with the call.
5. `team`: A team is a part of [Stream Video multi-tenancy support](/video/docs/api/multi-tenant/). You can separate different groups of videos and calls using this argument.
6. `notify` If notify is set to true, Stream will send a standard non-VoIP push notification to all the users in the call.

<admonition type="note">

By default, calling `getOrCreate()` assigns `admin` permission to each user who is supplied during creation. Depending on call permissions settings, call member may have different permissions than other users joining the call. For example, call can be configured so only members can join. See [here](#restricting-access) for more information.

</admonition>

When call is already active you can still manage members:

```dart
final call = client.makeCall(callType: StreamCallType.defaultType(), id: 'my-call-id');
call.getOrCreate(memberIds: ['alice', 'bob']);

// grant access to more users
await call.updateCallMembers(updateMembers: [const UserInfo(id: 'charlie', role: 'call_member')]);
// or
await call.addMembers([const UserInfo(id: 'charlie', role: 'call_member')]);

// remove access from some users
await call.updateCallMembers(removeIds: ['charlie']);
// or
await call.removeMembers(['charlie']);
```

### Call CRUD Operations

With calls, we make it easy to perform basic create, read, update, and delete (CRUD) operations on calls providing the user has sufficient permissions.

For example, once a call is created a user can `call.update` the information on the call by adding custom metadata such as a name, description, or any other arbitrary `Map<String, Object>` to the call before `getOrCreate` is invoked.

```dart
call.update(custom: {'name': 'My first Call'});
await call.getOrCreate();
```

Using the update method, a variety of settings can also be applied before the call is created such as:

- Ring
- Audio
- Video
- ScreenShare
- Recording
- Transcription
- Backstage
- Geofencing

### Joining Calls

To join a call that already exists, you must first know two things:

- The `callType` of the existing call
- The `ID` of the existing call

Similar to the flow of creating a call, we can use `makeCall` to construct a `Call` class for us to perform operations on.

```dart
final call = StreamVideo.instance.makeCall(callType: StreamCallType.defaultType(), id: 'My-existing-call-ID');
await call.getOrCreate();
```

Next, with our class instantiated, we can connect to the call and SFU by invoking `join`.

```dart
await call.join();
```

Unlike the call creation flow and functions, the user must have sufficient permissions to join the call or a `VideoError` will be returned. All users connected via the `join()` function have the permission type of `user` by default and are limited in the actions they can perform.

#### Backstage setup

The backstage feature makes it easy to build a use-case where you and your co-hosts can setup your camera before going live. Only after you call `call.goLive()` the regular users be allowed to join the livestream.

However, you can also specify a `joinAheadTimeSeconds`, which allows regular users to join the livestream before it is live, in the specified join time before the stream starts.

Here's an example how to do that:

```dart
final call = StreamVideo.instance.makeCall(callType: StreamCallType.livestream(), id: 'my-call-id');

const backstageSetting = StreamBackstageSettings(
  enabled: true,
  joinAheadTimeSeconds: 300,
);

await call.getOrCreate(
  memberIds: ['alice', 'bob'],
  startsAt: DateTime.now().add(const Duration(seconds: 500)),
  backstage: backstageSetting,
);

await call.join();
```

In the code snippet above, we are creating a call that starts 500 seconds from now. We are also enabling backstage mode, with a `joinAheadTimeSeconds` of 300 seconds. That means that regular users will be able to join the call 200 seconds from now.

### Restricting access

You can restrict access to a call by tweaking the [Call Type](/video/docs/flutter/guides/call-types/) permissions and roles. A typical use case is to restrict access to a call to a specific set of users -> call members.

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

​
On our [dashboard](https://dashboard.getstream.io), navigate to the **Video & Audio -> Roles & Permissions** section and select the appropriate role and scope. In this example, we will use `my-call-type` scope.

By default, all users unless specified otherwise, have the `user` role.

We start by removing the `JoinCall` permission from the `user` role for the `my-call-type` scope. It will prevent regular users from joining a call of this type.

![Revoke JoinCall](@video/flutter/_assets/core_concepts/user-revoke-joincall.png)

Next, let's ensure that the `call_member` role has the `JoinCall` permission for the `my-call-type` scope. It will allow users with the `call_member` role to join a call of this type.

![Grant JoinCall](@video/flutter/_assets/core_concepts/call_member-grant-joincall.png)

Once this is set, we can proceed with setting up a call instance.

---

This page was last updated at 2025-08-15T14:16:37.412Z.

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