108

I'm attempting to send out a notification to all app users (on Android), essentially duplicating what happens when a notification is sent via the Firebase admin console. Here is the CURL command I begin with:

curl --insecure --header "Authorization: key=AIzaSyBidmyauthkeyisfineL-6NcJxj-1JUvEM" --header "Content-Type:application/json" -d "{\"notification\":{\"title\":\"note-Title\",\"body\":\"note-Body\"}}" https://fcm.googleapis.com/fcm/send

Here's that JSON parsed out to be easier on your eyes:

{
"notification":{
    "title":"note-Title",
    "body":"note-Body"
    }
}

The response that comes back is just two characters:

to

That's it, the word "to". (Headers report a 400) I suspect this has to do with not having a "to" in my JSON. What would one even put for a "to"? I have no topics defined, and the devices have not registered themselves for anything. Yet, they are still able to receive notifications from the Firebase admin panel.

I'm want to attempt a "data only" JSON package due to the amazing limitation of Firebase notification processing whereby if your app is in the foreground, the notification gets processed by YOUR handler, but if your app is in the background, it gets processed INTERNALLY by the Firebase service and never passed to your notification handler. APPARENTLY this can be worked around if you submit your notification request via the API, but ONLY if you do it with data-only. (Which then breaks the ability to handle iOS and Android with the same message.) Replacing "notification" with "data" in any of my JSON has no effect.

Ok, then I attempted the solution here: Firebase Java Server to send push notification to all devices which seems to me to say "Ok, even though notifications to everyone is possible via the Admin console... it's not really possible via the API." The workaround is to have each client subscribe to a topic, and then push out the notification to that topic. So first the code in onCreate:

FirebaseMessaging.getInstance().subscribeToTopic("allDevices");

then the new JSON I send:

{
"notification":{
    "title":"note-Title",
    "body":"note-Body"
    },
"to":"allDevices"
}

So now I'm getting a real response from the server at least. JSON response:

{
"multicast_id":463numbersnumbers42000,
"success":0,
"failure":1,
"canonical_ids":0,
"results":
    [
    {
    "error":"InvalidRegistration"
    }
    ]
}

And that comes with a HTTP code 200. Ok... according to https://firebase.google.com/docs/cloud-messaging/http-server-ref a 200 code with "InvalidRegistration" means a problem with the registration token. Maybe? Because that part of the documentation is for the messaging server. Is the notification server the same? Unclear. I see elsewhere that the topic might take hours before it's active. It seems like that would make it useless for creating new chat rooms, so that seems off as well.

I was pretty excited when I could code up an app from scratch that got notifications in just a few hours when I had never used Firebase before. It seems like it has a long way to go before it reaches the level of, say, the Stripe.com documentation.

Bottom line: does anyone know what JSON to supply to send a message out to all devices running the app to mirror the Admin console functionality?

Community
  • 1
  • 1
MotoRidingMelon
  • 2,347
  • 2
  • 21
  • 28
  • Check your api token that you are using is correct. Also try to send the GCM message to a single device and see if that works. You can get the InstanceId by start the app in debug and put a breakpoint on the `FirebaseInstanceIdService` – Justin Slade Jul 07 '16 at 04:36
  • I don't see "api token" showing up in a quick google search of firebase.google.com/docs. Do you mean "ID token"? or "Auth token"? Or are you talking about my authorization key as supplied in the header? That one _is_ fine: if I change it from what it is, the server response switches to "unauthorized" instead of the error message I'm currently getting. – MotoRidingMelon Jul 07 '16 at 04:48
  • That blog appears to be about sending a message to a particular device, not, as the original discussion, about sending it to just all users of the app. – MotoRidingMelon Jul 01 '17 at 11:12
  • Read this blogpost for more details - > http://developine.com/how-to-send-firebase-push-notifications-from-app-server-tutorial/ – Developine Nov 11 '17 at 15:40

10 Answers10

118

Firebase Notifications doesn't have an API to send messages. Luckily it is built on top of Firebase Cloud Messaging, which has precisely such an API.

With Firebase Notifications and Cloud Messaging, you can send so-called downstream messages to devices in three ways:

  1. to specific devices, if you know their device IDs
  2. to groups of devices, if you know the registration IDs of the groups
  3. to topics, which are just keys that devices can subscribe to

You'll note that there is no way to send to all devices explicitly. You can build such functionality with each of these though, for example: by subscribing the app to a topic when it starts (e.g. /topics/all) or by keeping a list of all device IDs, and then sending the message to all of those.

For sending to a topic you have a syntax error in your command. Topics are identified by starting with /topics/. Since you don't have that in your code, the server interprets allDevices as a device id. Since it is an invalid format for a device registration token, it raises an error.

From the documentation on sending messages to topics:

https://fcm.googleapis.com/fcm/send
Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA

{
  "to": "/topics/foo-bar",
  "data": {
    "message": "This is a Firebase Cloud Messaging Topic Message!",
   }
}
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • I attempted submitting {"to":"/topic/allDevices","data":{"message":"This is a Firebase Cloud Messaging Topic Message!"}} and received the same InvalidRegistration response. I would assume that means the topic is not valid. I see no way in the console to look at a database of topics. – MotoRidingMelon Jul 07 '16 at 18:21
  • 7
    `/topics/` is plural – Frank van Puffelen Jul 07 '16 at 19:09
  • *smirk* Oh, typo, bane of the programmer. ;) New response is: {"message_id":5115052numbers95180} which certainly looks a lot more successful. So, sounds like the bottom line is that while there's a power functionality from the admin console to deliver notifications via "Notifications", there is NO way trigger that from API submissions, and one should just convert yourself to using messages. (Losing the scheduling and conversion tracking built into the Notifications) I'll look for messaging code to handle the receive. Thanks Frank. :) – MotoRidingMelon Jul 07 '16 at 19:51
  • 1
    Uhm... not really. The Notifications console just sends notification messages (see [this the documentation about message types](https://firebase.google.com/docs/cloud-messaging/concept-options)). The only thing it does that we [don't have an API for yet is sending to audiences](http://stackoverflow.com/questions/37995870/how-to-send-firebase-notifications-to-audience-via-http). – Frank van Puffelen Jul 07 '16 at 21:11
  • 1
    is it the same for iOS too? I tried the same and I am receiving the notification when the app is alive, but in background it doesn't work – vinbhai4u Aug 12 '16 at 07:22
  • Is there a way to send a heads up notification in this way as well? – AymanKun Dec 04 '16 at 17:44
  • how can multicast a message? – Mahdi Jan 01 '17 at 14:14
  • is there a quota on sending the notification? if yes, what is it? – Amjad Omari Mar 29 '17 at 11:11
  • For 4 days i'm trying to send notifications from android to topics. The only thing i omitted was `/topics/`. Thanks! – Cătălin Florescu Apr 27 '17 at 08:31
  • @FrankvanPuffelen What will do if I want to send notification from Device1 to Device2 from an App ? – Ajay Sharma Jul 04 '17 at 10:37
  • See my blog post here for one way to send notifications between devices: https://firebase.googleblog.com/2016/08/sending-notifications-between-android.html – Frank van Puffelen Jul 04 '17 at 14:59
  • Read this blogpost for more details - > http://developine.com/how-to-send-firebase-push-notifications-from-app-server-tutorial/ – Developine Nov 11 '17 at 15:40
  • But isn't this counted as very critical to have your sender id stored in your app? I think I can remember that this is a big big no-no-no. Anyone can find your sender id in your apk and can abuse it, sending messages on your behalf? – Grisgram Dec 13 '19 at 10:18
  • To send a message to a device, you need to know the sender ID **and** the FCM server key. If you read the [blog post](https://firebase.googleblog.com/2016/08/sending-notifications-between-android.html) I linked earlier, you'll see that the server key is only used on the server (in a Node.js process). – Frank van Puffelen Dec 13 '19 at 15:19
92

The most easiest way I came up with to send the push notification to all the devices is to subscribe them to a topic "all" and then send notification to this topic. Copy this in your main activity

FirebaseMessaging.getInstance().subscribeToTopic("all");

Now send the request as

{
    "to": "/topics/all",
    "data":
    {
        "title":"Your title",
        "message":"Your message"
        "image-url":"your_image_url"
    }
}

This might be inefficient or non-standard way, but as I mentioned above it's the easiest. Please do post if you have any better way to send a push notification to all the devices.

Just checked the FCM documentation, this is the only way to send notifications to all the devices (as of 8th July 2022).

As mentioned in the comments, the notification is not automatically displayed, you have to define a class that is derived from FirebaseMessagingService and then override the function onMessageReceived.

First register your service in app manifest.

<!-- AndroidManifest.xml -->

<service
    android:name=".java.MyFirebaseMessagingService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

Add these lines inside the application tag to set the custom default icon and custom color:

<!-- AndroidManifest.xml -->

<!-- Set custom default icon. This is used when no icon is set 
for incoming notification messages. -->
<meta-data
    android:name="com.google.firebase.messaging.default_notification_icon"
    android:resource="@drawable/ic_stat_ic_notification" />
<!-- Set color used with incoming notification messages. This is used 
when no color is set for the incoming notification message. -->
<meta-data
    android:name="com.google.firebase.messaging.default_notification_color"
    android:resource="@color/colorAccent" />

Now create your service to receive the push notifications.

// MyFirebaseMessagingService.java

package com.google.firebase.example.messaging;

import android.content.Context;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;
import androidx.work.Worker;
import androidx.work.WorkerParameters;

import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;

public class MyFirebaseMessagingService extends FirebaseMessagingService {

    private static final String TAG = "MyFirebaseMsgService";

    // [START receive_message]
    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        Log.d(TAG, "From: " + remoteMessage.getFrom());

        // Check if message contains a data payload.
        if (remoteMessage.getData().size() > 0) {
            Log.d(TAG, "Message data payload: " + remoteMessage.getData());

            if (/* Check if data needs to be processed by long running job */ true) {
                // For long-running tasks (10 seconds or more) use WorkManager.
                scheduleJob();
            } else {
                // Handle message within 10 seconds
                handleNow();
            }

        }

        // Check if message contains a notification payload.
        if (remoteMessage.getNotification() != null) {
            Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
        }

    }
    // [END receive_message]

    // [START on_new_token]
    /**
     * There are two scenarios when onNewToken is called:
     * 1) When a new token is generated on initial app startup
     * 2) Whenever an existing token is changed
     * Under #2, there are three scenarios when the existing token is changed:
     * A) App is restored to a new device
     * B) User uninstalls/reinstalls the app
     * C) User clears app data
     */
    @Override
    public void onNewToken(@NonNull String token) {
        Log.d(TAG, "Refreshed token: " + token);

        // If you want to send messages to this application instance or
        // manage this apps subscriptions on the server side, send the
        // FCM registration token to your app server.
        sendRegistrationToServer(token);
    }
    // [END on_new_token]

    private void scheduleJob() {
        // [START dispatch_job]
        OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(MyWorker.class)
                .build();
        WorkManager.getInstance(this).beginWith(work).enqueue();
        // [END dispatch_job]
    }

    private void handleNow() {
        Log.d(TAG, "Short lived task is done.");
    }

    private void sendRegistrationToServer(String token) {
        // TODO: Implement this method to send token to your app server.
    }

    public static class MyWorker extends Worker {

        public MyWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
            super(context, workerParams);
        }

        @NonNull
        @Override
        public Result doWork() {
            // TODO(developer): add long running task here.
            return Result.success();
        }
    }
}

You can follow this tutorial if you're new to sending push notifications using Firebase Cloud Messaging Tutorial - Push Notifications using FCM and Send messages to multiple devices - Firebase Documentation


To send a message to a combination of topics, specify a condition, which is a boolean expression that specifies the target topics. For example, the following condition will send messages to devices that are subscribed to TopicA and either TopicB or TopicC:

{
   "data":
   {
      "title": "Your title",
      "message": "Your message"
      "image-url": "your_image_url"
   },
   "condition": "'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)"
}

Read more about conditions and topics here on FCM documentation

Syed Rafay
  • 1,405
  • 2
  • 15
  • 19
  • It makes sense to me. But like you, I am not sure if it is inefficient. Please, can someone clarify it? – JCarlosR Feb 08 '18 at 01:50
  • 2
    I don't think it's inefficient, that might be the only possible way to send push notification to all the devices (i couldn't find any other way atleast) – Syed Rafay Feb 17 '18 at 12:19
  • 1
    Best answer! :) – wonsuc May 27 '18 at 19:28
  • 2
    Great answer. Since it wasn't mentioned anywhere it's important to know that you will receive this kind of messages in your `Service` that is extending `FirebaseMessagingService` in the `#onMessageReceived` function. So the notification isn't automatically displayed you have to take care of it on your own. – reVerse Sep 02 '18 at 16:22
65

EDIT: It appears that this method is not supported anymore (thx to @FernandoZamperin). Please take a look at the other answers!

Instead of subscribing to a topic you could instead make use of the condition key and send messages to instances, that are not in a group. Your data might look something like this:

{
    "data": {
        "foo": "bar"
    },
    "condition": "!('anytopicyoudontwanttouse' in topics)"
}

See https://firebase.google.com/docs/cloud-messaging/send-message#send_messages_to_topics_2

Walamana
  • 768
  • 6
  • 9
  • 3
    it's best answer. It can be run immediately without the need for app updates. great! – Tom Marvolo Riddle Nov 14 '19 at 02:23
  • finally found that one - they should really make it more obvious to use that – cwiesner Jan 21 '20 at 19:22
  • This looks promising but does it really works? It looks like a hack. I tried it on a sample project and it works. However, I am not sure if it will work with my prod app which has thousands of active users. Has anyone tried it with a prod app? – Sahil Patel Feb 20 '20 at 07:22
  • Definitely best answer. It solved my problem. Thanks. – torun Apr 18 '20 at 10:37
  • This is brilliant! – Sepak May 02 '20 at 22:18
  • 5
    I was using this approach for quite a while and was working perfectly, today for some reason I send the notification to the legacy api it returns me the message_id without errors, but the devices are not receiving the push notifications, any guesses what would be the problem? Tested send to a single token and it worked normally. – Fernando Zamperin Aug 31 '20 at 16:35
  • I confirm what @FernandoZamperin wrote. This approach appears to stop working. – Vit Veres Sep 03 '20 at 14:48
  • The official documentation still says it is supported, so it is most likely a bug in the firebase: https://firebase.google.com/docs/cloud-messaging/send-message#send-messages-to-topics !('TopicA' in topics) With this expression, any app instances that are not subscribed to TopicA, including app instances that are not subscribed to any topic, receive the message. – Vit Veres Sep 03 '20 at 17:49
  • 1
    @VitVeres I sent an issue to the firebase support team and they replied to me as: "Your current usage of expressions to fanout push notifications is not supported. Please ensure that topics listed in the expression exist for your app project.", so I guess I'll have to implement topics in my apps. – Fernando Zamperin Sep 03 '20 at 19:26
  • @FernandoZamperin Thank you for this info.. that sound's quite the opposite of the description from the docs. I wonder if we would introduce the topic name in the Firebase at least to a few clients if it would start sending notifications to the rest. Would you have a link for the issue tracker? – Vit Veres Sep 07 '20 at 08:45
  • "If you're trying to send to all users not in a topic, you would need a topic with all users and then exclude users from there - something like, "condition": "('allUsers in topics) && !('notopic' in topics)" this was the last reply from the firebase team @VitVeres, unfortunately I don't have the link since I'm communicating with them via ticket system... Actually does not make much sense, since if I send a condition to a topic it will not send to others, so in theory you can't use negation in condition logic. – Fernando Zamperin Sep 07 '20 at 15:57
  • 1
    It's quite disappointing that after waiting 3.5 years for a way to do this, we finally got one... and they killed it shortly after we figured it out. Sadly I feel like Firebase does 93 amazing things.... just not the three obvious things you need to do. ("3" being a made up disappointment number) – MotoRidingMelon Sep 24 '20 at 20:35
  • I've been using this technique successfully with no trouble. Now it works no more. Why would they change a working system if the change is not an addition? At least this one worked without requiring a new release of the app – josagyemang Sep 29 '20 at 21:26
  • I confirm this does not work anymore – Gomino Jun 10 '22 at 15:51
  • you still need to remember all topics name – Khalid Lakhani Jan 08 '23 at 12:44
4

One way to do that is to make all your users' devices subscribe to a topic. That way when you target a message to a specific topic, all devices will get it. I think this how the Notifications section in the Firebase console does it.

Abdalrahman Shatou
  • 4,550
  • 6
  • 50
  • 79
  • Read this blogpost for more details - > http://developine.com/how-to-send-firebase-push-notifications-from-app-server-tutorial/ – Developine Nov 11 '17 at 15:40
2

I was looking solution for my Ionic Cordova app push notification.

Thanks to Syed Rafay's answer.

in app.component.ts

const options: PushOptions = {
  android: {
    topics: ['all']
  },

in Server file

"to" => "/topics/all",
Keval Shah
  • 51
  • 8
2

Check your topic list on firebase console.

  1. Go to firebase console

  2. Click Grow from side menu

  3. Click Cloud Messaging

  4. Click Send your first message

  5. In the notification section, type something for Notification title and Notification text

  6. Click Next

  7. In target section click Topic

  8. Click on Message topic textbox, then you can see your topics (I didn't created topic called android or ios, but I can see those two topics.

  9. When you send push notification add this as your condition.

    "condition"=> "'all' in topics || 'android' in topics || 'ios' in topics",

Full body

array(
    "notification"=>array(
        "title"=>"Test",
        "body"=>"Test Body",
    ),
    "condition"=> "'all' in topics || 'android' in topics || 'ios' in topics",
);

If you have more topics you can add those with || (or) condition, Then all users will get your notification. Tested and worked for me.

mint
  • 345
  • 1
  • 3
  • 12
1

Just make all users who log in subscribe to a specific topic, and then send a notification to that topic.

0

For anyone wondering how to do it in cordova hybrid app:

  • go to index.js -> inside the function onDeviceReady() write :

      subscribe();
    

(It's important to write it at the top of the function!)

  • then, in the same file (index.js) find :

    function subscribe(){

     FirebasePlugin.subscribe("write_here_your_topic", function(){
    
     },function(error){
    
         logError("Failed to subscribe to topic", error);
     });
     }
    

and write your own topic here -> "write_here_your_topic"

NPLS
  • 521
  • 2
  • 8
  • 20
0

It is a PHP Admin-SDK example to subscribe an user to a Topic and to send messages to a device by device token or to a Topic. Note that the Topic is created automatically when you subscribe an user.

$testTokens = ['device token 1', 'device token 2', ....]

// CREDENTIALS, YOU HAVE TO DOWNLOAD IT FROM FIREBASE CONSOLE.
$factory = (new Factory())->withServiceAccount('credentials.json');

$messaging = $factory->createMessaging();

// Subscribe a token or a group of tokens to a topic (this topic is created automatically if not exists)
// YOU CAN DO THIS IN THE MOBILE APP BUT IS BETTER DO IT IN THE API.
$result = $messaging->subscribeToTopic('all', $testTokens); // 'all' is the topic name

// Send a message to a specific Topic (Channel) 
$message = CloudMessage::withTarget('topic', 'all')
    ->withNotification(Notification::create('Global message Title', 'Global message Body'))
    ->withData(['key' => 'value']); // optional
$messaging->send($message);

// Send a message to a token or a grup of tokens (ONLY!!!)
 foreach($testTokens as $i=>$token){
     $message = CloudMessage::withTarget('token', $token)
         ->withNotification(Notification::create('This is the message Title', 'This is the message Body'))
         ->withData(['custom_index' => $i]); // optional
     $messaging->send($message);

You can check this repo for more details: firebase-php-admin-sdk

manuelpgs
  • 1,283
  • 1
  • 14
  • 20
-3

Your can send notification to all devices using "/topics/all"

https://fcm.googleapis.com/fcm/send
Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA

{
  "to": "/topics/all",
  "notification":{ "title":"Notification title", "body":"Notification body", "sound":"default", "click_action":"FCM_PLUGIN_ACTIVITY", "icon":"fcm_push_icon" },
  "data": {
    "message": "This is a Firebase Cloud Messaging Topic Message!",
   }
}
Siva Karuppiah
  • 995
  • 1
  • 8
  • 17