Integrations — Licenses Creator: ox_inventory, qs-inventory, QBCore, ESX | Quasar Store | Quasar Store
Documentation
Integrations
Connect Licenses Creator to your FiveM server: license item snippets for ox_inventory, qs-inventory, qb-inventory, origen and tgiann, photo upload to Discord, Imgur, Cloudinary or Fivemanage, Discord logs and custom card fields.
Nothing inside Licenses Creator knows which framework or inventory you run. Every call to the outside world goes through a small, unencrypted bridge file under client/custom/ or server/custom/, and the right one is picked by looking at what is running.
Layer
Supported
Without it
Framework
es_extended · qb-core · qbx_core
Standalone: the player is their license identifier, money is a money item, the creator opens by ace.
Two config keys decide how many items your inventory needs:
Key
Default
Meaning
Config.SeparateItemPerLicense
true
One item per license, named after its template id in lowercase — id_Card becomes id_card. Each gets its own name, weight and picture in the inventory.
Config.Item
'license'
The single item every license uses when the switch above is false, told apart by its label — Driver License - John Doe.
Config.AuthorizationDocumentItem
'authorization_document'
One item for the whole server, shared by every license that needs approval. Added once.
The creator's Inventory item setup button prints the snippet for the license you opened, already matching these settings. For reference, here is the ID card in each format — swap in the name the creator shows you, and add the authorization document only once. Restart the inventory afterwards.
ox_inventory/data/items.lua, images in ox_inventory/web/images/. The server.export line is what makes using the item show the card.
lua
['id_card'] = {
label = 'Identification Card',
weight = 10,
stack = false,
close = true,
description = 'Official document issued by the government',
client = { image = 'id_card.png' },
server = { export = 'qs-licensescreator.useLicenseItem' },
},
-- once, for the whole server
['authorization_document'] = {
label = 'Authorization Document',
weight = 10,
stack = false,
close = true,
description = 'Approved authorization to present at the license office',
client = { image = 'authorization_document.png' },
},
qs-inventory/shared/items.lua, images in qs-inventory/html/images/. On QBCore, qs-inventory reads qb-core/shared/items.lua instead — use the qb-inventory snippet there.
Items live in qb-core: qb-core/shared/items.lua, images in qb-inventory/html/images/.
lua
id_card = { name = 'id_card', label = 'Identification Card', weight = 10, type = 'item', image = 'id_card.png', unique = true, useable = true, shouldClose = true, combinable = nil, description = 'Official document issued by the government' },
-- once, for the whole server
authorization_document = { name = 'authorization_document', label = 'Authorization Document', weight = 10, type = 'item', image = 'authorization_document.png', unique = true, useable = false, shouldClose = true, combinable = nil, description = 'Approved authorization to present at the license office' },
Plain ESX keeps its items in the items database table. The picture still comes from your inventory's image folder. Restart es_extended afterwards.
sql
INSERT INTO `items` (`name`, `label`, `weight`, `rare`, `can_remove`) VALUES
('id_card', 'Identification Card', 1, 0, 1)
ON DUPLICATE KEY UPDATE `label` =VALUES(`label`);
-- once, for the whole serverINSERT INTO `items` (`name`, `label`, `weight`, `rare`, `can_remove`) VALUES
('authorization_document', 'Authorization Document', 1, 0, 1)
ON DUPLICATE KEY UPDATE `label` =VALUES(`label`);
Neither owns an item list on QBCore — they read the framework's. Use the snippet for the file yours reads:
Inventory
On QBCore
On ESX
origen_inventory
qb-core/shared/items.lua
The items table
tgiann-inventory
qb-core/shared/items.lua
tgiann-inventory/shared/items.lua
Images go in and .
Every issued license writes its data into the item's metadata — the twenty card fields plus license_id, issued_id, generated_id, photo_url, label, description, image and imageurl — so another script can read a license straight from the inventory.
The template's Inventory image is written into each item, so every license shows its own picture even on a single shared item. A plain file name is resolved against your inventory's image folder from Config.InventoryConfig; a full URL is used as is. To force one license's picture regardless of the template:
Every license carries the holder's photo, taken once and reused on every license after that. Where it is stored is picked in Settings → Photo upload:
Provider
Needs
Discord
A webhook URL. The photo is posted to that channel and its link is stored — use a private channel.
Imgur
A client id.
Cloudinary
The cloud name, plus either an unsigned upload preset or an API key and secret.
Fivemanage
An API key. When possible the photo goes straight from the player to Fivemanage, which keeps large images off your server.
Custom
Your own handler, below.
For your own storage, define the handler in a server file that loads with the resource — server/custom/fields.lua is a good place — and pick Custom in Settings:
lua
_G.PhotoUpload_CustomHandler = function(source, base64Data, cfg, callback)-- upload base64Data wherever you like, then:
callback('https://your-cdn/photo.jpg', nil)
-- or on failure:-- callback(nil, 'Upload failed')end
The framing of the shot lives in shared/advanced.lua. If your host rejects uploads, lower quality and the maximum size first — it is almost always a file-size limit.
lua
Config.PhotoCapture = {
encoding = 'jpg',
quality = 0.92, -- 0.0 to 1.0
maxWidth = 480,
maxHeight = 360,
camFov = 28.0, -- lower zooms in on the face
camDistance = 1.3, -- metres from the player
camOffsetSide = 0.0,
camOffsetUp = 0.05,
}
Settings → Logs takes a webhook URL and decides what is posted:
Event
Carries
License purchased
Player, citizen id, license, document number, price, first issue or duplicate.
License reported
Citizen id, license, document number, stolen or lost.
License cancelled
Citizen id, license, document number, and who cancelled it: admin, terminal, export or points.
License about to expire
Citizen id, license, document number, days left — once a day per document.
With the webhook type set to Discord each event is a coloured embed; with Fivemanage it is one line of text. Titles, colours and layout are in server/webhook.lua, which is open for you to change.
server/custom/fields.lua overrides any of the twenty fields a card can bind. What ships fills Dynamic content: a fixed line per license, and the driving categories the holder actually owns on the driver license.
Change a line and the next card issued changes with it. To override any other field, add a getter for its key. It receives the holder's context and returns the text:
The keys are fullName, firstName, lastName, firstAndLastName, firstInitialLastName, lastNameFirstName, firstInitialDotLastName, firstNameDotLastName, dateOfBirth, nationality, citizenId, generatedId, codeGenerated, gender, job, expirationDate, issueDate, issuedBy, title and content. A getter that errors prints an empty field rather than breaking the card.
Each layer is detected by looking for started resources in priority order — the first one found wins. Move a line up in shared/config.lua to give it priority, or pin a value outright:
lua
-- Auto, the default: the first started resource in each list wins.
Config.Framework = detect(frameworks) or'standalone'
Config.Inventory = detect(inventories)
-- Pinned:
Config.Framework = 'qb'-- 'esx', 'qb' or 'standalone'
Config.Inventory = 'qs'-- 'ox', 'qs', 'qb', 'origen' or 'tgiann'
This is worth doing when two candidates run at once — an old inventory left started next to the new one — or when a resource has been renamed. Qbox is driven by the qb bridge.
Supporting an inventory that is not on the list is deliberately small:
Copy the closest bridge
Duplicate server/custom/inventory/ox.lua or qb.lua, change the guard at the top to your id, and point each function at your inventory's own exports. Every bridge implements the same eight:
Two more are optional: InventoryHasItemDefinition powers the missing-item warning on boot, and RegisterInventoryUsableItem hooks item use when the framework's own hook does not fire. Without them the framework's versions are used.
Register it
Add your resource and id to the inventories list in shared/config.lua, and its image folder to Config.InventoryConfig in shared/advanced.lua.
Frameworks extend the same way through server/custom/framework/ and client/custom/framework/. The server bridge is where player data comes from, so if your server stores something differently, replace the body of one function:
Function
Used for
GetIdentifier
Who holds a license — the citizen id stored on every record.
GetPlayerCharInfo
First name, last name, date of birth, gender, nationality.
41 translations ship in locales/: ar, bg, bn, ca, cs, da, de, el, en, es, et, fa, fi, fr, he, hi, hr, hu, id, it, ja, ko, lt, lv, ms, nl, no, pl, pt, ro, ru, sk, sl, sv, sw, th, tr, uk, ur, vi, zh. The language is picked in Settings → Validity → Panel language and switches for every player at once, notifications included.
Config.Languages is the list offered in that dropdown — trim it to what your staff uses. For a language of your own, copy locales/en.json to locales/<code>.json, translate it and add the code to the list. Translate Config.GenderLabels, Config.Nationality and the blip labels in shared/config.lua too — they are printed as written.