FiniAC Resource API
The FiniAC Resource API provides server-side integration points for your custom resources. Through events and commands, you can synchronize your queue systems with FiniAC's connection flow and trigger custom detections from your own anti-cheat logic.
Overview
The Resource API allows your resources to:
- Integrate with queue systems and connection handlers
- Trigger custom detections from your own anti-cheat logic
- Inspect, edit or cancel detections before FiniAC processes them
All API features run server-side and are designed to work seamlessly with FiniAC's detection and trigger systems.
Defer Events
FiniAC emits events during the player connection (deferral) process. These events allow you to integrate FiniAC with queue systems, custom connection handlers, or any logic that needs to run during the initial connection deferrals.
Why Use Defer Events?
Listening to FiniAC's defer events ensures that:
- Your queue system only displays after FiniAC has verified the player
- You avoid deferral message conflicts between FiniAC and your scripts
- You can make decisions based on whether FiniAC allowed or rejected the connection
- Your connection flow remains synchronized with FiniAC's security checks
Available Events
FiniAC:DeferStarted
Emitted when a player begins the connection verification process.
Parameters:
source(string): The temporary player source ID during connection
When it fires:
- Immediately after the player begins connecting
- Before any security checks are performed
- Before the player receives a permanent source ID
FiniAC:DeferFinished
Emitted when FiniAC completes all connection verification checks.
Parameters:
source(string): The player source IDjoinAllowed(boolean):trueif the player was allowed to join,falseif they were rejected
When it fires:
- After all security checks (bans, VPN, trust score, etc.)
- After Terms of Service acceptance (if required)
- Just before the player fully joins or gets kicked
Usage Examples
-- Listen for when a player starts connecting
AddEventHandler("FiniAC:DeferStarted", function(source)
print(("Player %s started FiniAC deferral"):format(source))
-- Your queue logic can start here
end)
-- Listen for when FiniAC finishes verification
AddEventHandler("FiniAC:DeferFinished", function(source, joinAllowed)
if joinAllowed then
print(("Player %s passed FiniAC deferral"):format(source))
else
print(("Player %s was rejected by FiniAC"):format(source))
end
end)// Listen for when a player starts connecting
on("FiniAC:DeferStarted", (source: string) => {
console.log(`Player ${source} started FiniAC deferral`);
// Your queue logic can start here
});
// Listen for when FiniAC finishes verification
on("FiniAC:DeferFinished", (source: string, joinAllowed: boolean) => {
if (joinAllowed) {
console.log(`Player ${source} passed FiniAC deferral`);
} else {
console.log(`Player ${source} was rejected by FiniAC`);
}
});Best Practices
TIP
- Only display your server's Terms of Service, rule acceptance, etc. deferrals after
FiniAC:DeferFinishedhas fired. - Wait until
FiniAC:DeferFinishedbefore adding players to queue to avoid banned players taking up slots.
Routing Bucket Integration
FiniAC isolates players in their own routing bucket during from loading screen until the anti-cheat is fully initialised. Using FiniAC:SetPlayerOriginalRoutingBucket ensures FiniAC can properly restore players to their correct bucket after initialisation or security checks.
Why Set Original Routing Bucket?
If your server uses an entry system that isolates new players in a custom routing bucket, you use the FiniAC:SetPlayerOriginalRoutingBucket event to inform FiniAC of routing bucket the player should be returned to. By default FiniAC returns players to the routing bucket they are in during the loading screen.
FiniAC:SetPlayerOriginalRoutingBucket Event
Use this event on the server to set the routing bucket FiniAC should return the player to after they've initialised.
Parameters:
source(string): The player's source IDbucket(number): The routing bucket number
When to use:
- Your server isolates new players in a custom routing bucket
- Your server uses multiple routing buckets for minigames, lobbies, etc.
Usage Examples
-- Example: Server isolates new players in bucket 100
local playerId = source
local queueBucket = 100
-- Set the player's routing bucket
SetPlayerRoutingBucket(playerId, queueBucket)
-- Notify FiniAC of the original bucket so it can restore later
TriggerEvent("FiniAC:SetPlayerOriginalRoutingBucket", tostring(playerId), queueBucket)// Example: Queue system places player in bucket 100
const playerId: string = source.toString();
const queueBucket: number = 100;
// Set the player's routing bucket
SetPlayerRoutingBucket(playerId, queueBucket);
// Notify FiniAC of the original bucket so it can restore later
emit("FiniAC:SetPlayerOriginalRoutingBucket", playerId, queueBucket);Best Practices
TIP
- Always call this event after setting the player's routing bucket with
SetPlayerRoutingBucket - This is only necessary if you're using custom routing buckets
- The event can only be triggered locally on the server
Detection Hooks
The AddDetectionHook export lets your resource inspect every detection before FiniAC processes it. A hook can let the detection through unchanged, edit its data, or cancel it entirely. Cancelled detections are never logged and never trigger a screenshot.
Why Use Detection Hooks?
- Suppress detections your own scripts, such as
ArmedPedbeing triggered by guards from heist scripts - Add context from your framework to a detection, such as the player's job or the inventory they had open
- Feed detections into your own logging or moderation tooling
AddDetectionHook Export
interface FiniACHookPlayer {
source: number;
name: string;
identifiers: {
license?: string;
license2?: string;
discord?: string;
fivem?: string;
xbl?: string;
live?: string;
ip?: string;
};
}
interface FiniACHookDetection {
type: string;
data: string[];
}
global.exports.FiniAC.AddDetectionHook(
(
player: FiniACHookPlayer,
detection: FiniACHookDetection,
): FiniACHookDetection | false | void => {
// return false to cancel, return detection to edit, return nothing to keep as is
},
);---@class FiniACHookPlayer
---@field source number The player's server ID
---@field name string The player's name
---@field identifiers { license?: string, license2?: string, discord?: string, fivem?: string, xbl?: string, live?: string, ip?: string }
---@class FiniACHookDetection
---@field type string The detection name, for example "NoclipV2"
---@field data (string|number)[] The detection data
---@param player FiniACHookPlayer
---@param detection FiniACHookDetection
---@return FiniACHookDetection|false|nil
exports.FiniAC:AddDetectionHook(function(player, detection)
-- return false to cancel, return detection to edit, return nil to keep as is
end)Return value:
false: The detection is cancelled- A table with the same
typeanddatashape: The detection's type and data are replaced with what you returned, then FiniAC continues processing it nilor no return: The detection continues unchanged
How detection hooks run:
- Hooks run on the server, synchronously, in the order they were registered. Each hook sees the result of the previous one.
- Hooks must not yield. Calling
Wait()inside a hook counts as an invalid return.
Registering Your Hook
FiniAC finishes starting shortly after the resource starts, so the export is not available in the first moments after start FiniAC. Two signals cover both start orders:
FiniAC:Started(server event): Fired every time FiniAC finishes starting. Register your hook in its handler, this also re-registers it after a FiniAC restart.FiniAC:Started(convar,1while FiniAC is running): Check it when your own resource starts, in case FiniAC was already running.
Usage Examples
local function registerHooks()
---@param player FiniACHookPlayer
---@param detection FiniACHookDetection
exports.FiniAC:AddDetectionHook(function(player, detection)
-- Cancel BlacklistedWeapon detections while the player in paintball
if detection.type == 'BlacklistedWeapon' and Player(player.source).state.inPaintball then
return false
end
-- Add context to a detection and let it through
if detection.type == 'BadTaze' then
table.insert(detection.data, ('job:%s'):format(MyFramework.getJob(player.source)))
return detection
end
-- Nothing returned: the detection continues unchanged
end)
end
-- FiniAC starts (or restarts) after this resource
AddEventHandler('FiniAC:Started', registerHooks)
-- FiniAC was already running when this resource started
if GetConvarInt('FiniAC:Started', 0) == 1 then
registerHooks()
end// FiniACHookPlayer and FiniACHookDetection as declared above
function registerHooks(): void {
global.exports.FiniAC.AddDetectionHook(
(player: FiniACHookPlayer, detection: FiniACHookDetection) => {
// Cancel BlacklistedWeapon detections while the player in paintball
if (
detection.type === "BlacklistedWeapon" &&
Player(player.source).state.inPaintball
) {
return false;
}
// Add context to a detection and let it through
if (detection.type === "BadTaze") {
detection.data.push(`job:${MyFramework.getJob(player.source)}`);
return detection;
}
// Nothing returned: the detection continues unchanged
},
);
}
// FiniAC starts (or restarts) after this resource
on("FiniAC:Started", registerHooks);
// FiniAC was already running when this resource started
if (GetConvarInt("FiniAC:Started", 0) === 1) {
registerHooks();
}Custom Detection Command
The fini detect command allows you to trigger FiniAC detections from your own server resources. This is useful for integrating custom anti-cheat logic, detecting exploits in your scripts, or flagging suspicious behavior that your code detects.
Command Syntax
fini detect <source> <message>Parameters:
source(string|number): The player's source ID or license identifiermessage(string): Description of the detection. Use quotes if it contains spaces.
How It Works
- Your resource detects suspicious behavior (e.g., plate swapping, inventory exploits)
- You execute
fini detectwith the player source and a description - FiniAC creates a
CommandDetectionlog entry - Your configured triggers can automatically ban, kick, or send webhooks based on the detection
Setting Up Permissions
Resources must have permission to execute the fini command. Add this to your server.cfg:
# Replace 'YourResourceName' with your actual resource name
add_ace resource.YourResourceName command.fini allowExamples:
add_ace resource.ox_inventory command.fini allow
add_ace resource.qb-smallresources command.fini allow
add_ace resource.my_anticheat command.fini allowUsage Examples
Basic Detection
-- Trigger a simple detection
local playerId = 5
ExecuteCommand(('fini detect %s "Suspicious behavior detected"'):format(playerId))
-- With spaces in the message (must use quotes)
ExecuteCommand(('fini detect %s "Player is plate swapping"'):format(playerId))// Trigger a simple detection
const playerId: number = 5;
ExecuteCommand(`fini detect ${playerId} "Suspicious behavior detected"`);
// With spaces in the message (must use quotes)
ExecuteCommand(`fini detect ${playerId} "Player is plate swapping"`);Detection from Client Event
This example shows how to trigger a detection when your server receives an event from a client, such as your custom anti-cheat detecting suspicious behavior:
-- Server-side: Listen for suspicious activity from your client-side anticheat
RegisterServerEvent("myanticheat:suspiciousActivity")
AddEventHandler("myanticheat:suspiciousActivity", function(detectionType, detectionData)
local src = source -- The player who triggered the event
local playerName = GetPlayerName(src)
-- Log the detection with FiniAC
local message = ("%s - %s - %s"):format(playerName, detectionType, detectionData)
ExecuteCommand(('fini detect %s "%s"'):format(src, message))
end)// Server-side: Listen for suspicious activity from your client-side anticheat
onNet(
"myanticheat:suspiciousActivity",
(detectionType: string, detectionData: string) => {
const src = (global as any).source; // The player who triggered the event
const playerName: string = GetPlayerName(src.toString());
// Log the detection with FiniAC
const message = `${playerName} - ${detectionType} - ${detectionData}`;
ExecuteCommand(`fini detect ${src} "${message}"`);
},
);Client-side code example:
-- Client-side: Detect suspicious activity and notify server
function DetectSuspiciousActivity(detectionType, detectionData)
TriggerServerEvent("myanticheat:suspiciousActivity", detectionType, detectionData)
end
-- Example usage
if playerIsUsingInvalidWeapon then
DetectSuspiciousActivity("InvalidWeapon", "Player has blacklisted weapon")
end// Client-side: Detect suspicious activity and notify server
function detectSuspiciousActivity(
detectionType: string,
detectionData: string,
): void {
emitNet("myanticheat:suspiciousActivity", detectionType, detectionData);
}
// Example usage
if (playerIsUsingInvalidWeapon) {
detectSuspiciousActivity("InvalidWeapon", "Player has blacklisted weapon");
}Custom detections appear in the FiniAC web panel under the Logs section with the type CommandDetection and the message containing your custom detection message.
Troubleshooting
Command Not Working
Problem: The fini detect command doesn't seem to trigger any detections.
Solutions:
- Verify ACE permissions are set correctly in
server.cfg:bashadd_ace resource.YourResourceName command.fini allow - Check that FiniAC is running and initialized
- Verify the source ID is valid and the player is connected
- Check server console for any error messages
Message Formatting Issues
Problem: The detection message appears malformed or truncated.
Solutions:
- Always wrap messages containing spaces in quotes
- Escape special characters (quotes, backslashes) in your message
- Keep messages under 1000 characters for best results
- Avoid using newlines or special formatting in messages
-- ❌ Wrong - no quotes around message with spaces
ExecuteCommand('fini detect ' .. src .. ' This will not work')
-- ✅ Correct - quotes around message
ExecuteCommand(('fini detect %s "This will work"'):format(src))
-- ✅ Correct - escaping quotes in message
local message = 'Player said "hello"'
ExecuteCommand(('fini detect %s "%s"'):format(src, message:gsub('"', '\\"')))// ❌ Wrong - no quotes around message with spaces
ExecuteCommand(`fini detect ${src} This will not work`);
// ✅ Correct - quotes around message
ExecuteCommand(`fini detect ${src} "This will work"`);
// ✅ Correct - escaping quotes in message
const message = 'Player said "hello"';
ExecuteCommand(`fini detect ${src} "${message.replace(/"/g, '\\"')}"`);