BeHeart Coherence API
Leve a prática de coerência cardíaca do BeHeart para dentro do seu app ou portal. Você embute uma URL fixa num iframe/WebView e lê resultados, pontos e HeartCoins calculados no servidor — prontos para alimentar o seu programa de bem-estar.
Introdução
A integração tem três participantes:
- Seu front-end — embute a URL fixa do widget (iframe/WebView) com
&user=ID. Sem token para gerenciar. - Seu backend — guarda a API key numa env e lê os resultados.
- A API BeHeart — cria as sessões, recebe medições e calcula pontos/HeartCoins (fonte de verdade).
URL base: https://api.beheart.app — todas as rotas abaixo são relativas a ela.
Todas as respostas são JSON (application/json) e toda chamada devolve um header X-Request-Id (saiba mais).
bhk_...) vive somente no seu servidor,
numa variável de ambiente. No front circula apenas a URL fixa do widget (chave pública bhw_...,
que só abre sessões de medição — não lê dado nenhum).
Quickstart
Você recebe duas coisas no portal BeHeart: a URL fixa do widget (pública, gerada uma única vez — nunca muda) e a API key (secreta, vai numa env). É só isso.
Copie a URL do seu widget no portal (aba Integração). Ela é fixa — guarde no config do seu front:
WIDGET_URL = "https://api.beheart.app/widget/?key=bhw_a1b2c3d4_9f8e..."
Embuta num iframe, acrescentando &user=ID para identificar quem mede
(id interno do seu sistema — o histórico da pessoa persiste por ele). allow="camera" é obrigatório:
<iframe src="WIDGET_URL&user=usr_123&lang=pt" allow="camera" style="width:100%;max-width:480px;height:760px;border:0;border-radius:16px"> </iframe>
Guarde a API key na env do seu backend. Ela serve para ler os resultados — nunca vai para o front:
# .env do SEU backend BEHEART_API_KEY=bhk_a1b2c3d4_9f8e7d6c...
Leia os resultados — em tempo real via postMessage, e no backend:
curl "https://api.beheart.app/v1/results?subject_id=usr_123" \
-H "Authorization: Bearer $BEHEART_API_KEY"const res = await fetch(
'https://api.beheart.app/v1/results?subject_id=usr_123',
{ headers: { 'Authorization': `Bearer ${process.env.BEHEART_API_KEY}` } },
);
const { results } = await res.json();import os, requests
res = requests.get(
"https://api.beheart.app/v1/results",
params={"subject_id": "usr_123"},
headers={"Authorization": f"Bearer {os.environ['BEHEART_API_KEY']}"},
)
results = res.json()["results"]Integrar no seu app — JavaScript, React e React Native
Tudo que você precisa: a URL fixa do widget (do portal — nunca muda) e, no backend, a API key na env para ler resultados. Escolha sua stack nas abas — a escolha vale para a página toda.
// config do seu front (a URL é pública e fixa — pode commitar)
const WIDGET_URL = 'https://api.beheart.app/widget/?key=bhw_SUA_WIDGET_KEY';
1. Abrir o widget e receber o resultado
<div id="beheart"></div>
<script>
// 1. abre o widget: só a URL fixa + o id do usuário no SEU sistema
const iframe = document.createElement('iframe');
iframe.src = WIDGET_URL + '&user=' + encodeURIComponent(usuario.id) + '&lang=pt';
iframe.allow = 'camera'; // obrigatório
iframe.style.cssText =
'width:100%;max-width:480px;height:760px;border:0;border-radius:16px';
document.getElementById('beheart').replaceChildren(iframe);
// 2. recebe o resultado em tempo real
window.addEventListener('message', (ev) => {
let msg; try { msg = JSON.parse(ev.data); } catch { return; }
if (msg.source !== 'beheart-coherence') return;
if (msg.type === 'result') {
console.log('pontos:', msg.data.heart_coins.points);
// atualize a UI; confirme no backend antes de premiar (passo 2 abaixo)
}
});
</script>import { useEffect } from 'react';
const WIDGET_URL = 'https://api.beheart.app/widget/?key=bhw_SUA_WIDGET_KEY';
export function BeHeartPractice({ userId, onResult }) {
// recebe o resultado em tempo real
useEffect(() => {
function onMessage(ev) {
let msg; try { msg = JSON.parse(ev.data); } catch { return; }
if (msg.source !== 'beheart-coherence') return;
if (msg.type === 'result') onResult?.(msg.data);
}
window.addEventListener('message', onMessage);
return () => window.removeEventListener('message', onMessage);
}, [onResult]);
// abre o widget: só a URL fixa + o id do usuário no SEU sistema
return (
<iframe
title="BeHeart Coherence"
src={`${WIDGET_URL}&user=${encodeURIComponent(userId)}&lang=pt`}
allow="camera"
style={{ width: '100%', maxWidth: 480, height: 760, border: 0, borderRadius: 16 }}
/>
);
}
// uso:
// <BeHeartPractice userId={usuario.id}
// onResult={(r) => console.log('pontos', r.heart_coins.points)} />// npm install react-native-webview
// Android: <uses-permission android:name="android.permission.CAMERA"/> no AndroidManifest.xml
// iOS: NSCameraUsageDescription no Info.plist
import { WebView } from 'react-native-webview';
const WIDGET_URL = 'https://api.beheart.app/widget/?key=bhw_SUA_WIDGET_KEY';
// o widget avisa via postMessage; este bridge encaminha para o React Native
const bridge = `
window.addEventListener('message', (ev) => {
if (typeof ev.data === 'string') window.ReactNativeWebView.postMessage(ev.data);
});
true;
`;
export function BeHeartPractice({ userId, onResult }) {
// abre o widget: só a URL fixa + o id do usuário no SEU sistema
return (
<WebView
source={{ uri: WIDGET_URL + '&user=' + encodeURIComponent(userId) + '&lang=pt' }}
injectedJavaScript={bridge}
onMessage={(ev) => {
// recebe o resultado em tempo real
let msg; try { msg = JSON.parse(ev.nativeEvent.data); } catch { return; }
if (msg.source !== 'beheart-coherence') return;
if (msg.type === 'result') onResult?.(msg.data);
}}
mediaCapturePermissionGrantType="grant"
allowsInlineMediaPlayback
style={{ flex: 1 }}
/>
);
}2. Confirmar no backend antes de premiar
O postMessage serve para a UX (mostrar "parabéns, +50 pontos" na hora). Para creditar
recompensas de verdade, confirme sempre no seu backend — os valores autoritativos vêm da API:
// no seu backend, disparado quando o front avisa que terminou:
const r = await fetch(
'https://api.beheart.app/v1/results?subject_id=usr_123&limit=1',
{ headers: { 'Authorization': `Bearer ${process.env.BEHEART_API_KEY}` } },
);
const { results } = await r.json();
const pontos = results[0]?.heart_coins.points; // fonte de verdade (calculado no servidor)
result do postMessage como
gatilho para o seu front avisar o seu backend, e o backend confirma com GET /v1/results.
Para totais acumulados use GET /v1/summary; para ranking, GET /v1/subjects.Autenticação
Três credenciais, com papéis bem separados:
| Credencial | Formato | Onde vive | Para quê |
|---|---|---|---|
| Widget key (pública) | bhw_<id>_<token> | Na URL fixa do widget — pode aparecer no front | SÓ abre sessões de medição. Gerada uma vez, nunca muda, não lê dado nenhum |
| API key (secreta) | bhk_<id>_<segredo> | Só no seu servidor (env var / cofre) | Ler resultados, uso e logs; emitir tokens (modo avançado) |
| Token de sessão | JWT (HS256) | Interno — o widget obtém sozinho pela widget key | Uma visita de medição; expira e renova sozinho |
A API key é exibida uma única vez ao ser criada — em disco fica apenas o hash SHA-256.
Perdeu ou vazou? Gere outra no portal (/portal → Chave de API); a anterior é revogada na hora e
a URL do widget não é afetada.
&user=ID
da URL liga a medição ao seu usuário e mantém o histórico dele entre visitas.
Sinais de credencial inválida: 401 invalid_api_key, 401 invalid_widget_key
(chave errada ou empresa desativada) e 401 invalid_session_token (sessão expirada — recarregar o widget renova).
Embutir o widget
O widget é uma página web hospedada pela BeHeart que roda a medição por fotopletismografia (câmera + lanterna do celular). Use a URL fixa da sua empresa (portal → Integração):
<iframe
src="https://api.beheart.app/widget/?key=bhw_SUA_WIDGET_KEY&user=usr_123&lang=pt"
allow="camera"
style="width:100%;max-width:480px;height:760px;border:0;border-radius:16px">
</iframe>
Parâmetros da URL
| Parâmetro | Descrição |
|---|---|
keyobrigatório | Sua widget key pública (já vem na URL fixa do portal) |
userrecomendado | Id do usuário no seu sistema (pseudonimizado). Liga a medição à pessoa e mantém o histórico dela entre visitas |
langopcional | pt (padrão), en ou es |
tokenavançado | Token de sessão de POST /v1/tokens — substitui key/user quando você mesmo emite as sessões |
Requisitos
- A página hospedeira precisa estar em HTTPS (getUserMedia não roda em HTTP).
allow="camera"no iframe é obrigatório.- Apps nativos: WebView com permissão de câmera (Android:
WebChromeClient.onPermissionRequest; iOS: WKWebView +NSCameraUsageDescription).
Eventos postMessage
O widget avisa a página hospedeira sobre o andamento da medição via window.postMessage.
Cada mensagem é um JSON (string) com source: "beheart-coherence".
window.addEventListener('message', (ev) => {
let msg; try { msg = JSON.parse(ev.data); } catch { return; }
if (msg.source !== 'beheart-coherence') return;
switch (msg.type) {
case 'ready': /* sessão validada, prática liberada */ break;
case 'practice_started': /* usuário iniciou a medição */ break;
case 'result': console.log('resultado', msg.data); break;
case 'session_invalid': /* token expirou — emita outro */ break;
}
});
| type | Quando dispara | data |
|---|---|---|
ready | Token validado, widget pronto | { session_id, subject_id } |
practice_started | Usuário iniciou a medição | — |
result | Medição concluída e registrada | O objeto Resultado |
session_invalid | Token expirado/ inválido | — |
Referência — endpoints da sua integração
Autenticados com a API key (Authorization: Bearer bhk_...). Chamadas servidor-a-servidor.
POST /v1/tokensAPI key · avançado
Modo avançado (opcional). A URL fixa do widget já cria sessões sozinha — use este endpoint
apenas se quiser emitir os tokens no seu backend (ex.: para controlar o subject_id server-side
ou definir TTLs curtos). O token vai na URL do widget como ?token=....
Body (JSON)
| Campo | Tipo | Descrição |
|---|---|---|
subject_idrecomendado | string | Id do usuário no seu sistema (pseudonimizado — não use CPF/e-mail). Liga a medição ao usuário e mantém o histórico dele entre sessões. Sem ele, o escopo é só a sessão. |
ttl_secondsopcional | int | Validade do token: 60–3600. Padrão 900 (15 min). |
Exemplo
curl -X POST https://api.beheart.app/v1/tokens \
-H "Authorization: Bearer $BEHEART_API_KEY" \
-H "Content-Type: application/json" \
-d '{"subject_id": "usr_123", "ttl_seconds": 900}'const res = await fetch('https://api.beheart.app/v1/tokens', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BEHEART_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ subject_id: 'usr_123', ttl_seconds: 900 }),
});
const { token, session_id, expires_at } = await res.json();import os, requests
res = requests.post(
"https://api.beheart.app/v1/tokens",
headers={"Authorization": f"Bearer {os.environ['BEHEART_API_KEY']}"},
json={"subject_id": "usr_123", "ttl_seconds": 900},
)
data = res.json() # token, session_id, expires_atResposta — 201
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"session_id": "ses_1a2b3c4d5e6f7a8b9c0d1e2f",
"expires_at": "2026-07-23T15:30:00.000Z"
}
GET /v1/resultsAPI key
Lista as medições da sua empresa, das mais recentes para as mais antigas.
Query params
| Parâmetro | Descrição |
|---|---|
limitopcional | 1–500, padrão 50 |
subject_idopcional | Só as medições de um usuário |
Exemplo
curl "https://api.beheart.app/v1/results?limit=50&subject_id=usr_123" \
-H "Authorization: Bearer $BEHEART_API_KEY"
Resposta — 200
{ "results": [ { ...objeto Resultado... }, ... ] }
GET /v1/summaryAPI key
Totais vitalícios e diário de 7 dias de um usuário — "quantos pontos ele tem".
Query params
| Parâmetro | Descrição |
|---|---|
subject_idobrigatório | Usuário a consultar |
Resposta — 200
{
"subject_id": "usr_123",
"total_points": 250,
"total_heart_coins": 1250,
"total_practices": 5,
"best_score": 0.87,
"last_practice_at": "2026-07-22T14:03:11.000Z",
"daily": { "2026-07-16": { "points": 0, "heart_coins": 0, "id": "2026-07-16" }, ... }
}
GET /v1/subjectsAPI key
Todos os usuários da sua empresa com totais agregados, mais ativos primeiro. Ideal para rankings e relatórios de engajamento.
Resposta — 200
{
"subjects": [
{
"subject_id": "usr_123",
"practices": 12,
"points": 480,
"heart_coins": 2400,
"best_score": 0.91,
"avg_score": 0.74,
"last_practice_at": "2026-07-22T14:03:11.000Z"
}
]
}
GET /v1/usageAPI key
Uso agregado da sua conta: volume de medições, usuários alcançados, pontos distribuídos e série diária dos últimos 30 dias.
Resposta — 200
{
"key_id": "a1b2c3d4",
"name": "Empresa X",
"practices": 340,
"subjects": 87,
"points": 14200,
"heart_coins": 71000,
"avg_score": 0.68,
"last_practice_at": "2026-07-23T09:12:44.000Z",
"daily": [ { "date": "2026-06-24", "count": 11 }, ... ]
}
GET /v1/requestsAPI key
Log das chamadas de API da sua integração — cada uma com Request ID, status e latência — mais estatísticas de 30 dias. Útil para depurar a integração sem falar com o suporte.
Query params
| Parâmetro | Descrição |
|---|---|
limitopcional | 1–500, padrão 50 |
errorsopcional | true = só chamadas com status ≥ 400 |
Resposta — 200
{
"requests": [
{ "id": "req_9f8e7d6c5b4a3f2e", "at": "2026-07-23T12:00:41.000Z",
"method": "POST", "path": "/v1/tokens", "status": 201, "ms": 4, "auth": "api_key" }
],
"stats": {
"requests": 1204, "errors": 3, "avg_ms": 6,
"daily": [ { "date": "2026-06-24", "count": 40 }, ... ],
"top_endpoints": [ { "endpoint": "POST /v1/tokens", "count": 610 }, ... ]
}
}
Referência — endpoints usados pelo widget
Autenticados com o token de sessão. O widget da BeHeart já chama tudo isso sozinho — documentados para transparência e para integrações customizadas.
POST /v1/widget/sessionswidget key (pública)
Troca a widget key da URL fixa por um token de sessão. O widget chama isto sozinho ao abrir —
documentado por transparência. Body: {"key": "bhw_...", "subject_id": "usr_123"} →
201 com token, session_id e expires_at (1 h).
GET /v1/tokens/verifytoken de sessão
Valida o token e devolve os dados da sessão (session_id, subject_id, key_id, expires_at). O widget chama ao carregar.
POST /v1/resultstoken de sessão
Registra a medição concluída. O servidor recalcula pontos e HeartCoins com as fórmulas oficiais —
valores enviados pelo cliente são ignorados. Resposta 201 com o objeto Resultado completo.
GET /v1/results/minetoken de sessão
Histórico do usuário da sessão (escopo: mesmo subject_id dentro da sua empresa — persiste entre sessões). Alimenta a tela de histórico do widget.
DELETE /v1/results/{id}token de sessão
Apaga uma medição do próprio usuário (direito de exclusão LGPD no nível do usuário). 404 se o id não for dele.
GET /v1/heart-coins/summarytoken de sessão
Resumo diário de pontos/HeartCoins dos últimos 7 dias do usuário (hoje por último) — o gráfico de barras do histórico do widget.
O objeto Resultado
{
"id": "res_1753274591000_42",
"session_id": "ses_1a2b3c4d5e6f7a8b9c0d1e2f",
"subject_id": "usr_123",
"key_id": "a1b2c3d4",
"created_at": "2026-07-23T12:03:11.000Z",
"score": 0.82,
"hr_average": 72.4,
"hrv_average": 48.1,
"psd_average": 0.61,
"total_duration": 300,
"duration_in_coherence": 240,
"duration_in_medium_coherence": 40,
"duration_in_low_coherence": 20,
"hr_history": [ ... ], "hrv_history": [ ... ], "psd_history": [ ... ],
"is_checkup": false,
"heart_coins": { "points": 50, "heart_coins": 250, "id": "ses_..." }
}
| Campo | Descrição |
|---|---|
score | Score técnico de coerência, 0 a 1 (autocorrelação) |
heart_coins.points | Pontos da prática — fórmula oficial, calculada no servidor |
heart_coins.heart_coins | HeartCoins da sessão |
total_duration | Duração total, em segundos |
duration_in_coherence | Tempo em coerência alta (s); há também _medium_ e _low_ |
hr_average / hrv_average | Frequência cardíaca média (bpm) / RMSSD mediano |
hr_history etc. | Séries temporais da sessão (omitidas em listagens agregadas) |
subject_id / session_id | Seu usuário (pseudonimizado) e a sessão de medição |
heart_coins.points e
heart_coins.heart_coins vindos da API para premiações — nunca valores calculados no cliente.Erros
A API usa códigos HTTP convencionais e um corpo JSON com um código estável de erro:
{ "error": "invalid_api_key" }
| HTTP | Código | Significado |
|---|---|---|
| 400 | invalid_json | Corpo da requisição não é JSON válido |
| 400 | subject_id_required | Parâmetro obrigatório ausente (em /v1/summary) |
| 401 | invalid_api_key | API key ausente, incorreta, revogada ou desativada |
| 401 | invalid_session_token | Token de sessão expirado ou inválido — emita outro |
| 403 | key_inactive | A chave da empresa está desativada (login do portal) |
| 404 | not_found | Recurso inexistente ou fora do seu escopo |
| 409 | email_taken | Já existe conta com esse e-mail (onboarding do portal) |
| 429 | rate_limited | Limite de requisições excedido — aguarde e tente de novo |
Recomendação: trate 401 invalid_session_token emitindo um token novo (não é erro fatal — tokens expiram por design), e 429 com retry + backoff exponencial.
Rate limits
O limite padrão é 120 requisições por minuto por credencial (API key ou token).
Excedido, a API responde 429 rate_limited.
- Emita um token por medição, não por pageview.
- Para dashboards internos, faça polling de
/v1/resultscom intervalo ≥ 30 s ou use o eventoresultdo postMessage como gatilho. - Precisa de mais volume? Fale com seu contato BeHeart.
Request IDs
Toda resposta da API traz o header X-Request-Id (formato req_...) e a chamada fica
registrada no log da sua conta (GET /v1/requests e portal → API & Logs).
HTTP/1.1 201 Created Content-Type: application/json X-Request-Id: req_9f8e7d6c5b4a3f2e
Ao abrir um chamado de suporte, inclua o Request ID — com ele localizamos a chamada exata (endpoint, horário, status, latência) em segundos.
Segurança & LGPD
Segurança da integração
- A API key nunca vai para navegador, app mobile ou repositório — env var / cofre de segredos, sempre.
- Em disco, a BeHeart guarda apenas o hash SHA-256 da chave — nem nós conseguimos recuperá-la.
- Suspeita de vazamento? Rotacione no portal — a chave antiga morre na hora, o histórico permanece.
- Tokens de sessão são JWTs assinados (HS256), de curta duração, escopados a uma empresa + usuário.
- Configure os domínios do seu front com a BeHeart (allowlist de CORS) para restringir quem pode embutir o widget.
LGPD
- Colete o consentimento no seu fluxo antes de abrir o widget.
- Use
subject_idpseudonimizado (código interno) — nunca CPF, e-mail ou nome. A BeHeart não conhece a identidade real dos seus usuários. - Trate relatórios de forma agregada; evite decisões individuais baseadas nas medições.
- Exclusão: o próprio usuário pode apagar medições pelo widget (DELETE /v1/results/{id}); para exclusão total de um titular, acione seu contato BeHeart.
- O contrato B2B com a BeHeart cobre papéis de controladora/operadora, retenção e subprocessadores.
Changelog
| Data | Mudança |
|---|---|
| 2026-07-23 | Integração sem token: URL fixa do widget por empresa (widget key pública bhw_... + POST /v1/widget/sessions) — copie a URL uma vez e pronto; POST /v1/tokens vira modo avançado. Exemplos prontos para JavaScript, React e React Native. |
| 2026-07-23 | Novo: GET /v1/subjects (usuários agregados), GET /v1/requests (log de uso com Request IDs), header X-Request-Id em todas as respostas, este site de documentação. |
| 2026-07-22 | Portal da empresa: contas por convite, login e-mail+senha, rotação de chave self-service. Histórico do usuário no widget. |
| 2026-07-21 | Lançamento da v1: tokens de sessão, widget embarcável, resultados, summary e usage. |
BeHeart Coherence API · v1 · Suporte: contato@beheart.app — inclua o X-Request-Id ao relatar um problema.