FiveM integration
CreatorOS connects to game servers through a small downloadable resource (integrations/fivem/creatoros-resource) and an ingestion API authenticated by a per-server key.
How it fits together
- Dashboard → Game servers → Add server creates a
game_serversrow and a server API key. Only the SHA-256 hash and a prefix are stored; the key is shown once. - The resource sends a heartbeat every 30 s with status, counts and the player list, and reports joins/drops as they happen.
- The public server page (
/servers/{id}) and/live/server/{id}.jsonrender a privacy-filtered view that the website polls every 10 s. - When a purchase includes a FiveM deliverable, a
delivery_jobsrow withchannel = fivemis queued. The resource polls pending entitlements, applies them (ESX / QBCore / ACE) and acks each by id so grants are applied exactly once. - A scheduler task marks servers offline after 120 s without a heartbeat and closes their sessions.
Install (server owners)
set platform_api_key "srv_..." # from the dashboard, shown once
set platform_server_id "12"
set platform_api_base "https://krunc.com/api"
ensure creatoros-resource
Ensure es_extended or qb-core before the resource if you want character info and framework-native grants. See the resource README.md for troubleshooting.
Ingestion API
Base: /api/v1/servers/{serverId} — header X-Server-Key: srv_... — JSON bodies. Rate limited per server key (240 req/min).
| Method | Path | Body | Response |
|---|---|---|---|
| POST | /heartbeat | status?, player_count, max_players?, queue_length?, uptime_seconds?, resource_version?, players?[] (slot,name,license,discord,steam,character,job,zone), zones?[] (zone,count) | {ok, pending_entitlements} |
| POST | /players/connect | name, slot?, license?, discord?, steam?, character?, job? | {ok, session_id} |
| POST | /players/disconnect | slot or license, reason? | {ok, closed} |
| POST | /players/update | license or slot, character?, job?, zone?, x?, y? | {ok, updated} |
| GET | /entitlements/pending | — | {ok, entitlements:[{id, action, kind, target, amount, license, discord_id}]} — marks them sent |
| POST | /entitlements/{id}/ack | {ok:bool, error?} | {ok, status} — idempotent |
Rules enforced server-side:
zoneis stored only when the server enables the zone map;x/yonly when *admin exact positions* is enabled.- Player sessions are reconciled against the heartbeat's
playerslist: anyone missing is marked disconnected. - A heartbeat with
status: "offline"zeroes the count and closes all sessions. - Each heartbeat fires the
server.player_count_aboveautomation trigger.
Privacy model (§9)
| Field | Shown publicly when |
|---|---|
| Player row | player show_online and server show_player_list |
| Username | player show_username (otherwise "Anonymous player") |
| Character / job | player show_character and server show_characters |
| Zone | player show_location and server show_zone_map |
| Zone map counts | server show_zone_map; zones under zone_min_count are displayed as <n |
| Exact positions | never public; admin map only, only if admin_exact_positions (viewing is audit-logged) |
Entitlement kinds
role/group/permission (target = group name), credits/money (amount), item (target + amount), command (uses Config.CustomCommands). Revokes (refund, subscription ended) are delivered with action: "revoke".
Dashboard pages
/dashboard/servers— list, add server/dashboard/servers/{id}— install snippet, rotate key, visibility settings, delivery queue, recent sessions/dashboard/servers/{id}/players— full session history with search/servers/{id}/admin-map— admin-only unfiltered view