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

  1. Dashboard → Game servers → Add server creates a game_servers row and a server API key. Only the SHA-256 hash and a prefix are stored; the key is shown once.
  2. The resource sends a heartbeat every 30 s with status, counts and the player list, and reports joins/drops as they happen.
  3. The public server page (/servers/{id}) and /live/server/{id}.json render a privacy-filtered view that the website polls every 10 s.
  4. When a purchase includes a FiveM deliverable, a delivery_jobs row with channel = fivem is queued. The resource polls pending entitlements, applies them (ESX / QBCore / ACE) and acks each by id so grants are applied exactly once.
  5. 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).

MethodPathBodyResponse
POST/heartbeatstatus?, 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/connectname, slot?, license?, discord?, steam?, character?, job?{ok, session_id}
POST/players/disconnectslot or license, reason?{ok, closed}
POST/players/updatelicense 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:

  • zone is stored only when the server enables the zone map; x/y only when *admin exact positions* is enabled.
  • Player sessions are reconciled against the heartbeat's players list: anyone missing is marked disconnected.
  • A heartbeat with status: "offline" zeroes the count and closes all sessions.
  • Each heartbeat fires the server.player_count_above automation trigger.

Privacy model (§9)

FieldShown publicly when
Player rowplayer show_online and server show_player_list
Usernameplayer show_username (otherwise "Anonymous player")
Character / jobplayer show_character and server show_characters
Zoneplayer show_location and server show_zone_map
Zone map countsserver show_zone_map; zones under zone_min_count are displayed as <n
Exact positionsnever 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