Catch up on everything announced at Firebase Summit, and learn how Firebase can help you accelerate app development and run your app with confidence. Learn More

Entendendo a entrega de mensagens

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

O FCM fornece três conjuntos de ferramentas para ajudá-lo a obter informações 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 seu projeto, você poderá configurá-lo na guia de integrações das configurações do seu projeto do Firebase.

Lembre-se de que os relatórios de muitas das estatísticas nesta página estão sujeitos a atrasos de até 24 horas devido ao lote de dados analíticos.

Relatórios de entrega de mensagens

Na guia Relatórios do console do Firebase, você pode ver os seguintes dados de mensagens enviadas para SDKs do FCM da plataforma Android ou Apple, incluindo aquelas enviadas por meio do compositor de notificações e das APIs do FCM:

  • Envios — A mensagem de dados ou mensagem de notificação foi enfileirada para entrega ou foi passada com sucesso para um serviço de terceiros como APNs para entrega. Consulte o tempo de vida de uma mensagem para obter mais informações.
  • Recebido (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 tem 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 está em segundo plano.
  • Abre — O usuário abriu a mensagem de notificação. Relatado apenas para notificações recebidas quando o aplicativo está em segundo plano.

Esses dados estão disponíveis para todas as mensagens com uma carga de notificação e todas as mensagens de dados rotulados . Para saber mais sobre marcadores, consulte Adicionar marcadores de análise 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 de análise personalizados

Adicionando rótulos de análise às mensagens

Rotular mensagens é muito útil para análise personalizada, 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 console do Firebase, 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 do FCM

A Firebase Cloud Messaging Data API permite recuperar informações que podem ajudar você 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 encaixam em cada uma das métricas a seguir. É possível que uma única mensagem se encaixe em várias métricas. Devido a limitações na forma como coletamos os dados e ao nível de granularidade em 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. Lembre-se de que essa contagem não incluirá mensagens direcionadas a usuários que desabilitaram 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 a perguntas como "Minhas mensagens estão sendo entregues?" e "O que está causando a queda das 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 excedendo o limite do FCM de 100 mensagens pendentes. Para atenuar isso, certifique-se de que seu aplicativo processe 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 a assinatura dos tópicos. Consulte Gerenciar tokens de registro do FCM para obter as melhores práticas nesta área.

Porcentagens de desempenho de entrega

Os campos no objeto DeliveryPerformancePercents fornecem informações sobre as mensagens que foram entregues com êxito. Ele pode responder a perguntas como "Minhas mensagens foram atrasadas?" 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 deve ajustar a taxa na qual você está enviando 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

Como esses dados diferem dos dados exportados para o BigQuery?

A exportação do BigQuery fornece registros de mensagens individuais sobre a aceitação de mensagens pelo back-end do FCM e a entrega de mensagens no SDK no 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 Firebase Cloud Messaging Data API fornece detalhes agregados sobre o que acontece especificamente na camada de transporte do Android (ou Etapa 3 da arquitetura do FCM ). Esses dados fornecem informações específicas sobre a entrega de mensagens de back-ends do FCM para o Android SDK. É particularmente útil para mostrar tendências de por que as mensagens foram atrasadas ou descartadas 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á ausente 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 essa API sofrerão atrasos 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 posterior. Além disso, os dados são fornecidos no melhor esforço. No caso de uma interrupção de dados, o FCM trabalhará para corrigir o avanço e não preencherá os dados após a correção do problema. 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 destinam-se a fornecer informações sobre as amplas tendências de entrega de mensagens. No entanto, eles não fornecem 100% de cobertura 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 usam. 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, de acordo com suas preferências.

Arredondamento e mínimos

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

Exportação de dados do BigQuery

Você pode exportar seus dados de 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 por meio da API ou do compositor de notificações.

Para mensagens enviadas para dispositivos com as seguintes versões mínimas do SDK do FCM, você tem a opção adicional de habilitar 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 os detalhes sobre como habilitar 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 BigQuery na parte inferior da página.

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

      Esta página exibe as 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 mais informações.

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

  • O Firebase exporta seus dados para o BigQuery. Observe que a propagação inicial de 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 pode ser alterado, mas você pode 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 local do conjunto de dados .

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

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

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

Ativar exportação de dados de entrega de mensagens

Dispositivos iOS com o FCM SDK 8.6.0 ou superior podem habilitar 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 em 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 .

Habilite a 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 habilitar 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 .

Ative a 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 direcionar tokens obsoletos ou registros inativos pode inflar algumas dessas estatísticas.

O esquema da tabela exportada é:

_PARTITIONTIME TIMESTAMP Essa 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').
event_timestamp TIMESTAMP Registro de data e hora do evento conforme registrado pelo servidor
projeto número INTEIRO O número do projeto identifica o projeto que enviou a mensagem
message_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_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. O tópico é usado para identificar a mensagem original para um envio de tópico ou campanha; as mensagens subsequentes são uma notificação ou uma mensagem de dados.
sdk_platform 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 é enfileirada para eventual entrega
prioridade INTEIRO A prioridade da mensagem. Os valores válidos são "normal" e "alto". No iOS, eles 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 do FCM se o dispositivo estiver offline
tema CORDA O nome do tópico para o qual uma 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 FCM SDK do aplicativo no dispositivo. Por padrão, este campo não é propagado. Para habilitar, siga as instruções fornecidas em setDeliveryMetricsExportToBigQuery(boolean) .
  • MISSING_REGISTRATIONS: a solicitação foi rejeitada devido a um registro ausente;
  • 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: o pedido de envio de uma mensagem foi rejeitado devido a uma incompatibilidade entre o id do remetente que envia a mensagem e o declarado para o ponto final;
  • QUOTA_EXCEEDED: a solicitação de envio de mensagem foi rejeitada por cota insuficiente;
  • INVALID_REGISTRATION: o pedido de envio de mensagem foi rejeitado devido a um registo inválido;
  • INVALID_PACKAGE_NAME: o pedido de envio de uma mensagem foi rejeitado devido a um nome de pacote inválido;
  • INVALID_APNS_CREDENTIAL: o pedido de envio de uma mensagem foi rejeitado devido a um certificado APNS inválido;
  • INVALID_PARAMETERS: o pedido de envio de uma mensagem foi rejeitado devido a parâmetros inválidos;
  • PAYLOAD_TOO_LARGE: o pedido de envio de mensagem foi rejeitado devido a um payload maior que o limite;
  • AUTHENTICATION_ERROR: o pedido de envio de uma mensagem foi rejeitado devido a um erro de autenticação (verifique a API Key utilizada para enviar a mensagem);
  • INVALID_TTL: o pedido de envio de uma mensagem foi rejeitado devido a um TTL inválido.
analytics_label CORDA Com a API HTTP v1 , o rótulo de análise pode ser definido ao enviar a mensagem, para marcar a mensagem para fins de análise

O que você pode fazer com os dados exportados?

As seções a seguir oferecem exemplos de consultas que podem ser executadas no BigQuery em relação aos 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 de aplicativos exclusivas 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

A hora de início do fanout é quando a solicitação original é recebida e a hora de término é a hora em que a última mensagem individual destinada 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 a 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;

Acompanhe 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;