> For the complete documentation index, see [llms.txt](https://ccfivem.gitbook.io/ccfivem-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ccfivem.gitbook.io/ccfivem-docs/vehicles/licenses-cc_licenses.md).

# Licenses (cc\_licenses)

Driving, motorcycle, truck, boat and pilot licences with theory and practical exams, a penalty points record, licence cards with photo, service ID cards for jobs and a police command.

**Frameworks:** ESX Legacy, QBCore, QBox (detected automatically, see Framework Bridge).

## Features

* Five examinable licences: `car`, `bike`, `truck` (requires `car`), `boat`, `pilot` (requires `car`), each with its own GTA vehicle classes
* An additional `weapon` licence without exam, granted and revoked only by the police (e.g. checked by other scripts via `Has(src, 'weapon')`)
* Three schools: driving school, marina and flight school
* Theory test: questions and answer order are chosen by the server, solutions are only sent after submission; optional time limit per question; some questions show drawn scenes
* Practical test: the route is built from the GTA road network (boat routes for boats, fixed air waypoints for flights) and validated by the server **before** charging; fault points for speeding, collisions and skipped waypoints
* Pay up front for each part; a retry delay applies after failing
* Penalty points on record; reaching the maximum revokes the licence automatically
* Police command to check, grant, revoke and add points
* Licence cards as inventory items with number and photo; card contents are always read from the server, so forged item metadata shows nothing and replaced cards show as invalid
* Service ID cards for jobs with per-job colours and emblems, one valid serial per person and job, invalid once the holder leaves the job
* Exam vehicles get a key via cc\_keys/00%20Overview\.md)
* Exports for other scripts (`Has`, `HasForClass`, `Grant`, `Revoke` and more)

## Optional integrations

| Resource                                     | Effect                                                     |
| -------------------------------------------- | ---------------------------------------------------------- |
| cc\_inventory/00%20Overview\.md)             | Cards as items; otherwise view only via `/licences`        |
| cc\_images%20(cc\_images)/00%20Overview\.md) | ID photo; otherwise empty                                  |
| cc\_keys/00%20Overview\.md)                  | Keys for exam vehicles; otherwise unlocked                 |
| cc\_jobs/00%20Overview\.md)                  | Job card offices placed in game (points of kind `jobcard`) |
| cc\_police/00%20Overview\.md)                | Police workflows using the licence exports                 |
| cc\_lib/00%20Overview\.md)                   | Shared notification style                                  |
| Company account resource                     | Only for `societyPays` on job cards                        |

{% hint style="info" %}
The script is protected by Cfx asset escrow. `config/`, `locales/` and the framework bridge files in `shared/` remain editable. Questions and routes are data in the config, not code.
{% endhint %}

## Pages

* Installation
* Configuration
* API
* FAQ

Need help? See Support.

## Installation

See also the general installation guide and dependencies.

### Requirements

| Resource                                                    | Required | Purpose                             |
| ----------------------------------------------------------- | -------- | ----------------------------------- |
| `oxmysql`                                                   | Yes      | Database (declared as `dependency`) |
| ESX Legacy / QBCore / QBox                                  | Yes      | Players, money, jobs                |
| `cc_lib`, `cc_inventory`, `cc_keys`, `cc_images`, `cc_jobs` | Optional | See overview                        |

### Steps

1. Copy the folder to `resources/[crankchaos]/cc_licenses`.
2. Add to your `server.cfg`:

```cfg
ensure oxmysql
ensure es_extended   # or qb-core / qbx_core
ensure cc_lib        # optional
ensure cc_inventory  # optional
ensure cc_keys       # optional
ensure cc_images     # optional
ensure cc_jobs       # optional, before cc_licenses
ensure cc_licenses
```

3. Restart the server.

### Database

No SQL file needs to be imported. These tables are created automatically on first start:

| Table               | Content                                            |
| ------------------- | -------------------------------------------------- |
| `cc_licences`       | Licences and points per character                  |
| `cc_licence_photos` | One ID photo per character                         |
| `cc_licence_cards`  | Licence cards (number, document, valid, delivered) |
| `cc_job_cards`      | Service ID cards                                   |
| `cc_job_badges`     | Badge numbers per person and job                   |

### Inventory items (cc\_inventory)

Add the items `driver_license`, `boat_license`, `pilot_license` and `job_card` to cc\_inventory with a use hook and `keep = true`:

```lua
-- licence cards
server = { export = 'cc_licenses.UseCard', keep = true }

-- job_card
server = { export = 'cc_licenses.UseJobCard', keep = true }
```

Without cc\_inventory there are no physical cards; licences can still be viewed via `/licences`.

### Framework notes

* The police command uses `Config.Police.job` (default `police`) and `minGrade` (default `2`).
* Job card offices refer to jobs by their framework name (`cardealer`, `fueler`, `mechanic`, `ambulance`, `police` by default). Adjust `Config.JobCards.offices` to your jobs.

### Permissions

No ACE permissions are required. The police command is restricted by job and grade, checked on the server.

## Configuration

Editable files: `config/config.lua`, `config/ui.lua` (look, language), `locales/de.json`, `locales/en.json`, and the framework bridge in `shared/` (`cc_framework.lua`, `cc_characters.lua`, `cc_society.lua`). All texts, including exam questions, live in the locale files; the config only holds keys. Set `locale = 'en'` in `config/ui.lua` for English.

### General

```lua
Config.Debug = false
Config.OpenKey = 38            -- E
Config.PaymentAccount = 'any'  -- 'cash', 'bank' or 'any' (cash first)
Config.RetryDelay = 600        -- seconds after a failed exam, per licence
Config.Blips = true
Config.SchoolDistance = 8.0
Config.UseKeys = true          -- give cc_keys keys for exam vehicles
```

### Licences

```lua
{
    id = 'truck',
    label = 'licence.truck',
    description = 'licence.truck.desc',
    icon = 'truck',
    price = 3000,             -- theory
    practical = true,
    practicalPrice = 4000,
    classes = { 10, 11, 12, 20 },
    requires = { 'car' },
    school = 'driving',
    route = 'truck',
    cardClass = 'CE',
},
```

| Licence  | Classes                         | School                               | Theory / practical price |
| -------- | ------------------------------- | ------------------------------------ | ------------------------ |
| `car`    | 0-7, 9, 22                      | driving                              | 1500 / 2500              |
| `bike`   | 8                               | driving                              | 1200 / 1800              |
| `truck`  | 10, 11, 12, 20 (requires `car`) | driving                              | 3000 / 4000              |
| `boat`   | 14                              | marina                               | 2000 / 3000              |
| `pilot`  | 15, 16 (requires `car`)         | flight                               | 12000 / 25000            |
| `weapon` | none                            | `authority` (no school, police only) | 0 / -                    |

Classes that are not assigned to any licence (e.g. 13 cycles, 21 trains) return `true` in `HasForClass`.

### Points and police

```lua
Config.Points = { max = 8 }
Config.Police = {
    job = 'police',
    minGrade = 2,
    command = 'licence',
}
```

Points do not expire on their own. Reduce them with a negative number, e.g. `/licence points 12 car -2`.

### Schools

```lua
{
    id = 'driving',
    label = 'licence.school.driving',
    ped = 's_m_m_highsec_01',
    coords = vector4(-815.2435, -1346.7644, 5.1503, 49.4132),
    scenario = 'WORLD_HUMAN_CLIPBOARD',
    blip = { sprite = 525, colour = 5, scale = 0.7 },
},
```

Schools: `driving`, `marina`, `flight`. A school offers every licence whose `school` points to it.

### Theory

```lua
Config.Theory = {
    questionCount = 10,
    passPercent = 70,
    timeLimit = 600,     -- seconds for the whole test
    timeGrace = 5,
    questionTime = 45,   -- seconds per question
    showSolutions = true,
    questions = {
        {
            id = 'road.speed',
            licences = { 'car', 'bike', 'truck' },
            question = 'theory.road.speed',
            answers = { 'theory.road.speed.a', 'theory.road.speed.b', 'theory.road.speed.c' },
            correct = 2,
            scene = 'speedSign',  -- optional drawn scene
        },
    },
}
```

The correct answer never leaves the server.

### Practical

```lua
Config.Practical = {
    maxFaults = 10,
    faults = { speed = 1, collision = 3, skipped = 4 },
    faultCooldown = 2500,
    speedTolerance = 5,
    speedGrace = 1500,
    radius = 12.0,
    serverTolerance = 40.0,
    minSecondsPerPoint = 4,
    timeout = 20,          -- minutes, 0 = none
    plate = 'FS',
    routes = { car = { ... }, bike = { ... }, truck = { ... }, boat = { ... }, air = { ... } },
    pathLoadTimeout = 4000,
}
Config.Practical.airRadius = 60.0
Config.Practical.waterRadius = 25.0
```

Route fields: `vehicle`, `vtype` (`automobile`, `bike`, `boat`, `heli`, `plane`), `speedLimit` (0 = none), and either `auto` (route generated from the road network) or fixed `spawn`/`points` (air).

`auto` options: `center`, `radius`, `minRadius`, `maxRadius`, `points`, `minSpacing`, `minLength`, `nodeType` (0 main roads, 1 incl. side roads, 3 water), `avoid` (`highway`, `offroad`, `tunnel`, `deadEnd`, `water`, `narrow`), `spawnDistance`, `spawnNodeType`, `useAnchors`, `anchorTolerance`. The server validates exactly these values; a rejected route is not charged.

### Licence cards

```lua
Config.Documents = {
    enabled = true,
    photo = { firstFree = true, price = 150, countdown = 3 },
    replacePrice = 250,
    showDistance = 3.0,
    types = {
        { id = 'driving', item = 'driver_license', label = 'document.driving',
          licences = { 'car', 'bike', 'truck' }, prefix = 'D', school = 'driving' },
        { id = 'boat', item = 'boat_license', ..., prefix = 'S', school = 'marina' },
        { id = 'pilot', item = 'pilot_license', ..., prefix = 'P', school = 'flight' },
    },
}
```

Photos and replacement cards are available at the school named in `school`.

### Service ID cards

```lua
Config.JobCards = {
    enabled = true,
    item = 'job_card',
    fee = 0,
    societyPays = false,
    photo = true,
    countdown = 3,
    cooldown = 30,
    distance = 1.8,
    serverDistance = 4.0,
    showDistance = 3.0,
    marker = { enabled = true, type = 27, scale = vector3(1.2, 1.2, 1.0), offsetZ = -0.95, distance = 12.0 },
    jobs = {
        police = { label = 'jobcard.job.police', short = 'LSPD', accent = '#2563EB', emblem = 'star' },
        -- ambulance, mechanic, cardealer, fueler
    },
    default = { short = 'SA', accent = '#64748B', emblem = 'shield' },
    offices = {
        { id = 'police', job = 'police', label = 'Revier Mission Row', coords = vector3(461.6, -983.1, 30.7) },
        -- pdm, fuel, mechanic, ambulance
    },
}
```

Per job: `label`, `short`, `accent`, `emblem` (`star`, `cross`, `wrench`, `car`, `fuel`, `shield`), and optionally `minGrade`, `fee`, `societyPays`. Offices can also be placed in game with cc\_jobs/00%20Overview\.md) (points of kind `jobcard`). The shipped offices match the offices of cc\_bossmenu/00%20Overview\.md); if you move one there, move it here too.

## Exports, Events & Commands

### Server exports: licences

Synchronous, read from memory (online players).

| Export                    | Returns                                             |
| ------------------------- | --------------------------------------------------- |
| `Has(src, licence)`       | `boolean`                                           |
| `HasForClass(src, class)` | `boolean` (classes without a licence return `true`) |
| `Grant(src, licence)`     | Grants a licence                                    |
| `Revoke(src, licence)`    | Revokes a licence                                   |
| `List(src)`               | The player's licences                               |

Offline-capable variants (e.g. for an MDT), by character identifier:

| Export                            |
| --------------------------------- |
| `GrantById(identifier, licence)`  |
| `RevokeById(identifier, licence)` |
| `ListById(identifier)`            |

```lua
if exports.cc_licenses:HasForClass(src, GetVehicleClass(vehicle)) then
    -- allowed to drive
end
exports.cc_licenses:Grant(src, 'weapon')
```

### Server exports: cards

| Export            | Returns                                    |
| ----------------- | ------------------------------------------ |
| `UseCard`         | Use hook for cc\_inventory (licence cards) |
| `GetPhoto(src)`   | Photo as data URL or `nil`                 |
| `GetCard(number)` | Card view without photo or `nil`           |

### Server exports: service ID cards

Call these from a thread.

| Export                                   | Returns                                 |
| ---------------------------------------- | --------------------------------------- |
| `UseJobCard`                             | Use hook for cc\_inventory (`job_card`) |
| `GetJobCard(src, job?)`                  | Table or `nil`                          |
| `HasValidJobCard(src, job?, mustCarry?)` | `boolean`                               |

### Client exports (display only)

| Export               | Returns   |
| -------------------- | --------- |
| `MyLicences()`       | Table     |
| `MyLicence(licence)` | `boolean` |

There are no public events.

### Commands

| Command                                   | Effect                              | Permission                          |
| ----------------------------------------- | ----------------------------------- | ----------------------------------- |
| `/licences`                               | Show your own licences and cards    | Everyone                            |
| `/ccschools`                              | Toggle school blips                 | Everyone                            |
| `/licence check <id>`                     | Show a player's licences and points | `Config.Police.job` from `minGrade` |
| `/licence grant <id> <licence>`           | Grant a licence                     | Police                              |
| `/licence revoke <id> <licence>`          | Revoke a licence                    | Police                              |
| `/licence points <id> <licence> <amount>` | Add points (negative to remove)     | Police                              |

The command name is set by `Config.Police.command`. Without arguments it explains itself.

## FAQ & Troubleshooting

**Players get no licence card.** Cards need cc\_inventory/00%20Overview\.md) with the items `driver_license`, `boat_license`, `pilot_license` and `job_card` and their use hooks. Without it, licences are viewable via `/licences` only.

**The card shows no photo.** Photos are taken with cc\_images%20(cc\_images)/00%20Overview\.md). The first photo is free (`Config.Documents.photo.firstFree`).

**The practical exam does not start.** The route is built from the road network and checked by the server before charging. If it is rejected, nothing is charged. Check the route's `auto` values (e.g. `minRadius`, `maxRadius`, `minLength`) if you moved a school.

**A player cannot retake an exam.** `Config.RetryDelay` (default 600 seconds) applies per licence after failing.

**Players can't take the truck or pilot exam.** Both require the `car` licence (`requires`).

**Passed theory is lost.** A passed theory awaiting the practical part is only kept in memory and is lost on a server restart.

**The police command does nothing.** The player needs `Config.Police.job` (default `police`) and at least `minGrade` (default `2`).

**A job card shows "invalid".** A newer card was issued for the same job, or the holder no longer has the job. A new rank also requires a new card.

**How do other scripts check for a licence?** Use the exports, e.g. `exports.cc_licenses:Has(src, 'weapon')`.

**Known limitation:** practical faults (speeding, collisions) are reported by the client. The route itself is checked by the server in order and with a minimum duration.

Still stuck? See Support.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://ccfivem.gitbook.io/ccfivem-docs/vehicles/licenses-cc_licenses.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
