Exports & API Reference
The developer surface of oxide-ems — exports, global state, player and entity statebags, events, and the compatibility surface.
The developer surface of oxide-ems — what other resources can read, call and listen to.
oxide-ems is a consumer of o-link, not a provider. It does not register an o-link namespace, so there is no olink.ems.*. Integration happens through the exports, events and statebags below.
Exports
Server
-- Read one setting's current effective value (the database value, not the file default).
local interval = exports['oxide-ems']:GetSetting('SalaryInterval')
-- Write one setting. Persists to the database, mirrors to clients and applies live.
exports['oxide-ems']:SetSetting('SalaryInterval', 600000)GetSetting takes any top-level setting key — the same keys documented in Configuration (Revive, Beds, Surgery, Dispatch, and so on) — and returns the whole value, table or scalar.
SetSetting writes it back and always returns true. It performs no validation, so prefer the in-game editor (/ems settings) for anything a human is doing; this export is for scripted setup.
Server (QBCore only)
local doctors = exports['oxide-ems']:GetDoctorCount()Number of medics currently on duty, across every department. Registered only when QBCore is the detected framework, as the replacement for the export qb-ambulancejob provided.
The matching QBCore callback is also registered:
QBCore.Functions.TriggerCallback('hospital:GetDoctors', function(count) end)On any framework, the same number is always readable from GlobalState['oxide:ems:onduty'] — that is the portable way to ask.
Global state
Server-written, readable from anywhere.
| Key | Value | Notes |
|---|---|---|
oxide:ems:onduty | number | Total on-duty medics across all departments. |
oxide:ems:beds:<facilityId> | { total, occupied } | total = placed beds at that hospital. occupied is a map of bed index (as a string) to true. |
oxide:ems:xraymachine:<facilityId> | { [pointId] = patientServerId } | Which x-ray machines have a patient positioned in them. pointId is a string. |
oxide:ems:surgerytable:<facilityId> | { [pointId] = { src, name } } | Which operating tables are occupied, and by whom. |
oxide-ems:config | string[] | The list of setting keys currently mirrored to clients. |
oxide-ems:config:<key> | (varies) | One setting's value, mirrored per key so clients can read the effective config without a callback. |
Global state survives a resource restart, so these are republished at boot.
Player statebags
Replicated, so any client can read them for any player.
oxide:ems:duty
Player(src).state['oxide:ems:duty']
-- { deptId, deptName, gradeRank, facilityId } -- on duty
-- nil -- off dutyThe authoritative duty state. Presence means on duty. This is the right thing for a HUD or a third-party script to read — do not keep your own mirror, because duty can be set and cleared server-side (a firing, a department deletion, a multi-job switch) without the player's client ever asking for it.
oxide:ems:triage
Player(src).state['oxide:ems:triage']
-- { critical, stabilized, paused, expiresAt, serverNow } -- downed or dead
-- nil -- otherwise| Field | Meaning |
|---|---|
critical | Too badly hurt for a street revive — must be stabilized, transported and revived at a bed. |
stabilized | A medic has stabilized them; their bleedout timer is frozen. |
paused | The superset a client should freeze on: stabilized or under a procedure hold (a medic is actively working on them). |
expiresAt | When the stabilization lapses, as a Unix timestamp in seconds. Absent when not stabilized. |
serverNow | The server's clock at the moment the bag was written. Compute expiresAt - serverNow when you receive it and count down locally — no clock sync needed. |
oxide:ems:downed
Player(src).state['oxide:ems:downed']
-- { phase = 'downed'|'dead', bleedoutEnd?, respawnEnd?, serverNow }
-- nil when aliveOnly written on QBCore and ESX, where oxide-ems runs the downed/dead loop. On Qbox, qbx_medical owns that state and this bag stays nil; on Oxide Core, the death provider owns it.
bleedoutEnd is present in the downed phase, respawnEnd in the dead phase, both as Unix timestamps in seconds with the same serverNow countdown pattern.
For a portable downed check across all frameworks, use o-link instead:
if olink.death.IsPlayerDowned(src) or olink.death.IsPlayerDead(src) then ... endEntity statebags — fleet vehicles
Written by the server on every spawned fleet vehicle. Clients never write these.
| Key | Value | Notes |
|---|---|---|
oxide:ems:fleet | number | The vehicle's database row id. This is the fleet identity — never trust a bare network id to identify a fleet vehicle across sessions. |
oxide:ems:fleetState | { plate, properties, livery, extras, fuel, body_health, engine_health } | The visual and mechanical state the first owning client applies. |
oxide:ems:fleetDecorated | true | Set once a client has applied the above. |
oxide:ems:bodyHealth / oxide:ems:engineHealth | number | Server-mirrored damage, fed by driver telemetry. Server-side health natives are unreliable while a client owns the entity, so read these instead. |
oxide:plate | string | The server-issued plate. A cross-resource convention shared with other Oxide resources. |
Events
Server-side, safe to listen to
AddEventHandler('oxide:ems:server:initialized', function() end)
AddEventHandler('oxide:ems:server:settingsReady', function() end)
AddEventHandler('oxide:ems:server:departmentsChanged', function(reason, data) end)
AddEventHandler('oxide:ems:server:respawnPointsChanged', function(points) end)
AddEventHandler('oxide:ems:medicJobMirror', function(action, charId, jobName, gradeRank, jobLabel) end)| Event | Fires when |
|---|---|
oxide:ems:server:initialized | Every module has loaded and the resource is ready. |
oxide:ems:server:settingsReady | Settings have been rebuilt from the database. Anything that reads a setting at boot must wait for this. |
oxide:ems:server:departmentsChanged | Any department, facility, grade, garage or vehicle changed. reason is a short description of what changed (department created, facility updated, …); data carries the affected ids. |
oxide:ems:server:respawnPointsChanged | The hospital respawn point registry was rebuilt. Carries the full list. |
oxide:ems:medicJobMirror | A medic was hired, promoted or fired. action is hired, gradeChanged or fired. This is what the g-multijob bridge listens to; use it to mirror EMS employment into your own systems. |
Client-side, safe to listen to
RegisterNetEvent('oxide:ems:departmentsUpdated', function() end)
RegisterNetEvent('oxide:ems:medicDataChanged', function() end)
RegisterNetEvent('oxide:ems:forceClockOut', function() end)
AddEventHandler('oxide-ems:client:configUpdated', function(key) end)| Event | Fires when |
|---|---|
oxide:ems:departmentsUpdated | Department data changed; clients rebuild their interaction points and blips. |
oxide:ems:medicDataChanged | This player's employment changed. |
oxide:ems:forceClockOut | The server clocked this player out. |
oxide-ems:client:configUpdated | A setting changed. key is the setting that changed, or nil on the initial full load. Local to this resource's client scope. |
Internal events — do not trigger these
These carry the resource's own state between its server and client sides. They validate their input, so triggering them from outside will not corrupt anything, but they are not an API and their shapes can change:
oxide:ems:client:bedEnter / bedExit / bedHealTick, vehicleEnter / vehicleExit, stretcherAttach / stretcherDetach, xrayEnter / xrayExit / xrayScreen, surgeryEnter / surgeryExit, runRespawnCutscene, openSettings, runDiag, oxide:ems:openAdminPanel, oxide:ems:vehicleRelinked / vehicleUntracked / vehicleDestroyed, and the oxide:ems:server:* net events (distress, reportDeath, reportReinjury, surgeryLeave, xrayLeave, xrayClearScreen, compat:qb:enterLaststand, reportVehicleState, reportVehicleType, fleetDecorated).
Compatibility surface
These are the events your framework's ambulance job used to own. oxide-ems re-registers them so existing resources keep working. Trigger them exactly as you did before.
All frameworks
| Event | Direction | Effect |
|---|---|---|
hospital:server:emergencyAlert | client → server | Raises an EMS alert at the caller's position and notifies on-duty medics. Net-triggered only. |
hospital:server:ambulanceAlert (text) | client → server | Same, with a custom message. This is what police "officer down" relays use. Net-triggered only. |
hospital:client:CheckStatus | server → client | Opens the examine overlay on the nearest patient. |
hospital:client:TreatWounds | server → client | Same. |
hospital:client:RevivePlayer | server → client | Runs the normal validated revive on the nearest downed person. |
QBCore only
| Event | Direction | Effect |
|---|---|---|
hospital:client:Revive | server → client | Fully revives and heals the target. |
hospital:client:KillPlayer | server → client | Kills the target. |
hospital:client:SetLaststand | server → client | Puts the target into the downed phase. Stock QBCore never registered this; oxide-ems does, which is what makes o-link's DownPlayer work on QBCore. |
hospital:client:adminHeal | server → client | Fully heals and resets hunger and thirst. |
hospital:server:SetDeathStatus (bool) | client → server | Keeps the isdead metadata accurate. |
hospital:server:SetLaststandStatus (bool) | client → server | Keeps the inlaststand metadata accurate. |
hospital:server:resetHungerThirst | client → server | Resets hunger and thirst to full. |
qb-inventory, qb-phone, qb-hud and qb-radialmenu all read the isdead and inlaststand metadata, and oxide-ems keeps both truthful through every transition.
ESX only
| Event | Direction | Effect |
|---|---|---|
esx_ambulancejob:revive (playerId) | either | Revives the target. Triggered server-side by another resource it acts immediately; triggered over the net, the caller must be an on-duty medic or an admin. |
The Player(src).state.isDead statebag is kept accurate throughout, and esx:onPlayerSpawn (plus esx_basicneeds:resetStatus and playerSpawned) is re-fired after every revive and respawn so ESX's own death detection and its add-ons re-arm.
txAdmin
txAdmin:events:healedPlayer is handled on QBCore and ESX — the heal button in the txAdmin panel revives the target, including the "heal everyone" form.
Usable items
The medical items your framework's ambulance job registered are re-registered by oxide-ems, with their original healing behaviour and one improvement: the item is only consumed when the action completes, where the stock resources consumed it up front.
| Framework | Items |
|---|---|
| QBCore | ifaks, bandage, painkillers, firstaid |
| Qbox | ifaks, bandage, painkillers, firstaid |
| ESX | medikit, bandage |
firstaid routes into the normal revive flow on the nearest downed person, and the revive pipeline consumes it itself on success — it is never removed twice.
medical_bag is registered on every framework and unpacks into field supplies.
Integrating a dispatch resource
oxide-ems raises alerts through olink.dispatch.CreateAlert. Among the dispatch adapters o-link ships, oxide-dispatch is the only one that implements the server-side CreateAlert — the others provide a client-side send only.
With any other dispatch resource, alerts are skipped and a debug line is logged; nothing errors. On-duty medics still receive the direct notifications for distress signals, /911e, clinic reception pages and bed-request alerts, because those do not go through dispatch. Only the automatic down and death alerts depend on it entirely.
Alerts are sent with the code 10-52 (or 911 for /911e), a heart-pulse icon, and a job list containing every enabled EMS department name plus the extra jobs configured under Dispatch.