Skip to content

Latest commit

 

History

144 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Sonoran.lua

Sonoran.lua is a Lua SDK for Sonoran CAD v2 endpoints with shared client code for FiveM and Roblox runtimes.

Installation

FiveM

Install from LuaRocks:

luarocks install sonoran.lua

LuaRocks package: sonoran.lua on LuaRocks

Or use the generated FiveM resource release asset and start it before resources that consume it.

ensure Sonoran.lua

In your consuming resource:

fx_version 'cerulean'
game 'gta5'
lua54 'yes'

server_scripts {
  'server.lua'
}

dependency 'Sonoran.lua'

Roblox

Install from Wally:

[dependencies]
Sonoran = "sonoransoftwaregit/sonoran-lua@^0.1.0"

Then require the package module:

local Sonoran = require(ReplicatedStorage.Packages.Sonoran)

Usage

FiveM resource usage:

local Sonoran = require("sonoran")

local sonoran = exports["Sonoran.lua"]:createClient({
  product = Sonoran.productEnums.CAD,
  apiKey = "your-cad-api-key",
  communityId = "your-community-id",
  apiUrl = "https://api.sonorancad.com",
  defaultServerId = 1,
  timeoutMs = 30000,
  logLevel = Sonoran.logLevels.ERROR
})

sonoran:setLogLevel(Sonoran.logLevels.DEBUG)
sonoran:setRoomId(1)

Roblox usage:

local sonoran = Sonoran.createClient({
  product = Sonoran.productEnums.CAD,
  apiKey = "your-cad-api-key",
  communityId = "your-community-id",
  apiUrl = "https://api.sonorancad.com",
  defaultServerId = 1,
  timeoutMs = 30000,
  logLevel = Sonoran.logLevels.ERROR
})

sonoran:setLogLevel(Sonoran.logLevels.DEBUG)

Config

  • apiKey: required for authenticated endpoints.
  • product: required; use Sonoran.productEnums.CAD, Sonoran.productEnums.CMS, or Sonoran.productEnums.RADIO.
  • communityId: optional; used by getLoginPageV2() when no explicit communityId is supplied.
  • apiUrl: optional; defaults to https://api.sonorancad.com.
  • defaultServerId: optional; defaults to 1 for CAD/CMS-style server-scoped helpers. Radio v2 helpers resolve the community route from communityId.
  • roomId: optional for CAD/CMS and required for Radio v2 room-scoped helpers.
  • headers: optional extra headers merged into every request.
  • timeoutMs: optional timeout for the FiveM adapter; defaults to 30000.
  • logLevel: optional; Sonoran.logLevels.ERROR by default. Supported values are OFF, ERROR, and DEBUG.

Debug Logging

Use setLogLevel() to toggle HTTP debug output at runtime:

sonoran:setLogLevel(Sonoran.logLevels.DEBUG)

Use setRoomId() to update the Radio room used by later room-scoped requests without creating a new client:

sonoran:setRoomId(1)

Use ERROR to only print failed requests and rate-limit events:

sonoran:setLogLevel(Sonoran.logLevels.ERROR)

When DEBUG is enabled, Sonoran.lua prints every HTTP request and response to the console, including the method, URL, headers, body, response status, and response headers. Sensitive request values such as Authorization and API-key style fields are redacted in both DEBUG and ERROR logs.

Response Shape

All public methods return:

{ success = true, data = ... }

or:

{ success = false, reason = ... }

Successful JSON responses are decoded automatically. Plain-text error responses are returned as strings. 204 No Content responses return data = nil.

Rate Limit Handling

For CAD v2 endpoints, Sonoran.lua automatically retries 429 Too Many Requests responses up to 2 times when it can safely honor the server's wait window. The client checks Retry-After first, including both delta-seconds and HTTP-date formats, then falls back to RateLimit-Reset, X-RateLimit-Reset, and finally a short exponential backoff. If the server asks for a longer wait than the client's automatic retry limit, or the active adapter cannot sleep, the request fails instead of retrying too early.

Examples

General endpoint:

local version = sonoran.cad:getVersionV2()
if version.success then
  print(version.data)
end

Civilian endpoint:

local characters = sonoran.cad:getCharactersV2({
  roblox = 1234567890
})

if characters.success then
  print(("Found %s character(s)"):format(#characters.data))
end

Emergency endpoint:

local created = sonoran.cad:createDispatchCallV2({
  serverId = 1,
  origin = 1,
  status = 1,
  priority = 1,
  block = "123",
  address = "Main St",
  postal = "100",
  title = "Traffic Stop",
  code = "TS",
  description = "Blue sedan heading north",
  notes = {},
  communityUserIds = { "1234567890" }
})

if not created.success then
  print(json.encode(created.reason))
end

Public API

All CAD v2 helpers are available under client.cad.*. The root-level methods are still present for backward compatibility.

General

  • getLoginPageV2(params?)
  • checkApiIdV2(apiId)
  • applyPermissionKeyV2(data)
  • banUserV2(data)
  • getPenalCodesV2()
  • setPenalCodesV2(codes)
  • setApiIdsV2(data)
  • getTemplatesV2(recordTypeId?)
  • createRecordV2(data)
  • updateRecordV2(recordId, data)
  • removeRecordV2(recordId)
  • sendRecordDraftV2(data)
  • lookupV2(data)
  • lookupByValueV2(data)
  • lookupCustomV2(data)
  • getAccountV2(query?)
  • getAccountsV2(query?)
  • createCommunityLinkV2(data)
  • checkCommunityLinkV2(data)
  • setCommunityLinkV2(data)
  • setAccountPermissionsV2(data)
  • heartbeatV2(serverId, playerCount)
  • getVersionV2()
  • getServersV2()
  • setServersV2(servers, deployMap?)
  • verifySecretV2(secret)
  • authorizeStreetSignsV2(serverId?)
  • setPostalsV2(postals)
  • sendPhotoV2(data)
  • uploadBodycamRecordingV2(data)
  • getInfoV2()

Civilian

  • getCharactersV2(query?)
  • removeCharacterV2(characterId)
  • setSelectedCharacterV2(data)
  • getCharacterLinksV2(query?)
  • addCharacterLinkV2(syncId, data)
  • removeCharacterLinkV2(syncId, data)

Emergency

  • getUnitsV2(query?)
  • getCallsV2(query?)
  • getCurrentCallV2(accountUuid)
  • updateUnitLocationsV2(data)
  • setUnitPanicV2(data)
  • setUnitStatusV2(data)
  • kickUnitV2(data)
  • getIdentifiersV2(accountUuid)
  • getAccountUnitsV2(data)
  • selectIdentifierV2(accountUuid, identId)
  • createIdentifierV2(accountUuid, data)
  • updateIdentifierV2(accountUuid, identId, data)
  • deleteIdentifierV2(accountUuid, identId)
  • addIdentifiersToGroupV2(data)
  • createEmergencyCallV2(data)
  • deleteEmergencyCallV2(callId, serverId?)
  • getDispatchTemplatesV2(templateId?)
  • createDispatchCallV2(data)
  • createCustomDispatchCallV2(data)
  • updateDispatchCallV2(callId, data)
  • attachUnitsToDispatchCallV2(callId, data)
  • detachUnitsFromDispatchCallV2(data)
  • setDispatchPostalV2(callId, postal, serverId?)
  • setDispatchPrimaryV2(callId, identId, trackPrimary?, serverId?)
  • addDispatchNoteV2(callId, data)
  • closeDispatchCallsV2(callIds, serverId?)
  • updateStreetSignsV2(data)
  • setStreetSignConfigV2(signs, serverId?)
  • setAvailableCalloutsV2(callouts, serverId?)
  • getPagerConfigV2(serverId?)
  • setPagerConfigV2(data)
  • setStationsV2(config, serverId?) Sends the provided top-level station payload as-is. Pass locations, tones, and unitColors directly on the request body.
  • getBlipsV2(serverId?)
  • createBlipV2(data)
  • updateBlipV2(blipId, data)
  • deleteBlipsV2(ids, serverId?)

Notes

  • Account-targeted CAD v2 helpers accept accountUuid, communityUserId, roblox, discord, and legacy apiId where supported by the backend.
  • updateUnitLocationsV2(data) uses the HTTP v2 endpoint for slower unit location updates, and each update can target communityUserId, roblox, or discord.
  • Unit location updates can target communityUserId, roblox, or discord through the v2 HTTP endpoint.
  • FiveM uses PerformHttpRequest, promise.new(), and Citizen.Await.
  • Roblox uses HttpService:RequestAsync().
  • Radio, CMS, and legacy CAD endpoints are intentionally out of scope for this initial port.

Granular CAD permissions (v2)

Use getPermissionCatalogV2, getAccountPermissionsV2, and replaceAccountPermissionsV2 for new permission integrations. The existing setAccountPermissionsV2 remains a legacy category adapter.

local catalog = sonoran.cad:getPermissionCatalogV2()
local account = sonoran.cad:getAccountPermissionsV2(accountUuid)
local response = sonoran.cad:replaceAccountPermissionsV2(accountUuid, { "global.police" })
-- Clear all grants explicitly:
local cleared = sonoran.cad:replaceAccountPermissionsV2(accountUuid, {})

Use the account UUID, not a community user ID, in these calls. Fetch the community catalog for exact, case-sensitive grant IDs and template IDs; legacyGrants maps uppercase legacy flags to current grants. Replacement overwrites the full grant list (version 2), and an empty list clears it. Never treat a failed read as an empty list. Only pending or active non-owner accounts can be edited. Nonempty grants activate pending accounts subject to the member limit; empty grants make active accounts pending. A granular save ends legacy category inheritance for future record templates.

Full and selected-field editing

Discover support from getPermissionCatalogV2(): use record.<templateId>.edit.selected only when that exact grant is returned. It requires the updated CAD backend and may not yet be available during rollout. No SDK method or permission-document version change is required.

  • edit.own: full editing of records owned by the account.
  • edit.any: full editing of anyone's records, including the account's own records; field opt-in does not limit this grant on the updated backend.
  • edit.selected: editing only fields marked Allow limited editing (editableByOthers: true) on another account's records. It does not include edit.own or edit.any.
  • supervise: an additional requirement for supervisor-only fields; it does not grant editing by itself or bypass limited-field opt-in. Read-only fields remain locked for account editing.

Existing grants and field settings are preserved, and the new grant is not automatically assigned. For limited access, remove that template's edit.any grant from every source and assign edit.selected instead; optionally retain edit.own. Permissions from keys or role mappings may combine, and any remaining edit.any grants full editing. Fetch the account first and preserve unrelated grants when replacing its complete permission set. Never treat a failed read as an empty grant list. Community API-key record operations retain their existing service authority; these grants govern community accounts.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages