A API Google Health permite que seu aplicativo receba notificações em tempo real quando os dados de saúde de um usuário mudam. Em vez de consultar as mudanças, seu servidor recebe um webhook de solicitação HTTPS POST assim que os dados estão disponíveis na API Google Health.
Tipos de dados compatíveis
As notificações de webhook são compatíveis com os seguintes tipos de dados:
| Tipo de dado | Operações disponíveis |
Escopo |
|---|---|---|
|
Minutos de atividade
dataType:
active-minutesparâmetro de filtro: active_minutes
Tipo de registro: Intervalo
Resolução de armazenamento : 1 minuto
Dispositivos compatíveis
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Minutos na faixa ativa
dataType:
active-zone-minutesparâmetro de filtro: active_zone_minutes
Tipo de registro: Intervalo
Resolução de armazenamento : 1 minuto
Dispositivos compatíveis
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Nível de atividade
dataType:
activity-levelparâmetro de filtro: activity_level
Tipo de registro: Intervalo
|
list, reconcile | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Altitude
dataType:
altitudeparâmetro de filtro: altitude
Tipo de registro: Intervalo
Resolução de armazenamento : 1 minuto
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Glicose no sangue
dataType:
blood-glucoseparâmetro de filtro: blood_glucose
Tipo de registro : amostra
|
list, get, reconcile, rollup, dailyRollup | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Gordura corporal
dataType:
body-fatparâmetro de filtro: body_fat
Tipo de registro : amostra
Dispositivos compatíveis
|
list, get, reconcile, rollup, dailyRollup, create, update, batchDelete | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Calorias na faixa de frequência cardíaca
dataType:
calories-in-heart-rate-zoneparâmetro de filtro: calories_in_heart_rate_zone
Tipo de registro: Intervalo
Resolução de armazenamento : 1 minuto
|
rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Variabilidade da frequência cardíaca diária
dataType:
daily-heart-rate-variabilityparâmetro de filtro: daily_heart_rate_variability
Tipo de registro : diário
Dispositivos compatíveis
|
list, reconcile | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Zonas de frequência cardíaca diárias
dataType:
daily-heart-rate-zonesparâmetro de filtro: daily_heart_rate_zones
Tipo de registro : diário
|
list, reconcile | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Saturação de oxigênio diária
dataType:
daily-oxygen-saturationparâmetro de filtro: daily_oxygen_saturation
Tipo de registro : diário
Dispositivos compatíveis
|
list, reconcile | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Ritmo respiratório diário
dataType:
daily-respiratory-rateparâmetro de filtro: daily_respiratory_rate
Tipo de registro : diário
Dispositivos compatíveis
|
list, reconcile | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Frequência cardíaca diária em repouso
dataType:
daily-resting-heart-rateparâmetro de filtro: daily_resting_heart_rate
Tipo de registro : diário
Dispositivos compatíveis
|
list, reconcile | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Variações diárias da temperatura durante o sono
dataType:
daily-sleep-temperature-derivationsparâmetro de filtro: daily_sleep_temperature_derivations
Tipo de registro : diário
Dispositivos compatíveis
|
list, reconcile | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Distância
dataType:
distanceparâmetro de filtro: distance
Tipo de registro: Intervalo
Resolução de armazenamento : 1 minuto
Dispositivos compatíveis
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Exercício
dataType:
exerciseparâmetro de filtro: exercise
Tipo de registro : sessão
Dispositivos compatíveis
|
list, get, reconcile, create, update, batchDelete | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Andares
dataType:
floorsparâmetro de filtro: floors
Tipo de registro: Intervalo
Resolução de armazenamento : 1 minuto
|
reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Frequência cardíaca
dataType:
heart-rateparâmetro de filtro: heart_rate
Tipo de registro : amostra
Resolução de armazenamento : 1 segundo
Dispositivos compatíveis
|
list, reconcile, rollup, dailyRollup | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Variabilidade da frequência cardíaca
dataType:
heart-rate-variabilityparâmetro de filtro: heart_rate_variability
Tipo de registro : amostra
Dispositivos compatíveis
|
list, reconcile | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Altura
|
list, get, reconcile, create, update, batchDelete | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
Registro de hidratação
dataType:
hydration-logparâmetro de filtro: hydration_log
Tipo de registro : sessão
|
list, get, reconcile, rollup, dailyRollup, create, update, batchDelete | .nutrition.readonly.nutrition.writeonly |
|
Registro de alimentação
dataType:
nutrition-logparâmetro de filtro: nutrition_log
Tipo de registro : sessão
Dispositivos compatíveis
|
list, get, reconcile, rollup, dailyRollup, create, update, batchDelete | .nutrition.readonly.nutrition.writeonly |
|
Resumo do sono: ritmo respiratório
dataType:
respiratory-rate-sleep-summaryparâmetro de filtro: respiratory_rate_sleep_summary
Tipo de registro : amostra
Dispositivos compatíveis
|
list, reconcile | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
|
VO₂ máx. da corrida
dataType:
run-vo2-maxparâmetro de filtro: run_vo2_max
Tipo de registro : amostra
Dispositivos compatíveis
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Período sedentário
dataType:
sedentary-periodparâmetro de filtro: sedentary_period
Tipo de registro: Intervalo
Dispositivos compatíveis
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Sono
dataType:
sleepparâmetro de filtro: sleep
Tipo de registro : sessão
Dispositivos compatíveis
|
list, get, reconcile, create, update, batchDelete | .sleep.readonly.sleep.writeonly |
|
Etapas
dataType:
stepsparâmetro de filtro: steps
Tipo de registro: Intervalo
Resolução de armazenamento : 1 minuto
Dispositivos compatíveis
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Tempo na faixa de frequência cardíaca
dataType:
time-in-heart-rate-zoneparâmetro de filtro: time_in_heart_rate_zone
Tipo de registro: Intervalo
Resolução de armazenamento : 1 minuto
|
list, reconcile, rollup, dailyRollup | .activity_and_fitness.readonly.activity_and_fitness.writeonly |
|
Peso
dataType:
weightparâmetro de filtro: weight
Tipo de registro : amostra
Dispositivos compatíveis
|
list, get, reconcile, rollup, dailyRollup, create, update, batchDelete | .health_metrics_and_measurements.readonly.health_metrics_and_measurements.writeonly |
Escopos de notificações
As notificações são enviadas para esses tipos de dados somente quando um usuário concede consentimento para um dos escopos correspondentes:
- Atividade, que abrange os tipos de dados de minutos de atividade, passos, altitude, distância e
andares:
https://www.googleapis.com/auth/googlehealth.activity_and_fitness.readonlyhttps://www.googleapis.com/auth/googlehealth.activity_and_fitness.writeonly
- Métricas de saúde, que abrange o tipo de dados de peso:
https://www.googleapis.com/auth/googlehealth.health_metrics_and_measurements.readonlyhttps://www.googleapis.com/auth/googlehealth.health_metrics_and_measurements.writeonly
- Sono, que abrange o tipo de dados de sono:
https://www.googleapis.com/auth/googlehealth.sleep.readonlyhttps://www.googleapis.com/auth/googlehealth.sleep.writeonly
Contas de serviço do IAM
Embora não seja obrigatório, recomendamos usar uma conta de serviço do IAM ao configurar assinantes para a API Google Health. As contas de serviço oferecem melhor segurança para cargas de trabalho de aplicativos em comparação com as contas de usuário padrão com os seguintes recursos:
- Credenciais automatizadas de curta duração:quando anexadas a um ambiente de execução do Google Cloud (como Compute Engine, Cloud Run ou Google Kubernetes Engine), as contas de serviço recebem e alternam automaticamente credenciais seguras de curta duração. Isso elimina os riscos de gerenciar e armazenar chaves estáticas persistentes.
- Princípio de privilégio mínimo:as contas de serviço fornecem identidades dedicadas para cargas de trabalho. É possível conceder a eles apenas as permissões específicas necessárias para gerenciar endpoints de assinantes, evitando um acesso mais amplo aos recursos do Google Cloud.
- Independência do ciclo de vida:as contas de serviço operam de forma independente da conta de qualquer usuário individual, garantindo que as mudanças de pessoal não afetem a estabilidade da autenticação a longo prazo.
Configurar uma conta de serviço
Para configurar o aplicativo assinante para autenticação usando uma conta de serviço:
- Crie uma conta de serviço:no console do Google Cloud, acesse a página IAM e administrador do projeto para criar uma nova conta de serviço gerenciada pelo usuário.
- Conceda os papéis do IAM necessários:atribua à conta de serviço os papéis adequados para gerenciar assinantes no seu projeto do Google Cloud.
- Anexe a conta de serviço à carga de trabalho:configure o ambiente que hospeda a lógica do assinante para ser executado como a nova conta de serviço.
Isso permite que o código do aplicativo (como as bibliotecas de cliente da API do Google) detecte e use automaticamente as credenciais de curta duração da conta de serviço ao chamar a API REST
projects.subscribers.
Papéis de CPE
Para gerenciar assinantes ou assinaturas da API Google Health, é preciso conceder o papel apropriado à conta de serviço personificada que faz as chamadas de API. Dependendo do nível de acesso necessário, atribua um dos seguintes papéis:
- Leitura da API Google Health
- Editor da API Google Health
- Administrador da API Google Health
Saiba mais sobre os papéis e as permissões do IAM da API Google Health.
Gerenciar inscritos
Antes de receber Notificações, é preciso registrar um assinante, que representa o Notification endpoints do aplicativo. É possível gerenciar assinantes
usando a API REST disponível em
projects.subscribers.
O endpoint do assinante precisa usar HTTPS (TLSv1.2+) e ser acessível publicamente.
Durante a criação e as atualizações de assinantes, a API Google Health realiza um
desafio de verificação para garantir que você seja o proprietário do URI do endpoint. Se a verificação
falhar, as operações de criação e atualização do assinante falharão com um
FailedPreconditionException.
Criar um assinante
Para registrar um novo assinante no projeto, use o endpoint
create. Você precisa informar:
project-id: o número do projeto em que a conta de serviço do webhook foi criada.subscriberId: um identificador exclusivo que você fornece para o assinante. Esse ID precisa ter entre 4 e 36 caracteres e corresponder à expressão regular ([a-z]([a-z0-9-]{2,34}[a-z0-9])).endpointUri: o URL de destino das notificações de webhook.subscriberConfigs: os tipos de dados para os quais você quer receber notificações e a política de assinatura de cada um.endpointAuthorization: o mecanismo de autorização do endpoint. Ele precisa conter umsecretfornecido por você. O valor desecreté enviado no cabeçalhoAuthorizationcom cada mensagem de notificação. Use esse token para verificar se as solicitações recebidas são da API Google Health. Por exemplo, é possível definirsecretcomoBearer R4nd0m5tr1ng123para autenticação de portador ouBasic dXNlcjpwYXNzd29yZA==para autenticação básica.
Em subscriberConfigs, é necessário definir subscriptionCreatePolicy para cada tipo de
dados. Defina como AUTOMATIC para usar assinaturas automáticas ou MANUAL se você
pretende gerenciar as assinaturas de usuários por conta própria. Consulte
assinaturas automáticas e
assinaturas manuais para mais detalhes sobre cada opção.
Solicitação
POST https://health.googleapis.com/v4/projects/project-id/subscribers?subscriberId=subscriber-id
{
"endpointUri": "https://myapp.com/webhooks/health",
"subscriberConfigs": [
{
"dataTypes": ["steps", "altitude", "distance", "floors", "weight"],
"subscriptionCreatePolicy": "AUTOMATIC"
},
{
"dataTypes": ["sleep"],
"subscriptionCreatePolicy": "MANUAL"
}
],
"endpointAuthorization": {
"secret": "Bearer example-secret-token"
}
}Resposta
{
"name": "projects/project-id/subscribers/subscriber-id",
"endpointUri": "https://myapp.com/webhooks/health",
"subscriberConfigs": [
{
"dataTypes": ["steps", "altitude", "distance", "floors", "weight"],
"subscriptionCreatePolicy": "AUTOMATIC"
},
{
"dataTypes": ["sleep"],
"subscriptionCreatePolicy": "MANUAL"
}
]
}Listar assinantes
Use o endpoint list para recuperar todos os assinantes registrados no seu projeto.
Solicitação
GET https://health.googleapis.com/v4/projects/project-id/subscribers
Resposta
{
"subscribers": [
{
"name": "projects/project-id/subscribers/subscriber-id",
"endpointUri": "https://myapp.com/webhooks/health",
"subscriberConfigs": [
{
"dataTypes": ["steps", "altitude", "distance", "floors", "weight"],
"subscriptionCreatePolicy": "AUTOMATIC"
},
{
"dataTypes": ["sleep"],
"subscriptionCreatePolicy": "MANUAL"
}
],
"endpointAuthorization": {
"authorizationTokenSet": true
}
}
],
"totalSize": 1
}Atualizar um assinante
Use o endpoint patch
para atualizar um assinante no seu projeto. Os campos que podem ser atualizados são
endpointUri, subscriberConfigs e endpointAuthorization.
Para atualizar campos, forneça um parâmetro de consulta updateMask e um corpo de
solicitação. O updateMask precisa conter uma lista separada por vírgulas de nomes de campos que
você quer atualizar, usando letras concatenadas para nomes de campos
(por exemplo, endpointUri). O corpo da solicitação precisa conter um objeto Subscriber
parcial com os novos valores para os campos que você quer atualizar. Somente os campos especificados
em updateMask são atualizados. Se você fornecer campos no corpo da solicitação que não estão em updateMask, eles serão ignorados.
Se você atualizar endpointUri ou endpointAuthorization, a verificação de endpoints será
realizada. Veja mais detalhes em Verificação de endpoints.
Ao atualizar subscriberConfigs, observe que é uma substituição completa, não uma
mesclagem. Se subscriberConfigs estiver incluído em updateMask, todas as configurações armazenadas para esse assinante serão substituídas pela lista fornecida no corpo da solicitação. Para adicionar ou remover uma configuração, é necessário fornecer o conjunto completo de
configurações. Se você estiver atualizando outros campos e quiser manter as configurações
atuais, omita subscriberConfigs de updateMask.
Solicitação
PATCH https://health.googleapis.com/v4/projects/project-id/subscribers/subscriber-id?updateMask=endpointUri
{
"endpointUri": "https://myapp.com/new-webhooks/health"
}Resposta
{
"name": "projects/project-id/subscribers/subscriber-id",
"endpointUri": "https://myapp.com/new-webhooks/health",
"subscriberConfigs": [
{
"dataTypes": ["steps", "altitude", "distance", "floors", "weight"],
"subscriptionCreatePolicy": "AUTOMATIC"
},
{
"dataTypes": ["sleep"],
"subscriptionCreatePolicy": "MANUAL"
}
]
}Excluir um assinante
Use o endpoint delete para remover um assinante do seu projeto. Depois de excluído, o assinante
não vai mais receber notificações.
Solicitação
DELETE https://health.googleapis.com/v4/projects/project-id/subscribers/subscriber-id
Resposta
Um corpo de resposta vazio com o status HTTP "200 OK" será retornado se a exclusão for bem-sucedida.{}Verificação de endpoints
Para garantir a segurança e a confiabilidade da entrega de notificações, a API Google
Health realiza um handshake de verificação em duas etapas obrigatório sempre que você
cria um assinante ou atualiza a configuração do endpoint dele (endpointUri ou
endpointAuthorization). Esse processo é realizado de forma síncrona durante a
chamada de API. O serviço envia duas solicitações POST automatizadas para o URI do endpoint,
usando o User-Agent Google-Health-API-Webhooks, com o corpo JSON
{"type": "verification"}.
- Handshake autorizado: a primeira solicitação é enviada com o cabeçalho
Authorizationconfigurado. Seu servidor precisa responder com um status200 OKou201 Created. - Desafio não autorizado: a segunda solicitação é enviada sem credenciais.
Seu servidor precisa responder com um status
401 Unauthorizedou403 Forbidden.
Esse handshake confirma que o endpoint está ativo e aplicando a segurança
corretamente. Se uma das etapas falhar, a solicitação de API vai falhar com um erro FAILED_PRECONDITION. Somente após esse handshake ser bem-sucedido, o
assinante é salvo e ativado para receber notificações de dados de saúde.
Rotação de chaves
Se você precisar alternar as chaves de endpointAuthorization, siga estas etapas:
- Configure o endpoint para aceitar os valores antigos e novos de
endpointAuthorization. - Atualize a configuração do assinante com o novo valor de
endpointAuthorizationusando uma solicitaçãopatchcom?updateMask=endpointAuthorization. - Configure o endpoint para aceitar apenas o novo valor
endpointAuthorizationdepois de confirmar que a etapa 2 foi concluída.
Assinaturas do usuário
A API Google Health ajuda a gerenciar as assinaturas de usuários de forma eficiente, reduzindo a necessidade de registro manual durante a integração.
Assinaturas automáticas
Recomendamos o uso de assinaturas automáticas. Para ativar esse recurso, defina
subscriptionCreatePolicy como AUTOMATIC no subscriberConfigs para os
tipos de dados específicos. Os dataTypes que você especifica com uma política AUTOMATIC são
os mesmos tipos de dados para os quais a API Google Health envia notificações,
desde que o consentimento do usuário também seja concedido para esses tipos de dados.
Quando um usuário concede consentimento ao aplicativo para escopos que correspondem a tipos de dados
com uma política AUTOMATIC, a API Google Health rastreia e
envia automaticamente notificações para os tipos de dados resultantes da interseção
entre os tipos de dados consentidos pelo usuário e os tipos de dados de configuração de assinante automático
para esse usuário. As notificações são enviadas ao seu endpoint
sempre que o usuário gera novos dados para esses tipos. Isso funciona para usuários que
concedem consentimento antes ou depois de você criar o assinante. As notificações
não são preenchidas para dados gerados antes da criação do assinante.
Se um usuário revogar o consentimento, as notificações dos tipos de dados correspondentes vão parar. As assinaturas automáticas são gerenciadas pelo Google e não podem ser listadas ou excluídas individualmente. Elas são removidas apenas quando o assinante pai é excluído.
Assinaturas manuais
Se o assinante estiver configurado com um subscription_create_policy MANUAL
para tipos de dados específicos, você precisará criar e gerenciar explicitamente as assinaturas
de cada usuário. Uma assinatura vincula um usuário específico ao seu endpoint de assinante para um conjunto definido de tipos de dados. Os desenvolvedores podem usar APIs específicas para:
- Criar assinaturas (manuais) por
healthUserId: cria uma nova assinatura para um usuário específico. Esse método exige que o assinante tenha umSubscriptionCreatePolicydefinido comoMANUALpara os tipos de dados solicitados. - Atualizar assinatura (manual): atualiza os tipos de dados de uma assinatura de usuário existente.
- Excluir assinatura (manual): exclui uma assinatura de usuário específica. Depois de excluído, o endpoint do assinante não vai mais receber notificações para esse usuário sobre os tipos de dados associados.
- Listar assinaturas (manual): lista todas as assinaturas ativas de um determinado assinante. É possível filtrar os resultados por usuário ou tipo de dados.
Notificações
Quando os dados de um usuário mudam para um tipo de dado inscrito, a API Google Health envia uma solicitação POST HTTPS para o URL do endpoint do inscrito.
Formato da notificação
O payload da notificação contém detalhes sobre a mudança de dados, incluindo o ID do usuário, o tipo de dados e os intervalos de tempo, que podem ser usados para consultar os dados atualizados. Quando várias atualizações ocorrem ou são agregadas, as notificações são agrupadas em lote e entregues como uma matriz JSON de objetos de notificação.
Quando uma notificação de webhook é acionada, a API Google Health envia uma solicitação HTTPS POST ao endpoint do assinante com o seguinte formato:
Upsert (dados de série temporal)
POST https://your-endpoint-uri
Authorization: Bearer example-secret-token
GOOGLE-HEALTH-API-SIGNATURE: signature-value
Content-Type: application/json
{
"data": {
"version": "1",
"clientProvidedSubscriptionName": "subscription-name",
"healthUserId": "health-user-id",
"operation": "UPSERT",
"dataType": "steps",
"intervals": [
{
"physicalTimeInterval": {
"startTime": "2026-03-08T01:29:00Z",
"endTime": "2026-03-08T01:34:00Z"
},
"civilDateTimeInterval": {
"startDateTime": {
"date": {
"year": 2026,
"month": 3,
"day": 7
},
"time": {
"hours": 17,
"minutes": 29
}
},
"endDateTime": {
"date": {
"year": 2026,
"month": 3,
"day": 7
},
"time": {
"hours": 17,
"minutes": 34
}
}
},
"civilIso8601TimeInterval": {
"startTime": "2026-03-07T17:29:00",
"endTime": "2026-03-07T17:34:00"
}
}
]
}
}Excluir (dados de série temporal)
POST https://your-endpoint-uri
Authorization: Bearer example-secret-token
GOOGLE-HEALTH-API-SIGNATURE: signature-value
Content-Type: application/json
[
{
"data": {
"version": "1",
"clientProvidedSubscriptionName": "subscription-name",
"healthUserId": "health-user-id",
"operation": "DELETE",
"dataType": "steps",
"intervals": [
{
"physicalTimeInterval": {
"startTime": "2026-08-12T12:00:00Z",
"endTime": "2026-08-12T12:30:00Z"
},
"civilIso8601TimeInterval": {
"startTime": "2026-08-12T12:00:00",
"endTime": "2026-08-12T12:30:00"
}
}
]
}
}
]Excluir (dados identificáveis)
POST https://your-endpoint-uri
Authorization: Bearer example-secret-token
GOOGLE-HEALTH-API-SIGNATURE: signature-value
Content-Type: application/json
{
"data": {
"version": "1",
"clientProvidedSubscriptionName": "subscription-name",
"healthUserId": "health-user-id",
"operation": "DELETE",
"dataType": "sleep",
"intervals": [
{
"physicalTimeInterval": {
"startTime": "2026-08-12T22:00:00Z",
"endTime": "2026-08-13T06:00:00Z"
},
"civilIso8601TimeInterval": {
"startTime": "2026-08-12T22:00:00",
"endTime": "2026-08-13T06:00:00"
}
}
],
"recordId": "9875412"
}
}Lote (vários usuários e tipos de dados)
POST https://your-endpoint-uri
Authorization: Bearer example-secret-token
GOOGLE-HEALTH-API-SIGNATURE: signature-value
Content-Type: application/json
[
{
"data": {
"version": "1",
"clientProvidedSubscriptionName": "auto-project-number-subscriber-id-WEBHOOK_DATA_TYPE_STEPS",
"healthUserId": "health-user-id-1",
"operation": "UPSERT",
"dataType": "steps",
"intervals": [
{
"physicalTimeInterval": {
"startTime": "2026-03-08T01:29:00Z",
"endTime": "2026-03-08T01:34:00Z"
},
"civilDateTimeInterval": {
"startDateTime": {
"date": {
"year": 2026,
"month": 3,
"day": 7
},
"time": {
"hours": 17,
"minutes": 29
}
},
"endDateTime": {
"date": {
"year": 2026,
"month": 3,
"day": 7
},
"time": {
"hours": 17,
"minutes": 34
}
}
},
"civilIso8601TimeInterval": {
"startTime": "2026-03-07T17:29:00",
"endTime": "2026-03-07T17:34:00"
}
}
]
}
},
{
"data": {
"version": "1",
"clientProvidedSubscriptionName": "auto-project-number-subscriber-id-WEBHOOK_DATA_TYPE_WEIGHT",
"healthUserId": "health-user-id-1",
"operation": "UPSERT",
"dataType": "weight",
"intervals": [
{
"physicalTimeInterval": {
"startTime": "2026-03-07T15:14:00Z",
"endTime": "2026-03-07T15:16:00Z"
},
"civilDateTimeInterval": {
"startDateTime": {
"date": {
"year": 2026,
"month": 3,
"day": 7
},
"time": {
"hours": 7,
"minutes": 14
}
},
"endDateTime": {
"date": {
"year": 2026,
"month": 3,
"day": 7
},
"time": {
"hours": 7,
"minutes": 16
}
}
},
"civilIso8601TimeInterval": {
"startTime": "2026-03-07T07:14:00",
"endTime": "2026-03-07T07:16:00"
}
}
]
}
},
{
"data": {
"version": "1",
"clientProvidedSubscriptionName": "auto-project-number-subscriber-id-WEBHOOK_DATA_TYPE_STEPS",
"healthUserId": "health-user-id-2",
"operation": "UPSERT",
"dataType": "steps",
"intervals": [
{
"civilDateTimeInterval": {
"startDateTime": {
"date": {
"year": 2026,
"month": 8,
"day": 20
},
"time": {
"hours": 0,
"minutes": 0
}
},
"endDateTime": {
"date": {
"year": 2026,
"month": 8,
"day": 21
},
"time": {
"hours": 0,
"minutes": 0
}
}
},
"civilIso8601TimeInterval": {
"startTime": "2026-08-20T00:00:00",
"endTime": "2026-08-21T00:00:00"
}
}
]
}
},
{
"data": {
"version": "1",
"clientProvidedSubscriptionName": "subscription-name",
"healthUserId": "health-user-id-2",
"operation": "UPSERT",
"dataType": "sleep",
"intervals": [
{
"physicalTimeInterval": {
"startTime": "2026-08-12T22:00:00Z",
"endTime": "2026-08-13T06:00:00Z"
},
"civilDateTimeInterval": {
"startDateTime": {
"date": {
"year": 2026,
"month": 8,
"day": 12
},
"time": {
"hours": 22,
"minutes": 0
}
},
"endDateTime": {
"date": {
"year": 2026,
"month": 8,
"day": 13
},
"time": {
"hours": 6,
"minutes": 0
}
}
},
"civilIso8601TimeInterval": {
"startTime": "2026-08-12T22:00:00",
"endTime": "2026-08-13T06:00:00"
}
}
],
"recordId": "9875412"
}
}
]O campo operation indica o tipo de mudança que acionou a
notificação:
UPSERT: enviado para qualquer adição ou modificação de dados.DELETE: enviado quando um usuário exclui dados explicitamente (por exemplo, excluindodataPointsusando o app Fitbit ou realizando uma exclusão em lote usando a API). As notificações de webhook não são enviadas para eventos de exclusão no nível do sistema, como um usuário revogando permissões do aplicativo ou excluindo a conta do Fitbit.
Recomendamos que a lógica de processamento de notificações seja idempotente, especialmente para
operações UPSERT, já que as novas tentativas podem causar o envio de notificações duplicadas.
O campo clientProvidedSubscriptionName é um identificador exclusivo:
- Para assinaturas com uma política
MANUAL, esse campo contém o nome persistente da assinatura fornecido pelo desenvolvedor e especificado quando a assinatura é criada. Isso fornece um ID estável para gerenciar assinaturas manuais. - Para assinaturas criadas com uma política
AUTOMATIC, a API Google Health gera e atribui automaticamente um identificador no formato:auto-<project-number>-<subscriber-id>-<WEBHOOK_DATA_TYPE_TOKEN>, em que:<project-number>é o número do projeto na nuvem do Google Cloud do desenvolvedor (por exemplo,362978535920).<subscriber-id>é o ID de registro / nome do assinante (por exemplo,google-health-sync).<WEBHOOK_DATA_TYPE_TOKEN>é o token que representa o tipo de dados específico. Consulte Vocabulário de tokens de tipo de dados de webhook para todos os tokens possíveis.
Incluir clientProvidedSubscriptionName para políticas manuais e
automáticas garante um formato de payload de notificação consistente em todos os
tipos de assinatura.
Mapeamento de ID do usuário
O healthUserId é um identificador da API Google Health para o usuário cujos dados foram alterados. Se o aplicativo for compatível com vários usuários, você poderá receber
notificações de qualquer usuário que tenha concedido consentimento ao aplicativo. Ao
receber uma notificação, use healthUserId para identificar de qual usuário
os dados foram alterados. Assim, você pode usar as credenciais OAuth para consultar os dados dele.
Para mapear as credenciais OAuth de um usuário para o healthUserId, use o endpoint getIdentity. Chame
esse endpoint com as credenciais de um usuário durante a integração para recuperar
o healthUserId dele e armazenar esse mapeamento. Esse mapeamento não muda com o tempo, então pode ser armazenado em cache indefinidamente. Para ver um exemplo, consulte
Receber o ID do usuário. Isso permite que você selecione as credenciais de usuário corretas ao consultar dados com base no healthUserId em uma notificação.
Intervalos de tempo
A matriz intervals contém um ou mais objetos que representam os intervalos de tempo dos dados modificados:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
physicalTimeInterval |
object |
Não | O intervalo de tempo físico UTC dos dados modificados (formato YYYY-MM-DDTHH:MM:SSZ). Preenchido para métricas intradiárias; omitido para resumos diários e atividades de dia inteiro. |
civilDateTimeInterval |
object |
Sim | A data e o intervalo de tempo do calendário civil (local) dos dados modificados, sem compensações de UTC ou fuso horário. |
civilIso8601TimeInterval |
object |
Sim | O intervalo de tempo civil (local) representado como uma string ISO 8601 (formato YYYY-MM-DDTHH:MM:SS) sem deslocamentos de UTC ou fuso horário. |
Para atividades de dia inteiro e agregados diários, o campo physicalTimeInterval
é omitido intencionalmente do payload de notificação. Isso ocorre porque
os eventos de dia inteiro são agrupados pela data civil nominal, e as durações de tempo físico
podem ser imprecisas ao cruzar fusos horários. Ao processar eventos de dia inteiro, use os campos civilDateTimeInterval ou
civilIso8601TimeInterval como prática recomendada. Para saber como consultar esses pontos de dados usando filtros de horário civil, consulte Filtrar dados por um horário de início civil de intervalo.
Veja a seguir um exemplo de payload de notificação para uma atividade de dia inteiro em que
o campo physicalTimeInterval é omitido:
{
"data": {
"version": "1",
"clientProvidedSubscriptionName": "auto-503461148219-lindasubscriber-WEBHOOK_DATA_TYPE_STEPS",
"healthUserId": "343864966078399341",
"operation": "UPSERT",
"dataType": "steps",
"intervals": [
{
"civilDateTimeInterval": {
"startDateTime": {
"date": {
"year": 2026,
"month": 7,
"day": 14
},
"time": {}
},
"endDateTime": {
"date": {
"year": 2026,
"month": 7,
"day": 15
},
"time": {}
}
},
"civilIso8601TimeInterval": {
"startTime": "2026-07-14T00:00:00",
"endTime": "2026-07-15T00:00:00"
}
}
]
}
}Vocabulário de tokens de tipo de dados de webhook
Ao usar uma política AUTOMATIC, o clientProvidedSubscriptionName gerado
contém um dos seguintes tokens de sufixo WEBHOOK_DATA_TYPE_* em maiúsculas,
dependendo do tipo de dados:
| Token de tipo de dados do webhook | Tipo de dados correspondente |
|---|---|
WEBHOOK_DATA_TYPE_ACTIVE_MINUTES |
active-minutes |
WEBHOOK_DATA_TYPE_ACTIVE_ZONE_MINUTES |
active-zone-minutes |
WEBHOOK_DATA_TYPE_ACTIVITY_LEVEL |
activity-level |
WEBHOOK_DATA_TYPE_ALTITUDE |
altitude |
WEBHOOK_DATA_TYPE_BLOOD_GLUCOSE |
blood-glucose |
WEBHOOK_DATA_TYPE_BODY_FAT |
body-fat |
WEBHOOK_DATA_TYPE_CALORIES_IN_HEART_RATE_ZONE |
calories-in-heart-rate-zone |
WEBHOOK_DATA_TYPE_DAILY_HEART_RATE_VARIABILITY |
daily-heart-rate-variability |
WEBHOOK_DATA_TYPE_DAILY_HEART_RATE_ZONES |
daily-heart-rate-zones |
WEBHOOK_DATA_TYPE_DAILY_OXYGEN_SATURATION |
daily-oxygen-saturation |
WEBHOOK_DATA_TYPE_DAILY_RESPIRATORY_RATE |
daily-respiratory-rate |
WEBHOOK_DATA_TYPE_DAILY_RESTING_HEART_RATE |
daily-resting-heart-rate |
WEBHOOK_DATA_TYPE_DAILY_SLEEP_TEMPERATURE_DERIVATIONS |
daily-sleep-temperature-derivations |
WEBHOOK_DATA_TYPE_DISTANCE |
distance |
WEBHOOK_DATA_TYPE_EXERCISE |
exercise |
WEBHOOK_DATA_TYPE_FLOORS |
floors |
WEBHOOK_DATA_TYPE_HEART_RATE |
heart-rate |
WEBHOOK_DATA_TYPE_HEART_RATE_VARIABILITY |
heart-rate-variability |
WEBHOOK_DATA_TYPE_HEIGHT |
height |
WEBHOOK_DATA_TYPE_HYDRATION_LOG |
hydration-log |
WEBHOOK_DATA_TYPE_NUTRITION_LOG |
nutrition-log |
WEBHOOK_DATA_TYPE_RESPIRATORY_RATE_SLEEP_SUMMARY |
respiratory-rate-sleep-summary |
WEBHOOK_DATA_TYPE_RUN_VO2_MAX |
run-vo2-max |
WEBHOOK_DATA_TYPE_SEDENTARY_PERIOD |
sedentary-period |
WEBHOOK_DATA_TYPE_SLEEP |
sleep |
WEBHOOK_DATA_TYPE_STEPS |
steps |
WEBHOOK_DATA_TYPE_TIME_IN_HEART_RATE_ZONE |
time-in-heart-rate-zone |
WEBHOOK_DATA_TYPE_WEIGHT |
weight |
WEBHOOK_DATA_TYPE_USER_DELETED |
user-deleted (evento do sistema) |
WEBHOOK_DATA_TYPE_USER_REVOKED_ACCESS |
user-revoked-access (evento do sistema) |
Agregação de atualizações
Para minimizar o tráfego de rede, a API Google Health agrupa as mensagens em lotes. O sistema está configurado para enviar no máximo 99 mensagens por lote, que são enviadas automaticamente à medida que ficam disponíveis.
Quando as mensagens são agrupadas em lote, o payload é entregue como uma matriz JSON que contém vários objetos de notificação. Um único lote pode incluir atualizações para vários usuários em vários tipos de dados inscritos. Para ver um exemplo de payload, consulte o exemplo Lote (vários usuários e tipos de dados) na seção "Formato de notificação".
Responder a uma notificação
O servidor precisa validar o cabeçalho Authorization, processar o handshake síncrono de
verificação de endpoint e responder imediatamente às notificações de mudança de dados
com um código de status HTTP 204 No Content. Para
evitar tempos limite, processe o payload da notificação de forma assíncrona depois de enviar
a resposta. Se a API Google Health receber qualquer outro código de status ou se o tempo limite da solicitação expirar, ela tentará enviar a notificação novamente mais tarde.
Exemplo de Node.js (Express):
app.post('/webhook-receiver', (req, res) => {
if (req.headers.authorization !== EXPECTED_SECRET) {
return res.status(401).send();
}
if (req.body && !Array.isArray(req.body) && req.body.type === 'verification') {
return res.status(200).send();
}
// 1. Immediately acknowledge the notification batch
res.status(204).send();
// 2. Normalize single-object or batched array notifications and reconcile asynchronously
const notifications = Array.isArray(req.body) ? req.body : [req.body];
setImmediate(async () => {
for (const notification of notifications) {
const { healthUserId, dataType, intervals = [] } = notification.data || {};
console.log(`Update for user ${healthUserId} of type ${dataType}`);
// Query /v4/users/${healthUserId}/dataTypes/${dataType}/dataPoints:reconcile?filter=...
// for each interval in `intervals` (using >= and < comparators; for sleep, filter on
// sleep.interval.end_time or sleep.interval.civil_end_time).
}
});
});
Para buscar os dados atualizados de cada intervalo notificado, chame o endpoint
reconcile
com um filtro para o período do intervalo. Isso
elimina automaticamente a duplicação de intervalos sobrepostos de dispositivos conectados. Para
detalhes e exemplos, consulte
Receber uma visualização reconciliada de dados de intervalo.
Há três estratégias comuns para atualizar o banco de dados depois de receber uma notificação de mudança de dados:
- Enfileiramento (recomendado): coloque as notificações recebidas em uma fila de mensagens e processe-as fora da fila com base nos recursos do sistema.
- Recuperação imediata: direcione seu back-end para chamar imediatamente a API Google Health para buscar os dados atualizados e confirmá-los no banco de dados.
- Atualizações lentas: marque os registros do banco de dados local do usuário como sujos e busque os dados atualizados da API Google Health somente quando o usuário acessar o aplicativo novamente.
A estratégia de fila permite processar dados quase em tempo real, além de ser resiliente a uma enxurrada de notificações (por exemplo, se uma interrupção de serviço fizer com que um backlog de notificações seja entregue de uma só vez). Embora seja possível processar essas filas em uma programação de lote de intervalo longo (por exemplo, a cada 3 horas) para economizar recursos, a desvantagem é que os usuários normalmente esperam que os dados sejam atualizados quase em tempo real.
Práticas recomendadas
Siga estas práticas recomendadas para garantir o processamento confiável e eficiente das atualizações de dados:
- Use a API Subscription: use a API Subscription para receber notificações quando um usuário tiver novos dados para download, em vez de consultar endpoints.
- Colocar em fila e processar notificações separadamente: ao receber notificações de assinatura, coloque-as em uma fila e processe-as separadamente com base nos recursos do seu sistema. Mantenha o endpoint do webhook leve confirmando a solicitação imediatamente.
Verificação de assinatura
Para garantir a autenticidade das notificações de webhooks, o payload JSON bruto de
cada notificação de webhook de saída é assinado com uma chave privada usando o PublicKeySign
do Tink,
fornecendo a assinatura codificada em Base64 no cabeçalho HTTP
GOOGLE-HEALTH-API-SIGNATURE na solicitação. Essas chaves de assinatura são alternadas automaticamente a cada 30
dias, e o conjunto de chaves públicas oficial correspondente é distribuído como um arquivo JSON
no URL permanente
https://www.gstatic.com/googlehealthapi/webhooks/webhooks_public_keyset.json.
Como verificar a assinatura
Usando o Tink (recomendado): os desenvolvedores podem verificar a assinatura usando a primitiva
PublicKeyVerify
do Tink. Busque o conjunto de chaves públicas no URL permanente, instancie o
primitivo PublicKeyVerify com o conjunto de chaves e verifique o cabeçalho
GOOGLE-HEALTH-API-SIGNATURE decodificado em relação ao payload JSON bruto do webhook.
Verificação manual (sem o Tink): se os desenvolvedores optarem por não usar o Tink, eles poderão verificar a assinatura manualmente seguindo estas etapas:
- Decodifica em Base64 o cabeçalho
GOOGLE-HEALTH-API-SIGNATUREpara separar o prefixo do Tink de 5 bytes (que contém um prefixo de versão de 1 byte e um keyId de 4 bytes) da assinatura real codificada em DER. - Busque o conjunto de chaves JSON em https://www.gstatic.com/googlehealthapi/webhooks/webhooks_public_keyset.json.
- Localize a chave correspondente ao keyId analisado e decodifique em Base64 o campo de valor, que contém um buffer de protocolo EcdsaPublicKey serializado.
- Extraia as coordenadas x e y big-endian (tags 3 e 4 do Protobuf) desse payload binário.
- Instancie uma chave pública ECDSA P-256 padrão em uma biblioteca de criptografia integrada usando as coordenadas x e y extraídas.
- Verifique o payload JSON bruto do webhook em relação à assinatura DER extraída usando o algoritmo SHA-256.
Status e recuperação de assinantes
Se o endpoint do assinante ficar indisponível ou retornar um código de status de erro
(qualquer coisa diferente de 204), a API Google Health vai armazenar as notificações
pendentes por até sete dias e tentar novamente o envio com espera exponencial.
Quando o endpoint estiver on-line novamente e responder com 204, a API
vai entregar automaticamente as mensagens armazenadas. As notificações com mais de
7 dias são descartadas e não podem ser recuperadas.
Erros comuns
| Código do erro | Mensagem | Descrição | Recomendação |
|---|---|---|---|
| 400 Solicitação inválida | Número de projeto inválido no nome do recurso | Ao excluir ou atualizar um assinante usando o ID do projeto do Google Cloud no URL da solicitação em vez do número do projeto. Isso se aplica às assinaturas de webhook que usam o endpoint projects.subscribers. |
Use o número do projeto do Google Cloud no URL da solicitação, não o ID do projeto. |
| 403 Proibido | O autor da chamada não tem permissão | Ao criar ou listar assinantes usando o ID do projeto do Google Cloud no URL da solicitação em vez do número do projeto. Isso se aplica às assinaturas de webhook que usam o endpoint projects.subscribers. |
Use o número do projeto do Google Cloud no URL da solicitação, não o ID do projeto. |