Linux (Headless C SDK)
This Linux headless C SDK is in beta. The API and behavior may still change.
This SDK is headless. There is no embedded UI or camera handling. You must capture frames, submit them to the SDK, and build any user guidance yourself.
This SDK targets arm64 Linux and exposes a C API for running Shen.AI measurements without an embedded UI.
The shipped header shenai_sdk_c.h is the source of truth for the full C API
surface (functions, structs, and enums).
Download
See system requirements for runtime dependencies.
Quickstart (minimal flow)
#include "shenai_sdk_c.h"
int main(void) {
shen_context_t* ctx = NULL;
shen_init_options_t opts = {
.api_key_or_token = "YOUR_API_KEY_OR_TOKEN",
.user_id = "user-123",
.language = "en",
.offline_mode = 0
};
if (shen_initialize(&opts, &ctx) != SHEN_STATUS_OK || ctx == NULL) {
return 1;
}
shen_set_operating_mode(ctx, SHEN_OPERATING_MEASURE);
for (;;) {
shen_frame_desc_t desc = {
.width = 1280,
.height = 720,
.stride_bytes = 1280 * 3,
.format = SHEN_PIXEL_FORMAT_BGR24,
.timestamp_ns = 0
};
const uint8_t* frame_data = get_next_frame(&desc);
if (!frame_data) {
break;
}
shen_submit_frame(ctx, &desc, frame_data);
shen_measurement_state_t state = SHEN_MEAS_NOT_STARTED;
shen_get_measurement_state(ctx, &state);
if (state == SHEN_MEAS_FINISHED || state == SHEN_MEAS_FAILED) {
break;
}
}
shen_measurement_results_t results;
if (shen_get_measurement_results(ctx, &results) == SHEN_STATUS_OK) {
/* Use results. */
}
shen_deinitialize(ctx);
shen_destroy_runtime();
return 0;
}Replace get_next_frame() with your camera or video capture pipeline.
Status codes
Most functions return a shen_status_t value:
SHEN_STATUS_OKSHEN_STATUS_NOT_INITIALIZEDSHEN_STATUS_INVALID_ARGUMENTSHEN_STATUS_INVALID_STATESHEN_STATUS_LICENSE_INVALIDSHEN_STATUS_CONNECTION_ERRORSHEN_STATUS_NO_DATASHEN_STATUS_INTERNAL_ERROR
Frame input
shen_submit_frame() accepts raw BGR frames:
- Format:
SHEN_PIXEL_FORMAT_BGR24(8-bit, 3 channels, B-G-R order) - Layout: row-major,
stride_bytes >= width * 3 timestamp_ns: optional, set to 0 if unknown
shen_frame_desc_t desc = {
.width = width,
.height = height,
.stride_bytes = stride,
.format = SHEN_PIXEL_FORMAT_BGR24,
.timestamp_ns = timestamp_ns
};
shen_submit_frame(ctx, &desc, frame_data);Initialization and configuration
Use shen_init_options_t to pass your API key or token, user id, language, and
offline mode preference. Set offline_mode to non-zero to enable offline
processing. The SDK can be initialized multiple times; subsequent calls reuse
the same engine and return SHEN_STATUS_OK.
Configuration options are set through explicit setters. You can also apply a
full SDK config JSON string via shen_apply_sdk_config() and inspect the
current configuration with shen_get_sdk_config_string().
shen_set_precision_mode(ctx, SHEN_PRECISION_STRICT);
shen_set_measurement_preset(ctx, SHEN_MEAS_PRESET_ONE_MINUTE_ALL_METRICS);
const char* config_json = load_config_json();
shen_apply_sdk_config(config_json);Replace load_config_json() with your config loader.
Custom measurement config
shen_custom_measurement_config_t cfg = {0};
cfg.has_duration_seconds = 1;
cfg.duration_seconds = 30.0f;
shen_metric_t instant_metrics[] = { SHEN_METRIC_HEART_RATE };
cfg.instant_metrics = instant_metrics;
cfg.instant_metrics_count = sizeof(instant_metrics) / sizeof(instant_metrics[0]);
shen_set_custom_measurement_config(ctx, &cfg);If you call shen_get_custom_measurement_config(), release the returned arrays
with shen_release_custom_measurement_config().
Preset semantics and metric meanings match the documentation in
Configuration. Use the corresponding
SHEN_MEAS_PRESET_*, SHEN_METRIC_*, and SHEN_HEALTH_INDEX_* constants.
Operating mode and state
Because there is no UI, you control the measurement flow via the API:
shen_set_operating_mode(ctx, SHEN_OPERATING_POSITIONING);
shen_set_operating_mode(ctx, SHEN_OPERATING_MEASURE);
shen_operating_mode_t mode;
shen_get_operating_mode(ctx, &mode);
shen_measurement_state_t state;
shen_get_measurement_state(ctx, &state);
float progress = 0.0f;
shen_get_progress_percent(ctx, &progress);Typical flow is to keep the SDK in SHEN_OPERATING_POSITIONING, wait until
shen_get_face_state() returns SHEN_FACE_OK, then switch to
SHEN_OPERATING_MEASURE.
You can provide your own positioning guidance using:
shen_face_state_t face_state;
shen_get_face_state(ctx, &face_state);
shen_normalized_face_bbox_t bbox;
shen_get_normalized_face_bbox(ctx, &bbox);
shen_face_pose_t pose;
shen_get_face_pose(ctx, &pose);Realtime metrics and heartbeats
shen_realtime_metrics_t metrics;
shen_get_realtime_metrics(ctx, 30.0, &metrics);
int hr_10s = 0;
shen_get_heart_rate_10s(ctx, &hr_10s);
shen_heartbeat_array_t heartbeats = {0};
shen_get_realtime_heartbeats(ctx, 30.0, &heartbeats);
shen_release_heartbeat_array(&heartbeats);Final results and history
shen_measurement_results_t exposes all final metrics. Use has_* flags to
check availability.
Results map to the metrics described in Results, with additional optional fields for blood pressure confidence, weight, and height in the C API.
shen_measurement_results_t results;
if (shen_get_measurement_results(ctx, &results) == SHEN_STATUS_OK) {
/* Results are available in results.* */
}
shen_heartbeat_array_t hb = {0};
shen_get_measurement_results_with_heartbeats(ctx, &results, &hb);
shen_release_heartbeat_array(&hb);
shen_measurement_results_history_t history = {0};
if (shen_get_measurement_results_history(ctx, &history) == SHEN_STATUS_OK) {
shen_release_measurement_results_history(&history);
}Health risks (optional)
The C API includes health risks computation. Set has_* flags for each provided
field:
shen_risks_factors_t factors = {0};
factors.has_age = 1;
factors.age = 42;
factors.has_gender = 1;
factors.gender = SHEN_GENDER_FEMALE;
shen_health_risks_t risks;
shen_compute_health_risks(ctx, &factors, &risks);Raw data, reports, and FHIR
The headless C SDK exposes the same optional outputs as other platforms:
int recording_enabled = 0;
shen_set_recording_enabled(ctx, 1);
shen_get_recording_enabled(ctx, &recording_enabled);
float signal_quality = 0.0f;
shen_get_current_signal_quality_metric(ctx, &signal_quality);
float bad_signal_seconds = 0.0f;
shen_get_total_bad_signal_seconds(ctx, &bad_signal_seconds);
shen_blob_t png = {0};
shen_get_face_texture_png(ctx, &png);
shen_release_blob(&png);
shen_blob_t quality_png = {0};
shen_get_signal_quality_map_png(ctx, &quality_png);
shen_release_blob(&quality_png);
shen_blob_t meta_png = {0};
shen_get_meta_prediction_image_png(ctx, &meta_png);
shen_release_blob(&meta_png);
shen_float_array_t signal = {0};
shen_get_full_ppg_signal(ctx, &signal);
shen_release_float_array(&signal);
shen_request_measurement_results_pdf_url(ctx);
const char* url = shen_get_measurement_results_pdf_url();
shen_request_measurement_results_pdf_bytes(ctx);
shen_blob_t pdf = {0};
if (shen_get_measurement_results_pdf_bytes(ctx, &pdf) == SHEN_STATUS_OK) {
shen_release_blob(&pdf);
}
const char* fhir = shen_get_result_as_fhir_observation();Use shen_send_measurement_results_pdf_to_email() to deliver reports by email,
and shen_send_result_fhir_observation() to push the FHIR payload to a server.
Memory ownership
Release buffers and arrays returned by the SDK using the matching helpers:
shen_release_custom_measurement_config()shen_release_heartbeat_array()shen_release_measurement_results_history()shen_release_health_risks_factors()shen_release_blob()shen_release_float_array()
Use shen_free() for any heap memory explicitly documented as allocated by the
SDK. Strings returned by functions such as shen_get_version() are owned by the
library and must not be freed.
Metadata and debug helpers
const char* version = shen_get_version();
int initialized = shen_is_initialized();
const char* plan = shen_get_pricing_plan();
const char* trace_id = shen_get_trace_id();
const char* measurement_id = shen_get_measurement_id();