Browse docs
Patient & Vital Data
Numeric API Version
The numeric release has one vital-sign model. It does not expose legacy state strings and does not
add a second vitalSignsV2 object beside the patient profile.
Every valid profile uses:
profile.vitalSchemaVersion == 2
If an integration receives another schema version, it must stop reading the profile and report an unsupported version. Do not guess values or fall back to string states.
Client Export
Use GetLocalInjuryProfile() to read the local player's active injury profile. It returns nil
when no profile is active.
local profile = exports["sky_ambulancejob"]:GetLocalInjuryProfile()
if not profile then
print("No active injury profile")
return
end
The export returns an internal snapshot for reading. Treat every returned table as read-only. Changing a field does not constitute a supported treatment and can desynchronise your local copy.
Profile Contract
local profile = {
vitalSchemaVersion = 2,
-- Resting or initial reference values.
baselineVitals = {
heart_rate = 78,
blood_pressure = {
systolic = 118,
diastolic = 76
},
oxygen_level = 98,
blood_sugar = 100
},
-- Server-calculated medical targets.
vitalTargets = {
heart_rate = 78,
blood_pressure = {
systolic = 118,
diastolic = 76
},
oxygen_level = 98,
blood_sugar = 100
},
-- Current values shown by monitors and diagnosis interfaces.
vitals = {
heart_rate = 117,
blood_pressure = {
systolic = 135,
diastolic = 76
},
oxygen_level = 98,
blood_sugar = 100
},
conditions = {
cardiacArrest = false,
activityMode = "run",
activityLoad = 0.55
}
}
Field Semantics
| Field | Meaning |
|---|---|
vitalSchemaVersion | Required schema discriminator. Numeric profiles use 2. |
baselineVitals | Resting or initial reference values. |
vitalTargets | Server-calculated targets from injuries, conditions, and treatments. |
vitals | Current values, including gradual changes and temporary activity effects. |
conditions.cardiacArrest | Explicit cardiac-arrest state. |
conditions.activityMode | Last activity mode accepted by the server. |
conditions.activityLoad | Smoothed activity load from 0.0 to 1.0. |
Accepted activity modes are rest, walk, run, sprint, swim, dive, climb, and
combat. Treat any other value as an invalid profile.
Use vitals for HUDs, bedside monitors, and live displays. vitalTargets describes where the
medical model is currently moving, not what a monitor should display at that instant.
Do not infer cardiac arrest from low blood pressure or unconsciousness. Read
conditions.cardiacArrest.
Units
| Key | Type | Unit | Accepted range |
|---|---|---|---|
heart_rate | number | beats per minute (bpm) | 0 to 220 |
blood_pressure.systolic | number | millimetres of mercury (mmHg) | 0 to 260 |
blood_pressure.diastolic | number | millimetres of mercury (mmHg) | 0 to 160 |
oxygen_level | number | oxygen saturation (% SpO2) | 0 to 100 |
blood_sugar | number | milligrams per decilitre (mg/dL) | 20 to 600 |
All values must be finite numbers. Blood pressure must be either 0/0 for cardiac arrest or have a
systolic value greater than its diastolic value.
Additional profile fields may be added in future releases. Ignore unknown fields and require a new
parser only when vitalSchemaVersion changes.
Validated Reader
The following helper rejects missing, mixed, or unsupported profiles instead of inventing values:
local accepted_activity_modes = {
rest = true,
walk = true,
run = true,
sprint = true,
swim = true,
dive = true,
climb = true,
combat = true
}
local function is_number_in_range(value, minimum, maximum)
return type(value) == "number"
and value == value
and value > -math.huge
and value < math.huge
and value >= minimum
and value <= maximum
end
local function is_vital_set(value)
if type(value) ~= "table" then return false end
local pressure = value.blood_pressure
if type(pressure) ~= "table"
or not is_number_in_range(pressure.systolic, 0, 260)
or not is_number_in_range(pressure.diastolic, 0, 160)
then
return false
end
local valid_pressure = pressure.systolic == 0 and pressure.diastolic == 0
or pressure.systolic > pressure.diastolic
return valid_pressure
and is_number_in_range(value.heart_rate, 0, 220)
and is_number_in_range(value.oxygen_level, 0, 100)
and is_number_in_range(value.blood_sugar, 20, 600)
end
local function is_valid_activity_state(conditions)
return type(conditions) == "table"
and type(conditions.cardiacArrest) == "boolean"
and accepted_activity_modes[conditions.activityMode] == true
and is_number_in_range(conditions.activityLoad, 0, 1)
end
local function get_local_vital_profile()
local profile = exports["sky_ambulancejob"]:GetLocalInjuryProfile()
if type(profile) ~= "table" then
return nil, "no_profile"
end
if profile.vitalSchemaVersion ~= 2 then
return nil, "unsupported_schema"
end
if not is_vital_set(profile.vitals) then
return nil, "invalid_vitals"
end
if not is_vital_set(profile.baselineVitals)
or not is_vital_set(profile.vitalTargets)
then
return nil, "invalid_references"
end
if not is_valid_activity_state(profile.conditions) then
return nil, "invalid_conditions"
end
return profile
end
Keep the failure reason in logs during development. Production HUDs should hide or mark unavailable data rather than display fabricated normal values.
Display Example
local profile, reason = get_local_vital_profile()
if not profile then
print("Vital profile unavailable: " .. tostring(reason))
return
end
local vitals = profile.vitals
local pressure = vitals.blood_pressure
print(("Pulse: %.0f bpm"):format(vitals.heart_rate))
print(("Blood pressure: %.0f / %.0f mmHg"):format(
pressure.systolic,
pressure.diastolic
))
print(("Oxygen: %.0f%% SpO2"):format(vitals.oxygen_level))
print(("Blood glucose: %.0f mg/dL"):format(vitals.blood_sugar))
Activity-Aware Displays
Running, sprinting, swimming, diving, or climbing can temporarily move vitals away from
vitalTargets. The server controls this behaviour.
For a live monitor:
- display
vitals; - optionally show
conditions.activityMode; - expect values to recover gradually after activity stops;
- do not overwrite the values with locally calculated Native results;
- do not treat activity alone as a medical emergency.
The client may detect activity, but the server validates the mode and calculates the actual vital changes. External resources must never submit arbitrary heart-rate, blood-pressure, oxygen, or blood-glucose values chosen by a client.
Reading Updates
There is no documented public vital-update event yet. Read the export when your interface opens or poll it at a restrained interval such as once per second for a live monitor. The export is a local lookup and does not perform a server callback.
Do not poll every frame. Cache the last values and update your UI only when the numeric data changed. A public update event will be documented here only after it exists in the released resource.
Server-Side Integrations
The public API does not currently expose a server-side patient-profile reader. Do not work around that by:
- querying or editing
ambulance_users.injury_datadirectly; - accepting vital values forwarded by an untrusted client;
- triggering internal synchronization or treatment events;
- retaining and mutating internal patient tables.
Those approaches bypass validation and can race the normal treatment and persistence flow. A public server read export must return a read-only numeric snapshot and will be documented only after it is implemented.
Changing Patient State
GetLocalInjuryProfile() is a read API. There is intentionally no public raw "set vital" event.
For custom treatments, use Custom Medications. Its server callbacks receive the authoritative patient and supported mutation helpers. Use the documented revive or heal APIs only when those exact actions are required.
All gameplay mutations must remain server-authoritative and pass the normal permission, distance, item, and treatment checks.
Migrating an Existing Integration
The numeric release is a breaking data-contract change. Update integrations before deploying it:
- Apply the current
sky_jobs_base/import.sqlandsky_ambulancejob/import.sqlmigrations. The migration requiresambulance_users,sky_jobs_configurator_settings, andsky_ambulance_vital_migration_backupto use InnoDB. - Require
profile.vitalSchemaVersion == 2. - Remove comparisons with
too_low,low,normal,high, andtoo_high. - Read numbers directly from
profile.vitals. - Read blood pressure from
systolicanddiastolic. - Apply the documented units and ranges.
- Read
conditions.cardiacArrestinstead of guessing from other values. - Treat
baselineVitalsandvitalTargetsas numeric sets. - Keep returned tables read-only.
- Avoid per-frame polling and unnecessary network events.
- Keep all writes and gameplay decisions on the server.
On the first resource start, verify the server console reports
Numeric vital migration: state=complete with errors=0. A failed or unsafe migration keeps vital
profile persistence write-protected; resolve the reported database or data errors before opening the
server to players.
Do not add a local legacy fallback. Servers must migrate the Ambulancejob resource, stored patient profiles, Job Configurator values, and dependent integrations as one coordinated major update.
Support
When opening an integration support request, include the Sky Ambulancejob version, whether your code runs on the client or server, and a redacted sample profile.
Join Discord