# Admin Permissions (/docs/guides/admin-permissions)

To become admin in ESX Legacy, set your ESX group to `admin`: run `setgroup <yourServerId> admin` in the server console, or set `users.group` to `admin` in the database while you are offline. ESX then links that group to FiveM's ACE system (`group.admin`), which is what actually unlocks the admin commands, so `server.cfg` also needs the ACE lines shown below.

## Overview [#overview]

ESX permissions have two layers that work together:

| Layer | Stored in | Used for |
| --- | --- | --- |
| ESX group | `users.group` (default `user`) | `xPlayer.getGroup()`, scripts that check groups, the `Player(source).state.group` state bag |
| ACE principal | Added at runtime by es_extended | FiveM command permissions (`command.setjob` and so on) |

When a player loads, es_extended runs `add_principal identifier.<license> group.<esxGroup>`. When you change the group with `/setgroup` or `xPlayer.setGroup()`, it removes the old principal and adds the new one. Every command registered with `ESX.RegisterCommand` gets an `add_ace group.<group> command.<name> allow` entry for each allowed group.

The default groups are `user`, `admin` and `owner`. The column is free text, so you can invent more, but each group needs entries in `Config.CommandPermissions` to be useful. A group named `superadmin` is converted to `admin` when the player loads.

## Required server.cfg lines [#required-servercfg-lines]

These lines come with the official txAdmin recipe and `server.cfg`. If you set up the server by hand, check that they exist:

```bash title="server.cfg"
add_principal group.admin group.user
add_ace group.admin command allow       # allow all commands
add_ace group.admin command.quit deny   # but don't allow quit
add_ace resource.es_extended command.add_ace allow
add_ace resource.es_extended command.add_principal allow
add_ace resource.es_extended command.remove_principal allow
add_ace resource.es_extended command.stop allow
```

The `resource.es_extended` lines let es_extended run `add_ace`, `add_principal` and `remove_principal` itself. Without them, ESX groups never turn into ACE permissions and admin commands are denied even when the database says `admin`.

## Make yourself admin [#make-yourself-admin]

<Steps>
<Step>
### Join the server once [#join-the-server-once]

ESX creates your row in `users` on first join. Note your server ID (txAdmin player list, or the scoreboard).
</Step>
<Step>
### Set the group from the console [#set-the-group-from-the-console]

In the server console or the txAdmin live console, without a slash:

```bash
setgroup 1 admin
```

The arguments are `[playerId] [group]`. In game, an existing admin can run `/setgroup me admin` or `/setgroup 5 owner`. The group is applied immediately; it is written to the database on the next player save (every 10 minutes, on disconnect, or with `/save [playerId]`).
</Step>
<Step>
### Verify [#verify]

Run `/group` in game. The command prints your current group to the **server console**. In scripts, read `xPlayer.getGroup()`.
</Step>
</Steps>

### Alternative: edit the database [#alternative-edit-the-database]

With the player offline, update the row directly. The identifier is the Rockstar license hash without the `license:` prefix; with esx_multicharacter it is prefixed with the character slot, for example `char1:`.

```sql
UPDATE `users` SET `group` = 'admin' WHERE `identifier` = 'char1:1a2b3c4d5e6f...';
```

Do this only while the player is offline. An online player's group lives in memory and is saved back on the next save, which would overwrite your change.

### Alternative: admin on first join through ACE [#alternative-admin-on-first-join-through-ace]

When a **new** player row is created, es_extended checks `IsPlayerAceAllowed(source, 'command')`. If the player already has ACE access to `command` (for example through an `add_principal identifier.license:... group.admin` line in `server.cfg`), they are created with group `admin`. The txAdmin recipe adds such lines for the txAdmin master account (`{{addPrincipalsMaster}}`), which is why the server owner is often admin automatically. This only affects new characters; existing ones keep their stored group.

```bash title="server.cfg"
add_principal identifier.license:1a2b3c4d5e6f... group.admin
```

On a local test server with `sv_lan` set to `true`, every player counts as admin.

## Config.CommandPermissions [#configcommandpermissions]

In `es_extended/shared/config/main.lua`, `Config.CommandPermissions` maps each built-in command to the groups that may use it:

```lua title="es_extended/shared/config/main.lua"
Config.CommandPermissions = {
    ["setjob"]    = { "owner", "admin" },
    ["car"]       = { "owner", "admin" },
    ["noclip"]    = { "owner", "admin" },
    ["job"]       = { "user", "admin", "owner" },
    -- ...
}
```

To let a `mod` group use `/goto` and `/bring` but nothing else:

```lua title="es_extended/shared/config/main.lua"
["goto"]  = { "owner", "admin", "mod" },
["bring"] = { "owner", "admin", "mod" },
```

Then `setgroup <id> mod`. A command without an entry is restricted to `admin` with a console warning.

`Config.AdminGroups` (`owner` and `admin`) is marked deprecated in 1.16.4 but is still read by `xPlayer.isAdmin()`. Keep it in sync if third-party scripts rely on it.

## Built-in admin commands [#built-in-admin-commands]

All of these accept `me` as the player ID in game. Most also work from the server console.

| Command | Arguments | What it does |
| --- | --- | --- |
| `/setgroup` | `playerId group` | Change ESX group |
| `/setjob` | `playerId job grade` | Change job ([Create a job](/docs/guides/create-a-job)) |
| `/giveaccountmoney` | `playerId account amount` | Add money to `money`, `bank` or `black_money` |
| `/setaccountmoney` / `/removeaccountmoney` | `playerId account amount` | Set or remove account money |
| `/giveitem` | `playerId item count` | Give an item (default inventory only) |
| `/giveweapon`, `/giveammo`, `/giveweaponcomponent` | | Weapons (default inventory only) |
| `/car`, `/cardel` (`/dv`), `/fix` (`/repair`) | | Spawn, delete, repair vehicles |
| `/setcoords` (`/tp`), `/tpm`, `/goto`, `/bring` | | Teleporting |
| `/noclip`, `/kill`, `/freeze`, `/unfreeze` | | Player control |
| `/setdim` (`/setbucket`) | | Routing bucket |
| `/players`, `/save`, `/saveall`, `/refreshjobs`, `/refreshitems` | | Server management |

With `Config.AdminLogging = true` and a webhook configured in the logs config, many of these commands are logged to Discord. For a full menu, see [esx_adminmenu](/docs/addons/esx_adminmenu). A full list of commands is on the [Commands](/docs/es_extended/commands) page.

## Add your own admin-only command [#add-your-own-admin-only-command]

`ESX.RegisterCommand` handles the ACE setup, argument parsing and the chat suggestion for you:

```lua title="server/main.lua"
ESX = exports['es_extended']:getSharedObject()

ESX.RegisterCommand('announce', { 'owner', 'admin' }, function(xPlayer, args, showError)
    if #args.message < 3 then
        return showError('Message too short')
    end

    TriggerClientEvent('chat:addMessage', -1, {
        args = { 'Announcement', args.message },
    })
end, true, {
    help = 'Send a server-wide announcement',
    arguments = {
        { name = 'message', help = 'Text to send', type = 'merge' },
    },
})
```

| Argument | Description |
| --- | --- |
| `name` | Command name, or a table of aliases like `{ 'announce', 'ann' }` |
| `group` | Group name or list of groups allowed to run it |
| `cb` | `function(xPlayer, args, showError)`; `xPlayer` is `false` when run from the console |
| `allowConsole` | `true` to allow the server console |
| `suggestion` | `help`, `arguments` (types: `number`, `player`, `playerId`, `string`, `item`, `weapon`, `coordinate`, `merge`, `any`) and optional `validate = true` to require the exact argument count |

To check permissions in your own events, read the group on the server, never trust the client:

```lua title="server/main.lua"
RegisterNetEvent('myresource:adminAction', function()
    local xPlayer = ESX.GetPlayerFromId(source)
    if not xPlayer or xPlayer.getGroup() ~= 'admin' then
        return
    end
    -- privileged code
end)
```

## Common issues [#common-issues]

<Accordions>
<Accordion title="Group is admin but commands are denied">
The `add_ace resource.es_extended ...` lines are missing from `server.cfg`, or another resource removed the principal. Restart after adding the lines.
</Accordion>
<Accordion title="My group resets to user after a restart">
You edited the database while the player was online, and the in-memory group was saved back over it. Use `/setgroup` instead, or edit while offline.
</Accordion>
<Accordion title="/setgroup accepted a typo">
`/setgroup` does not validate group names. A group like `amdin` has no permissions. Set it again with the correct name.
</Accordion>
<Accordion title="The owner group cannot use non-ESX commands">
`server.cfg` only grants `command allow` to `group.admin`. `owner` only gets the ESX commands listed for it in `Config.CommandPermissions`. Add `add_principal group.owner group.admin` if owners should inherit everything admins have.
</Accordion>
</Accordions>

## Related [#related]

- [Commands](/docs/es_extended/commands)
- [server.cfg](/docs/getting-started/server-cfg)
- [xPlayer reference](/docs/es_extended/server/xplayer)
- [Main config](/docs/es_extended/config)
- [esx_adminmenu](/docs/addons/esx_adminmenu)
