# Start an audio and video call (/en/realtime-media/im/build/moderate-and-manage-client-behavior/callkit)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

<_PlatformTabsGroup groupMode="structured" canonicalPlatform="web" platforms="[&#x22;android&#x22;,&#x22;flutter&#x22;,&#x22;react-native&#x22;,&#x22;unity&#x22;,&#x22;windows&#x22;,&#x22;ios&#x22;,&#x22;web&#x22;]" showTabs="true">
  <_PlatformPanel platform="android">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />

    `AgoraChatCallKit` is an open-source audio and video UI library developed based on Agora's real-time communications and signaling services. With this library, you can implement audio and video calling functionalities with enhanced synchronization between multiple devices. In use-cases where a user ID is logged in to multiple devices, once the user deals with an incoming call that is ringing on one device, all the other devices stop ringing simultaneously.

    This page describes how to implement real-time audio and video communications using the `AgoraChatCallKit`.

    ## Understand the tech [#understand-the-tech]

    The basic process for implementing real-time audio and video communications with `AgoraChatCallKit` is as follows:

    1. Initialize `AgoraChatCallKit` by calling `init` and add the `AgoraChatCallKit` event listener by calling `setCallKitListener`.
    2. Call `startSingleCall` or `startInviteMultipleCall` on the caller's client to send a call invitation for one-to-one or group calls.
    3. On the callee's client, accept or decline the call invitation after receiving `onReceivedCall`. Once a user accepts the invitation, they enter the call.
    4. When the call ends, the SDK triggers the `onEndCallWithReason` callback.

    ## Prerequisites [#prerequisites]

    Before proceeding, ensure that your development environment meets the following requirements:

    * [Java Development Kit](https://www.oracle.com/java/technologies/downloads/) 1.8 or later.
    * Android Studio 3.5 or later.
    * `targetSdkVersion` 30.
    * `minSdkVersion` 21.
    * Gradle 4.6 or later.
    * a Chat project that has integrated the Chat SDK and implemented the [basic real-time chat functionalities](../../get-started-sdk), including users logging in and out and sending and receiving messages.

    ## Project setup [#project-setup]

    Take the following steps to integrate the `AgoraChatCallKit` into your project and set up the development environment.

    1. Add the dependency

       `AgoraChatCallKit` is developed upon `io.agora.rtc:chat-sdk:x.x.x` (1.0.5 and later) and `io.agora.rtc:full-rtc-basic:x.x.x` (3.6.2 and later). Follow the steps to add `AgoraChatCallKit` using Gradle.

       In `/Gradle Scripts/build.gradle(Module: .app)`, add the following lines to integrate the `AgoraChatCallKit` into your Android project:

       ```java
       implementation 'io.agora.rtc:chat-callkit:1.0.1'
       ```

    2. Add project permissions

       In `/app/Manifests/AndroidManifest.xml`, add the following permissions after `</application>`:

       ```xml
       <!-- Add alert window -->
       <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
       <!-- Access to the internet -->
       <uses-permission android:name="android.permission.INTERNET" />
       <!-- Access to the microphone -->
       <uses-permission android:name="android.permission.RECORD_AUDIO" />
       <!-- Access to the camera -->
       <uses-permission android:name="android.permission.CAMERA" />
       <!-- Access to the tasks -->
       <uses-permission android:name="android.permission.REORDER_TASKS" />
       ```

    3. Add the `AgoraChatCallKit` Activity

       In `/app/Manifests/AndroidManifest.xml`, add activities for the one-to-one audio and video call, and the group video call, for example, `CallSingleBaseActivity` (inherited from `EaseCallSingleBaseActivity`) and `CallMultipleBaseActivity` (inherited from `EaseCallMultipleBaseActivity`).

       ```xml
       <activity
           android:name=".av.CallSingleBaseActivity"
           android:configChanges="orientation|keyboardHidden|screenSize"
           android:excludeFromRecents="true"
           android:label="@string/demo_activity_label_video_call"
           android:launchMode="singleInstance"
           android:screenOrientation="portrait" />
       <activity
           android:name=".av.CallMultipleBaseActivity"
           android:configChanges="orientation|keyboardHidden|screenSize"
           android:excludeFromRecents="true"
           android:label="@string/demo_activity_label_multi_call"
           android:launchMode="singleInstance"
           android:screenOrientation="portrait" />
       ```

    ## Implement audio and video calling [#implement-audio-and-video-calling]

    This section introduces the core steps for implementing audio and video calls in your project.

    ### Initialize AgoraChatCallKit [#initialize-agorachatcallkit]

    Call `init` to initialize the `AgoraChatCallKit` after the Chat SDK is initialized. You can also add callback events to listen for and set the configurations. Refer to the following sample code:

    ```java
    // Construct the CallKitConfig class.
    EaseCallKitConfig callKitConfig = new EaseCallKitConfig();
    // Set the call out time (ms).
    callKitConfig.setCallTimeOut(30 * 1000);
    // Set the Agora App ID.
    callKitConfig.setAgoraAppId("******");
    // Set whether to enable token authentication.
    callKitConfig.setEnableRTCToken(true);
    // Set the default avatar.
    callKitConfig.setDefaultHeadImage(getUsersManager().getCurrentUserInfo().getAvatar());
    // Set the ringtone file.
    String ringFile = EaseFileUtils.getModelFilePath(context,"huahai.mp3");
    callKitConfig.setRingFile(ringFile);
    // Set the user information.
    Map userInfoMap = new HashMap<>();
    userInfoMap.put("***",new EaseCallUserInfo("****",null));
    userInfoMap.put("***",new EaseCallUserInfo("****",null));
    callKitConfig.setUserInfoMap(userInfoMap);
    // Call init to initialie EaseCallKit.
    EaseCallKit.getInstance().init(context, callKitConfig);
    // Register the activity added in the Manifest file.
    EaseCallKit.getInstance().registerVideoCallClass(CallSingleBaseActivity.class);
    EaseCallKit.getInstance().registerMultipleVideoClass(CallMultipleBaseActivity.class);
    // Add event listeners to the AgoraChatCallKit.
    addCallkitListener();
    ```

    In this method, you need to set the `EaseCallKitConfig` class. Some of the configurations include the following:

    ```java
    /**
     * The EaseCallKitConfig class.
     * @param defaultHeadImage The default avatar. Set it as the absolute path of a local file or a URL.
     * @param userInfoMap      The dictionary of the user information. The data format is key-value pairs, where key represents the user ID and value is EaseCallUserInfo.
     * @param callTimeOut      The timeout duration for the call invitation, in miliseconds. The default value is 30 seconds.
     * @param agoraAppId       The Agora App ID.
     * @param ringFile         The ringtone file, represented by the absolute path of a local file.
     * @param enableRTCToken   Whether to enable token authentication for using the RTC service. If you enabled token authentication in Agora Console, set this parameter as true. The default value is false.
      */
      public class EaseCallKitConfig {
        private String defaultHeadImage;
        private Map userInfoMap = new HashMap<>();
        private String ringFile;
        private String agoraAppId;
        private long callTimeOut = 30 * 1000;
        private boolean enableRTCToken = false;
         public EaseCallKitConfig(){
      ...
      }
    ```

    ### Send a call invitation [#send-a-call-invitation]

    From the caller's client, call `startSingleCall` or `startInviteMultipleCall` to send a call invitation for a one-to-one call or group call. You need to specify the call type when calling the method.

    * One-to-one audio and video call

      Call `startSingleCall` to start a one-to-one call. You need to specify the call type as an audio call or video call in this method.

      ```java
      /**
      * Starts a one-to-one call.
      *
      * @param type The call type:
      * - CONFERENCE_VOICE_CALL: A one-to-one voice call.
      * - CONFERENCE_VIDEO_CALL: A one-to-one video call.
      * @param user The user ID of the callee. Ensure that you set this parameter.
      * @param ext  The extension information in the call invitation. Set it as null if you do not need this information.
      */
      public void startSingleCall(final EaseCallType type, final String user, final Map ext){}
      ```

    The following screenshot gives an example of the user interface after sending a call invitation for one-to-one audio call:

    * Group audio and video call

      Call `startInviteMultipleCall` to start a group call. Set the call type as audio call or video call. You can set the users you want to invite from a chat group or the contact list. Refer to `ConferenceInviteActivity` in the sample project for implementation.

      ```java
      /**
      * Starts a group call.
      *
      * @param type The call type:
      * - SINGLE_VOICE_CALL: A one-to-one voice call.
      * - SINGLE_VIDEO_CALL: A one-to-one video call.
      * @param user The user ID of the callee. Ensure that you set this parameter.
      * @param ext  The extension information in the call invitation. Set it as null if you do not need this information.
      */
      public void startInviteMultipleCall(final EaseCallType type, final String[] users, final Map ext) {}
      ```

    ### Receive the invitation [#receive-the-invitation]

    Once a call invitaion is sent, the callee receives the invitation in the `onReceivedCall` callback.

    ```java
    /**
     * Occurs when a call invitation is received.
     * @param callType The call type.
     * @param fromUserId The user ID of the caller.
     * @param ext The extension information in the call invitation. Set it as null if you do not need this information.
     */
    void onReceivedCall(EaseCallType callType, String fromUserId, JSONObject ext);
    ```

    If the callee is online and available for a call, you can pop out a user interface that allows the callee to accept or decline the invitation. Refer to the following screenshot to implement the interface:

    ### Send a call invitation during a group call [#send-a-call-invitation-during-a-group-call]

    In call sessions with multiple users, these users can also send call invitations to other users. After sending the invitation, the SDK triggers the `onInviteUsers` callback in `EaseCallKitListener` on the sender's client.

    ```java
    /**
     * Occurs when the local user sends a call invitation during a group call.
     * @param callType The call type.
     * @param existMembers The current members of the group call, excluding the local user.
     * @param ext The extension information in the call invitation. Set it as null if you do not need this information.
     */
    void onInviteUsers(EaseCallType callType, String[] existMembers, JSONObject ext);
    ```

    Refer to `io.agora.chatdemo.group.fragments.MultiplyVideoSelectMemberChildFragment` in the [sample project](https://github.com/AgoraIO-Usecase/AgoraChat-android) for the user interface of the call invitation.

    ### Listen for callback events [#listen-for-callback-events]

    When a remote user joins the call, all the other users in the call receive the `onRemoteUserJoinChannel` callback. You need to look up the Chat user ID corresponding to the Agora UID in your app server.

    * If you find the Chat user ID, construct the user ID to an `EaseUserAccount` object and return it to the app using the `callback` parameter in `onRemoteUserJoinChannel`. For the `callback` parameter, implement `onUserAccount` in `EaseCallGetUserAccountCallback`.
    * If you fail to find the Chat user ID, report the error code and descriptions using the `onSetUserAccountError` callback in the `EaseCallGetYserAccountCallback` class.

    ```java
    /**
     * Occurs when the remote user joins the channel.
     *
     * @param channelName The channel name.
     * @param userName The Chat user ID.
     * @param uid The Agora UID.
     * @param callback The callback object.
     */
    void onRemoteUserJoinChannel(String channelName, String userName, int uid, EaseCallGetUserAccountCallback callback);
    ```

    ### End the call [#end-the-call]

    A one-to-one call ends as soon as one of the two users hangs up, while a group call ends only after the local user hangs up. When the call ends, the SDK triggers the `onEndCallWithReason` callback.

    ```java
    /**
     * Occurs when a call ends.
     * @param callType The call type.
     * @param channelName The channel name.
     * @param reason The reason why the call ends.
     * @param callTime The call duration.
     */
    void onEndCallWithReason(EaseCallType callType, String channelName, EaseCallEndReason reason, long callTime);

    // The reasons for a call ending.
    public enum EaseCallEndReason {
        EaseCallEndReasonHangup(0), // One of the call members hangs up.
        EaseCallEndReasonCancel(1), // The local user cancels the call.
        EaseCallEndReasonRemoteCancel(2), // The remote user cancels the call.
        EaseCallEndReasonRefuse(3),// The remote user rejects the call.
        EaseCallEndReasonBusy(4), // The remote user is busy.
        EaseCallEndReasonNoResponse(5), // The local user did not answer the phone.
        EaseCallEndReasonRemoteNoResponse(6), // The remote user did not answer the phone.
        EaseCallEndReasonHandleOnOtherDeviceRefused(7),// The call is rejected on another device.
        EaseCallEndReasonHandleOnOtherDeviceAgreed; // The call is answered on another device.
       ....
    }
    ```

    ## Next steps [#next-steps]

    This section contains extra steps you can take for the audio and video call functionalities in your project.

    ### Call exceptions [#call-exceptions]

    If exceptions or errors occur during a call, the SDK triggers the `onCallError` callback in the `EaseCallKitListener` class, which reports the detailed information of the exception in `AggoraChatCallError`.

    ```java
    /**
     * Reports call exceptions.
     * @param type        The error type.
     * @param errorCode   The error code.
     * @param description The error description.
     */
    void onCallError(EaseCallError type, int errorCode, String description);
    ```

    Types of call errors are as follows:

    ```java
    public enum EaseCallError {
        // The process error.
        PROCESS_ERROR,
        // The RTC service error.
        RTC_ERROR,
        // The IM service error.
        IM_ERROR
    }
    ```

    ### Update the call kit configuration [#update-the-call-kit-configuration]

    After initializing the `AgoraChatCallKit`, you can refer to the following sample code to update the configuration of the call kit:

    ```java
    /**
     * Gets the current call kit configuration.
     *
     * @return The current call kit configuration.
     */
    public EaseCallKitConfig getCallKitConfig();
    // Sets the default avatar.
    EaseCallKitConfig config = EaseCallKit.getInstance().getCallKitConfig();
      if(config != null){
        String Image = EaseFileUtils.getModelFilePath(context,"bryant.png"……);
        callKitConfig.setDefaultHeadImage(Image);
    }
    ```

    ### Update the user avatar or nickname [#update-the-user-avatar-or-nickname]

    When changes to the UI or the channel state occur, for example, when a new user joins the channel, `onUserInfoUpdated` is triggered to notify the app to update the UI.

    After a user updates the user information, call `io.agora.chat.callkit.general.EaseCallKitConfig#setUserInfo` to set the modified user information. Ensure that this method is implemented in a synchronous function so that the UI is updated timely.

    ```java
    /**
     * \~chinese
     * Occurs when the user information is updated.
     * @param userName The Chat user ID.
     */
    void onUserInfoUpdate(String userName){
        // For example,
        /**
        EaseUser user = mUsersManager.getUserInfo(userName);
        EaseCallUserInfo userInfo = new EaseCallUserInfo();
            if (user != null) {
                userInfo.setNickName(user.getNickname());
                userInfo.setHeadImage(user.getAvatar());
            }
        EaseCallKit.getInstance().getCallKitConfig().setUserInfo(userName, userInfo);
        */
    }
    ```

    ### Authenticate users with the RTC token [#authenticate-users-with-the-rtc-token]

    To enhance communication security, Agora recommends that you authenticate app users with the RTC token before they join a call. To do this, you need to make sure that the [Primary Certificate of your project is enabled](/en/realtime-media/im/get-started/manage-agora-account#enable-the-primary-certificate), and `setEnableRTCToken` in the `AgoraChatCallKit` is set to `true`.

    ```java
    EaseCallKitConfig callKitConfig = new EaseCallKitConfig();
     ……
     callKitConfig.setEnableRTCToken(true);
     ……
     EaseCallKit.getInstance().init(context, callKitConfig);
    ```

    Once you enable token authentication, the SDK triggers the `onGenerateRTCToken` callback. You need to retrieve an RTC token in this callback following the steps:

    1. Retrieve the RTC token and Agora UID from your app server.
    2. Trigger `onSetToken` to pass the token and UID to the callback object.
    3. `AgoraChatCallKit` uses the token and UID to join the channel.

    Tokens are generated on your app server using the token generator provided by Agora. For how to generate a token on the server, see [Secure authentication with tokens](../secure-access-and-authentication/authentication).

    ```java
    /**
     * Occurs when RTC token authentication is enabled.
     *
     * @param userId       The Chat user ID of the current user.
     * @param channelName  The channel name.
     * @param callback     The callback object.
     */
    default void onGenerateRTCToken(String userId, String channelName, EaseCallKitTokenCallback callback) {
        // Pass the token and UID to the callback object.
        // callback.onSetToken(token, uid);
    }
    ```

    ### Push notifications [#push-notifications]

    In use-cases where the app runs on the background or goes offline, use push notifications to ensure that the callee receives the call invitation. To enable push notifications, see [Set up push notifications](../notifications-and-event-handling/offline-push/configure-push-notifications).

    Once push notifications are enabled, when a call invitation arrives, a notification message pops out on the notification panel. Users can click the message to see the call invitation.

    ## Reference [#reference]

    This section provides other reference information that you can refer to when implementing real-time audio and video communications functionalities.

    ### API list [#api-list]

    The `AgoraChatCallKit` provides the following APIs:

    | Method                     | Description                                       |
    | -------------------------- | ------------------------------------------------- |
    | init                       | Initializes AgoraChatCallKit.                     |
    | setCallKitListener         | Sets the event listener of the call kit.          |
    | startSingleCall            | Starts a one-to-one call.                         |
    | startInviteMultipleCall    | Starts a group call.                              |
    | getCallKitConfig           | Retrieves the configurations of AgoraChatCallKit. |
    | registerVideoCallClass     | Registers a one-to-one video call class.          |
    | registerMultipleVideoClass | Registers a group video call class.               |

    `EaseCallKitListener` provides the following callback events:

    | Event                   | Description                                                                                                                                        |
    | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
    | onEndCallWithReason     | Occurs when the call ends.                                                                                                                         |
    | onInviteUsers           | Occurs when a member of the group call invites other users to the call.                                                                            |
    | onReceivedCall          | Occurs when the call invitation is received and the device rings.                                                                                  |
    | onGenerateToken         | Requests the RTC token. You need to pass the token to AgoraChatCallKit with callbacks.                                                             |
    | onCallError             | Reports exceptions and errors during the call.                                                                                                     |
    | onInviteCallMessageSent | Occurs when the call invitation is sent.                                                                                                           |
    | onRemoteUserJoinChannel | Occurs when a remote user joins the call.                                                                                                          |
    | onUserInfoUpdated       | Occurs when the user information is updated. This callback is triggered when changes occur to the UI or the channel state in the AgoraChatCallKit. |

    `EaseCallGetUserAccountCallback` contains the following APIs:

    | Event                 | Description                                                               |
    | --------------------- | ------------------------------------------------------------------------- |
    | onUserAccount         | Occurs when the Chat user ID corresponding to the Agora UID is retrieved. |
    | onSetUserAccountError | Occurs when the app fails to retrieve the user account.                   |

    `EaseCallKitTokenCallback` contains the following APIs:

    | Event           | Description                                                                 |
    | --------------- | --------------------------------------------------------------------------- |
    | onSetToken      | Occurs when the app passes the retrieved RTC token to the AgoraChatCallKit. |
    | onGetTokenError | Occurs when the app fails to get the RTC token.                             |

    ### Sample project [#sample-project]

    Agora provides an open-source [AgoraChat-android](https://github.com/AgoraIO-Usecase/AgoraChat-android) sample project on GitHub. You can download the sample to try it out or view the source code.

    The sample project uses the Chat user ID to join a channel, which enables displaying the user ID in the view of the call. If you use the methods of the Agora to start a call, you can also use the Integer UID to join a channel.

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="flutter">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="flutter" />

    **Currently, there is no CallKit for this platform.**

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="react-native">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="react-native" />

    **Currently, there is no CallKit for this platform.**

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="unity">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="unity" />

    **Currently, there is no CallKit for this platform.**

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="windows">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="windows" />

    **Currently, there is no CallKit for this platform.**

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="ios">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />

    `AgoraChatCallKit` is an open-source audio and video UI library developed based on Agora's real-time communications and signaling services. With this library, you can implement audio and video calling functionalities with enhanced synchronization between multiple devices. In use-cases where a user ID is logged in to multiple devices, once the user deals with an incoming call that is ringing on one device, all the other devices stop ringing simultaneously.

    This page describes how to implement real-time audio and video communications using the `AgoraChatCallKit`.

    ## Understand the tech [#understand-the-tech-1]

    The basic process for implementing real-time audio and video communications with `AgoraChatCallKit` is as follows:

    1. Initialize `AgoraChatCallKit` by calling `init`.
    2. Call `startSingleCallWithUid` or `startInviteUsers` on the caller's client to send a call invitation for one-to-one or group calls.
    3. On the callee's client, accept or decline the call invitation after receiving `callDidReceive`. Once a user accepts the invitation, they enter the call.
    4. When the call ends, the SDK triggers the `callDidEnd` callback.

    ## Prerequisites [#prerequisites-1]

    Before proceeding, ensure that your development environment meets the following requirements:

    * Xcode 9.0 or later.
    * A device running iOS 10.0 or later.
    * A Chat project that has integrated the Chat SDK and implemented the [basic real-time chat functionalities](../../get-started-sdk), including users logging in and out and sending and receiving messages.

    ## Project setup [#project-setup-1]

    Take the following steps to integrate the `AgoraChatCallKit` into your project and set up the development environment.

    ### Add the AgoraChatCallKit framework [#add-the-agorachatcallkit-framework]

    `AgoraChatCallKit` is developed upon `Agora_Chat_iOS`, `Masonry`, `AgoraRtcEngine_iOS/RtcBasic`, and `SDKWebImage`. Refer to the following steps to import `AgoraChatCallKit` into your project. If you prefer to add the framework manually, refer to [Manually import AgoraChatCallKit](#import).

    1. Install CocoaPods if you have not. For details, see [Getting Started with CocoaPods](https://guides.cocoapods.org/using/getting-started.html#getting-started).

    2. If your project does not have a `Podfile` file, navigate to the project root directory, and run the `pod init` command to create a text file `Podfile` in the project folder.

    3. Drag `AgoraChatCallKit.framework` to the project and set the `Embed` attribute of `AgoraChatCallKit.framework` as `Embed & Sign` under `Frameworks, Libraries, and Embedded Content`.

       ```objc
        use_frameworks!
        // Replace AppName with the name of your project
        target 'AppName' do
            pod 'AgoraChatCallKit'
            // Other frameworks
        end
       ```

    4. In the project root directory, run `pod install` to add the framework. When the SDK is installed successfully, you can see `Pod installation complete!` in the Terminal and an `xcworkspace` file in the project folder.

    5. Open the `xcworkspace` file in Xcode.

    ### Add permissions [#add-permissions]

    Open the `info.plist` file, and add the following permissions:

    | Key                                      | Type   | Value                                                                            |
    | ---------------------------------------- | ------ | -------------------------------------------------------------------------------- |
    | `Privacy - Microphone Usage Description` | String | The description for using the microphone. For example, to access the microphone. |
    | `Privacy - Camera Usage Description`     | String | The description for using the camera. For example, to access the camera.         |

    ## Implement audio and video calling [#implement-audio-and-video-calling-1]

    This section introduces the core steps for implementing audio and video calls in your project.

    ### Initialize AgoraChatCallKit [#initialize-agorachatcallkit-1]

    Call `init` to initialize the `AgoraChatCallKit`.

    ```objc
    AgoraChatCallConfig *config = [[AgoraChatCallConfig alloc] init];
    config.agoraAppId = @"agoraAppId";
    config.enableRTCTokenValidate = YES;
    config.enableIosCallKit = YES;
    [AgoraChatCallManager.sharedManager initWithConfig:config delegate:self];
    ```

    In this method, you need to set the `AgoraChatCallConfig` interface. Some of the configurations include the following:

    ```objc
    @interface AgoraChatCallConfig : NSObject
    /**
     * The timeout duration for the call invitation, in seconds. The default value is 30.
     */
    @property (nonatomic) UInt32 callTimeOut;
    /**
     * The dictionary of the user information. The data format is key-value pairs, where key represents the user ID and value is EaseCallUser.
     */
    @property (nonatomic,strong) NSMutableDictionary* users;
    /**
     * Specify the file URL of the ringtone. This field is valid only when enableIosCallKit is set to NO.
     */
    @property (nonatomic,strong) NSURL* ringFileUrl;
    /**
     * The App ID of the project, which you can obtain from Agora Console.
     */
    @property (nonatomic,strong) NSString* agoraAppId;
    /**
     *  Whether to enable token authentication when users join the Agora channel:
     *  - (Default) YES: Enable token authentication. Once you set it as YES, you must implement the callDidRequestRTCTokenForAPPId callback. After receiving this callback, you need to call setRTCToken to pass in the token to start or join a call.
     *  - NO: Do not enable token authentication.
     */
    @property (nonatomic) BOOL enableRTCTokenValidate;
    /**
     * Whether to use the iOS CallKit framework.
     */
    @property (nonatomic, assign) BOOL enableIosCallKit;

    @end
    ```

    ### Send a call invitation [#send-a-call-invitation-1]

    From the caller's client, call `startSingleCallWithUId` or `startInviteUsers` to send a call invitation for a one-to-one call or group call. You need to specify the call type when calling the method.

    * One-to-one audio call

      ```objc
      [AgoraChatCallManager.sharedManager startSingleCallWithUId:userId type:AgoraChatCallType1v1Audio ext:nil completion:^(NSString * callId, AgoraChatCallError * aError) {
      }];
      ```

    * One-to-one video call

      ```objc
      [AgoraChatCallManager.sharedManager startSingleCallWithUId:userId type:AgoraChatCallType1v1Video ext:nil completion:^(NSString * callId, AgoraChatCallError * aError) {
      }];
      ```

    * Group audio call

      ```objc
      ConfInviteUsersViewController *controller = [[ConfInviteUsersViewController alloc] initWithGroupId:groupId excludeUsers:@[AgoraChatClient.sharedClient.currentUsername]];
      controller.didSelectedUserList = ^(NSArray * _Nonnull aInviteUsers) {
          for (NSString *strId in aInviteUsers) {
              AgoraChatUserInfo *info = [UserInfoStore.sharedInstance getUserInfoById:strId];
              if (info && (info.avatarUrl.length > 0 || info.nickname > 0)) {
                      AgoraChatCallUser *user = [AgoraChatCallUser userWithNickName:info.nickname image:[NSURL URLWithString:info.avatarUrl]];
                  [[AgoraChatCallManager.sharedManager getAgoraChatCallConfig] setUser:strId info:user];
              }
          }
          [AgoraChatCallManager.sharedManager startInviteUsers:aInviteUsers groupId:groupId callType:AgoraChatCallTypeMultiAudio ext:@{
              @"groupId":groupId
          } completion:^(NSString * callId, AgoraChatCallError * aError) {

          }];
      };

      controller.modalPresentationStyle = UIModalPresentationPageSheet;
      [viewController presentViewController:controller animated:YES completion:nil];
      ```

    * Group video call

      ```objc
      ConfInviteUsersViewController *controller = [[ConfInviteUsersViewController alloc] initWithGroupId:groupId excludeUsers:@[AgoraChatClient.sharedClient.currentUsername]];
      controller.didSelectedUserList = ^(NSArray * _Nonnull aInviteUsers) {
          for (NSString *strId in aInviteUsers) {
              AgoraChatUserInfo *info = [UserInfoStore.sharedInstance getUserInfoById:strId];
              if (info && (info.avatarUrl.length > 0 || info.nickname > 0)) {
                      AgoraChatCallUser *user = [AgoraChatCallUser userWithNickName:info.nickname image:[NSURL URLWithString:info.avatarUrl]];
                  [[AgoraChatCallManager.sharedManager getAgoraChatCallConfig] setUser:strId info:user];
              }
          }
          [AgoraChatCallManager.sharedManager startInviteUsers:aInviteUsers groupId:groupId callType:AgoraChatCallTypeMultiVideo ext:@{
              @"groupId":groupId
          } completion:^(NSString * callId, AgoraChatCallError * aError) {

          }];
      };

      controller.modalPresentationStyle = UIModalPresentationPageSheet;
      [viewController presentViewController:controller animated:YES completion:nil];
      ```

    The following screenshot gives an example of the user interface after sending a call invitation for one-to-one audio call:

    ### Receive the invitation [#receive-the-invitation-1]

    Once a call invitaion is sent, the callee receives the invitation in the `callDidReceive` callback.

    ```objc
    - (void)callDidReceive:(EaseCallType)aType inviter:(NSString*_Nonnull)user ext:(NSDictionary*)aExt
    {

    }
    ```

    If the callee is online and available for a call, you can pop out a user interface that allows the callee to accept or decline the invitation. If you have enabled the iOS CallKit, the system call user interface is launched. Otherwise, you can refer to the following screenshot to implement the interface:

    ### Send a call invitation during a group call [#send-a-call-invitation-during-a-group-call-1]

    In call sessions with multiple users, these users can also send call invitations to other users. After sending the invitation, the SDK triggers the `multiCallDidInvitingWithCurVC` callback in `AgoraChatCallDelegate` on the sender's client.

    ```objc
    /**
     * Occurs when the local user sends a call invitation during a group call.
     * vc     The current UI view controller.
     * users  The user IDs that are already in the group call.
     * aExt   Extension information.
     */
    - (void)multiCallDidInvitingWithCurVC:(UIViewController*_Nonnull)vc excludeUsers:(NSArray *_Nullable)users ext:(NSDictionary *)aExt
      {
        NSString *groupId = nil;
        if (aExt) {
            groupId = [aExt objectForKey:@"groupId"];
        }
        if (!groupId || groupId.length  0 || info.nickname.length > 0)) {
                    AgoraChatCallUser *user = [AgoraChatCallUser userWithNickName:info.nickname image:[NSURL URLWithString:info.avatarUrl]];
                    [[AgoraChatCallManager.sharedManager getAgoraChatCallConfig] setUser:strId info:user];
                }
            }
            [AgoraChatCallManager.sharedManager startInviteUsers:aInviteUsers groupId:groupId callType:AgoraChatCallTypeMultiAudio ext:aExt completion:nil];
        };
        confVC.modalPresentationStyle = UIModalPresentationPopover;
        [vc presentViewController:confVC animated:YES completion:nil];
    }
    ```

    ### Listen for callback events [#listen-for-callback-events-1]

    During the call, you can also listen for the following callback events:

    * `callDidJoinChannel`, occurs when the local user successfully joins the call.

      ```objc
      - (void)callDidJoinChannel:(NSString*_Nonnull)aChannelName uid:(NSUInteger)aUid
      {
          [self _fetchUserMapsFromServer:aChannelName];
      }
      ```

    * `remoteuserDidJoinChannel`, occurs when the remote user successfully joins the call.

      ```objc
      -(void)remoteUserDidJoinChannel:( NSString*_Nonnull)aChannelName uid:(NSInteger)aUid username:(NSString*_Nullable)aUserName
      {
          if (aUserName.length > 0) {
              AgoraChatUserInfo* userInfo = [[UserInfoStore sharedInstance] getUserInfoById:aUserName];
              if (userInfo && (userInfo.avatarUrl.length > 0 || userInfo.nickname.length > 0)) {
                  AgoraChatCallUser* user = [AgoraChatCallUser userWithNickName:userInfo.nickname image:[NSURL URLWithString:userInfo.avatarUrl]];
                  [[AgoraChatCallManager.sharedManager getAgoraChatCallConfig] setUser:aUserName info:user];
              }
          } else {
              [self _fetchUserMapsFromServer:aChannelName];
          }
      }
      ```

    When receiving the `callDidJoinChannel:uid` or `remoteUserDidJoinChannel:uid:username:` callback, the user needs to look up the Chat user ID corresponding to the Agora UID in your app server. If the Chat user ID is found, construct a dictionary with Agora UID and Chat user ID and then set it to the app using `setUsers:channelName:`.

    ### End the call [#end-the-call-1]

    A one-to-one call ends as soon as one of the two users hangs up, while a group call ends only after the local user hangs up. When the call ends, the SDK triggers the `callDidEnd` callback.

    ```objc
    - (void)callDidEnd:(NSString*_Nonnull)aChannelName reason:(AgoraChatCallEndReason)aReason time:(int)aTm type:(AgoraChatCallType)aType
    {
        NSString *msg = @"";
        switch (aReason) {
            case AgoraChatCallEndReasonHandleOnOtherDevice:
                msg = NSLocalizedString(@"otherDevice", nil);
                break;
            case AgoraChatCallEndReasonBusy:
                msg = NSLocalizedString(@"remoteBusy", nil);
                break;
            case AgoraChatCallEndReasonRefuse:
                msg = NSLocalizedString(@"RemoteRefuseCall", nil);
                break;
            case AgoraChatCallEndReasonCancel:
                msg = NSLocalizedString(@"cancelCall", nil);
                break;
            case AgoraChatCallEndReasonNoResponse:
                msg = NSLocalizedString(@"remoteNoResponse", nil);
                break;
            case AgoraChatCallEndReasonHangup:
                msg = [NSString stringWithFormat:NSLocalizedString(@"callendPrompt", nil),aTm];
                break;
            default:
                break;
        }
        if (msg.length > 0) {
            [self showHint:msg];
        }
    }
    ```

    ## Next steps [#next-steps-1]

    This section contains extra steps you can take for the audio and video call functionalities in your project.

    ### Call exceptions [#call-exceptions-1]

    If exceptions or errors occur during a call, the SDK triggers the `callDidOccurError` callback, which reports the detailed information of the exception in `AgoraChatCallError`.

    ```objc
    - (void)callDidOccurError:(AgoraChatCallError *_Nonnull)aError
    {
    }
    ```

    ### Update the user avatar or nickname [#update-the-user-avatar-or-nickname-1]

    After a user joins the call, you can call `setUser` to modify the avatar and nickname of the current user and the other users in the channel.

    ```objc
    AgoraChatCallUser *user = [AgoraChatCallUser userWithNickName:info.nickname image:[NSURL URLWithString:info.avatarUrl]];
    [[AgoraChatCallManager.sharedManager getAgoraChatCallConfig] setUser:userId info:user];
    ```

    ### Authenticate users with the RTC token [#authenticate-users-with-the-rtc-token-1]

    To enhance communication security, Agora recommends that you authenticate app users with the RTC token before they join a call. To do this, you need to make sure that the [Primary Certificate of your project is enabled](/en/realtime-media/im/get-started/manage-agora-account#enable-the-primary-certificate), and `enableRTCTokenValidate` in the `AgoraChatCallKit` is set to `YES`.

    ```objc
    config.enableRTCTokenValidate = YES;
    [AgoraChatCallManager.sharedManager initWithConfig:config delegate:self];
    ```

    Once you enable token authentication, ensure that you listen for the `callDidRequestRTCTokenForAppId` callback. When `callDidRequestRTCTokenForAppId` is triggered, you need to retrieve the token and call `setRTCToken` to pass the token to the CallKit. Tokens are generated on your app server using the token generator provided by Agora. For how to generate a token on the server and retrieve and renew the token on the client, see [Secure authentication with tokens](../secure-access-and-authentication/authentication).

    ```objc
    - (void)callDidRequestRTCTokenForAppId:(NSString *)aAppId channelName:(NSString *)aChannelName account:(NSString *)aUserAccount uid:(NSInteger)aAgoraUid
    {
        [AgoraChatCallManager.sharedManager setRTCToken:rtcToken channelName:aChannelName uid: The Agora UID];
    }
    ```

    ## Reference [#reference-1]

    This section provides other reference information that you can refer to when implementing real-time audio and video communications functionalities.

    ### API list [#api-list-1]

    The `AgoraChatCallKit` framework contains the `AgoraChatCallManager` and `AgoraChatCallDelegate` classes.

    The following table lists the core methods in `AgoraChatCallManager`:

    | Method                                                | Description                                                    |
    | ----------------------------------------------------- | -------------------------------------------------------------- |
    | initWithConfig:delegate                               | Initializes AgoraChatCallKit.                                  |
    | startSingleCallWithUId\:type\:ext:completion          | Starts a one-to-one call.                                      |
    | startInviteUsers\:groupId\:callType\:ext\:completion: | Starts a group call.                                           |
    | getAgoraChatCallConfig                                | Retrieves the configurations of AgoraChatCallKit.              |
    | setRTCToken\:channelName\:uid:                        | Sets the RTC Token.                                            |
    | setUsers\:channelName:                                | Sets the mapping between Chat user ID and Agora user ID (UID). |

    The following table lists the callbacks in `AgoraChatCallDelegate`:

    | Event                                                       | Description                                                             |
    | ----------------------------------------------------------- | ----------------------------------------------------------------------- |
    | callDidEnd\:reason\:time\:type:                             | Occurs when the call ends.                                              |
    | multiCallDidInvitingWithCurVC\:callType\:excludeUsers\:ext: | Occurs when a member of the group call invites other users to the call. |
    | callDidReceive\:inviter\:ext:                               | Occurs when the call invitation is received and the device rings.       |
    | callDidRequestRTCTokenForAppId\:channelName\:account\:uid:  | Requests the RTC token.                                                 |
    | callDidOccurError:                                          | Reports exceptions and errors during the call.                          |
    | remoteUserDidJoinChannel\:uid\:username:                    | Occurs when a remote user joins the call.                               |
    | callDidJoinChannel\:uid:                                    | Occurs when the current user joins the call.                            |

    ### Sample project [#sample-project-1]

    Agora provides an open-source [AgoraChat-ios](https://github.com/AgoraIO-Usecase/AgoraChat-ios) sample project on GitHub. You can download the sample to try it out or view the source code.

    The sample project uses the Chat user ID to join a channel, which enables displaying the user ID in the view of the call. If you use the methods of the Agora to start a call, you can also use the Integer UID to join a channel.

    ### Import AgoraChatCallKit manually [#import-agorachatcallkit-manually]

    Refer to the following steps to manually add `AgoraChatCallKit` to your project:

    1. [Download AgoraChatCallKit](https://github.com/AgoraIO-Usecase/AgoraChat-CallKit-ios), and extract the downloaded file.
    2. Copy and paste `AgoraChatCallKit.framework` to the directory of your project.
    3. In Xcode, go to **TARGETS** > **Project Name** > **General**. Drag `AgoraChatCallKit.framework` under **Frameworks, Libraries, and Embedded Content**, and set the **Embed** attribute of `AgoraChatCallKit.framework` as **Embed & Sign**.

    ### Other project settings [#other-project-settings]

    You can configure the following project settings according to your use case:

    * To run the app in the background, go to `info.plist`, click `+`, and add `Required background modes`. Set `Type` as `Array`, and add `App plays audio or streams audio/video using AirPlay` as a value under `Required background modes`.
    * To use the Apple `PushKit` and `CallKit`, go to **TARGETS** > **Project Name** > **Signing and Capabilities**. Under **Background Modes**, check `Voice over IP`.

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="web">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />

    `AgoraChatCallKit` is an open-source audio and video UI library developed based on Agora's real-time communications and signaling services. With this library, you can implement audio and video calling functionalities with enhanced synchronization between multiple devices. In use-cases where a user ID is logged in to multiple devices, once the user deals with an incoming call that is ringing on one device, all the other devices stop ringing simultaneously.

    This page describes how to implement real-time audio and video communications using the `AgoraChatCallKit`.

    ## Understand the tech [#understand-the-tech-2]

    The basic process for implementing real-time audio and video communications with `AgoraChatCallKit` is as follows:

    1. Initialize `AgoraChatCallKit` by calling `init`.
    2. Call `startCall` on the caller's client to send a call invitation for one-to-one or group calls.
    3. On the callee's client, accept or decline the call invitation after receiving `onInvite`. Once a user accepts the invitation, they enter the call.
    4. When the call ends, the SDK triggers the `onStateChange` callback.

    ## Prerequisites [#prerequisites-2]

    Before proceeding, ensure that your development environment meets the following requirements:

    * A valid [Agora account](/en/realtime-media/im/get-started/manage-agora-account#create-an-agora-account).
    * An Agora project that has [enabled Chat](../../get-started/enable).
    * A Chat project that has integrated the Chat SDK and implemented the [basic real-time chat functionalities](../../get-started-sdk), including users logging in and out and sending and receiving messages.

    ## Project setup [#project-setup-2]

    1. In your terminal, run the following command to install the call kit:

       ```bash
       npm install chat-callkit
       ```

    2. Add the following line to import the callkit:

       ```javascript
       import Callkit from 'chat-callkit';
       ```

    ## Implement audio and video calling [#implement-audio-and-video-calling-2]

    This section introduces the core steps for implementing audio and video calls in your project.

    ### Initialize AgoraChatCallKit [#initialize-agorachatcallkit-2]

    Call `init` to initialize the `AgoraChatCallKit`.

    ```javascript
    /**
     * Initialize AgoraChatCallKit
     *
     * @param appId       The Agora App ID.
     * @param agoraUid    The Agora user ID (UID).
     * @param connection  The Chat SDK Connection instance.
     */
    CallKit.init(appId, agoraUid, connection);
    ```

    ### Send a call invitation [#send-a-call-invitation-2]

    From the caller's client, call `startCall` to send a call invitation for a one-to-one call or group call. You need to specify the call type when calling the method.

    * One-to-one call

      In a one-to-one call, the caller sends a text message to the callee as the call invitation.

      ```javascript
      let options = {
          /** The call type:
           * 0: One-to-one audio call
           * 1: One-to-one video call
           * 2: Group video call
           * 3: Group audio call
           */
          callType: 0,
          chatType: 'singleChat',
          /** The Chat user ID. */
          to: 'userId',
          /** The invitation message. */
          message: 'Join me on the call',
          /** The channel name for the call. */
          channel: 'channel',
          /** The RTC token. */
          accessToken: 'Agora token',
      };
      CallKit.startCall(options);
      ```

    * Group call

      In a group call, the caller sends a text message to the chat group or chat room, while sending a CMD message to the users for joining the call.

      ```javascript
      let options = {
          /** The call type:
           * 0: One-to-one audio call
           * 1: One-to-one video call
           * 2: Group video call
           * 3: Group audio call
           */
          callType: 2,
          chatType: 'groupChat',
          /** The Chat user ID. */
          to: ['userId'],
          /** The invitation message. */
          message: 'Join me on the call',
          /** The group ID. */
          groupId: 'groupId',
          /** The group name. */
          groupName: 'group name',
          /** The RTC token. */
          accessToken: 'Agora token',
          /** The channel name for the call. */
          channel: 'channel',
      };
      CallKit.startCall(options);
      ```

    The following screenshot gives an example of the user interface after sending a call invitation for one-to-one video call:

    ![1655259671848](https://web-cdn.agora.io/docs-files/1655259671848)

    ### Receive the invitation [#receive-the-invitation-2]

    Once a call invitation is sent, if the callee is online and available for a call, the callee receives the invitation in the `onInvite` callback. You can pop out a user interface that allows the callee to accept or decline the invitation in this callback.

    ```javascript
    /**
     * Handles the call invitation.
     *
     * @param result Whether to pop out the user interface for answering the call.
     *               - true: Yes.
     *               - false: No. In this situation, you do not need to pass the RTC token.
     * @param accessToken The RTC token.
     */
    CallKit.answerCall(result, accessToken);
    ```

    The following screenshot gives an example of the user interface after receiving a call invitation for one-to-one video call:

    ![1655259682186](https://web-cdn.agora.io/docs-files/1655259682186)

    ### Send a call invitation during a group call [#send-a-call-invitation-during-a-group-call-2]

    In call sessions with multiple users, these users can also send call invitations to other users. After sending the invitation, the SDK triggers the `onAddPerson` callback on the sender's client. In this callback, you can ask the senders to specify the user they want to invite to the group call and then call `startCall` to send out the invitation.

    ### Listen for callback events [#listen-for-callback-events-2]

    During the call, you can also listen for the following callback events:

    ```javascript
    function Call() {
      // Handles call state changes.
      const handleCallStateChange = (info) => {
        switch (info.type) {
          case 'hangup':
            // The call is hung up.
            break;
          case 'accept':
            // The callee accepts the call invitation.
            break;
          case 'refuse':
            // The callee refuses the call invitation.
            break;
          case 'user-published':
            // A remote user publishes media streams during the call.
            break;
          case 'user-unpublished':
            // A remote user stops publishing media streams during the call.
            break;
          case 'user-left':
            // A remote user leaves the call.
            break;
          default:
            break;
        }
      };
      return ;
    }
    ```

    ### End the call [#end-the-call-2]

    A one-to-one call ends as soon as one of the two users hangs up, while a group call ends only after the local user hangs up. If the local user hangs up the call, the SDK triggers `onStateChange` with the `info.type` of `hangup`. If the remote user hangs up the call, the SDK triggers `onStateChange` with the `info.type` of `user-left`.

    ## Next steps [#next-steps-2]

    This section contains extra steps you can take for the audio and video call functionalities in your project.

    ### Authenticate users with the RTC token [#authenticate-users-with-the-rtc-token-2]

    To enhance communication security, Agora recommends that you authenticate app users with the RTC token before they join a call. To do this, you need to make sure that the [Primary Certificate of your project is enabled](/en/realtime-media/im/get-started/manage-agora-account#enable-the-primary-certificate).

    Tokens are generated on your app server using the token generator provided by Agora. After you retrieve the token, pass the token to the callkit when calling `startCall` and `answerCall`. For how to generate a token on the server and retrieve and renew the token on the client, see  [Secure authentication with tokens](../secure-access-and-authentication/authentication).

    ## Reference [#reference-2]

    This section provides other reference information that you can refer to when implementing real-time audio and video communications functionalities.

    ### API list [#api-list-2]

    `AgoraChatCallKit` provides the following APIs:

    Methods:

    | Method                  | Description                                                                                                                    |
    | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
    | initWithConfig:delegate | Initializes `AgoraChatCallKit`.                                                                                                |
    | startCall               | Starts a call.                                                                                                                 |
    | answerCall              | Answers the call.                                                                                                              |
    | setUserIdMap            | Sets the mapping between Chat user ID and Agora user ID (UID). The format is `{[uid1]: 'custom name', [uid2]: 'custom name'}`. |

    Callbacks:

    | Event         | Description                                            |
    | ------------- | ------------------------------------------------------ |
    | onAddPerson   | Occurs when the user invites another user to the call. |
    | onInvite      | Occurs when the call invitation is received.           |
    | onStateChange | Occurs when the call state changes.                    |

    Attributes:

    | Attribute     | Description                                   |
    | ------------- | --------------------------------------------- |
    | contactAvatar | The avatar displayed during one-to-one calls. |
    | groupAvatar   | The avatar displayed during group calls.      |
    | ringingSource | The ringtone file.                            |

    ### Sample project [#sample-project-2]

    Agora provides an open-source [AgoraChatCallKit](https://github.com/AgoraIO-Usecase/AgoraChat-CallKit-web/tree/master/demo) sample project on GitHub. You can download the sample to try it out or view the source code.

    The sample project uses the Chat user ID to join a channel, which enables displaying the user ID in the view of the call. If you use the methods of the Agora to start a call, you can also use the Integer UID to join a channel.

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>
</_PlatformTabsGroup>
