healthhipaahow-toprivacyhealthtechdeveloper-guide

How to Build a HIPAA-Compliant Patient Body Assessment Feature

· 6 min read · Martin Hejda

Healthcare applications that collect height and weight have clinical reasons to use that data beyond BMI. Pre-surgical planning, physical therapy progression, medical device fitting, and remote patient assessment all benefit from derived body dimensions. A surgeon planning a hip replacement needs leg length and hip width. A physical therapist designing a home exercise programme needs grip strength correlation with hand size. A medical device company needs chest circumference for wearable sensor placement.

The barrier to using a body dimension API in healthcare is typically regulatory: HIPAA. Developers assume that any API call involving patient data requires a Business Associate Agreement (BAA), security risk assessments, and compliance overhead that delays or kills the feature.

This guide explains when a BAA is required and when it isn’t — and builds a stateless body dimension feature that falls clearly outside PHI scope.


The HIPAA question

A BAA is required when a third-party service receives, processes, or stores Protected Health Information (PHI) on your behalf.

PHI under HIPAA is defined as individually identifiable health information: data that connects a health condition, treatment, or record to a specific identified or identifiable individual. Height and weight are PHI when they’re linked to a patient identifier — a name, medical record number, date of birth, address, or any other identifier that makes the individual identifiable.

The DimensionsPot API is stateless: it receives height, weight, and gender; returns predicted dimensions; and retains nothing. No patient identifiers are transmitted in the API call. Height, weight, and gender alone are not PHI — they are not individually identifiable without the accompanying patient identifier.

The corollary: if your application sends height and weight to the API without including any patient identifier, no PHI leaves your system. No BAA is required.


What the API receives vs. what your system knows

Your system knowsWhat you send to the API
Patient ID + name + MRN✗ Never sent
Height (cm)✓ Sent as body_height in mm
Weight (kg)✓ Sent as body_mass
Gender / sex✓ Sent as gender
Diagnosis / condition✗ Never sent
Date of birth✗ Never sent

The API receives an anonymous biometric tuple: a height, a weight, and a gender. This is epidemiological data, not patient data. The API cannot re-identify a patient from this input.


Step 1: Server-side proxy (keeps patient identifiers server-side)

The critical architecture decision: the API call is made server-side. The patient identifier stays in your backend. Your frontend receives only the predicted dimensions, not the patient record.

// patient-dimensions.js — runs inside your HIPAA-compliant backend
const express = require('express');
const router  = express.Router();

const API_URL      = 'https://dimensionspot-bodysize-engine.p.rapidapi.com/v1/predict';
const RAPIDAPI_KEY = process.env.RAPIDAPI_KEY;

// Auth middleware (your existing patient auth — abbreviated for clarity)
const requirePatientAuth = require('../middleware/patientAuth');

router.post('/patient-dimensions', requirePatientAuth, async (req, res) => {
  // patientId comes from the authenticated session — never from the request body
  const patientId = req.user.patientId;

  // Fetch the patient's biometrics from your internal records
  // This query happens inside your PHI boundary — no external call
  const patient = await db.patients.findById(patientId, {
    select: ['gender', 'height_cm', 'weight_kg', 'height_updated_at'],
  });

  if (!patient) return res.status(404).json({ error: 'Patient not found' });

  const heightMm = Math.round(parseFloat(patient.height_cm) * 10);
  const weightKg = parseFloat(patient.weight_kg);

  // API call — only the anonymous tuple crosses the PHI boundary
  const apiRes = await fetch(API_URL, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-rapidapi-key': RAPIDAPI_KEY,
      'x-rapidapi-host': 'dimensionspot-bodysize-engine.p.rapidapi.com',
    },
    body: JSON.stringify({
      input_data: {
        input_unit_system: 'metric',
        subject: {
          gender: patient.gender,
          input_origin_region: patient.region ?? 'GLOBAL',
        },
        anchors: { body_height: heightMm, body_mass: weightKg },
      },
      output_settings: {
        calculation: { calculation_model: 'AUTO', target_region: patient.region ?? 'GLOBAL', body_build_type: 'CIVILIAN' },
        requested_dimensions: { bundle: 'FULL_BODY' },
        output_format: { unit_system: 'metric', confidence_score_threshold: 0, include_range_95: true, include_iso_codes: false },
      },
    }),
  });

  if (!apiRes.ok) return res.status(502).json({ error: 'Dimension service unavailable' });

  const data = await apiRes.json();

  // Map to a named object for clinical use
  const dimensions = Object.fromEntries(
    Object.entries(data.body_dimensions).map(([key, d]) => [key, {
      value:            d.value,
      confidence_score: d.confidence_score,
      range_95:         d.range_95,
    }])
  );

  // Store the derived dimensions in the patient record (inside your PHI system)
  await db.patients.update(patientId, {
    derived_dimensions:         dimensions,
    dimensions_computed_at:     new Date(),
    dimensions_source_height:   patient.height_cm,
    dimensions_source_weight:   patient.weight_kg,
  });

  res.json({ dimensions });
});

module.exports = router;

Step 2: Clinical use cases

Pre-surgical assessment

Body dimensions can support pre-surgical planning without manual measurement:

function preSurgicalAssessment(dimensions, procedureType) {
  const d = (id) => dimensions[id]?.value ?? null;
  const conf = (id) => dimensions[id]?.confidence_score ?? 0;

  const assessment = { procedure: procedureType, dimensions_used: [], notes: [] };

  if (procedureType === 'hip_replacement') {
    assessment.dimensions_used = ['inseam_length', 'hip_circumference', 'thigh_circumference'];
    assessment.inseam_mm       = d('inseam_length');
    assessment.hip_circ_mm     = d('hip_circumference');
    assessment.thigh_circ_mm   = d('thigh_circumference');
    assessment.leg_length_ratio = d('inseam_length') && d('body_height')
      ? (d('inseam_length') / d('body_height')).toFixed(3)
      : null;

    if (conf('inseam_length') < 70) {
      assessment.notes.push('Inseam confidence below 70 — verify with physical measurement before implant selection.');
    }
  }

  if (procedureType === 'shoulder_arthroplasty') {
    assessment.dimensions_used = ['shoulder_width', 'arm_length', 'chest_circumference'];
    assessment.shoulder_width_mm = d('shoulder_width');
    assessment.arm_length_mm     = d('arm_length');
  }

  if (procedureType === 'spinal') {
    assessment.dimensions_used = ['torso_length', 'sitting_height', 'shoulder_width'];
    assessment.torso_length_mm  = d('torso_length');
    assessment.sitting_height_mm = d('sitting_height');
  }

  return assessment;
}

Physical therapy home programme

Adapt exercise parameters to patient proportions:

function ptHomeProgram(dimensions, program) {
  const inseam   = dimensions.inseam_length?.value   ?? 790;
  const arm      = dimensions.arm_length?.value      ?? 590;
  const shoulder = dimensions.shoulder_width?.value  ?? 400;

  const adaptations = {};

  if (program.includes('resistance_band_exercises')) {
    // Band anchor height for shoulder-level pulls
    adaptations.band_anchor_height_cm = Math.round(
      (dimensions.eye_height_sitting?.value ?? 1200) / 10
    );
  }

  if (program.includes('step_exercises')) {
    // Step height relative to inseam: 20–25% of inseam
    adaptations.recommended_step_height_cm = Math.round(inseam * 0.22 / 10);
  }

  if (program.includes('grip_strengthening')) {
    adaptations.hand_breadth_mm  = dimensions.hand_breadth?.value;
    adaptations.grip_width_note  = dimensions.hand_breadth?.value < 80
      ? 'Small hand: use narrower grip tools (30–35 mm diameter).'
      : 'Standard grip tools (35–45 mm diameter) appropriate.';
  }

  return { program, adaptations };
}

Medical wearable placement

Body surface area estimation and sensor placement:

// BSA from DuBois formula: 0.007184 × height_cm^0.725 × weight_kg^0.425
function bodyMetrics(heightCm, weightKg, dimensions) {
  const bsa = parseFloat(
    (0.007184 * Math.pow(heightCm, 0.725) * Math.pow(weightKg, 0.425)).toFixed(3)
  );

  // Chest sensor band sizing
  const chestCircCm = (dimensions.chest_circumference?.value ?? 960) / 10;

  return {
    bsa_m2: bsa,
    chest_sensor_band_cm: Math.round(chestCircCm),
    chest_band_size: chestCircCm < 90 ? 'S' : chestCircCm < 100 ? 'M' : chestCircCm < 110 ? 'L' : 'XL',
    abdominal_sensor_placement_cm: Math.round(
      (dimensions.waist_circumference_omphalion?.value ?? 840) / 10
    ),
  };
}

Step 3: Data governance in your system

Since the dimensions are stored inside your PHI system (alongside the patient record), standard HIPAA data governance applies to the stored values:

// HIPAA audit log entry — log access to derived dimensions
async function logDimensionAccess(patientId, accessedBy, purpose) {
  await db.auditLog.insert({
    event:       'DIMENSIONS_ACCESS',
    patient_id:  patientId,
    accessed_by: accessedBy,
    purpose,
    timestamp:   new Date(),
    // log the fact of access, not the dimension values themselves
  });
}

// Dimension data should age out when underlying biometrics change significantly
async function checkDimensionCurrency(patientId) {
  const patient = await db.patients.findById(patientId,
    ['height_cm', 'weight_kg', 'dimensions_source_height', 'dimensions_source_weight', 'dimensions_computed_at']
  );

  const heightDrift  = Math.abs(patient.height_cm  - patient.dimensions_source_height);
  const weightDrift  = Math.abs(patient.weight_kg  - patient.dimensions_source_weight);
  const ageInDays    = (Date.now() - patient.dimensions_computed_at) / 86400000;

  // Recompute if weight changed by more than 5 kg or it's been more than 180 days
  return heightDrift > 1 || weightDrift > 5 || ageInDays > 180;
}

What does and doesn’t require a BAA

ScenarioBAA required?
Patient identifier sent to the APIYes — this would be PHI transmission
Height + weight + gender sent to the API (no identifier)No — not PHI
API stores your data between requestsNot applicable — API is stateless, retains nothing
You store dimensions with a patient record in your systemGoverned by your existing HIPAA controls, not the API
Using the API for clinical decision support in a regulated applicationFollow FDA SaMD guidance regardless of PHI scope

The key architectural rule: the patient identifier must never cross the PHI boundary into the API call. Height, weight, and gender are the inputs. The API is an anonymous prediction service, not a patient data processor.


Clinical caveats

Body dimension predictions are population-level estimates, not clinical measurements. For surgical planning and medical device fitting, treat the API output as a pre-assessment aid that informs the clinical measurement workflow — not a replacement for it. Confidence scores below 75 should prompt physical verification before any clinical decision is made on that dimension.

For FDA-regulated Software as a Medical Device (SaMD), dimensional estimates from a prediction service would require independent clinical validation as part of your regulatory strategy. This guide covers development implementation; regulatory classification is a separate determination.

Try DimensionsPot

Free tier — 100 requests/month, no credit card required.

Get API on RapidAPI