BeHeart Coherence · Docs

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).

Regra de ouro: a API key secreta (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"]
Não há token para gerenciar. A URL fixa contém a widget key pública da sua empresa; o widget troca por uma sessão sozinho ao abrir. A emissão manual de tokens (POST /v1/tokens) continua existindo como modo avançado.

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)
Ainda não há webhooks — use o evento 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:

CredencialFormatoOnde vivePara quê
Widget key (pública)bhw_<id>_<token>Na URL fixa do widget — pode aparecer no frontSÓ 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ãoJWT (HS256)Interno — o widget obtém sozinho pela widget keyUma 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.

Ninguém mede sem credencial. O widget só abre com a widget key da sua URL (ou um token de sessão, no modo avançado) — e widget key só existe para empresa com contrato ativo. O &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âmetroDescrição
keyobrigatórioSua widget key pública (já vem na URL fixa do portal)
userrecomendadoId do usuário no seu sistema (pseudonimizado). Liga a medição à pessoa e mantém o histórico dela entre visitas
langopcionalpt (padrão), en ou es
tokenavançadoToken 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).
iOS/Safari: o controle da lanterna via navegador só existe no Chrome/Android; no iOS o sinal depende da iluminação ambiente. Valide a experiência com seus usuários iOS antes do rollout.

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;
  }
});
typeQuando disparadata
readyToken validado, widget pronto{ session_id, subject_id }
practice_startedUsuário iniciou a medição
resultMedição concluída e registradaO objeto Resultado
session_invalidToken expirado/ inválido
Use o postMessage para UX, não para premiar. Para conceder pontos/recompensas, confirme sempre pelo backend (GET /v1/results) — os valores autoritativos são os do servidor.

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)

CampoTipoDescrição
subject_idrecomendadostringId 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_secondsopcionalintValidade 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_at

Resposta — 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âmetroDescrição
limitopcional1–500, padrão 50
subject_idopcionalSó 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âmetroDescrição
subject_idobrigatórioUsuá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âmetroDescrição
limitopcional1–500, padrão 50
errorsopcionaltrue = 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_..." }
}
CampoDescrição
scoreScore técnico de coerência, 0 a 1 (autocorrelação)
heart_coins.pointsPontos da prática — fórmula oficial, calculada no servidor
heart_coins.heart_coinsHeartCoins da sessão
total_durationDuração total, em segundos
duration_in_coherenceTempo em coerência alta (s); há também _medium_ e _low_
hr_average / hrv_averageFrequência cardíaca média (bpm) / RMSSD mediano
hr_history etc.Séries temporais da sessão (omitidas em listagens agregadas)
subject_id / session_idSeu usuário (pseudonimizado) e a sessão de medição
Fonte de verdade: use sempre 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" }
HTTPCódigoSignificado
400invalid_jsonCorpo da requisição não é JSON válido
400subject_id_requiredParâmetro obrigatório ausente (em /v1/summary)
401invalid_api_keyAPI key ausente, incorreta, revogada ou desativada
401invalid_session_tokenToken de sessão expirado ou inválido — emita outro
403key_inactiveA chave da empresa está desativada (login do portal)
404not_foundRecurso inexistente ou fora do seu escopo
409email_takenJá existe conta com esse e-mail (onboarding do portal)
429rate_limitedLimite 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/results com intervalo ≥ 30 s ou use o evento result do 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

Medições de coerência cardíaca são dados de saúde (dado pessoal sensível, art. 5º, II da LGPD). O tratamento exige base legal adequada — em geral, consentimento específico do titular.
  • Colete o consentimento no seu fluxo antes de abrir o widget.
  • Use subject_id pseudonimizado (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

DataMudança
2026-07-23Integraçã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-23Novo: 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-22Portal da empresa: contas por convite, login e-mail+senha, rotação de chave self-service. Histórico do usuário no widget.
2026-07-21Lanç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.