API A91 — captação & fornecimento
Como os aparelhos e apps enviam dados de saúde para a plataforma, e como o app do paciente e o profissional leem esses dados — sempre via Bearer token e respeitando o consentimento.
Visão geral
API REST sobre HTTPS; corpo e respostas em JSON (Content-Type: application/json). Toda chamada exige autenticação por Bearer token.
| Ambiente | Base URL |
|---|---|
| Produção | https://api.a91.health/v1 |
| Sandbox | https://sandbox.a91.health/v1 |
Autenticação
A API usa Bearer token (JWT). O cliente troca suas credenciais por um access_token de curta duração e o envia no cabeçalho Authorization de toda requisição.
1. Obter o token
Fluxo client credentials para dispositivos/serviços, ou password para login de usuário/profissional no app.
POST /v1/auth/token Content-Type: application/json { "grant_type": "client_credentials", "client_id": "dev_anel_a91", "client_secret": "••••••••••••", "scope": "dados:escrever" }
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "dados:escrever",
"refresh_token": "rt_9f2c…"
}
2. Usar o token
Inclua o token em todas as chamadas:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6...
Escopos
| Escopo | Permite | Quem usa |
|---|---|---|
| dados:escrever | Enviar medições (captação) | Anel, pulseira, balança, app |
| saude:ler | Ler resumo/leituras/bioimpedância | App do paciente, profissional |
| comunicacao | Mensagens e planos | App, profissional |
Convenções
| Tema | Regra |
|---|---|
| IDs | UUID v4. usuario_id é o pseudônimo — PII nunca trafega nestes endpoints. |
| Datas | ISO-8601 em UTC: 2026-05-24T08:12:00Z |
| Unidades | bpm, %, °C, passos, minutos (sono), kg. |
| Idempotência | Envios aceitam header Idempotency-Key; reenvio com a mesma chave não duplica. |
| Paginação | ?limit= (máx 1000) + ?cursor=; resposta traz proxima_pagina. |
| Rate limit | Headers X-RateLimit-Remaining / -Reset. Estouro → 429. |
Captação · Leituras
Envia um lote de medições do anel/pulseira (steps, heart_rate, spo2, temperature, sleep, battery). Aceita batch para economizar bateria/rede.
| Campo | Tipo | Descrição |
|---|---|---|
| usuario_id obrig | uuid | Pseudônimo do paciente. |
| equipamento_id obrig | uuid | Aparelho de origem. |
| tipo_dispositivo obrig | enum | anel · pulseira · balanca |
| metrica obrig | enum | steps · heart_rate · spo2 · temperature · sleep · battery |
| valor obrig | number | Valor medido. |
| unidade | string | bpm, %, °C… |
| ts obrig | datetime | Momento da medição (UTC). |
| payload | object | Bruto adicional do aparelho (opcional). |
POST /v1/ingest/leituras Authorization: Bearer <token> Idempotency-Key: a1f3c-2026-05-24-08 { "leituras": [ { "usuario_id": "usr_8a1f3c", "equipamento_id": "eqp_anel_03", "tipo_dispositivo": "anel", "metrica": "heart_rate", "valor": 62, "unidade": "bpm", "ts": "2026-05-24T08:12:00Z" }, { "usuario_id": "usr_8a1f3c", "equipamento_id": "eqp_pulseira_01", "tipo_dispositivo": "pulseira", "metrica": "steps", "valor": 8432, "ts": "2026-05-24T08:00:00Z" } ] }
{
"recebidas": 2,
"ignoradas_duplicadas": 0,
"lote_id": "ing_77c1"
}
Captação · Bioimpedância
Um envio por pesagem. A balança manda muitos campos — vão todos dentro de dados (objeto livre/JSONB).
POST /v1/ingest/bioimpedancia Authorization: Bearer <token> { "usuario_id": "usr_8a1f3c", "equipamento_id": "eqp_bal_01", "ts": "2026-05-22T07:30:00Z", "dados": { "peso": 78.5, "imc": 23.4, "gordura_pct": 18.2, "musculo_kg": 38.5, "agua_pct": 56, "proteina_pct": 17, "gordura_visceral": 7, "massa_ossea": 3.2, "metabolismo_basal": 1720, "idade_metabolica": 33 } }
{ "id": "bio_5d20", "status": "armazenado" }
Captação · Eventos do dispositivo
Bateria, sincronização e estado de conexão de cada aparelho.
POST /v1/ingest/eventos Authorization: Bearer <token> { "equipamento_id": "eqp_bal_01", "usuario_id": "usr_8a1f3c", "tipo": "conexao", // sync | bateria | conexao "estado": "offline", "bateria": 60, "ts": "2026-05-22T07:31:00Z" }
{ "status": "ok" }
Fornecimento · Resumo do dia
O que o dashboard mostra: 1 valor por métrica (a fonte é escolhida pela regra de negócio) + a idade fisiológica.
| Parâmetro | Tipo | Descrição |
|---|---|---|
| data | date (query) | Dia desejado (default: hoje). |
GET /v1/usuarios/usr_8a1f3c/resumo?data=2026-05-24 Authorization: Bearer <token>
{
"usuario_id": "usr_8a1f3c",
"data": "2026-05-24",
"idade_fisiologica": 33,
"metricas": {
"passos": { "valor": 8432, "fonte": "pulseira" },
"heart_rate": { "valor": 62, "fonte": "anel" },
"spo2": { "valor": 98, "fonte": "anel" },
"temperatura": { "valor": 36.4, "fonte": "pulseira" },
"sono": { "valor": "7h24", "fonte": "anel" }
}
}
Fornecimento · Série de leituras
Série temporal de uma métrica num intervalo (ex.: batimento ao longo do dia). Paginada.
| Parâmetro | Tipo | Descrição |
|---|---|---|
| metrica obrig | enum (query) | heart_rate, steps… |
| from / to | datetime (query) | Janela de tempo (UTC). |
| limit / cursor | int / string | Paginação. |
GET /v1/usuarios/usr_8a1f3c/leituras?metrica=heart_rate &from=2026-05-24T00:00:00Z&to=2026-05-24T23:59:59Z&limit=500 Authorization: Bearer <token>
{
"metrica": "heart_rate",
"unidade": "bpm",
"dados": [
{ "ts": "2026-05-24T08:00:00Z", "valor": 61 },
{ "ts": "2026-05-24T08:01:00Z", "valor": 62 }
],
"proxima_pagina": "cursor_x9f2"
}
Fornecimento · Bioimpedância
Última pesagem (ou histórico com ?historico=true).
{
"ts": "2026-05-22T07:30:00Z",
"dados": {
"peso": 78.5, "imc": 23.4,
"gordura_pct": 18.2, "musculo_kg": 38.5,
"idade_metabolica": 33
}
}
Fornecimento · Dispositivos
Aparelhos pareados, com bateria e estado de conexão (o app mostra "2 de 3 conectados").
{
"dispositivos": [
{ "equipamento_id": "eqp_anel_03", "tipo": "anel",
"conectado": true, "bateria": 82, "ultima_sync": "2026-05-24T08:12:00Z" },
{ "equipamento_id": "eqp_pulseira_01", "tipo": "pulseira",
"conectado": true, "bateria": 47, "ultima_sync": "2026-05-24T08:09:00Z" },
{ "equipamento_id": "eqp_bal_01", "tipo": "balanca",
"conectado": false, "bateria": 60, "ultima_sync": "2026-05-22T07:31:00Z" }
]
}
Códigos de erro
Erros seguem um corpo padrão:
{ "erro": "sem_consentimento", "mensagem": "Sem consentimento ativo no escopo 'cardio'." }
| HTTP | Código | Quando |
|---|---|---|
| 400 | requisicao_invalida | JSON malformado ou campo faltando. |
| 401 | nao_autenticado | Bearer token ausente, inválido ou expirado. |
| 403 | sem_consentimento / sem_escopo | Token válido, mas sem permissão/consentimento para o dado. |
| 404 | nao_encontrado | Usuário ou recurso inexistente. |
| 409 | conflito_idempotencia | Idempotency-Key reusada com corpo diferente. |
| 422 | dados_invalidos | Valor fora do domínio (ex.: métrica desconhecida). |
| 429 | limite_excedido | Rate limit. Tente após X-RateLimit-Reset. |
| 5xx | erro_interno | Falha no servidor. Reenvio seguro com Idempotency-Key. |