Skip to content

Server Hooks

Server hooks allow you to react to events on the server side. Use these to integrate with external systems (MDT, Discord, dashboards), log activity, trigger custom server logic, or extend Advanced Crafting functionality.

How to Use Hooks

Hooks in sd-crafting use a callback registration system, not standard FiveM events. Register handlers using either:

From within the sd-crafting resource (in server/hooks.lua):

lua
ServerHooks.Register('onCraftCompleted', function(data)
    print(('[SD-CRAFTING] %s crafted %dx %s'):format(
        data.identifier, data.totalOutputCount, data.outputItem))
end)

From another resource (via export):

lua
exports['sd-crafting']:registerServerHook('onCraftCompleted', function(data)
    print(('[SD-CRAFTING] %s crafted %dx %s'):format(
        data.identifier, data.totalOutputCount, data.outputItem))
end)

Both methods return a handlerId that can be used to unregister the hook later:

lua
local id = exports['sd-crafting']:registerServerHook('onCraftCompleted', function(data) ... end)
exports['sd-crafting']:unregisterServerHook('onCraftCompleted', id)

All hook callbacks receive a single data table with event-specific fields. Hooks are fire-and-forget — returning a value from a callback or throwing an error from it does not cancel the underlying action, and every callback is wrapped in pcall so a buggy handler from one integration cannot break others.


Crafting Lifecycle Events

onCraftStarted

Triggered after ingredients have been successfully consumed (and any required cost deducted) at the start of a craft. A unique craftToken is included that the server uses to validate the matching onCraftCompleted / onCraftRefunded event later.

lua
ServerHooks.Register('onCraftStarted', function(data)
    print(('[SD-CRAFTING] %s started crafting %dx %s at %s'):format(
        data.identifier, data.quantity, data.recipeLabel, data.stationId))
end)
ParameterTypeDescription
sourcenumberPlayer server ID
identifierstringPlayer character identifier (citizenid / license)
recipeIdstringRecipe identifier
recipeLabelstringRecipe display name
quantitynumberNumber of items being crafted
stationIdstringStation identifier (e.g. 'workbench', 'placed_42')
ingredientstableArray of { item, amount, label } consumed
costnumberTotal cash deducted for this craft (0 if free)
craftTimenumberTotal craft duration in seconds (recipe.craftTime * quantity)
craftTokenstringUnique token tying this craft to its completion/refund
timestampnumberUnix timestamp

onCraftCompleted

Triggered after a craft has successfully completed and output items have been granted (either to the player's inventory or to the crafting stash). Fires for both fully-successful and partially-successful crafts; pure failures fire onCraftFailed instead.

lua
ServerHooks.Register('onCraftCompleted', function(data)
    print(('[SD-CRAFTING] %s crafted %dx %s (%d successful, %d failed)'):format(
        data.identifier, data.totalOutputCount, data.outputItem,
        data.successfulCrafts, data.failedCrafts))
end)
ParameterTypeDescription
sourcenumberPlayer server ID
identifierstringPlayer character identifier
recipeIdstringRecipe identifier
recipeLabelstringRecipe display name
quantitynumberNumber of items the player requested
successfulCraftsnumberNumber of items that passed the quality roll
failedCraftsnumberNumber of items that failed (partial-failure mode only)
outputItemstringItem name that was granted
outputAmountnumberOutput amount per craft (recipe.outputAmount, defaults to 1)
totalOutputCountnumberTotal items granted (successfulCrafts * outputAmount)
addedToStashbooleanWhether output went to the crafting stash instead of player inventory
stationIdstringStation identifier
workbenchTypestring|nilWorkbench type (e.g. 'basic', 'advanced')
blueprintDestroyedbooleanWhether the recipe's blueprint was destroyed by this craft
blueprintItemstring|nilBlueprint item name if destroyed
timestampnumberUnix timestamp

onCraftFailed

Triggered when a craft fails the quality roll (controlled by recipe.failChance). Ingredients have already been consumed at this point — they are not refunded.

lua
ServerHooks.Register('onCraftFailed', function(data)
    print(('[SD-CRAFTING] %s failed %dx %s (%s)'):format(
        data.identifier, data.failedCrafts, data.recipeLabel, data.reason))
end)
ParameterTypeDescription
sourcenumberPlayer server ID
identifierstringPlayer character identifier
recipeIdstringRecipe identifier
recipeLabelstringRecipe display name
quantitynumberNumber of items the player requested
failedCraftsnumberNumber of items that failed
failChancenumberThe recipe's configured fail chance percentage
stationIdstringStation identifier
workbenchTypestring|nilWorkbench type
reasonstring'quality_check_whole_batch' or 'quality_check_all_items_failed'
timestampnumberUnix timestamp

onCraftRefunded

Triggered when a queued craft is cancelled and its ingredients/cost are returned to the player or to the staging inventory.

lua
ServerHooks.Register('onCraftRefunded', function(data)
    print(('[SD-CRAFTING] %s cancelled %dx %s'):format(
        data.identifier, data.quantity, data.recipeLabel))
end)
ParameterTypeDescription
sourcenumberPlayer server ID
identifierstringPlayer character identifier
recipeIdstringRecipe identifier
recipeLabelstringRecipe display name
quantitynumberNumber of items refunded
stationIdstringStation identifier
refundedIngredientstableArray of { item, amount, label } returned
refundedCostnumberCash returned (0 if recipe had no cost)
timestampnumberUnix timestamp

Staging Inventory Events

onItemStaged

Triggered when a player moves an item from their personal inventory into the crafting staging area.

lua
ServerHooks.Register('onItemStaged', function(data)
    print(('[SD-CRAFTING] %s staged %dx %s at %s'):format(
        data.identifier, data.count, data.item, data.stationId))
end)
ParameterTypeDescription
sourcenumberPlayer server ID
identifierstringPlayer character identifier
stationIdstringStation identifier
itemstringItem name
labelstringItem display label
countnumberQuantity staged
slotnumber|nilTarget staging slot
durabilitynumber|nilItem durability if applicable (ox_inventory only)
metadatatable|nilFull item metadata table if non-empty
timestampnumberUnix timestamp

onItemUnstaged

Triggered when a player moves an item out of the crafting staging area back into their personal inventory.

lua
ServerHooks.Register('onItemUnstaged', function(data)
    print(('[SD-CRAFTING] %s unstaged %dx %s from %s'):format(
        data.identifier, data.count, data.item, data.stationId))
end)
ParameterTypeDescription
sourcenumberPlayer server ID
identifierstringPlayer character identifier
stationIdstringStation identifier
itemstringItem name
countnumberQuantity unstaged
slotnumber|nilSource staging slot
timestampnumberUnix timestamp

Shared Queue Events

onQueueItemAdded

Triggered when a craft is added to the shared queue of a station (only fires for stations with CraftingBehavior.sharedCrafting enabled).

lua
ServerHooks.Register('onQueueItemAdded', function(data)
    print(('[SD-CRAFTING] %s queued a craft at %s (queue length: %d)'):format(
        data.identifier, data.stationId, data.queueLength))
end)
ParameterTypeDescription
sourcenumberPlayer server ID that added the item
identifierstringOwner character identifier
stationIdstringStation identifier
queueItemtableThe full queue item (id, recipe, quantity, totalTime, owner, ownerName, etc.)
queueLengthnumberNew queue length after the addition
timestampnumberUnix timestamp

onQueueItemRemoved

Triggered when an item is removed from a shared queue, either via cancellation by its owner or by a privileged player removing the currently-processing item.

lua
ServerHooks.Register('onQueueItemRemoved', function(data)
    print(('[SD-CRAFTING] Queue item %s removed at %s'):format(
        data.queueItemId, data.stationId))
end)
ParameterTypeDescription
sourcenumberPlayer server ID that removed the item
identifierstringCharacter identifier of the remover
stationIdstringStation identifier
queueItemIdstringThe queue item ID that was removed
removedByOwnerbooleanWhether the remover was the item's original owner
timestampnumberUnix timestamp

onQueueItemCompleted

Triggered when the first item in a shared queue is completed and removed by the server. Fires regardless of who owns the completed item.

lua
ServerHooks.Register('onQueueItemCompleted', function(data)
    print(('[SD-CRAFTING] Queue item completed at %s (remaining: %d)'):format(
        data.stationId, data.queueLength))
end)
ParameterTypeDescription
sourcenumberPlayer server ID that triggered the completion
identifierstringCharacter identifier of the triggering player
stationIdstringStation identifier
queueLengthnumberQueue length after the completion
timestampnumberUnix timestamp

Blueprint Events

onBlueprintAttached

Triggered when a player attaches a blueprint to a station, unlocking the recipes it gates.

lua
ServerHooks.Register('onBlueprintAttached', function(data)
    print(('[SD-CRAFTING] %s attached blueprint %s to %s (durability: %s)'):format(
        data.identifier, data.blueprintItem, data.stationId,
        tostring(data.durability or 'n/a')))
end)
ParameterTypeDescription
sourcenumberPlayer server ID
identifierstringPlayer character identifier
stationIdstringStation identifier
blueprintItemstringBlueprint item name
durabilitynumber|nilBlueprint durability at the time of attachment (ox_inventory only)
slotnumber|nilSource inventory slot the blueprint was taken from
timestampnumberUnix timestamp

onBlueprintDetached

Triggered when a player detaches a blueprint from a station and returns it to their inventory.

lua
ServerHooks.Register('onBlueprintDetached', function(data)
    print(('[SD-CRAFTING] %s detached blueprint %s from %s (via %s)'):format(
        data.identifier, data.blueprintItem, data.stationId, data.sourceLocation))
end)
ParameterTypeDescription
sourcenumberPlayer server ID
identifierstringPlayer character identifier
stationIdstringStation identifier
blueprintItemstringBlueprint item name
durabilitynumber|nilRemaining durability when detached
sourceLocationstring'station' (attached natively) or 'staging' (held in staging inventory)
timestampnumberUnix timestamp

Tech Tree Events

onTechNodeUnlocked

Triggered when a player spends tech points to unlock a node in a tech tree. Fires for both player-based progression and shared-workbench progression.

lua
ServerHooks.Register('onTechNodeUnlocked', function(data)
    print(('[SD-CRAFTING] %s unlocked %s (%s) for %d points'):format(
        data.identifier, data.nodeLabel, data.treeLabel, data.cost))
end)
ParameterTypeDescription
sourcenumberPlayer server ID
identifierstringPlayer character identifier
treeIdstringTech tree identifier
nodeIdstringTech node identifier
nodeLabelstringNode display label
treeLabelstringTree display label
costnumberTech points spent
remainingPointsnumberTech points the unlocker has left
stationIdstringStation identifier where the unlock happened
workbenchTypestring|nilWorkbench type (when per-workbench tech is enabled)
isSharedbooleantrue if this unlock applied to a shared-workbench tech pool, false if to player progression
workbenchIdnumber|nilNumeric placed-workbench ID (shared unlocks only)
timestampnumberUnix timestamp

Workbench Placement Events

onWorkbenchPlaced

Triggered after a player successfully places a portable workbench in the world and the database row has been created.

lua
ServerHooks.Register('onWorkbenchPlaced', function(data)
    print(('[SD-CRAFTING] %s placed workbench %d (%s) at %.2f, %.2f, %.2f'):format(
        data.identifier, data.workbenchId, data.item,
        data.coords.x, data.coords.y, data.coords.z))
end)
ParameterTypeDescription
sourcenumberPlayer server ID
identifierstringOwner character identifier
workbenchIdnumberDatabase ID of the new workbench
itemstringWorkbench item name
workbenchTypestringWorkbench type
propstringProp model name
coordsvector3Placement coordinates
headingnumberPlacement heading
techIdstring|nilPersistent tech ID for shared tech data (reused across pickup/placement)
timestampnumberUnix timestamp

onWorkbenchPickedUp

Triggered when the owner of a placed workbench picks it up. Fires before the workbench database row is deleted, but data fields reflect the workbench as it existed.

lua
ServerHooks.Register('onWorkbenchPickedUp', function(data)
    print(('[SD-CRAFTING] %s picked up workbench %d (%s)'):format(
        data.identifier, data.workbenchId, data.item))
end)
ParameterTypeDescription
sourcenumberPlayer server ID
identifierstringOwner character identifier
workbenchIdnumberDatabase ID of the workbench
itemstringWorkbench item name
workbenchTypestringWorkbench type
coordsvector3|nilLast known coordinates
timestampnumberUnix timestamp

Station Session Events

onStationOpened

Triggered when a player opens a station (the server-side session is registered in OpenStations).

lua
ServerHooks.Register('onStationOpened', function(data)
    print(('[SD-CRAFTING] %s opened station %s'):format(data.identifier, data.stationId))
end)
ParameterTypeDescription
sourcenumberPlayer server ID
identifierstringPlayer character identifier
stationIdstringStation identifier
timestampnumberUnix timestamp

onStationClosed

Triggered when a player closes a station and the server-side session is cleared.

lua
ServerHooks.Register('onStationClosed', function(data)
    print(('[SD-CRAFTING] %s closed station %s'):format(data.identifier, data.stationId))
end)
ParameterTypeDescription
sourcenumberPlayer server ID
identifierstringPlayer character identifier
stationIdstringStation identifier
timestampnumberUnix timestamp

onShopOpened

Triggered when a player opens a crafting shop (the in-resource NPC shops, not sd-shops). The proximity check passes before this fires.

lua
ServerHooks.Register('onShopOpened', function(data)
    print(('[SD-CRAFTING] %s opened shop %s'):format(data.identifier, data.shopId))
end)
ParameterTypeDescription
sourcenumberPlayer server ID
identifierstringPlayer character identifier
shopIdstringShop identifier
timestampnumberUnix timestamp

onShopClosed

Triggered when a player closes a crafting shop and the server-side session is cleared.

lua
ServerHooks.Register('onShopClosed', function(data)
    print(('[SD-CRAFTING] %s closed shop %s'):format(data.identifier, data.shopId))
end)
ParameterTypeDescription
sourcenumberPlayer server ID
identifierstringPlayer character identifier
shopIdstringShop identifier
timestampnumberUnix timestamp

Shop Purchase Events

onShopItemPurchased

Triggered when a player successfully purchases an item from a crafting shop and the item has been added to their inventory.

lua
ServerHooks.Register('onShopItemPurchased', function(data)
    print(('[SD-CRAFTING] %s bought %dx %s for %d %s'):format(
        data.identifier, data.quantity, data.label,
        data.totalPrice, data.currency))
end)
ParameterTypeDescription
sourcenumberPlayer server ID
identifierstringPlayer character identifier
shopIdstringShop identifier
itemIdstringShop item identifier
itemstringInventory item name granted
labelstringItem display label
quantitynumberQuantity purchased
pricenumberPrice per unit
totalPricenumberTotal price paid
currencystring'cash', 'bank', 'money', or an inventory item name
newBalancenumberPlayer's resulting balance (cash + bank, or item count)
timestampnumberUnix timestamp

onShopPurchaseFailed

Triggered every time a shop purchase is rejected. Fires from multiple code paths — access denied, invalid shop, item not found, insufficient funds (cash/bank), insufficient items (item-currency shop), and inventory-full refund. The reason field distinguishes them.

lua
ServerHooks.Register('onShopPurchaseFailed', function(data)
    print(('[SD-CRAFTING] %s failed to buy at %s: %s'):format(
        data.identifier, data.shopId, data.reason))
end)
ParameterTypeDescription
sourcenumberPlayer server ID
identifierstringPlayer character identifier
shopIdstringShop identifier
itemIdstringShop item identifier
labelstring|nilItem display label (only present once the item is resolved)
quantitynumberQuantity attempted
totalPricenumber|nilTotal price (only present once the item is resolved)
currencystring|nilCurrency type (only present once the item is resolved)
reasonstringFailure reason ('Access denied', 'Invalid shop', 'Item not found', 'Not enough money', 'Not enough <currency>', 'Inventory full')
timestampnumberUnix timestamp

Admin Events — Recipes

onAdminRecipeCreated

Triggered when an admin creates a new recipe through the admin panel.

lua
ServerHooks.Register('onAdminRecipeCreated', function(data)
    print(('[SD-CRAFTING] Admin %s created recipe %s in table %s'):format(
        data.identifier, data.recipeId, data.tableName))
end)
ParameterTypeDescription
sourcenumberAdmin server ID
identifierstringAdmin character identifier
tableNamestringRecipe table the recipe was added to
recipeIdstringNewly created recipe ID
recipetableFull recipe object
timestampnumberUnix timestamp

onAdminRecipeUpdated

Triggered when an admin edits an existing recipe through the admin panel.

lua
ServerHooks.Register('onAdminRecipeUpdated', function(data)
    print(('[SD-CRAFTING] Admin %s updated recipe %s'):format(
        data.identifier, data.recipeId))
end)
ParameterTypeDescription
sourcenumberAdmin server ID
identifierstringAdmin character identifier
tableNamestringRecipe table the recipe belongs to
recipeIdstringRecipe ID that was updated
recipetableUpdated recipe object
timestampnumberUnix timestamp

onAdminRecipeDeleted

Triggered when an admin deletes a recipe through the admin panel. Config-defined recipes get a tombstone row; admin-created recipes are fully removed from the database.

lua
ServerHooks.Register('onAdminRecipeDeleted', function(data)
    print(('[SD-CRAFTING] Admin %s deleted recipe %s from %s'):format(
        data.identifier, data.recipeId, data.tableName))
end)
ParameterTypeDescription
sourcenumberAdmin server ID
identifierstringAdmin character identifier
tableNamestringRecipe table the recipe was in
recipeIdstringRecipe ID that was deleted
timestampnumberUnix timestamp

onAdminRecipeMoved

Triggered when an admin moves a recipe from one recipe table to another.

lua
ServerHooks.Register('onAdminRecipeMoved', function(data)
    print(('[SD-CRAFTING] Admin %s moved recipe %s: %s -> %s'):format(
        data.identifier, data.recipeId, data.fromTable, data.toTable))
end)
ParameterTypeDescription
sourcenumberAdmin server ID
identifierstringAdmin character identifier
fromTablestringSource recipe table
toTablestringDestination recipe table
recipeIdstringRecipe ID that was moved
timestampnumberUnix timestamp

onAdminRecipesReordered

Triggered when an admin reorders the recipes inside a table via drag-and-drop.

lua
ServerHooks.Register('onAdminRecipesReordered', function(data)
    print(('[SD-CRAFTING] Admin %s reordered %d recipes in %s'):format(
        data.identifier, #data.orderedIds, data.tableName))
end)
ParameterTypeDescription
sourcenumberAdmin server ID
identifierstringAdmin character identifier
tableNamestringRecipe table that was reordered
orderedIdstableArray of recipe IDs in the new display order
timestampnumberUnix timestamp

Admin Events — Recipe Tables

onAdminTableCreated

Triggered when an admin creates a new (empty) recipe table.

lua
ServerHooks.Register('onAdminTableCreated', function(data)
    print(('[SD-CRAFTING] Admin %s created table %s'):format(
        data.identifier, data.tableName))
end)
ParameterTypeDescription
sourcenumberAdmin server ID
identifierstringAdmin character identifier
tableNamestringNew table name
timestampnumberUnix timestamp

onAdminTableDeleted

Triggered when an admin deletes a recipe table along with all of its recipes. Only admin-created tables can be deleted — config-defined tables are protected and this hook will not fire for them.

lua
ServerHooks.Register('onAdminTableDeleted', function(data)
    print(('[SD-CRAFTING] Admin %s deleted table %s'):format(
        data.identifier, data.tableName))
end)
ParameterTypeDescription
sourcenumberAdmin server ID
identifierstringAdmin character identifier
tableNamestringTable name that was deleted
timestampnumberUnix timestamp

Admin Events — Stations & Types

onAdminStationCreated

Triggered when an admin creates a new station through the admin panel.

lua
ServerHooks.Register('onAdminStationCreated', function(data)
    print(('[SD-CRAFTING] Admin %s created station %s'):format(
        data.identifier, data.stationKey))
end)
ParameterTypeDescription
sourcenumberAdmin server ID
identifierstringAdmin character identifier
stationKeystringGenerated station key
stationtableFull station config
timestampnumberUnix timestamp

onAdminStationUpdated

Triggered when an admin edits an existing station. Fires for static stations (via override), placed workbenches (via override), and admin-created stations.

lua
ServerHooks.Register('onAdminStationUpdated', function(data)
    print(('[SD-CRAFTING] Admin %s updated station %s'):format(
        data.identifier, data.stationKey))
end)
ParameterTypeDescription
sourcenumberAdmin server ID
identifierstringAdmin character identifier
stationKeystringStation key that was updated
stationtableUpdated station config
timestampnumberUnix timestamp

INFO

For static stations and placed workbenches, the update flows through the override system. This hook fires only for the admin-created station path — the static and placed-workbench update paths return early from helper functions and do not currently fire onAdminStationUpdated.


onAdminStationDeleted

Triggered when an admin deletes an admin-created station.

lua
ServerHooks.Register('onAdminStationDeleted', function(data)
    print(('[SD-CRAFTING] Admin %s deleted station %s'):format(
        data.identifier, data.stationKey))
end)
ParameterTypeDescription
sourcenumberAdmin server ID
identifierstringAdmin character identifier
stationKeystringStation key that was deleted
timestampnumberUnix timestamp

onAdminWorkbenchTypeDeleted

Triggered when an admin deletes an admin-created workbench type. The deletion is rejected (and the hook does not fire) if any station or placed workbench still uses the type.

lua
ServerHooks.Register('onAdminWorkbenchTypeDeleted', function(data)
    print(('[SD-CRAFTING] Admin %s deleted workbench type %s'):format(
        data.identifier, data.typeId))
end)
ParameterTypeDescription
sourcenumberAdmin server ID
identifierstringAdmin character identifier
typeIdstringWorkbench type that was deleted
timestampnumberUnix timestamp

Admin Events — Tech Trees

onAdminTechTreeCreated

Triggered when an admin creates a new tech tree.

lua
ServerHooks.Register('onAdminTechTreeCreated', function(data)
    print(('[SD-CRAFTING] Admin %s created tech tree %s'):format(
        data.identifier, data.treeId))
end)
ParameterTypeDescription
sourcenumberAdmin server ID
identifierstringAdmin character identifier
treeIdstringNew tree identifier
treetableFull tree object (label, icon, color, nodes)
timestampnumberUnix timestamp

onAdminTechTreeUpdated

Triggered when an admin edits a tech tree's metadata or its node list.

lua
ServerHooks.Register('onAdminTechTreeUpdated', function(data)
    print(('[SD-CRAFTING] Admin %s updated tech tree %s'):format(
        data.identifier, data.treeId))
end)
ParameterTypeDescription
sourcenumberAdmin server ID
identifierstringAdmin character identifier
treeIdstringTree identifier
treetableUpdated tree object
timestampnumberUnix timestamp

onAdminTechTreeDeleted

Triggered when an admin deletes a tech tree. Config-defined trees get a tombstone row; admin-created trees are fully removed.

lua
ServerHooks.Register('onAdminTechTreeDeleted', function(data)
    print(('[SD-CRAFTING] Admin %s deleted tech tree %s'):format(
        data.identifier, data.treeId))
end)
ParameterTypeDescription
sourcenumberAdmin server ID
identifierstringAdmin character identifier
treeIdstringTree identifier that was deleted
timestampnumberUnix timestamp

onAdminTechNodeCreated

Triggered when an admin creates a new node inside an existing tech tree.

lua
ServerHooks.Register('onAdminTechNodeCreated', function(data)
    print(('[SD-CRAFTING] Admin %s created node %s in tree %s'):format(
        data.identifier, data.nodeId, data.treeId))
end)
ParameterTypeDescription
sourcenumberAdmin server ID
identifierstringAdmin character identifier
treeIdstringParent tree identifier
nodeIdstringNew node identifier
nodetableFull node object (id, recipeId, cost, prerequisites, position)
timestampnumberUnix timestamp

onAdminTechNodeUpdated

Triggered when an admin edits an existing tech tree node.

lua
ServerHooks.Register('onAdminTechNodeUpdated', function(data)
    print(('[SD-CRAFTING] Admin %s updated node %s'):format(
        data.identifier, data.nodeId))
end)
ParameterTypeDescription
sourcenumberAdmin server ID
identifierstringAdmin character identifier
treeIdstringParent tree identifier
nodeIdstringNode identifier
nodetableUpdated node object
timestampnumberUnix timestamp

onAdminTechNodeDeleted

Triggered when an admin deletes a tech tree node. References to the deleted node in other nodes' prerequisites are also cleaned up.

lua
ServerHooks.Register('onAdminTechNodeDeleted', function(data)
    print(('[SD-CRAFTING] Admin %s deleted node %s from %s'):format(
        data.identifier, data.nodeId, data.treeId))
end)
ParameterTypeDescription
sourcenumberAdmin server ID
identifierstringAdmin character identifier
treeIdstringParent tree identifier
nodeIdstringNode identifier that was deleted
timestampnumberUnix timestamp

Integration Examples

Discord Webhook for Crafting

lua
local webhookUrl = 'https://discord.com/api/webhooks/...'

ServerHooks.Register('onCraftCompleted', function(data)
    PerformHttpRequest(webhookUrl, function() end, 'POST', json.encode({
        embeds = {{
            title = 'Item Crafted',
            description = ('**%s** crafted **%dx %s** at `%s`'):format(
                data.identifier, data.totalOutputCount, data.outputItem, data.stationId),
            color = 3066993,
            timestamp = os.date('!%Y-%m-%dT%H:%M:%SZ', data.timestamp)
        }}
    }), { ['Content-Type'] = 'application/json' })
end)

MDT Weapon Registration

lua
ServerHooks.Register('onCraftCompleted', function(data)
    -- Only register crafted weapons in the MDT
    if not data.outputItem:find('^weapon_') then return end

    exports['your-mdt']:RegisterWeapon({
        weapon = data.outputItem,
        owner = data.identifier,
        craftedAt = os.date('%Y-%m-%d %H:%M:%S', data.timestamp),
        station = data.stationId,
    })
end)

Admin Audit Trail

lua
local function logAdminAction(action, data)
    MySQL.insert('INSERT INTO admin_audit (identifier, action, payload, created_at) VALUES (?, ?, ?, NOW())', {
        data.identifier, action, json.encode(data)
    })
end

ServerHooks.Register('onAdminRecipeCreated',  function(d) logAdminAction('recipe_created',  d) end)
ServerHooks.Register('onAdminRecipeUpdated',  function(d) logAdminAction('recipe_updated',  d) end)
ServerHooks.Register('onAdminRecipeDeleted',  function(d) logAdminAction('recipe_deleted',  d) end)
ServerHooks.Register('onAdminStationCreated', function(d) logAdminAction('station_created', d) end)
ServerHooks.Register('onAdminStationDeleted', function(d) logAdminAction('station_deleted', d) end)

Integration Ideas

Use server hooks to:

  • Stream crafting activity to a Discord webhook or analytics dashboard
  • Register crafted weapons / items in a police MDT or item-tracking system
  • Build a full audit log of admin recipe and station changes
  • Reward players via external systems on milestone crafts
  • Track shared-workbench usage for ownership analytics
  • Implement custom anti-exploit logic on craft completion patterns
  • Notify Discord on tech-tree progression milestones