Skip to content

Exports

Mixing these up is the most common integration mistake.

What it identifies ESX QBCore / Qbox
account identifier one player’s account 1a2b3c… (bare licence) license:1a2b3c…
character key ONE character on it char2:1a2b3c… ABC12345 (a citizenid)

Slots, memberships and grants hang off the account identifier. Outfits and character locks hang off the character key.

Get an account identifier with GetIdentifier(source), or turn a pasted string into one with NormaliseIdentifier(token). Get a character key with GetCharacterKey(identifier, slot). Never build one by hand.

Several exports read the database, and GetMembership may call the Discord API. Call them from inside a CreateThread, not straight from the top of a command handler, or the await will error.


exports.nuggs_multicharacter:ExportName(args)
Export Signature Returns
GetIdentifier (source) string? — account identifier, nil when unreadable
NormaliseIdentifier (token) string? — account identifier, nil when it makes no sense
GetFramework () "esx" | "qbcore" | "qbox" | nil
GetCharacterKey (identifier, slot) string? — nil when the slot is empty
Export Signature Returns
GetMembership (source) table? — see shape below
GetSlots (source) number — characters this account may keep
GetTiers () table[] — the tiers from Config.Membership
GetGrants (identifier) table[] — grant history, expired rows included
GrantSlots (identifier, amount, days?, reference?) boolean — false when the reference was already redeemed
GrantTier (identifier, tier, days?, reference?) boolean
RevokeGrants (identifier, kind?) number — rows removed
RefreshMembership (identifier?) — nil clears every account
SetBaseSlots (identifier, slots) boolean
ClearBaseSlots (identifier) boolean

GetMembership returns:

{
tier = {
id = "gold",
rank = 3, -- higher wins when two sources disagree
label = "GOLD MEMBER",
short = "GOLD",
colour = "#f0d68a",
icon = "gem",
slots = 3, -- extra characters the tier is worth
blurb = "...",
perks = { "...", "..." },
},
premium = true, -- any tier above rank 0
expires = 1780000000, -- unix time, nil when it never expires
sources = { "discord", "tebex" },
slots = {
total = 8, -- the number that actually applies
base = 3, -- Config.Slots, or the /setslots override
tier = 3, -- the tier bonus
extra = 2, -- purchased + Discord role slots
max = 8, -- Config.MaxSlots
capped = true, -- they have earned past the ceiling
},
}
  • A negative amount on GrantSlots takes slots away — that is how a chargeback is handled without unpicking the original row.
  • reference is unique across the grants table. Passing the same one twice grants once, which is what stops a retried webstore delivery paying out twice. Leave it nil for a grant that should be repeatable.
  • days of nil or 0 means the grant never expires.
Export Signature Returns
GetCharacters (identifier, limit?) table<number, table> keyed by slot
GetCharacterOutfits (charKey) table[]{ slot, label, values } in slot order
PublishCharacterOutfits (charKey) string[] — the wardrobe stores that took them
IsCharacterLocked (charKey) boolean
SetCharacterLocked (charKey, locked) boolean

Each character row: { slot, firstname, lastname, dateofbirth, sex, job, jobGrade, cash, bank, playtime, lastSeen, skin, position, tattoos, disabled }.

Export Signature Returns
GetClothingResource () table?{ id, resource, store }, nil when nothing detected
GetTattooResource () table?{ id, resource, owns, identity }, nil when no dedicated tattoo resource is running
GetCharacterTattoos (charKey, identifier?) any? — that resource’s own list for one character. nil means “no answer”
GetHousingScripts () table[]{ id, resource, table } per running housing script
GetCharacterProperties (charKey) table[]{ id, key, adapter, label, kind, coords }
  • store on GetClothingResource says where the appearance actually lives: "illenium" or "qbclothing" for the two playerskins shapes, "skinchanger" for ESX’s users.skin, "self" for a resource with its own tables. Ask rather than detecting separately, or your script will fight this one over the same ped.
  • On GetCharacterProperties, id is this resource’s own card id and means nothing outside it. key is the housing script’s own identifier — that is the one you want.
  • On GetTattooResource, owns = true means it clears and redraws ped decorations from its own store, so anything you draw with AddPedDecorationFromHashes is removed by its next pass. identity says what it keys that store on — "account" means every character on one licence shares a set of ink until that resource’s own multicharacter step is done.
  • GetCharacterTattoos hands back the list in that resource’s own shape. nil means no answer at all; an empty table means the character has no ink.

These read the database or another resource — call them from inside a CreateThread.

Export Signature Returns
IsChoosing (source) boolean — still on the character screen
RefreshCharacterScreen (source) boolean — rebuild and resend the line-up
ReturnToCharacterScreen (source) boolean — what /relog does

Export Signature Returns
IsOpen () boolean — selection or creation is on screen
GetScreen () "selection" | "creation" | nil
IsPickingSpawn () boolean — the arrival picker is over the top
GetSlot () number? — the slot being viewed or built
GetSlots () number — slots this account has, 0 before the list arrives
IsCharacterLoaded () boolean — a character is in the world
GetFramework () "esx" | "qbcore" | "qbox" | nil
GetClothingResource () table?{ id, resource, store }
GetTattooResource () table?{ id, resource, owns, identity }
ReapplyTattoos () boolean — ask the tattoo resource to redraw the player’s ink

There is no event for “the ped’s decorations were cleared”, so a resource that clears them has to say so — call ReapplyTattoos after ClearPedDecorations, a model change, a disguise, or a revive that repaints the skin:

ClearPedDecorations(ped)
-- ... your own decoration work ...
exports.nuggs_multicharacter:ReapplyTattoos()

It returns false and does nothing when no tattoo resource is running, so it is safe to call unconditionally.