Sonoran.js is a library that allows you to interact with the Sonoran CAD, Sonoran Radio, and Sonoran CMS API. Based off of and utilizes several Discord.js library techniques for ease of use.
npm i @sonoransoftware/sonoran.jsUtilizing Sonoran CMS, Sonoran CAD & Sonoran Radio
const Sonoran = require('sonoran.js');
const instance = Sonoran.instance({
cadCommunityId: 'mycommunity',
cadApiKey: 'DF58F1E-FD8A-44C5-BA',
cmsCommunityId: 'mycommunity',
cmsApiKey: 'e6ba9d68-ca7a-4e59-a9e2-93e275b4e0bf'
radioCommunityId: 'mycommunity'
radioApiKey: 'e6ba9d68-ca7a-4e59-a9e2-93e275b4e0bf'
});Utilizing just Sonoran CMS or Sonoran CAD
const Sonoran = require('sonoran.js');
const instance = Sonoran.instance({
communityId: 'mycommunity',
apiKey: 'e6ba9d68-ca7a-4e59-a9e2-93e275b4e0bf',
product: Sonoran.productEnums.CMS
});const Sonoran = require('sonoran.js');
const instance = Sonoran.instance({
communityId: 'mycommunity',
apiKey: 'e6ba9d68-ca7a-4e59-a9e2-93e275b4e0bf',
product: Sonoran.productEnums.CMS,
serverId: 2 // Optional - The default server id for both CAD & CMS is 1
});
// This will verify the whitelist of the given API ID or account ID for server id 2 as specified above
instance.cms.verifyWhitelist('459798465498798432');
// OR
// This will verify the whitelist of the given API ID for server id 1 since I specified that
instance.cms.verifyWhitelist({
apiId: '459798465498798432',
serverId: 1
});
// OR
// This will verify the whitelist of the given account ID for server id 1 since I specified that
instance.cms.verifyWhitelist({
accId: 'd5663516-ee35-11e9-9714-5600023b2434',
serverId: 1
});Most CAD manager helpers return a CADStandardResponse<T> of { success, data?, reason? }. Legacy helpers (getAccount, setClockTime, joinCommunity, leaveCommunity) keep their original response shapes.
getVersion()- resolves to the numeric CAD subscription level.getAccount({ apiId?, username? })- fetches account details.setClockTime({ serverId, currentUtc, currentGame, secondsPerHour })- synchronizes in-game time.joinCommunity(internalKey, accounts)/leaveCommunity(internalKey, accounts)- manages community membership (requires an internal key).setPenalCodes(codes)- replaces the penal code configuration.setAccountApiIds(data)- assigns API IDs to a username.checkApiId(apiId)- confirms whether an API ID exists.applyPermissionKey(apiId?, permissionKey)- applies a permission key to an account.setAccountPermissions(changes)- bulk add/remove CAD permissions.banUser(data)- kicks or bans an account via the CAD API.verifySecret(secret)- validates a configured secret.authorizeStreetSigns(serverId)- authorizes map street-sign updates.setPostals(entries)- overwrites the postal table.sendPhoto(apiId?, url)- attaches a photo to an account.
const account = await instance.cad.getAccount({ apiId: '1234567890' });
const penalCodes = await instance.cad.setPenalCodes([
{ code: '1A', type: 'Felony', title: 'Example', bondType: 'None', jailTime: '0', bondAmount: 0 }
]);
await instance.cad.setAccountApiIds({ username: 'SomeUser', apiIds: ['1234567890'], pushNew: true });
const permissionUpdate = await instance.cad.setAccountPermissions({ apiId: '1234567890', add: ['admin'], remove: [] });getRecordTemplates(recordTypeId?)createRecord(data)/updateRecord(data)/removeRecord(id)lookupByInt(criteria)- identifier-based lookup.lookupRecords(query)- plate/name-based lookup.
await instance.cad.createRecord({ user: '1234567890', useDictionary: true, recordTypeId: 2, replaceValues: { NAME: 'Jane Doe' } });
const lookup = await instance.cad.lookupRecords({ apiId: '1234567890', types: [2], first: 'Jane', last: 'Doe', mi: '', plate: '', partial: false });getCharacters(apiId)- lists civilian characters for an API ID.createCharacter(data)/updateCharacter(data)/removeCharacter(id)- CRUD helpers for civilian profiles.
getIdentifiers(apiId)modifyIdentifier(change)/setIdentifier(apiId?, identId)setUnitPanic(apiId?, isPanic)/setUnitStatus(apiId?, status, serverId)getActiveUnits(options)- direct CAD fetch for active units.kickUnit(apiId?, reason, serverId)updateUnitLocations(locations)
getBlips(serverId)addBlips(blips)/updateBlips(blips)/removeBlip(id)setStreetSignConfig(serverId, signConfig)updateStreetSign(serverId, signData)
create911Call(details)/remove911Call(callId)getCalls(options)createDispatch(data)attachUnits(serverId, callId, units)/detachUnits(serverId, units)setCallPostal(serverId, callId, postal)/setCallPrimary(serverId, callId, primary, trackPrimary)addCallNote(serverId, callId, note)closeCall(serverId, callId)
const dispatch = await instance.cad.createDispatch({
serverId: 1,
origin: Sonoran.CADDispatchOriginEnums.Caller,
status: Sonoran.CADDispatchStatusEnums.Active,
priority: 1,
block: '123',
address: 'Main St',
postal: '100',
title: 'Traffic Stop',
code: 'TS',
primary: 42,
trackPrimary: true,
description: 'Blue sedan headed north',
metaData: {},
units: ['unit-1']
});
await instance.cad.attachUnits(1, 1001, ['unit-2']);getServers()- fetches configured CAD servers.setServers(servers, deployMap?)- updates server configuration and refreshes the cache.
const servers = await instance.cad.servers?.getServers();
await instance.cad.servers?.setServers(servers ?? [], false);CADActiveUnitsManager#getActiveUnits(options?) proxies the CAD endpoint and returns a CADStandardResponse.
const activeUnits = await cadActiveUnitsManager.getActiveUnits({ includeOffline: true, limit: 25 });
if (activeUnits.success) {
console.log(activeUnits.data);
}Returns the community's CMS subscription version.
const version = await instance.cms.getSubscriptionVersion();Verifies that a user is whitelisted in the specified server.
Type object {accId?: string, apiId?: string, username?: string, discord?: string, uniqueId?: number, serverId?: number}
Note: If passing a string for data (Account UUID or API ID) the serverId will default to 1
const params = {
accId: '',
apiId: '',
username: '',
discord: '',
uniqueId: 1234,
serverId: 1
};
// Check if user with Unique ID 1234 is whitelisted on Server ID 1
const isWhitelisted = await instance.cms.verifyWhitelist(params);Returns a full list of whitelisted users in the specified server.
// Get the full whitelist for server ID 1
const fullWhitelist = await instance.cms.getFullWhitelist(1);Returns the user's account object
Type object {accId?: string, apiId?: string, username?: string, discord?: string, uniqueId?: string}
const params = {
accId: '',
apiId: '',
username: '',
discord: '',
uniqueId: '1234',
};
// Get a user's account as an object
const getAccount = await instance.cms.getComAccount(params);Returns a user account's ranks
Type object {accId?: string, apiId?: string, username?: string, discord?: string, uniqueId?: string}
const params = {
accId: '',
apiId: '',
username: '',
discord: '',
uniqueId: '1234',
};
// Get a user's ranks
const getRanks = await instance.cms.getAccountRanks(params);Clock a user in or out in the CMS system
Type object {accId?: string, apiId?: string, forceClockIn?: boolean, discord?: string, uniqueId?: string, type?: string}
const params = {
accId: '',
apiId: '',
forceClockIn: true,
discord: '',
uniqueId: '1234',
type: 'clockin-type-uuid'
};
// Clocks a user in or out
const clock = await instance.cms.clockInOut(params);Returns the configured clock-in types.
const types = await instance.cms.getClockInTypes();
// [{ id: 'uuid', label: 'Patrol' }]Checks if a given API ID is attatched to any account within the community, and if true, returns the username of the associated account.
// Checks if API ID is attatched to a user, returns username if true
const apiIdUsername = await instance.cms.checkComApiId('1234');Gets all department information for a CMS community
// Gets department information for community
const getDepts = await instance.cms.getDepartments();Updates the CMS account's ranks using the identifiers provided.
Note: Only one identifier is required (Discord, accID, etc.) pass in undefined for variables you are not searching by
const params = {
set: ['9ad00ded-93d1-422e-8470-d2515f02652c'],
add: undefined,
remove: undefined
};
// Wipe users existing ranks, and set ones provided
// Add and Remove are undefined as we don't want to call them here
// Sets account ranks by the discord ID parameter
const setRanks = await instance.cms.setAccountRanks(params, undefined, undefined, undefined, '12345678', undefined);Sets the display name used in CMS for an account.
await instance.cms.setAccountName(undefined, undefined, 'account-uuid', undefined, undefined, 'New Display Name');Adds a ban flag to the targeted account.
await instance.cms.cmsBanAccount({ apiId: '1234' });Performs a CMS kick request for the targeted account.
await instance.cms.cmsKickAccount({ discord: '1234567890' });Manually triggers a CMS force-sync for the targeted identifiers.
await instance.cms.forceSync({ username: 'SomeUser' });Fetches the configured promotion flows.
const flows = await instance.cms.getPromotionFlows();Executes promotion or demotion flows for one or more users.
await instance.cms.triggerPromotionFlows([{
userId: 'u-123',
flowId: 'flow-abc',
users: ['u-123', 'u-456'],
promote: true
}]);Fetches the current clock-in entry for the account if one exists.
const currentEntry = await instance.cms.getCurrentClockIn({ apiId: '1234' });Gets the latest clock-in or activity entries for an account.
// Clock-in history
const clockins = await instance.cms.getLatestActivity({ accId: 'account-uuid', type: 'clockin' });
// Activity history (requires serverId)
const activity = await instance.cms.getLatestActivity({ accId: 'account-uuid', type: 'activity', serverId: 1 });Returns an array of clock-in logs (AccountClockInLog) or activity logs (AccountActivityLog); each item includes objKey alongside the other fields.
Retrieves CMS accounts with optional pagination and status filters.
const accounts = await instance.cms.getAccounts({ take: 50, banned: false });Returns profile field definitions configured for the community.
const profileFields = await instance.cms.getProfileFields();Toggles RSVP for an event for the provided account identifiers.
await instance.cms.rsvp('event-id', { accId: 'account-uuid' });Retrieves form submissions with optional pagination.
const submissions = await instance.cms.getFormSubmissions(42, { skip: 0, take: 25 });Moves a form to the specified stage for an account.
await instance.cms.changeFormStage({
formId: 42,
newStageId: 'approved',
accId: 'account-uuid',
uniqueId: 1234
});Updates profile fields for an account.
await instance.cms.editAccountProfileFields({
accId: 'account-uuid',
profileFields: [
{ fieldId: 10, value: 'Value' }
]
});Returns the current ERLC player list for the join code.
const players = await instance.cms.erlcGetOnlinePlayers('join-code');Returns the current ERLC player queue count for the join code.
const queue = await instance.cms.erlcGetPlayerQueue('join-code');Adds a moderation record for a player in ERLC.
await instance.cms.erlcAddNewRecord({
robloxJoinCode: 'join-code',
executerDiscordId: '1234567890',
type: 'Warning',
reason: 'Reckless driving'
});Fetches the configured CMS game servers. Returns an array of server objects.
const cmsServers = await instance.cms.servers?.getGameServers();Replaces the configured CMS game servers and refreshes the cache with the response payload.
await instance.cms.servers?.setGameServers([
{ name: 'Server 1', description: 'Primary server', allowedRanks: ['admin'] }
]);Retrieves configured community channel groups and channels.
const channels = await instance.radio.getCommunityChannels();Lists all connected radio users in the community.
const users = await instance.radio.getConnectedUsers();Fetches a specific connected radio user by room and identity.
const user = await instance.radio.getConnectedUser(1, 'account-uuid');Updates a user's transmit or scan channels.
await instance.radio.setUserChannels('account-uuid', { transmit: 12, scan: [10, 11, 12] });Sets the user's radio display name.
await instance.radio.setUserDisplayName('account-uuid', 'Dispatch 101');Resolves the community's subscription level for the calling server IP.
const subscription = await instance.radio.getServerSubscriptionFromIp();Registers the push event URL for radio webhooks.
await instance.radio.setServerIp('https://example.com/sonoran-radio');Publishes available in-game speaker locations for tone routing.
await instance.radio.setInGameSpeakerLocations(
[{ name: 'Station 1', x: 123.4, y: 567.8, z: 90.1 }],
'optional-bearer-token'
);Dispatches tones to channels, groups, or in-game speakers.
await instance.radio.playTone(1, [1001, 1002], [
{ label: 'Primary Dispatch', type: 'channel', value: 10, group: 2 },
{ label: 'Station Speakers', type: 'game', value: 'station-1', group: null }
]);More documentation for Sonoran CAD specific methods and usage can be found here, Sonoran CMS specific methods and usage can be found here, and usage information for the REST class here.