For the XMPP protocol (if used), the server must be able to generate message IDs to uniquely
identify each message it sends (the FCM HTTP backend generates message IDs
and returns them in the response). XMPP message IDs should be unique per sender ID.

Choosing a server option

You'll need to decide on a way to interact with FCM servers: either using the
Firebase Admin SDK or the raw protocols.
Because of its support across popular programming languages and its convenience methods for
handling authentication and authorization, the Firebase Admin SDK is the recommended method.

The Firebase Admin SDK provides an API for
subscribing and unsubscribing devices to and from FCM topics.
These operations can subscribe or unsubscribe up to 1000 device registration
tokens at a time. For more information, see
Manage topics from the server.

FCM Server Protocols

Your app server can use these protocols
separately or in tandem. Because it is the most up-to-date and most flexible
for sending messages to multiple platforms, the
FCM HTTP v1 API is recommended wherever feasible. If your requirements
include upstream messaging from devices to the server, you'll need to
implement the XMPP protocol.

XMPP messaging differs from HTTP messaging in the following ways:

Upstream/Downstream messages

HTTP: Downstream only, cloud-to-device.

XMPP: Upstream and downstream (device-to-cloud, cloud-to-device).

Messaging (synchronous or asynchronous)

HTTP: Synchronous. App servers send messages as HTTP POST requests and
wait for a response. This mechanism is synchronous and blocks the sender from sending
another message until the response is received.

XMPP: Asynchronous. App servers send/receive messages to/from all their
devices at full line speed over persistent XMPP connections.
The XMPP connection server sends acknowledgment or failure notifications (in the
form of special ACK and NACK JSON-encoded XMPP messages) asynchronously.

JSON

HTTP: JSON messages sent as HTTP POST.

XMPP: JSON messages encapsulated in XMPP messages.

Plain Text

HTTP: Plain Text messages sent as HTTP POST.

XMPP: Not supported.

Multicast downstream send to multiple registration tokens.

HTTP: Supported in JSON message format.

XMPP: Not supported.

Implementing the HTTP server protocol

To send a message, the app server issues a POST request with
an HTTP header and an HTTP body comprised of JSON key value pairs.
For details on the header and body options, see
Build App Server Send Requests

Implementing the XMPP server protocol

The JSON payload for FCM messages is similar to
the HTTP protocol, with these
exceptions:

There is no support for multiple recipients.

FCM adds the field message_id, which is required. This ID uniquely
identifies the message in an XMPP connection. The ACK or NACK from FCM uses the
message_id to identify a message sent from app servers to FCM.
Therefore, it's important that this message_id not only be unique (per
sender ID), but always present.

XMPP uses the server key to authorize a persistent connection to FCM.
See Authorize Send Requests for more information.

In addition to regular FCM messages, control messages are sent, indicated by
the message_type field in the JSON object. The value can be either
'ack' or 'nack', or 'control' (see formats below). Any FCM message with an
unknown message_type can be ignored by your server.

For each device message your app server receives from FCM, it needs to send
an ACK message.
It never needs to send a NACK message. If you don't send an ACK for a message,
FCM resends it the next time a new XMPP connection is established, unless the
message expires first.

FCM also sends an ACK or NACK for each server-to-device message. If you do not
receive either, it means that the TCP connection was closed in the middle of the
operation and your server needs to resend the messages. See
Flow Control for details.

See the Server Reference for a complete list of the
NACK error codes. Unless otherwise
indicated, a NACKed message should not be retried. Unexpected NACK error codes
should be treated the same as INTERNAL_SERVER_ERROR.

Stanza error

You can also get a stanza error in certain cases.
A stanza error contains:

Control messages

Periodically, FCM needs to close down a connection to perform load balancing. Before it
closes the connection, FCM sends a CONNECTION_DRAINING message to indicate that the connection is being drained
and will be closed soon. "Draining" refers to shutting off the flow of messages coming into a
connection, but allowing whatever is already in the pipeline to continue. When you receive
a CONNECTION_DRAINING message, you should immediately begin sending messages to another FCM
connection, opening a new connection if necessary. You should, however, keep the original
connection open and continue receiving messages that may come over the connection (and
ACKing them)—FCM handles initiating a connection close when it is ready.

Receive delivery receipts

For Android and Chrome client apps, you can get delivery receipts (sent from FCM to
your app server) when
a device confirms that it received a message sent by FCM.

Delivery receipts are not supported for messages sent
to iOS devices.

To enable this feature, the message your app server sends to FCM must include
the field delivery_receipt_requested. When this field is set to
true, FCM sends a delivery receipt when a device confirms that it received a particular message.

Here is an XMPP stanza containing a JSON
message with "delivery_receipt_requested" set to true:

The "message_status" is set to "MESSAGE_SENT_TO_DEVICE",
indicating that the device received the message. Notice that in this case,
"message_status" is not a field but rather part of the data payload.

The receipt message ID consists of the original message ID, but with a
dr2: prefix. Your app server must use the same connection to send an ACK back with this
ID, which in this example is dr2:m-1366082849205.

The original message ID, the device registration token, and the status are inside the
"data" field.

If the connection between FCM and the device is poor, Firebase Cloud Messaging may send multiple, duplicate delivery
receipts. You can safely ignore such duplicates.

Flow control

Every message sent to FCM receives either an ACK or a NACK response. Messages
that haven't received one of these responses are considered pending. If the pending
message count reaches 100, the app server should stop sending new messages
and wait for FCM to acknowledge some of the existing pending messages as illustrated in
figure 1:

Figure 1. Message/ack flow.

Conversely, to avoid overloading the app server, FCM stops sending
if there are too many unacknowledged messages. Therefore, the app server
should "ACK" upstream messages, received from the client application via FCM, as soon as possible
to maintain a constant flow of incoming messages. The aforementioned pending message limit doesn't
apply to these ACKs. Even if the pending message count reaches 100, the app server
should continue sending ACKs for messages received from FCM to avoid blocking delivery of new
upstream messages.

ACKs are only valid within the context of one connection. If the connection is
closed before a message can be ACKed, the app server should wait for FCM
to resend the upstream message before ACKing it again. Similarly, all pending messages for which an
ACK/NACK was not received from FCM before the connection was closed should be sent again.