Omni Health
Developer guide › Sample code

Sample code

Copy-paste starting points. Set your secrets as environment variables — never hardcode them.

Webhook receiver

Verifies the signature, rejects replays, acknowledges quickly and processes idempotently — the four things every receiver should do.

<?php
// webhook.php — OmniHealth webhook receiver (verify signature, be idempotent).
$SECRET = getenv('OMNIHEALTH_WEBHOOK_SECRET');   // from your workspace

$raw = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_OMNIHEALTH_SIGNATURE'] ?? '';

// 1. Parse "t=<ts>,v1=<hmac>" and verify HMAC-SHA256 of "<ts>.<raw>".
if (!preg_match('/t=(\d+),v1=([a-f0-9]+)/', $sig, $m)) { http_response_code(400); exit; }
[$all, $ts, $mac] = $m;
$expected = hash_hmac('sha256', $ts . '.' . $raw, $SECRET);
if (!hash_equals($expected, $mac)) { http_response_code(400); exit; }

// 2. Reject old timestamps (replay protection).
if (abs(time() - (int)$ts) > 300) { http_response_code(400); exit; }

// 3. Acknowledge fast, then process idempotently.
$event = json_decode($raw, true);
$key = $event['event'] . ':' . ($event['data']['patient_id'] ?? '') . ':' . $event['created_at'];
if (already_processed($key)) { http_response_code(200); exit; }   // your own store

switch ($event['event']) {
    case 'service.enabled':  start_sync($event['data']['patient_id']);  break;
    case 'service.disabled': stop_sync($event['data']['patient_id']);   break;
    case 'observation.created': /* react to new data */ break;
}
mark_processed($key);
http_response_code(200);
echo 'ok';
// webhook.js — Express receiver. IMPORTANT: use the RAW body for the HMAC.
const express = require('express');
const crypto = require('crypto');
const app = express();
const SECRET = process.env.OMNIHEALTH_WEBHOOK_SECRET;
const seen = new Set();   // replace with a real store

app.post('/webhook', express.raw({ type: '*/*' }), (req, res) => {
  const raw = req.body;                    // Buffer — do NOT JSON.parse before hashing
  const sig = req.headers['x-omnihealth-signature'] || '';
  const m = sig.match(/t=(\d+),v1=([a-f0-9]+)/);
  if (!m) return res.status(400).end();

  const [, ts, mac] = m;
  const expected = crypto.createHmac('sha256', SECRET).update(ts + '.' + raw).digest('hex');
  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(mac))) return res.status(400).end();
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return res.status(400).end();

  const ev = JSON.parse(raw.toString('utf8'));
  const key = `${ev.event}:${ev.data?.patient_id}:${ev.created_at}`;
  if (seen.has(key)) return res.status(200).end();
  seen.add(key);

  if (ev.event === 'service.enabled')  startSync(ev.data.patient_id);
  if (ev.event === 'service.disabled') stopSync(ev.data.patient_id);
  res.status(200).send('ok');
});
app.listen(3000);
Hash the raw body. Compute the HMAC over the exact bytes we sent, before any JSON parsing or re-serialization — otherwise the signature won’t match. In Express, that means express.raw(), not express.json().

Cached token helper (PHP)

Fetch a token once and reuse it until just before it expires.

<?php
// A tiny cached-token helper. Fetch once, reuse until it expires.
function omnihealth_token(): string {
    static $tok = null, $exp = 0;
    if ($tok && time() < $exp - 30) return $tok;
    $r = json_decode(file_get_contents('https://omnihealth.solutions/fhir/token', false, stream_context_create([
        'http' => ['method' => 'POST', 'header' => 'Content-Type: application/x-www-form-urlencoded',
            'content' => http_build_query([
                'grant_type' => 'client_credentials',
                'client_id' => getenv('OMNIHEALTH_CLIENT_ID'),
                'client_secret' => getenv('OMNIHEALTH_CLIENT_SECRET'),
                'scope' => 'system/Observation.write system/Notification.write',
            ])]]])), true);
    $tok = $r['access_token']; $exp = time() + (int)$r['expires_in'];
    return $tok;
}

Next

Import the OpenAPI spec into Postman or Swagger UI to generate a full client, and follow the 10-minute tutorial for the end-to-end flow.

×