Overview

Non-ringing push notifications let Stream notify a user about a call-related events. They are regular push notifications (FCM/APN) — your app receives the payload, displays a banner, and handle notification interaction. Unlike ringing (which the SDK handles end-to-end), non-ringing notifications are not managed by the SDK. Stream sends push notifications directly to the device; the rest is your app's responsibility.

Notification types

Stream sends push notifications for the following call events. Each event can be enabled, disabled, or customized (title/body templates) in the call type settings or in corresponding section on the dashboard.

EventWhen it's sent
call.ringDispatched to call members when someone starts a call with the ring flag set to true — the start of the ringing flow. Comes as a regular notification on Android. On iOS it is sent as a VoIP push.
call.notificationA call was created and the user should be notified without ringing (e.g. group calls, generic call notifications).
call.missedA ringing call was not answered (neither accepted nor rejected) within the configured timeout.
call.live_startedA backstage call went live (e.g. a livestream the user follows started).
call.session_startedA call session started — i.e. the first participant joined an active call.

call.ring is handled automatically by the SDK as part of the ringing setup — it powers the CallKit (iOS) and Telecom (Android) ringing UI. You don't need to handle it yourself. The remaining four events are non-ringing and can be managed inside your app depending on exact use cases.

All five arrive via the same transport — Firebase Cloud Messaging on Android and APNs on iOS — and carry a sender: "stream.video" marker in the payload so you can distinguish them from app's other notifications.

High-level flow

Non-ringing notifications are end-to-end your app's responsibility. The typical flow is:

  1. Obtain the device push token on the client — the FCM registration token on Android, the APNs device token on iOS.
  2. Register the token with Stream via client.addDevice(token, provider, providerName). The provider name must match a push provider configured on the Stream Dashboard.
  3. Handle the incoming push when it arrives. Parse the Stream payload, filter for non-ringing events, and display a local notification with the content you want users to see.
  4. Handle background / killed state. On Android, Stream sends data-only FCM messages, so your app must render the notification itself — typically from a background message handler. On iOS, Stream sends standard APNs alert pushes with aps.alert populated; iOS renders the banner natively, so no extra work is needed on the iOS side to display it.
  5. Handle notification taps to navigate the user to the relevant screen (e.g. the call screen identified by call_cid).
  6. Unregister on logout via client.removeDevice(token) so Stream stops sending pushes to that device.

To receive non-ringing push notifications, your app needs to register a device push token with Stream via client.addDevice — the FCM registration token on Android and the APNs device token on iOS. Registration is idempotent, so it's safe to call regardless of whether the ringing setup is also enabled.

Note that the iOS VoIP token registered automatically as part of the ringing flow doesn't apply here — VoIP tokens are PushKit-only, so a separate APNs device token must be registered for non-ringing notifications.

Payload shape

Stream's push notification payload contains the following fields in data (Android) or nested under stream (iOS). On iOS, the payload also carries a standard aps block with alert.title / alert.body populated from the call type's push template — that's what iOS renders as the banner natively, with no app code required.

iOS APNs payload
{
  "aps": {
    "alert": {
      "title": "Missed call",
      "body": "You missed a call from John Doe"
    },
    "category": "stream.video",
    "mutable-content": 1,
    "sound": "default",
    "thread-id": "video-default:call_id_123"
  },
  "stream": {
    "sender": "stream.video",
    "type": "call.missed",
    "version": "v2",
    "call_cid": "default:call_id_123",
    "call_display_name": "",
    "created_by_id": "john_doe",
    "created_by_display_name": "John Doe",
    "receiver_id": "artem",
    "title": "Missed call",
    "body": "You missed a call from John Doe"
  }
}
Android FCM payload (data-only)
{
  "data": {
    "sender": "stream.video",
    "type": "call.missed",
    "version": "v2",
    "call_cid": "default:call_id_123",
    "call_display_name": "",
    "created_by_id": "john_doe",
    "created_by_display_name": "John Doe",
    "receiver_id": "artem",
    "title": "Missed call",
    "body": "You missed a call from John Doe"
  }
}
FieldDescription
senderAlways "stream.video" — use this to identify Stream pushes and filter out unrelated notifications from your app.
typeThe event name — one of call.ring, call.notification, call.missed, call.live_started, call.session_started (see the table above).
versionThe payload schema version. Currently v2. Useful for forward compatibility if Stream adds new fields in future versions.
call_cidThe call identifier in <type>:<id> format (e.g. default:call_id_123). Use this to navigate to the call on tap or to fetch call details.
call_display_nameThe call's display name, if one was set when the call was created. Empty string when no display name was provided.
created_by_idID of the user that created the call.
created_by_display_nameDisplay name of the user that created the call. Empty string when the user has no display name.
receiver_idID of the user the notification is addressed to — i.e. the currently signed-in user on the device receiving the push.
titlePre-rendered notification title from the call type's push template (placeholders like {{ user.display_name }} already substituted). May be empty when no template is configured.
bodyPre-rendered notification body from the call type's push template, with placeholders substituted. May be empty when no template is configured.