# Developer > Exports and open hooks for ML Tattoos Category: TATTOOS · Source: https://miciomods.it/docs/ml-tattoos-developer · Last updated: 2026-07-09 ## Overview ML Tattoos exposes server exports for the uses economy, a client export to open the shop, and override hooks in `open/`. Mutating use exports are restricted to the resources listed in `Config.AllowedConsumerResources`; read exports are open. ## Server Exports All under `exports.ml_tattoos:(...)`. `identifier` is the player's framework id; `ctype` is `'solo'`, `'faction'` or `'ytd'`. - `GrantTattooCredit(identifier, ctype, amount, reason?)`: Grants uses to a player (capped per type). Caller must be an allowed resource. Returns `ok, granted` - `GetTattooCredits(identifier)`: Returns the player's balances: `{ solo, faction, ytd, total_granted, total_consumed }`. Open to any resource - `ConsumeTattooCredit(identifier, ctype)`: Consumes one use of a type. Caller must be an allowed resource. Returns `ok, reason?` - `RefundTattooCredit(identifier, ctype, reason?)`: Refunds one use of a type (capped). Caller must be an allowed resource Two server exports are reserved for the Tebex hub and are called only by the `ml_tebex` dispatcher: `OnTebexPackage(ev)` (grants the package's uses) and `OnTebexRefund(ev)` (reverses a refunded transaction, returning `{ status, detail }`). You do not call these yourself. ## Client Exports - `openShop(opts)`: Opens the tattoo shop. `opts` may carry `mode` (`'shop'` / `'item'`), a `limit`, and preset filters (`zones`, `categories`, `hashes`, `allowCustom`, `customOnly`). Returns `false` if the shop is already open ## open/ Handlers Override these in the `open/` files. They are not escrowed. ### Client **open/client.lua** ```lua function OpenClient.BeforeOpenShop(shopData) return true -- return false to block the shop from opening end function OpenClient.OnTattooPurchased(tattoos) -- runs client-side after a successful checkout end ``` ### Server **open/server.lua** ```lua function OpenServer.OnCheckout(src, tattoos, totalCost) -- runs after a successful checkout (src, full tattoo list, money charged) end function OpenServer.OnCustomApproved(src, data) return true -- return false to block a custom tattoo submission end ``` ### Shared **open/shared.lua** ```lua function OpenShared.CanBuyTattoo(tattooHash, tattooDlc) return true -- return false to block a specific tattoo end ``` ## Examples **Grant a use from another resource** Add your resource to `Config.AllowedConsumerResources` first, then: ```lua -- e.g. inside a coin-donator redemption, server-side: local identifier = exports.ml_bridge:GetPlayerId(src) local ok, granted = exports.ml_tattoos:GrantTattooCredit(identifier, 'solo', 1, 'coin_redeem') if ok then print(('granted %d solo use(s)'):format(granted)) end ``` **Read a player's use balance** ```lua local identifier = exports.ml_bridge:GetPlayerId(src) local bal = exports.ml_tattoos:GetTattooCredits(identifier) print(('solo=%d faction=%d ytd=%d'):format(bal.solo, bal.faction, bal.ytd)) ``` **Gate a specific tattoo** **open/shared.lua** ```lua function OpenShared.CanBuyTattoo(tattooHash, tattooDlc) -- block one DLC pack entirely: if tattooDlc == 'mpluxe2_overlays' then return false end return true end ``` **Open the shop from a client script** Client examples use `ox_lib` `cache.*` for player state. ```lua -- open the full shop: exports.ml_tattoos:openShop({ mode = 'shop' }) -- open filtered to one zone, one tattoo: exports.ml_tattoos:openShop({ mode = 'item', limit = 1, zones = { 'ZONE_LEFT_ARM' }, allowCustom = false, }) ``` **React to a purchase server-side** **open/server.lua** ```lua function OpenServer.OnCheckout(src, tattoos, totalCost) if totalCost > 0 then print(('player %d paid %d for %d tattoo(s)'):format(src, totalCost, #tattoos)) end end ```