Contracts
Version 1.0.0. The listing, or pick it up in the shop and run it.
Contracts
Contracts gives players a job board with escrow. The poster puts the reward up front, another player takes the job, and the money only moves when the poster confirms, the timer runs out or staff settle a dispute. For context, players already make deals with each other (a ride to Paleto, twenty steel for a builder, a lawyer for a hearing). Most of those deals run on trust, and when one side doesn't pay or doesn't show up, staff get a ticket with no record of what was agreed.

What you get
- A player job board. Any player posts a task with a title, details, a category and a reward. Others browse it with search, category, type, lowest reward and sort filters, and see the poster's record before they take anything.
- Escrow. The reward leaves the poster's bank when they post and is held with the contract. It's paid out once, to the right person, no matter how many times anyone clicks or how many servers share the database.
- A clear lifecycle. Open, in progress, handed in, completed. The poster can cancel for a full refund only while nobody has taken the job. If the poster goes quiet after the worker hands in, the contract completes on its own after 24 hours (you choose the hours). Open contracts nobody takes come off the board and are refunded after 72 hours.
- Disputes. Either side can open a dispute once the job is taken. The money stays held and staff settle it from a dispute queue: pay the worker, refund the poster, or split it by percent. Staff see the dispute text, both players' records and the full history of the contract.
- Item deliveries (ox_inventory). A poster can ask for an amount of one item. The worker hands the items in through the board, they leave the worker's inventory, and they go to the poster when the contract completes.
- Reputation. Completed contracts, dispute rate and an average rating from 1 to 5, kept separately for posting and for working, and shown on every listing.
- A fee, if you want one. A percentage of each payout goes to a money sink or into one character's bank, for example a city treasury. The fee comes out of the reward, so the poster pays exactly what they posted and nothing is lost to rounding.
- Limits: contracts per player, contracts a player can work at once, lowest and highest reward, a cooldown between posts, length caps on every text field, and a blocked words list.
- Payments that wait. Money or items owed to someone who is offline (or has no room in their inventory) wait on the board and are paid when the character loads or opens the board.
- Exports so other scripts can post contracts and read reputation.
- Framework support: Qbox, QBCore and ESX Legacy.

Requirements
- Qbox, QBCore or ESX Legacy (Contracts uses the bank account, so it needs one of them)
- oxmysql
- ox_inventory for item deliveries. Without it, only plain contracts are offered and the item section of the form is hidden.
Install
- Unzip so that
resources/contracts/fxmanifest.luaexists (notresources/contracts-v1.0.0/contracts/..., which Windows "Extract All" creates), then addensure contractsafter your framework and oxmysql. Start order doesn't matter much, it waits up to 30 seconds for the framework and prints what it found. - Give staff the dispute queue in
server.cfg:add_ace group.admin contracts.staff allow - Start the server. The tables create themselves on every start (
sql/install.sqlis there to read, you never run it by hand). - Type
/contractsin game. SetConfig.OpenKeyif you want a default key, and players can change it in the game's key bindings.
Setting it up
Everything lives in config.lua, with a comment next to each setting.
| Setting | Default | What it does |
|---|---|---|
Config.Reward.min / max |
100 / 250000 | Lowest and highest reward a player can post |
Config.Fee.percent |
5 | Cut of each payout to the worker. 0 turns it off |
Config.Fee.sink |
'none' |
'none' removes fees from the economy, 'character' pays them into Config.Fee.character's bank |
Config.AutoConfirmHours |
24 | Hours after a hand-in before the contract completes without the poster |
Config.OpenExpiryHours |
72 | Hours an untaken contract stays up before it is refunded. 0 keeps it up |
Config.Limits.maxActivePerPlayer |
3 | Unfinished contracts one character can have posted |
Config.Limits.maxWorkingPerPlayer |
2 | Contracts one character can work at once |
Config.Limits.postCooldownSeconds |
120 | Time between two posts by the same character |
Config.Limits.blockedWords |
a short list | Words or phrases that get a post refused. Add your own |
Config.Categories |
six | The categories on the board |
Config.Items |
on | Item deliveries, the most items per contract and items nobody can ask for |
Config.StaffAce |
contracts.staff |
The ACE that opens the dispute queue |
How the money moves
- The full reward is taken from the poster's bank when they post. If the bank refuses, nothing is posted.
- The worker gets the reward minus the fee. With a 5 percent fee, a $1,000 contract pays the worker $950 and $50 goes to the sink. Fees are whole money, rounded down, so the fee on $99 is $4.
- A cancelled or expired contract, or a staff refund, returns everything to the poster with no fee.
- A split gives the worker the chosen percent of the reward, minus the fee on that part, and returns the rest to the poster.
- Every change to a contract is one database update that only goes through if the contract is still in the state it was read in. A second click, a second player or a second server sharing the database finds it already changed, so nothing is paid twice. Each payout is claimed the same way before it's paid.
Item deliveries
Item deliveries need ox_inventory. When the worker hands in, the items leave their inventory straight away and are held with the contract, then go to the poster when it completes. If staff refund the poster, the items go back to the worker, and on a split they go to the poster. Anyone who is offline or has no room gets a Deliveries line on their board with a Collect button. Also, weapons and the items in Config.Items.blocked can't be asked for, because the board wouldn't keep their serial numbers and ammo.

Exports
These read the database, so call them from a server thread or event handler.
-- A character's record: posted, worked, posterRating, workerRating (one decimal),
-- posterRatings, workerRatings (how many), posterDisputeRate, workerDisputeRate (whole percent)
local rep = exports.contracts:GetReputation(citizenid)
-- A contract as the board shows it, or nil
local c = exports.contracts:GetContract(42)
-- Post a contract for an online player. The reward comes from their bank, with every
-- limit the board applies. Returns the id, or nil and a reason such as 'no_money'.
local id, why = exports.contracts:PostContract(source, {
title = 'Deliver 20 steel to the docks', details = 'Pier 4', category = 'delivery', reward = 2500,
item = 'steel', qty = 20, -- optional, needs ox_inventory
})
Contracts also fires a server event whenever a fee is taken, so a society or treasury script can pick it up:
AddEventHandler('contracts:feeCollected', function(contractId, amount, sink) end)
Commands
| Command | Who | What |
|---|---|---|
/contracts |
everyone with a character loaded | Opens the board. Staff with the ACE also see the Disputes tab |
Troubleshooting
- Nothing happens and there's no
[contracts]line in the console: the folder is probably nested. Check thatresources/contracts/fxmanifest.luaexists, and notresources/contracts/contracts/fxmanifest.lua. - "No framework finished starting within 30s": your framework isn't running or has a different resource name. The line lists what it found. Set
Config.Frameworkif you run a renamed core. - "Database setup failed": oxmysql isn't connected or your database user can't create tables. Fix the connection string in
server.cfg, or have someone with more rights runsql/install.sqlonce. - The item section of the post form is missing: ox_inventory isn't started, or
Config.Items.enabledis false. The startup line tells you which. - "The inventory doesn't know that item": the name has to match ox_inventory's item name, for example
steel, not its label. - A player says they were never paid: open the board as that character. Anything owed shows under Deliveries, and Collect pays it. The console also prints a line for any payout the framework refused.
- Staff can't see the Disputes tab: the ACE isn't granted. Check
add_ace group.admin contracts.staff allowand that the staff member is in that group.
Escrow
The core is encrypted with Cfx asset escrow. These stay open so you can adapt them: config.lua, server/bridge.lua (all framework and inventory code lives there) and sql/install.sql.
Support
Open a ticket in the Discord with your framework, inventory, oxmysql version and the server console output from startup.