Ring Calls

Creating ring calls

The Stream Video API offers a ringing flow that simulates traditional phone calls. This feature lets developers implement call experiences where users can receive incoming calls with ringtones and choose to accept or decline.

You can also show missed call notifications if someone doesn’t answer the call on time or is busy in another call.

To start the ringing flow, we need to set the ring flag to true and provide the list of members we want to call. It is important to note that the caller should also be included in the list of members.

Stream API allows reusing call ids (for example, to implement recurring meetings), but this is not something we advise for ring calls. You should create a unique id for every ring call (for example by using UUIDs).

// Make sure to use a unique id when creating ring calls
await client.call("default", crypto.randomUUID()).getOrCreate({
  ring: true,
  data: {
    created_by_id: "myself",
    members: [{ user_id: "myself" }, { user_id: "my friend" }],
  },
});

Accepting and rejecting ring calls

Accepting and rejecting calls are client-side operations, you can read more about them in SDK documentations.

Ring call events

The ring flow relies on the following events:

  • call.ring
  • call.accepted
  • call.rejected
  • call.missed

call.ring event

This is the start of the ring flow. This event is dispatched to call members when someone starts a call with the ring flag set to true.

It’s possible to send push notifications to call members on this event, for more information see Notification settings section

call.ring event reference

call.accepted event

This event is dispatched to the call members when a callee accepts an incoming call.

call.accepted event reference

call.rejected event

This event is dispatched to the call members when the caller or callee rejects the call.

There are multiple reasons why someone could reject a call (for example, someone explicitly rejecting a call with a “Decline” button is different from someone not answering the call). The call.rejected event contains a reason field to indicate why a call was rejected.

You can pass any string to the reason field, but the Stream API and the SDKs recognize the following values by default:

NameDescription
rejectedThe callee explicitly rejected the call (for example, with a “Decline” button)
cancelThe caller cancelled the call before it was answered. SDKs will also send this event if all callees rejected the call.
timeout1. When a callee is online but doesn’t answer the call in incoming_call_timeout_ms time.
2. When no callee accepts the call in auto_cancel_timeout_ms time, the caller will send this event
busySignals that the callee is busy. By default, SDKs don’t send this reason; integrators can implement this in their own application logic.

call.rejected event reference

call.missed event

The call.missed event is dispatched to callees who didn’t answer (accept or reject) an incoming call within a given time.

The event is dispatched in the following scenarios:

  • The callee rejected the call with a timeout or busy reason
  • When a callee didn’t accept or reject the call in missed_call_timeout_ms time (probably the user was offline)
  • The caller rejected the call with any reason, unless the callee previously rejected the call (declined the call)

It’s possible to send push notifications to call members on this event, for more information see Notification settings section

call.missed event reference

Examples

SDKs implement ringing flow in a way that while a call is in the ringing phase, there is no active session. The session starts when the first callee accepts the call.

All accept

Example: Sara calls Ben and Jane. Both Ben and Jane accept the call.

  1. Sara rings Ben and Jane (with ring: true): call.created and call.ring events from Sara to all call members - ringing flow starts
  2. Ben accepts the call: call.accepted event from Ben
  3. Ben joins the call: call.session_started and call.session_participant_joined events
  4. As soon as Ben accepts the call Sara also joins: call.session_participant_joined
  5. Jane also accepts, and joins: call.accepted and call.session_participant_joined
  6. Jane leaves the call: call.participant_left
  7. Ben and Sara also leave: 2x call.participant_left
  8. call.session_ended when inactivity_timeout_seconds elapsed since last participant left the call

Someone rejects

Example: Sara calls Ben and Jane. Ben accepts the call, Jane rejects.

  1. Sara rings Ben and Jane (with ring: true): call.created and call.ring events from Sara to all call members - ringing flow starts
  2. Ben accepts the call: call.accepted event from Ben
  3. Ben joins the call: call.session_started and call.session_participant_joined
  4. As soon as Ben accepts the call Sara also joins: call.session_participant_joined
  5. Jane declines the call: call.rejected event with reason: "decline" from Jane
  6. Ben and Sara leave the call: 2x call.participant_left
  7. call.session_ended when inactivity_timeout_seconds elapsed since last participant left the call

All reject

Example: Sara calls Ben and Jane. Both Jane and Ben reject the call.

  1. Sara rings Ben and Jane (with ring: true): call.created and call.ring events from Sara to all call members - ringing flow starts
  2. Ben declines the call: call.rejected event with reason: "decline" from Ben
  3. Jane is online, but doesn’t interact with the incoming call screen: call.rejected event with reason: "timeout" from Jane after incoming_call_timeout_ms elapsed
  4. call.missed is sent to Jane
  5. Since both Ben and Jane rejected the call Sara will reject as well (this is sent from SDKs): call.rejected event with reason: "cancel" from Sara

Caller cancels

Example: Sara calls Ben and Jane, but Sara cancels before anyone answers.

  1. Sara rings Ben and Jane (with ring: true): call.created and call.ring events from Sara to all call members - ringing flow starts
  2. Sara changes her mind, and cancels: call.rejected event with reason: "cancel" from Sara

No one answers the call

Example: Sara calls Ben and Jane, but no one answers.

  1. Sara rings Ben and Jane (with ring: true): call.created and call.ring events from Sara to all call members - ringing flow starts
  2. No one accepts or rejects the call in auto_cancel_timeout_ms time (this can happen if users are offline, and away from their devices): call.rejected event with reason: "timeout" from Sara
  3. call.missed events are sent to both Ben and Jane

Ring call settings

Timeouts

You can control how much time to wait before automatic call rejections and missed call events are dispatched.

RingSettingsRequest

NameTypeDescriptionConstraints
auto_cancel_timeout_msintegerWhen none of the callees accept a ring call in this time a rejection will be sent by the caller with reason 'timeout' by the SDKsRequired, Minimum: 5000, Maximum: 180000
incoming_call_timeout_msintegerWhen a callee is online but doesn't answer a ring call in this time a rejection will be sent with reason 'timeout' by the SDKsRequired, Minimum: 5000, Maximum: 180000
missed_call_timeout_msintegerWhen a callee doesn't accept or reject a ring call in this time a missed call event will be sentMinimum: 5000, Maximum: 180000

These can be set on the call type level or on the call level:

// on call type level
client.video.updateCallType({
  name: callTypeName,
  settings: {
    ring: {
      incoming_call_timeout_ms: 10000,
      auto_cancel_timeout_ms: 15000,
      missed_call_timeout_ms: 15000,
    },
  },
});

// or on call level
call.update({
  settings_override: {
    ring: {
      incoming_call_timeout_ms: 10000,
      auto_cancel_timeout_ms: 15000,
      missed_call_timeout_ms: 15000,
    },
  },
});

Push notifications

You can enable/disable push notifications for the call.ring and call.missed events.

client.video.createCallType({
  name: "<call type name>",
  notification_settings: {
    enabled: true,
    call_notification: {
      apns: {
        title: "{{ user.display_name }} calls you",
        body: "{{ user.display_name }} calls you",
      },
      enabled: true,
    },
    call_ring: {
      apns: {
        title: "{{ user.display_name }} calls you",
        body: "{{ user.display_name }} calls you",
      },
      enabled: true,
    },
    call_live_started: {
      enabled: true,
      apns: {
        title: "{{ call.display_name }} started",
        body: "{{ user.display_name }} started",
      },
    },
    call_missed: {
      enabled: true,
      apns: {
        title: "missed call from {{ user.display_name }}",
        body: "missed call from {{ user.display_name }}",
      },
    },
    session_started: {
      enabled: true,
      apns: {
        title: "{{ call.display_name }} started",
        body: "{{ call.display_name }} started",
      },
    },
  },
});
© Getstream.io, Inc. All Rights Reserved.