Entendendo a entrega de mensagens

O FCM fornece três conjuntos de ferramentas para ajudar você a obter insights sobre a entrega de mensagens:

  • Relatórios de entrega de mensagens do Firebase console
  • Métricas agregadas de entrega do Android SDK da Firebase Cloud Messaging Data API
  • Exportação abrangente de dados para o Google BigQuery

Todas as ferramentas de relatórios descritas nesta página requerem o Google Analytics para funcionar. Se o Google Analytics não estiver ativado para o seu projeto, você poderá configurá-lo na guia integrações das configurações do seu projeto do Firebase.

Tenha em mente que os relatórios de muitas das estatísticas nesta página estão sujeitos a atrasos de até 24 horas devido ao agrupamento de dados analíticos.

Relatórios de entrega de mensagens

Na guia Relatórios do Console do Firebase, você pode visualizar os seguintes dados de mensagens enviadas para SDKs FCM da plataforma Android ou Apple, incluindo aquelas enviadas por meio do compositor do Notifications e das APIs FCM:

  • Envios — A mensagem de dados ou mensagem de notificação foi enfileirada para entrega ou foi passada com êxito para um serviço de terceiros, como APNs, para entrega. Consulte o tempo de vida de uma mensagem para obter mais informações.
  • Recebida (disponível apenas em dispositivos Android) — A mensagem de dados ou mensagem de notificação foi recebida pelo aplicativo. Esses dados estão disponíveis quando o dispositivo Android receptor possui o FCM SDK 18.0.1 ou superior instalado.
  • Impressões (disponível apenas para mensagens de notificação em dispositivos Android) — A notificação de exibição foi exibida no dispositivo enquanto o aplicativo estava em segundo plano.
  • Abre — O usuário abriu a mensagem de notificação. Informado apenas para notificações recebidas quando o aplicativo está em segundo plano.

Esses dados estão disponíveis para todas as mensagens com carga útil de notificação e todas as mensagens de dados rotuladas . Para saber mais sobre rótulos, consulte Adicionar rótulos analíticos a mensagens .

Ao visualizar relatórios de mensagens, você pode definir um intervalo de datas para os dados exibidos, com a opção de exportar para CSV. Você também pode filtrar por estes critérios:

  • Plataforma (iOS ou Android)
  • Aplicativo
  • Rótulos analíticos personalizados

Adicionando rótulos analíticos às mensagens

Rotular mensagens é muito útil para análises personalizadas, permitindo filtrar estatísticas de entrega por rótulos ou conjuntos de rótulos. Você pode adicionar um rótulo a qualquer mensagem enviada por meio da API HTTP v1 definindo o campo fcmOptions.analyticsLabel no objeto de mensagem ou nos campos AndroidFcmOptions ou ApnsFcmOptions específicos da plataforma.

Os rótulos do Analytics são strings de texto no formato ^[a-zA-Z0-9-_.~%]{1,50}$ . Os rótulos podem incluir letras maiúsculas e minúsculas, números e os seguintes símbolos:

  • -
  • ~
  • %

O comprimento máximo é de 50 caracteres. Você pode especificar até 100 rótulos exclusivos por dia; mensagens com marcadores adicionados além desse limite não são relatadas.

Na guia Relatórios de mensagens do Firebase Console, você pode pesquisar uma lista de todos os rótulos existentes e aplicá-los individualmente ou em combinação para filtrar as estatísticas exibidas.

Dados de entrega agregados por meio da API de dados FCM

A API Firebase Cloud Messaging Data permite recuperar informações que podem ajudar a entender os resultados das solicitações de mensagens direcionadas a aplicativos Android. A API fornece dados agregados em todos os dispositivos Android habilitados para coleta de dados em um projeto. Isso inclui detalhes sobre a porcentagem de mensagens entregues sem atraso, bem como quantas mensagens foram atrasadas ou descartadas na camada de transporte do Android . A avaliação desses dados pode revelar tendências amplas na entrega de mensagens e ajudá-lo a encontrar maneiras eficazes de melhorar o desempenho de suas solicitações de envio. Consulte Cronogramas de dados agregados para obter informações sobre a disponibilidade do intervalo de datas nos relatórios.

A API fornece todos os dados disponíveis para um determinado aplicativo. Consulte a documentação de referência da API .

Como os dados são divididos?

Os dados de entrega são divididos por aplicativo, data e rótulo de análise . Uma chamada para a API retornará dados para cada combinação de data, aplicativo e rótulo de análise. Por exemplo, um único objeto JSON androidDeliveryData ficaria assim:

 {
  "appId": "1:23456789:android:a93a5mb1234efe56",
  "date": {
    "year": 2021,
    "month": 1,
    "day": 1
  },
  "analyticsLabel": "foo",
  "data": {
    "countMessagesAccepted": "314159",
    "messageOutcomePercents": {
      "delivered": 71,
      "pending": 15
    },
   "deliveryPerformancePercents": {
      "deliveredNoDelay": 45,
      "delayedDeviceOffline": 11
    }
  }

Como interpretar as métricas

Os dados de entrega descrevem a porcentagem de mensagens que se enquadram em cada uma das métricas a seguir. É possível que uma única mensagem se ajuste a múltiplas métricas. Devido às limitações na forma como coletamos os dados e ao nível de granularidade com que agregamos as métricas, alguns resultados de mensagens não são representados nas métricas, portanto, as porcentagens abaixo não somarão 100%.

Contar mensagens aceitas

A única contagem incluída no conjunto de dados é a contagem de mensagens que foram aceitas pelo FCM para entrega em dispositivos Android. Todas as porcentagens usam esse valor como denominador. Tenha em mente que esta contagem não incluirá mensagens direcionadas a usuários que desativaram a coleta de informações de uso e diagnóstico em seus dispositivos.

Porcentagens de resultados de mensagens

Os campos incluídos no objeto MessageOutcomePercents fornecem informações sobre os resultados das solicitações de mensagens. As categorias são todas mutuamente exclusivas. Ele pode responder perguntas como “Minhas mensagens estão sendo entregues?” e "O que está causando o descarte de mensagens?"

Por exemplo, um valor alto para o campo droppedTooManyPendingMessages pode sinalizar que as instâncias do aplicativo estão recebendo volumes de mensagens não recolhíveis que excedem o limite de 100 mensagens pendentes do FCM. Para atenuar isso, certifique-se de que seu aplicativo lide com chamadas para onDeletedMessages e considere enviar mensagens recolhíveis. Da mesma forma, altas porcentagens para droppedDeviceInactive podem ser um sinal para atualizar os tokens de registro em seu servidor, removendo tokens obsoletos e cancelando sua inscrição nos tópicos. Consulte Gerenciar tokens de registro do FCM para conhecer as práticas recomendadas nesta área.

Porcentagens de desempenho de entrega

Os campos no objeto DeliveryPerformancePercents fornecem informações sobre mensagens que foram entregues com êxito. Ele pode responder a perguntas como "Minhas mensagens atrasaram?" e "Por que as mensagens estão atrasadas?" Por exemplo, um valor alto para delayedMessageThrottled indicaria claramente que você está excedendo os limites máximos por dispositivo e deverá ajustar a taxa de envio de mensagens.

Porcentagens de insights de mensagens

Este objeto fornece informações adicionais sobre todos os envios de mensagens. O campo priorityLowered expressa a porcentagem de mensagens aceitas que tiveram prioridade reduzida de HIGH para NORMAL . Se esse valor for alto, tente enviar menos mensagens de alta prioridade ou certifique-se de sempre exibir uma notificação quando uma mensagem de alta prioridade for enviada. Consulte nossa documentação sobre prioridade de mensagens para obter mais informações

Qual a diferença entre esses dados e os dados exportados para o BigQuery?

A exportação do BigQuery fornece registros de mensagens individuais sobre aceitação de mensagens pelo back-end do FCM e entrega de mensagens no SDK do dispositivo (etapas 2 e 4 da arquitetura do FCM ). Esses dados são úteis para garantir que mensagens individuais foram aceitas e entregues. Leia mais sobre a exportação de dados do BigQuery na próxima seção.

Por outro lado, a API Firebase Cloud Messaging Data fornece detalhes agregados sobre o que acontece especificamente na camada de transporte do Android (ou na Etapa 3 da arquitetura FCM ). Esses dados fornecem especificamente informações sobre a entrega de mensagens dos back-ends do FCM para o SDK do Android. É particularmente útil para mostrar tendências sobre o motivo pelo qual as mensagens foram atrasadas ou perdidas durante esse transporte.

Em alguns casos, é possível que os dois conjuntos de dados não correspondam precisamente devido ao seguinte:

  • As métricas agregadas mostram apenas uma parte de todas as mensagens
  • As métricas agregadas são arredondadas
  • Não apresentamos métricas abaixo de um limite de privacidade
  • Uma parte dos resultados das mensagens está faltando devido a otimizações na forma como gerenciamos o grande volume de tráfego.

Limitações da API

Cronogramas de dados agregados

A API retornará 7 dias de dados históricos; no entanto, os dados retornados por esta API sofrerão atraso de até 5 dias. Por exemplo, em 20 de janeiro, os dados de 9 a 15 de janeiro estariam disponíveis, mas não de 16 de janeiro ou mais tarde. Além disso, os dados são fornecidos na melhor das hipóteses. No caso de uma interrupção de dados, o FCM trabalhará para corrigir o problema e não preencherá os dados depois que o problema for corrigido. Em interrupções maiores, os dados podem ficar indisponíveis por uma semana ou mais.

Cobertura de dados

As métricas fornecidas pela API Firebase Cloud Messaging Data têm como objetivo fornecer insights sobre tendências amplas de entrega de mensagens. No entanto, eles não fornecem cobertura de 100% de todos os cenários de mensagens. Os cenários a seguir são resultados conhecidos não refletidos nas métricas.

Mensagens recolhidas

As mensagens que foram recolhidas por outra mensagem não aparecem no conjunto de dados.

Mensagens para dispositivos inativos

As mensagens enviadas para dispositivos inativos podem ou não aparecer no conjunto de dados, dependendo do caminho de dados que elas percorrem. Isso pode levar a alguns erros de contagem nos campos droppedDeviceInactive e pending .

Mensagens para dispositivos com determinadas preferências do usuário

Os usuários que desabilitaram a coleta de informações de uso e diagnóstico em seus dispositivos não terão suas mensagens incluídas em nossa contagem, conforme suas preferências.

Arredondamento e Mínimos

O FCM arredonda e exclui deliberadamente contagens onde os volumes não são grandes o suficiente.

Exportação de dados do BigQuery

Você pode exportar os dados da sua mensagem para o BigQuery para análise posterior. O BigQuery permite analisar os dados usando o BigQuery SQL, exportá-los para outro provedor de nuvem ou usar os dados para seus modelos de ML personalizados. Uma exportação para o BigQuery inclui todos os dados disponíveis para mensagens, independentemente do tipo de mensagem ou se a mensagem é enviada pela API ou pelo compositor do Notifications.

Para mensagens enviadas para dispositivos com as seguintes versões mínimas do SDK do FCM, você tem a opção adicional de ativar a exportação de dados de entrega de mensagens para seu aplicativo:

  • Android 20.1.0 ou superior.
  • iOS 8.6.0 ou superior
  • SDK da Web do Firebase 9.0.0 ou superior

Veja abaixo detalhes sobre como ativar a exportação de dados para Android e iOS .

Para começar, vincule seu projeto ao BigQuery:

  1. Escolha uma das seguintes opções:

    • Abra o compositor de notificações e clique em Acessar o BigQuery na parte inferior da página.

    • Na página Integrações do Firebase Console, clique em Link no cartão do BigQuery .

      Esta página exibe opções de exportação do FCM para todos os aplicativos habilitados para FCM no projeto.

  2. Siga as instruções na tela para ativar o BigQuery.

Consulte Vincular o Firebase ao BigQuery para obter mais informações.

Ao ativar a exportação do BigQuery para Cloud Messaging:

  • O Firebase exporta seus dados para o BigQuery. Observe que a propagação inicial dos dados para exportação pode levar até 48 horas para ser concluída.

  • Após a criação do conjunto de dados, o local não poderá ser alterado, mas você poderá copiar o conjunto de dados para um local diferente ou mover (recriar) manualmente o conjunto de dados em um local diferente. Para saber mais, consulte Alterar a localização do conjunto de dados .

  • O Firebase configura sincronizações regulares dos dados do seu projeto do Firebase com o BigQuery. Essas operações diárias de exportação começam às 4h, horário do Pacífico, e geralmente terminam em 24 horas.

  • Por padrão, todos os aplicativos do seu projeto são vinculados ao BigQuery, e todos os aplicativos adicionados posteriormente ao projeto são automaticamente vinculados ao BigQuery. Você pode gerenciar quais aplicativos enviam dados .

Para desativar a exportação do BigQuery, desvincule seu projeto no Firebase Console.

Habilitar exportação de dados de entrega de mensagens

Dispositivos iOS com o FCM SDK 8.6.0 ou superior podem ativar a exportação de dados de entrega de mensagens do aplicativo. O FCM oferece suporte à exportação de dados para notificações de alerta e de segundo plano. Antes de ativar essas opções, você deve primeiro criar o link FCM-BiqQuery para seu projeto, conforme descrito em Exportação de dados do BigQuery .

Habilitar exportação de dados de entrega para notificações de alerta

Como apenas notificações de alerta podem acionar extensões de aplicativo de serviço de notificação, você deve adicionar uma extensão de serviço de notificação ao seu aplicativo e chamar essa API dentro de uma extensão de serviço para ativar o rastreamento de mensagens de exibição. Consulte a documentação da Apple sobre como modificar conteúdo em notificações recém-entregues .

A seguinte chamada deve ser feita para cada notificação recebida:

Rápido

// For alert notifications, call the API inside the service extension:
class NotificationService: UNNotificationServiceExtension {
  override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
  Messaging.extensionHelper()
      .exportDeliveryMetricsToBigQuery(withMessageInfo:request.content.userInfo)
  }
}

Objetivo-C

// For alert notifications, call the API inside the service extension:
@implementation NotificationService
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request
                   withContentHandler:(void (^)(UNNotificationContent *_Nonnull))contentHandler {
  [[FIRMessaging extensionHelper] exportDeliveryMetricsToBigQueryWithMessageInfo:request.content.userInfo];
}
@end

Se você estiver criando solicitações de envio usando a API HTTP v1, certifique-se de especificar mutable-content = 1 no objeto de carga útil .

Habilitar exportação de dados de entrega para notificações em segundo plano

Para mensagens em segundo plano recebidas quando o aplicativo está em primeiro ou segundo plano, você pode chamar a API de exportação de dados dentro do manipulador de mensagens de dados do aplicativo principal. Esta chamada deve ser feita para cada notificação recebida:

Rápido

// For background notifications, call the API inside the UIApplicationDelegate or NSApplicationDelegate method:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
  Messaging.extensionHelper().exportDeliveryMetricsToBigQuery(withMessageInfo:userInfo)
}

Objetivo-C

// For background notifications, call the API inside the UIApplicationDelegate or NSApplicationDelegate method:
@implementation AppDelegate
- (void)application:(UIApplication *)application
    didReceiveRemoteNotification:(NSDictionary *)userInfo
          fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  [[FIRMessaging extensionHelper] exportDeliveryMetricsToBigQueryWithMessageInfo:userInfo];
}
@end

Quais dados são exportados para o BigQuery?

Observe que a segmentação de tokens obsoletos ou registros inativos pode inflar algumas dessas estatísticas.

O esquema da tabela exportada é:

_PARTIÇÃOTIME TIMESTAMP Esta pseudocoluna contém um carimbo de data/hora para o início do dia (em UTC) em que os dados foram carregados. Para a partição YYYYMMDD, esta pseudocoluna contém o valor TIMESTAMP('YYYY-MM-DD').
evento_timestamp TIMESTAMP Carimbo de data/hora do evento conforme registrado pelo servidor
projeto número INTEIRO O número do projeto identifica o projeto que enviou a mensagem
mensagem_id CORDA O ID da mensagem identifica uma mensagem. Gerado a partir do ID do aplicativo e do carimbo de data/hora, o ID da mensagem pode, em alguns casos, não ser globalmente exclusivo.
ID_da_instância CORDA O ID exclusivo do aplicativo para o qual a mensagem é enviada (quando disponível). Pode ser um ID de instância ou um ID de instalação do Firebase.
tipo de mensagem CORDA O tipo da mensagem. Pode ser mensagem de notificação ou mensagem de dados. Tópico é utilizado para identificar a mensagem original de um tópico ou envio de campanha; as mensagens subsequentes são uma mensagem de notificação ou de dados.
plataforma_sdk CORDA A plataforma do aplicativo destinatário
nome do aplicativo CORDA O nome do pacote para aplicativos Android ou o ID do pacote para aplicativos iOS
colapso_key CORDA A chave de recolhimento identifica um grupo de mensagens que podem ser recolhidas. Quando um dispositivo não está conectado, apenas a última mensagem com uma determinada chave de recolhimento é colocada na fila para eventual entrega
prioridade INTEIRO A prioridade da mensagem. Os valores válidos são “normal” e “alto”. No iOS, correspondem às prioridades 5 e 10 dos APNs
TTL INTEIRO Este parâmetro especifica por quanto tempo (em segundos) a mensagem deve ser mantida no armazenamento FCM se o dispositivo estiver offline
tema CORDA O nome do tópico para o qual a mensagem foi enviada (quando aplicável)
bulk_id INTEIRO O ID em massa identifica um grupo de mensagens relacionadas, como um envio específico para um tópico
evento CORDA O tipo do evento. Os valores possíveis são:
  • MESSAGE_ACCEPTED: a mensagem foi recebida pelo servidor FCM e a solicitação é válida;
  • MESSAGE_DELIVERED: a mensagem foi entregue ao SDK FCM do aplicativo no dispositivo. Por padrão, este campo não é propagado. Para ativar, siga as instruções fornecidas em setDeliveryMetricsExportToBigQuery(boolean) .
  • MISSING_REGISTRATIONS: solicitação foi rejeitada por falta de cadastro;
  • UNAUTHORIZED_REGISTRATION: a mensagem foi rejeitada porque o remetente não está autorizado a enviar para o cadastro;
  • MESSAGE_RECEIVED_INTERNAL_ERROR: ocorreu um erro não especificado ao processar a solicitação de mensagem;
  • MISMATCH_SENDER_ID: a solicitação de envio de mensagem foi rejeitada devido a uma incompatibilidade entre o id do remetente que envia a mensagem e o declarado para o endpoint;
  • QUOTA_EXCEEDED: a solicitação de envio de mensagem foi rejeitada por cota insuficiente;
  • INVALID_REGISTRATION: a solicitação de envio de mensagem foi rejeitada devido a um cadastro inválido;
  • INVALID_PACKAGE_NAME: a solicitação de envio de mensagem foi rejeitada devido a um nome de pacote inválido;
  • INVALID_APNS_CREDENTIAL: a solicitação de envio de mensagem foi rejeitada devido a um certificado APNS inválido;
  • INVALID_PARAMETERS: a solicitação de envio de mensagem foi rejeitada devido a parâmetros inválidos;
  • PAYLOAD_TOO_LARGE: a solicitação de envio de mensagem foi rejeitada devido a um payload maior que o limite;
  • AUTHENTICATION_ERROR: a solicitação de envio de mensagem foi rejeitada devido a um erro de autenticação (verifique a chave API utilizada para enviar a mensagem);
  • INVALID_TTL: a solicitação de envio de mensagem foi rejeitada devido a um TTL inválido.
rótulo_de_análise CORDA Com a API HTTP v1 , o rótulo analítico pode ser definido ao enviar a mensagem, a fim de marcar a mensagem para fins analíticos

O que você pode fazer com os dados exportados?

As seções a seguir oferecem exemplos de consultas que você pode executar no BigQuery nos dados exportados do FCM.

Contar mensagens enviadas por aplicativo

SELECT app_name, COUNT(1)
FROM `project ID.firebase_messaging.data`
WHERE
  _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
  AND event = 'MESSAGE_ACCEPTED'
  AND message_id != ''
GROUP BY 1;

Contar instâncias exclusivas de aplicativos segmentadas por mensagens

SELECT COUNT(DISTINCT instance_id)
FROM `project ID.firebase_messaging.data`
WHERE
  _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
  AND event = 'MESSAGE_ACCEPTED';

Contar mensagens de notificação enviadas

SELECT COUNT(1)
FROM `project ID.firebase_messaging.data`
WHERE
  _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
  AND event = 'MESSAGE_ACCEPTED'
  AND message_type = 'DISPLAY_NOTIFICATION';

Contar mensagens de dados enviadas

SELECT COUNT(1)
FROM `project ID.firebase_messaging.data`
WHERE
  _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
  AND event = 'MESSAGE_ACCEPTED'
  AND message_type = 'DATA_MESSAGE';

Contar mensagens enviadas para um tópico ou campanha

SELECT COUNT(1)
FROM `project ID.firebase_messaging.data`
WHERE
  _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
  AND event = 'MESSAGE_ACCEPTED'
  AND bulk_id = your bulk id AND message_id != '';

Para rastrear eventos de uma mensagem enviada para um tópico específico, modifique esta consulta para substituir AND message_id != '' por AND message_id = <your message id>; .

Calcular a duração do fanout para um determinado tópico ou campanha

O horário de início do fanout é quando a solicitação original é recebida, e o horário de término é o horário em que a última mensagem individual direcionada a uma única instância é criada.

SELECT
  TIMESTAMP_DIFF(
    end_timestamp, start_timestamp, MILLISECOND
  ) AS fanout_duration_ms,
  end_timestamp,
  start_timestamp
FROM (
    SELECT MAX(event_timestamp) AS end_timestamp
    FROM `project ID.firebase_messaging.data`
    WHERE
      _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
      AND event = 'MESSAGE_ACCEPTED'
      AND bulk_id = your bulk id
  ) sent
  CROSS JOIN (
    SELECT event_timestamp AS start_timestamp
    FROM `project ID.firebase_messaging.data`
    WHERE
      _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
      AND event = 'MESSAGE_ACCEPTED'
      AND bulk_id = your bulk id
      AND message_type = 'TOPIC'
  ) initial_message;

Contar porcentagem de mensagens entregues

SELECT
  messages_sent,
  messages_delivered,
  messages_delivered / messages_sent * 100 AS percent_delivered
FROM (
    SELECT COUNT(DISTINCT CONCAT(message_id, instance_id)) AS messages_sent
    FROM `project ID.firebase_messaging.data`
    WHERE
      _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
      AND event = 'MESSAGE_ACCEPTED'
  ) sent
  CROSS JOIN (
    SELECT COUNT(DISTINCT CONCAT(message_id, instance_id)) AS messages_delivered
    FROM `project ID.firebase_messaging.data`
    WHERE
      _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
      AND (event = 'MESSAGE_DELIVERED'
      AND message_id
      IN (
        SELECT message_id FROM `project ID.firebase_messaging.data`
        WHERE
          _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
          AND event = 'MESSAGE_ACCEPTED'
        GROUP BY 1
      )
  ) delivered;

Rastreie todos os eventos para um determinado ID de mensagem e ID de instância

SELECT *
FROM `project ID.firebase_messaging.data`
WHERE
    _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
    AND message_id = 'your message id'
    AND instance_id = 'your instance id'
ORDER BY event_timestamp;

Calcular a latência para um determinado ID de mensagem e ID de instância

SELECT
  TIMESTAMP_DIFF(
    MAX(delivered_time), MIN(accepted_time), MILLISECOND
  ) AS latency_ms
FROM (
    SELECT event_timestamp AS accepted_time
    FROM `project ID.firebase_messaging.data`
    WHERE
      _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD')
      AND message_id = 'your message id'
      AND instance_id = 'your instance id'
      AND event = 'MESSAGE_ACCEPTED'
  ) sent
  CROSS JOIN (
    SELECT event_timestamp AS delivered_time
    FROM `project ID.firebase_messaging.data`
    WHERE
      _PARTITIONTIME = TIMESTAMP('date as YYYY-MM-DD') AND
      message_id = 'your message id' AND instance_id = 'your instance id'
      AND (event = 'MESSAGE_DELIVERED'
  ) delivered;