All documentation

Paid scripts

Poggy Markets

Player-owned stores for RedM, NPC stores you stock from one catalog file, and optional living prices, a commodities exchange and quiet-server customers. For owners who want a real shop economy players can run.

  • poggy_marketsv1.6.2
  • FrameworksVORP Core, RSG Core, QBCore RedM
  • Updated5 Oct 2026

Overview#

Poggy Markets turns the stores on your map into an economy. NPC stores in every town sell and buy from item lists you control. Players can buy a storefront or found their own shop with a deed, then stock it, price it, hire staff and watch a dashboard that shows what actually sells.

Three optional modules sit on top: prices that move with supply and demand, a commodities exchange where players bet on those prices, and unseen customers that keep a shop earning a little on a quiet server. All three ship switched on; turn off any you do not want.

  • Player shops. Buy a storefront or found one anywhere with the shop deed. Set prices, choose what the shop buys from the public, hide back-stock, upgrade storage, rename it, move it, show it on the map or keep it off.
  • A dashboard. Takings for today and the week, best sellers by volume and by revenue, low-stock warnings, a revenue chart and a full ledger with filters.
  • Staff. Hire any player who is online as a manager or an employee. You decide exactly what each role may do.
  • NPC stores with clerks. General stores, gunsmiths, butchers, doctors, fish markets, lumber yards and camp outfitters ship placed across the map. A clerk is part of its store: delete the store and the clerk goes too.
  • Easy placement. Stand where you want a store, run /pmhere, and paste the block it prints. Or add it in the /poggy settings hub.
  • Tax and repossession. Shops pay a tax from their ledger. A shop that cannot pay is repossessed, and nothing is deleted.
  • Admin tools. A shop admin panel (/pmadmin) and live panels in /poggy to transfer, repossess, fix ledgers and manage staff.

Requirements#

NeedsDetails
FrameworkVORP Core, RSG Core or QBCore RedM. RedM only.
poggy_core0.17.0 or newer, started before Poggy Markets. 0.18.0 or newer adds the in-game settings hub (/poggy), and 0.23.0 or newer lets the shop screens follow the Poggy theme. Use the latest.
oxmysqlFor shops, sales, prices and the exchange.

poggy_core talks to your framework and draws the notifications. There is nothing to set for it.

Installation#

  1. Download poggy_markets and put the folder in your resources folder.
  2. Add it to server.cfg after oxmysql and poggy_core:
    ensure oxmysql
    ensure poggy_core
    ensure poggy_markets
  3. Copy docs/shoptoken.png into your inventory's item image folder, so the shop deed has a picture.
  4. Start the server and read the console. It says which framework it found, how many stores it loaded, and every catalog item that does not exist on your server.

Database tables are created automatically the first time the script starts. You import nothing.

On VORP the shop deed item (shoptoken, "Shop Deed") is added to the items table for you. On a framework without an items table, such as RSG (rsg-core/shared/items.lua), add shoptoken to the framework's item list yourself. The console warns at every start until it is there, and nobody can found a shop without it.

Expect a long list of missing items on first start. The shipped catalog uses common RedM item names, and every server names items differently. A missing item is skipped, never sold. See Make the catalog yours below.

Coming from syn_stores

There is nothing to migrate. Poggy Markets uses the same playershops and shop_sales_log tables, so shops, ledgers, stock and sales history are already where it expects them. The install only adds missing columns; it drops nothing. To keep your storefronts for sale at the same places and prices, give each store in config/stores.lua a shopId pointing at its playershops row.

Configuration#

Every setting can be changed in game. Type /poggy, open Poggy Markets, change what you need, save, and restart poggy_markets. The hub has eight tabs: General, Access & permissions, Locations, Catalogs, Player shops, Economy & prices, Text & language and Advanced. You can still edit the files by hand:

FileWhat it holds
config/config.luaModule switches, keys, player shops, tax, staff roles, shop jobs, admin, interface skin
config/stores.luaWhere the stores are, and the store types
config/catalog_default.luaWhat each kind of store sells and buys
config/keys.luaKey names, so you can write "G" instead of a control code
config/pricing.luaDynamic pricing
config/exchange.luaThe commodities exchange
config/ghostbuyer.luaGhost buyers
translations.luaEvery message players see

A config change needs a restart of poggy_markets. /pmreload reloads shops from the database, not the config files.

General

SettingDefaultWhat it does
Config.Modules.dynamicPricingtruePrices move with supply and demand.
Config.Modules.exchangetrueThe commodities exchange. Needs dynamic pricing.
Config.Modules.ghostBuyertrueUnseen customers buy now and then from player shops.
Config.openKey"G"Opens a store, a shop or an exchange desk. Any key named in config/keys.lua.
Config.purchaseKey"B"Buys a storefront that is for sale. Asks for a second press before taking money.
Config.interactionDistance1.5How close (metres) a player must stand to get the prompt.
Config.currency"$"Symbol shown before every price.
Config.analyticsChartDays14Days of history in the dashboard's revenue chart.
Config.fallbackClerkModel"cs_mp_travellingsaleswoman"Clerk used when a store's model does not exist on your server.
Config.blacklistedItemsemptyItems no store or player shop may ever trade (quest items, licences, admin tools).
Config.UI.skin"leather"The look of the shop, manager and exchange. "leather" is stitched leather and parchment like the game's satchel; "default" is a dark theme that follows the Poggy theme set in poggy_core.
Config.debugfalseExtra console output. Leave off on a live server.

To make your own skin, copy ui/css/skin-leather.css under a new name (skin-<name>.css), change the image names in it, and set skin to that name. Every image is optional, and the image sizes are listed at the top of skin-leather.css.

Player shops

SettingDefaultWhat it does
Config.PlayerShops.enabledtrueMaster switch. Off means NPC stores only, no player ownership.
Config.PlayerShops.creationItem"shoptoken"The deed item used up to found a shop. false stops player-founded shops (storefronts can still be bought).
Config.PlayerShops.maxPerPlayer2Shops one character may own.
Config.PlayerShops.adminBypassMaxtrueAdmins ignore that limit.
Config.PlayerShops.startingSlots1000Storage a new shop starts with.
Config.PlayerShops.slotUpgradeCost0.75What the owner pays, from their own cash, for each extra storage slot.
Config.PlayerShops.minSpacing10.0Minimum distance (metres) between a new or moved shop and any other shop or store.
Config.PlayerShops.relocateCost750What the owner pays, from their own cash, to move a player-founded shop.
Config.PlayerShops.blipSprite-242384756Map blip for player shops.
Config.PlayerShops.allowOwnerWebhookstrueLets owners set their own Discord webhook for shop logs. Only Discord webhook addresses are accepted.
Config.Transfers.enabledtrueOwners can hand their shop to another player. Off means only admins can move a shop to a new owner.
Config.Transfers.distance10.0How close (metres) the other player must stand.
Config.Transfers.offerSeconds60Seconds the other player has to accept.

Tax and repossession

SettingDefaultWhat it does
Config.Tax.enabledtrueOff switches tax and repossession off completely.
Config.Tax.amount75Taken from each shop's ledger on collection day.
Config.Tax.schedule"monthly""monthly" or "weekly".
Config.Tax.monthlyday 1, 12:00Monthly: the day of the month and the time.
Config.Tax.weeklydays 3, 10, 17, 24 at 06:10"Weekly": these days of the month (not weekdays), at this time.

Times follow the server's own clock. A shop that cannot pay is repossessed: it leaves the map and the owner and staff lose access. Stock, ledger and staff list all survive, and an admin can restore it.

Staff roles

Config.Roles sets what an owner, a manager and an employee may do in the shop manager. The defaults:

PermissionOwnerManagerEmployee
View dashboard, stock, ledger, analytics, sold itemsyesyesyes
Stock items, deposit money into the ledgeryesyesyes
Set prices, remove stock, manage the buy listyesyesno
Withdraw money from the ledgeryesyesno
Manage staffyesnono
Change settings (rename, blip, upgrade, relocate, webhook, hand over)yesnono

Each one is a separate switch in /poggy under Access & permissions. Admins can do everything in every shop.

Shop jobs (off by default)

With Config.ShopJobs.enabled = false, Poggy Markets never touches anyone's job. Shop access comes only from ownership and the staff list. Turn it on and a shop that has a job gives it to its owner and everyone they hire, so job-locked content elsewhere (crafting recipes, doors, boss menus) works for shop staff. See Permissions for how a shop's job is set.

SettingDefaultWhat it does
Config.ShopJobs.enabledfalseGive shop jobs to owners and staff.
Config.ShopJobs.gradesemployee 1, manager 2, owner 3The job grade for each role. Promoting or demoting someone moves their grade.
Config.ShopJobs.ownerGradefalseOlder setting. A number here is used for the owner instead of grades.owner.
Config.ShopJobs.fallbackJob"unemployed"Without poggy_multijob: the job someone is moved to when they lose a shop job they are wearing. false leaves their job alone.
Config.ShopJobs.grantAccessByJobtrueAnyone holding the shop's job gets in without being hired, by grade (owner grade and up as owner, and so on).

With poggy_multijob (1.7.3 or newer) running, the job is added to the character's job list and their current job is left alone; they switch with /multijob, and it works for offline characters too. Without it, the job replaces their current job, for online players only.

Poggy Markets only takes away jobs it gave. Someone who already had the job before being hired keeps it when they leave. Every start, every /pmreload and every character login checks all shops and puts right any missing or wrong job, with one summary line in the console.

With Access by job on, giving two shops the same job lets that job's holders into both, owner-grade holders with ledger access. Give each shop its own job, or turn grantAccessByJob off, if that is not what you want.

On RSG and QBCore RedM the job must exist in the framework's shared jobs; an unknown name is refused. On VORP jobs are free text.

Adding a store

A store needs a type, a name and a position. The type brings the item lists, the map blip and the clerk. The quickest way:

  1. Stand where the player should get the prompt, facing the way the clerk should face.
  2. Run /pmhere <type> <name>, for example /pmhere gunsmith Valentine Gunsmith. With no type it lists every type in the console.
  3. Copy the block from the server console into config/stores.lua.
  4. Restart poggy_markets.

Or in the hub: /poggy → Poggy Markets → Locations → Stores → Add store, pick the type and name, stand on the spot and press Use my position, save, restart.

A store in the file looks like this:

{ type = "blacksmith", name = "Valentine Blacksmith",
  coords = vector3(-127.44, 632.21, 114.02), heading = 88.0 },

Store types that ship: generalstore, gunsmith, blacksmith, horsesupply, saloon, butcher (buys only), doctor (sells only), fishmarket, lumber and outfitter (sells only). Blacksmiths, saloons and horse supply stores ship without locations, because the right spot depends on your interiors and map edits; place them with /pmhere.

A store can involve three different positions:

FieldWhat it is
coordsWhere the player stands to get the prompt.
npc.coordsWhere the clerk stands: behind the counter, through a window. Absolute, not an offset. Left out, the clerk stands on the prompt.
purchaseCoordsWhere the FOR SALE prompt appears. Falls back to the playershops row's position, then the prompt.

Most town shops need no clerk because the game already puts a shopkeeper there; set npc = false for those. Other optional fields per store: blip = false to hide it from the map, blipSprite, sell / buy to override the type's lists, and jobLock = { "sheriff" } so only those jobs may use it. The commented example at the bottom of config/stores.lua shows every field.

Selling a storefront to players

None of the shipped stores are for sale. A storefront shows a FOR SALE prompt only when all three are true:

  • it has purchasable = true (For sale in the hub);
  • it has a shopId pointing at a row in the playershops table;
  • that row is unowned or repossessed.

Set the price with price (Sale price); left out, the row's own price is used. The prompt disappears as soon as someone buys it.

{ type = "generalstore", name = "Valentine General Store",
  coords = vector3(-325.42, 797.53, 117.88), heading = 88.0,
  shopId = 16, purchasable = true, price = 3000 },

To sell it only to holders of a job (a stable to the stable's job), add purchaseJobs = { "valstables" } (Buyers' jobs). The job they are wearing counts, and with poggy_multijob so does every job on their list. Anyone else is told which job they need and is not charged. Admins are never refused.

Make the catalog yours

What NPC stores sell and buy lives in config/catalog_default.lua, or the Catalogs tab in the hub. Each catalog (GeneralStore, Gunsmith, Butcher, Fish and so on) has a Sells to players and a Buys from players list. A store type points at one catalog for each, so one edit changes every store of that type: nine general stores, one edit.

{ name = "consumable_bread", label = "Bread", price = 0.75 },
FieldWhat it does
nameThe item's name in your framework's item list. Must match.
labelThe name players see.
pricePrice per unit.
type"item_weapon" for guns and weapon tools. Default is a normal item.
stockStarting stock. Left out means unlimited.
hiddentrue: stocked but never shown, bought or sold.

Fix the item names first. The console names every one it cannot find at start, and /pmdiag shows which lists loaded and how many items each has.

Dynamic pricing

Every tracked item has a price multiplier that starts at 1.0. Players selling an item to stores push its price down, buying pushes it up, and left alone it drifts back toward normal. Items are grouped into markets: dumping one pelt softens the price of every pelt. Pelts and fish ship as markets; an ore market is in the file, commented out.

This changes the economy of your whole server. Start with one market and watch it for a week before adding more.

SettingDefaultWhat it does
Config.Pricing.marketspelts, fishWhat is tracked: catalog lists, item name patterns, and items to leave out.
floor / ceiling0.20 / 1.80How far a price can fall or rise, as a multiple of its catalog price.
decayPerUnit0.012Drop per unit players sell (about 65 pelts halve the price).
demandPerUnit0.008Rise per unit players buy.
recoveryPerHour0.004Pull back toward normal while an item is not traded.
scarcityHours / scarcityBonus24 / 0.006After this many hours with no sales an item recovers faster and may rise above normal.
spillover0.40How much one item's trade moves the others in its market. 0 keeps items independent.
tradeFee0.015A small spread each way, so buying and selling back to back is always a small loss.
drift.enabledtrueSmall random movement every 5 minutes, so prices are never perfectly still.
historyDays30Days of price history kept for the charts.

Commodities exchange

Trading desks where players speculate on those same markets. They pay into an exchange balance, then hold positions instead of goods: long if they think prices will rise, short if they expect a crash. Positions settle in cash at the live price. It needs dynamic pricing on. Three desks ship, in Blackwater, Saint Denis and Valentine; add or move them under Locations → Exchange desks.

SettingDefaultWhat it does
Config.Exchange.tradableMarkets{ "pelts", "fish" }Which markets can be traded. A market left out still moves, but nobody can speculate on it.
maxPositionSize100Largest position, in units, per commodity. The main brake on cornering a market.
maxOpenPositions10Separate open positions per character.
tradeFee0.015Fee on opening and on closing, on top of the pricing spread.
allowShortsfalseShort selling. Off by default, because a short can lose more than it staked.
shortMargin0.50Collateral held against a short, as a share of its value.
minimumDeposit10.0Smallest deposit into an exchange balance.

The exchange lets players make money without leaving town. Raise the position size and fee limits carefully.

Ghost buyers

Unseen customers who now and then buy a few items from player shops: stock goes down, the ledger goes up. The defaults are stingy on purpose, a few dollars a day.

SettingDefaultWhat it does
blockMinHours / blockMaxHours7 / 12Each shop's buying round lasts a random length between these.
minTotalStock10A shop needs this many units in stock before anyone buys there.
visibleItemsOnlytrueHidden back-stock is never bought.
maxItemPrice25.0Items dearer than this are never bought (0 = no cap). Stops an owner pricing one item at $999,999 to print money.
minItemStock3An item needs this many units to qualify.
maxRevenuePerSale50.0Hard ceiling on one sale.
payTo"ledger""ledger": income goes to the shop and the owner must visit to withdraw. "cash": paid to the owner while online (ledger when offline).
notifyOwnerfalseTell the owner about each sale. Off keeps the customers feeling real.

How many sales a round brings depends on the shop's stock (Ghost buyer sales per round in the hub).

Settings you can ignore

Config.Selling (item condition and metadata) and Config.Exchange.useSeparateAccount are reserved. This version does not read them, so changing them has no effect.

Commands#

CommandWhoWhat it does
/pmhere <type> <name>Admins, in gamePrints a ready-to-paste store block for where you stand to the server console. No type lists the store types.
/pmdiagAdmins, consolePrints what loaded: framework, stores, catalog lists, modules, tracked markets.
/pmshopsAdmins, consoleLists every shop in the server console with its ID, owner, ledger, job and whether it is repossessed.
/pmreloadAdmins, consoleReloads shops from the database and rechecks shop jobs. Config changes still need a restart.
/pmmanage [shopId]Admins, in gameOpens any shop's manager. No ID means the shop you are in.
/pmadminAdmins or the ACE, in gameThe shop admin panel. See Using it in game.
/pmgiveshop <serverId>Admins, consoleFounds a shop at that player's feet and makes them the owner. No deed needed.
/pmdelshop <shopId>Admins, consoleDeletes a shop permanently, with its stock and ledger.
/pmmoveshop <shopId> [x y z]Admins, consoleMoves a shop. No coordinates means where you stand; from the console, coordinates are required.
/pmreposhop <shopId>Admins, consoleRepossesses a shop: hidden from players, owner locked out, nothing deleted.
/pmunreposhop <shopId>Admins, consoleRestores a repossessed shop.
/pmshopjob <shopId> [job|none|reset]Admins, consoleSets the job a shop gives its owner and staff. none = no job, reset = back to the job in config/stores.lua, no job = show the current one.
/openstoreEveryoneOpens the "name your store" box, for frameworks where the deed cannot be used from the inventory. The deed is still used up.

The eight commands in Config.Admin.commands (pmhere, pmgiveshop, pmdelshop, pmmoveshop, pmreposhop, pmunreposhop, pmshopjob, pmadmin) can be renamed, or removed by setting them to false. /pmdiag, /pmshops, /pmreload, /pmmanage and /openstore have fixed names. /openstore exists only while player shops and the deed item are on.

Keys

KeyWhereWhat it does
GAt a store, shop or exchange deskOpens it. Owners and staff get the shop manager; everyone else gets the shop. Also confirms the new spot when moving a shop.
BAt a FOR SALE promptBuys the storefront. Press once to see the price, again within six seconds to pay.

Change them with Config.openKey and Config.purchaseKey.

Permissions#

Admins

Admins are characters whose framework group is in Config.Admin.groups: admin, superadmin, god and owner by default. They can use every admin command, open any shop's manager, buy job-reserved storefronts and do everything in every shop. The server console can run every admin command that does not need a position in game.

/pmadmin also opens for anyone given the ACE in Config.Admin.ace (poggy_markets.admin), so you can let moderators manage shops without making them framework admins. Add to server.cfg:

add_ace group.moderator poggy_markets.admin allow

The ACE opens only the /pmadmin panel, not the other admin commands. Set ace = false to use groups only.

Shop owners and staff

What owners, managers and employees may do in a shop is set by Config.Roles (see Staff roles). Roles are checked again on the server for every action.

Who sets a shop's job

Only server staff. No player can, owner included; staff see it in the shop's staff tab as a locked title. The job comes from, first match wins:

  1. What staff saved: /poggy → Poggy Markets → Player shops tab → Shop jobs (also on the Poggy Multijob page), the /pmadmin panel, or /pmshopjob. Applies at once, no restart, and survives restarts.
  2. The store's job field in config/stores.lua.

Locked stores

A store with jobLock opens only for those jobs. A storefront with purchaseJobs can only be bought by those jobs.

The /poggy live panels

The Shop jobs, Player shops, Market prices and Exchange accounts panels need the hub's edit permission, set in poggy_core.

Using it in game#

Shopping

  1. Walk up to a store or player shop on foot (not on a horse) and press G at the prompt.
  2. Buy from the shelves, or sell what the store is buying. Max offers as many as the store has and you can carry.

The server sets every price, and your satchel space is checked before any money moves. Owners cannot buy from or sell to their own shop.

Owning a shop

  • Buy a storefront: stand at its FOR SALE prompt, press B to see the price, and B again within six seconds to pay.
  • Found a shop: use the Shop Deed from your inventory (or type /openstore), name the shop, and it opens where you stand. It must be at least minSpacing from other shops, and you cannot own more than the limit. The deed is used up only when everything else has passed.

Press G at your shop to open the manager: stock it from your satchel, set prices and what you buy from the public, hide back-stock, read the dashboard and ledger, deposit and withdraw money, upgrade storage, rename it, toggle the blip, set a Discord webhook for shop logs, or move it (a player-founded shop only: walk to the new spot and press G within a minute).

Customers' money goes into the shop's ledger. Tax comes out of it on collection day, and the shop is repossessed if the ledger cannot pay, so keep some money in it.

Hiring staff

In the manager's staff tab, the owner picks any player who is online and hires them as a manager or an employee. Change their role, or let them go, from the same tab. If shop jobs are on, the tab also shows the shop's job, locked.

Handing a shop over

  1. In the manager's Settings tab, under Hand Over Shop, press Find players nearby.
  2. Pick a player. Anyone greyed out cannot take it, and the list says why.
  3. Type the shop's name to confirm, then Offer shop.
  4. The other player gets an Accept / Decline box. Nothing changes until they accept.

Stock, ledger and staff go with the shop. A storefront reserved for a job only goes to someone holding that job, nobody can end up with more shops than the limit, and a repossessed shop cannot be handed over.

The exchange

At an exchange desk, press G. Deposit money into your exchange balance, then open a position on a commodity and close it later at the live price. Charts show recent price history.

Staff tools

/pmadmin lists every shop in one table. Search by shop ID, name, owner or job, or filter to owned, no-owner or repossessed shops. Pick one to:

ActionWhat it does
Route to shopSets a GPS route to it.
Open managerOpens its manager as the owner would see it.
TransferSearch every character, online or not, by name or character id, and type the shop ID to confirm. The job and shop limits do not apply here.
Repossess / RestoreHides the shop from its owner and staff, or brings it back. Nothing is deleted.
LedgerChange the balance and give a reason; it shows in the shop's ledger as an admin adjustment.
Shop jobThe job the owner and staff are given.
StaffRemoves someone from the shop's staff.

In /poggy, four live panels change the script's data at once, with no restart:

  • Shop jobs (Player shops tab): set or clear each shop's job.
  • Player shops (Player shops tab): owner, stock, ledger, staff; transfer, repossess or restore, adjust the ledger, remove staff, change a shelf price.
  • Market prices (Economy & prices tab): nudge a live price or reset it to base, and see a week of price history. Needs dynamic pricing.
  • Exchange accounts (Economy & prices tab): correct a balance with a reason, or close a position at the live price. Needs the exchange.

Every change from /pmadmin and these panels is written to the server console and the admin webhook (Config.Admin.webhook) with who made it.

For developers#

Server exports, available while dynamic pricing is on:

ExportReturns
exports.poggy_markets:GetPrice(itemName, basePrice, "sell")The live price for that item, or nil if it is not tracked.
exports.poggy_markets:GetMarket("pelts")Every item in a market.
exports.poggy_markets:IsTracked(itemName)true or false.

Troubleshooting#

A store shows no items
Run /pmdiag. The store type's list name must match a catalog, and the item names must exist on your server. The start-up console lists every missing item.
"That item is not available on this server"
The item's name is not in your framework's item list. Fix the name in the catalog.
Nobody can found a shop
The deed item is missing from your item list (the console says so at start), or Config.PlayerShops.enabled is off, or creationItem is false. If using the deed does nothing on your framework, players can type /openstore.
"That is too close to another shop"
A new or moved shop must be at least Config.PlayerShops.minSpacing metres from every shop and store.
The FOR SALE prompt never appears
The store needs purchasable = true, a shopId, and an unowned or repossessed playershops row.
No prompt at all
Get off your horse; prompts do not show while mounted. Stand within Config.interactionDistance of the store's coords.
The clerk stands on the prompt
Give the store an npc.coords behind the counter.
A clerk looks wrong, or two shopkeepers stand together
A missing model is replaced by Config.fallbackClerkModel and reported once in the client console. Where the game already has a shopkeeper, set npc = false.
The exchange is empty
Config.Exchange.tradableMarkets must name markets in Config.Pricing.markets, those markets must match catalog items, and dynamic pricing must be on. /pmdiag shows both.
A shop disappeared
It was probably repossessed for unpaid tax. /pmshops marks it REPOSSESSED; restore it with /pmunreposhop or /pmadmin. Nothing was deleted.
Shop staff did not get the shop's job
Check Config.ShopJobs.enabled is on and the shop has a job (/pmshops lists it). On RSG and QBCore RedM the job must exist in the framework's shared jobs.
A config change did nothing
Restart poggy_markets. /pmreload reloads shops, not config files.

Updating#

poggy_core's updater installs new versions of Poggy Markets and keeps your values in its config and translation files. See Updates.

New database columns and tables are added automatically when the script starts. Nothing is dropped, and shops, ledgers and stock carry over.

Join the Discord Discord