Browse docs
Vehicle Registry
Use these integrations to check a vehicle in a garage, show its certificates in another police tablet, or log completed workshop paperwork. The shared registry powers Sky Mechanic Job and Sky Police Job. All exports and events on this page belong to sky_jobs_base and run on the server.
Setup
Install a Jobs Base build containing the vehicle registry exports and start it before your integration:
dependency "sky_jobs_base"
Technical inspection and vehicle registration can each be enabled or disabled in the Mechanic Job Configurator (/jobconfig). Disabling a feature removes its status from query results. If both are disabled, status queries return feature_disabled. Install matching resource updates with their registry migrations. Updating GitHub source alone does not replace an installed encrypted Cfx package.
Server exports
| Export | Arguments | Use case |
|---|---|---|
GetVehicleRegistryFeatures | None | Check enabled features and service availability. |
GetVehicleRegistryStatus | plate | Check inspection and registration status without holder details or signatures. |
GetVehicleRegistryDetail | source, plate | Read certificates and signed history for an authorized mechanic or police player. |
SetVehicleRegistrationAuthority | provider | Explicitly bind a citizen-office registration authority to the calling server resource. |
ClearVehicleRegistrationAuthority | None | Release the calling resource's authority and cancel its open sessions. |
EnsureVehicleTemporaryRegistration | plate | Issue a provisional permit once or reconcile an ownership transfer. |
PrepareVehicleRegistration | source, { plate } | Prepare an owner-signed registration at the authorized office. |
CompleteVehicleRegistration | source, { id } | Complete a consented authority session and charge the configured bank fee. |
ReissueTemporaryVehicleDocument | source, { plate } | Replace a lost provisional document without resetting its budget. |
GetVehicleRegistrationDocument | source, documentId | Read a current document for its owner or authorized registry staff. |
RecordVehiclePlateStyleChange | plate, change | Append a trusted mechanic's plate-style change to canonical history. |
Call database-backed queries when a vehicle is selected or used, rather than every frame. The exports yield while waiting for database results; call them from a server thread, event handler, or callback.
GetVehicleRegistryFeatures()
Returns a new snapshot of the current settings and availability. Changing this table does not change the server configuration.
local registry = exports["sky_jobs_base"]:GetVehicleRegistryFeatures()
-- registry.available: boolean; the registry has finished initializing successfully
-- registry.features.kuv: boolean; technical inspection is enabled
-- registry.features.registration: boolean; vehicle registration is enabled
-- registry.version: number; changes whenever registry configuration is applied
-- registry.error: string or nil; reason the service is unavailable
-- registry.registrationAuthority: { resource?, ready, temporarySeconds?, temporaryPlateIndex?, registeredPlateIndex? }
-- registry.registrationFees: { fee, transferFee, requireKuv } or nil before policy initialization
-- registry.plateStyleAuditAvailable: boolean; the Mechanic provider supports the optional audit API
available = true can also mean that both features are deliberately disabled. Read the feature switches before deciding whether your integration should require certificates.
registrationAuthority.ready means a provider is installed. It does not enable the existing feature switches or the separate Mechanic audit. plateStyleAuditAvailable describes support, not whether an audit consumer has opted in. An older compiled package can lack these fields entirely.
GetVehicleRegistryStatus(plate)
Accepts a string plate. Surrounding whitespace is removed and letters are normalized to uppercase; internal spaces remain part of the plate. The plate must belong to an owned vehicle in the configured framework vehicle database. The vehicle does not need to be spawned or nearby for this read.
local result = exports["sky_jobs_base"]:GetVehicleRegistryStatus("SKY123")
if not result.success then
print(("Vehicle registry query failed: %s"):format(result.error))
return
end
local vehicle = result.data
local inspection_ok = not vehicle.features.kuv
or vehicle.kuv.status == "valid"
or vehicle.kuv.status == "due_soon"
local registration_ok = not vehicle.features.registration
or vehicle.registration.status == "registered"
print(("Vehicle %s: inspection=%s, registration=%s"):format(
vehicle.plate, tostring(inspection_ok), tostring(registration_ok)
))
Successful responses have { success = true, data = ... }:
| Field | Type | Description |
|---|---|---|
plate | string | Normalized current plate. |
vehicleId | string? | Stable registry vehicle ID; absent if no registry identity has been assigned yet. |
features | table | Independent kuv and registration switches. |
version | number | Registry configuration version used by this query. |
kuv | table? | Enabled inspection: status, optional documentId, issuedAt, validUntil. |
registration | table? | Enabled registration: status, optional documentId, issuedAt. |
registration.plateIndex | number? | Authority-assigned display type when an authority is installed. |
registration.temporary | table? | Provisional status, remainingSeconds, documentId, version, issuedAt. |
Times are Unix timestamps in seconds. Status responses contain no holder names, player identifiers, notes, reports, or signatures. This export is for trusted server resources and does not require a mechanic or police player. An integration that exposes it to clients must authorize its own vehicle access.
| Inspection status | Meaning |
|---|---|
missing | No inspection has been issued. |
valid | The inspection is current. |
due_soon | Still valid, within the configured warning period. |
expired | The validity deadline has been reached. |
failed | The latest inspection failed. |
revoked | The current inspection was revoked. |
| Registration status | Meaning |
|---|---|
unregistered | No registration has been issued. |
registered | Registered to the current framework owner. |
transfer_required | Ownership changed after registration. |
deregistered | The vehicle was deregistered. |
revoked | The registration was revoked. |
Provisional status is nested under registration.temporary.status (valid or expired). It does not replace the existing registration.status values. Remaining time uses the holder character's cumulative online seconds; offline time pauses. Reissue and transfer never create a fresh grace period for an existing permit.
GetVehicleRegistryDetail(source, plate)
Reads the same authorized record used by the job tablets, including the latest certificates and up to 30 history entries. source is the requesting player's server ID, not a job name or persistent identifier. Pass the actual caller from your server handler.
local record = exports["sky_jobs_base"]:GetVehicleRegistryDetail(source, "SKY123")
if not record.success then
print(("Vehicle record unavailable: %s"):format(record.error))
return
end
for _, entry in ipairs(record.data.history) do
print(entry.kind, entry.occurredAt, entry.documentId)
if entry.consent then
print("Signed by:", entry.consent.signerName)
print("Signed at:", entry.consent.signedAt)
end
end
The server checks current job, duty, active organization and registry read permissions. Ordinary vehicle owners do not receive staff records through this export. Detailed inspection or registration rights also grant read access.
data contains vehicle, context, summary, revision, optional inspection and registration, and history. Signed certificates expose their document through snapshot.consent; history entries expose consent. It includes the original declaration, operation title, plate, fee, verified signer, signing time and drawn signature. Older records can have no consent document. Invoice references in history remain restricted to staff with the relevant mechanic rights.
Errors
Status and detail failures return { success = false, error = "code" }. Never treat an unsuccessful query as proof of a valid certificate.
| Code | Meaning |
|---|---|
invalid_plate | Plate is empty, invalid, contains control characters, or exceeds 24 bytes. |
vehicle_not_found | No unique owned vehicle matches the plate. |
feature_disabled | Both inspection and registration are disabled. |
invalid_request | Detail query has an invalid player source. |
not_authorized | Player lacks current registry access. |
configuration_changed | Configuration changed during the database read. |
database_unavailable | A database query failed. |
Initialization and framework lookup failures also return their existing service error, such as migration_required, transaction_driver_required, or registry_unavailable. Use GetVehicleRegistryFeatures() to inspect service readiness.
Citizen-office registration
This integration is optional and activated only through server exports. There is no additional Config switch. Without a registered authority, the existing workshop registration workflow stays available. With an authority, workshop registration/deregistration is rejected with external_registration_required; workshop KUV inspection remains in place.
SetVehicleRegistrationAuthority(provider) and ClearVehicleRegistrationAuthority()
SetVehicleRegistrationAuthority accepts this provider contract:
| Field | Contract |
|---|---|
authorize | Function (source) -> boolean; validate the player's actual office access/location. |
getPlaytimeSeconds | Function (characterIdentifier) -> number; cumulative online seconds, including active sessions and persisted offline characters. |
name | Nonempty localized authority name. |
documentTitle | Nonempty localized certificate title. |
declaration | Nonempty localized declaration shown for owner consent. |
operationTitle | Optional localized operation title. |
temporarySeconds | Optional integer budget; defaults to 259200 (72 online hours), range 1–31536000. |
temporaryPlateIndex | Optional integer 0–5; defaults to 2. |
registeredPlateIndex | Optional integer 0–5; defaults to 0, and must differ from the temporary index. |
Both exports return { success = true } or { success = false, error = "code" }. Ownership is bound to the actual calling resource, not a supplied resource-name field. Another resource cannot replace or clear an installed provider. Stopping its owner clears it and cancels its sessions; register again after an authority or Jobs Base restart. Saved certificates and history persist.
Use trusted server-side character playtime. Missing or regressed values fail with playtime_unavailable; do not substitute zero to issue a new permit. Registering an authority changes no existing feature switches or fee settings.
EnsureVehicleTemporaryRegistration(plate)
Only the authority resource may call this mutating export. Call it after a new vehicle is saved or its ownership changes. It assigns a stable registry identity, creates the initial permit once, and reconciles changed holders using the previous holder's remaining online seconds. Checkpoint the seller's playtime before the ownership transaction. A registered vehicle changes to transfer_required without receiving a new provisional grace period.
local result = exports["sky_jobs_base"]:EnsureVehicleTemporaryRegistration("SKY123")
if not result.success then
print(("Temporary registration failed: %s"):format(result.error))
return
end
-- Same data contract as GetVehicleRegistryStatus, plus:
-- result.data.temporaryChanged: true for initial issue or provisional transfer
-- result.data.registration.plateIndex: required registration display type
An unchanged owner checkpoints consumption without replacing the document. The authority integration applies/persists the returned plate type and issues inventory metadata; this export does not spawn vehicles or inventory items.
PrepareVehicleRegistration(source, { plate })
Requires the current owner, access through the provider's authorize function, enabled KUV/registration features and a currently valid KUV. A parked vehicle needs no spawned entity. Success returns data = { id, kind = "registration", fee, expiresAt, status = "awaiting_consent", plate } and starts the existing drawn-signature workflow.
local prepared = exports["sky_jobs_base"]:PrepareVehicleRegistration(source, {
plate = "SKY123"
})
if not prepared.success then
print(("Registration preparation failed: %s"):format(prepared.error))
end
Preparation alone does not confirm consent. The existing consent/session workflow publishes sessionChanged with status = "ready" after valid owner consent. The authority integration must bind that session to the real player and then request completion; a client-supplied accepted boolean is not a certificate.
CompleteVehicleRegistration(source, { id })
Call from the owning authority resource after confirmed consent, using the prepared session ID. The server revalidates owner, character, office access, KUV, policy and revision. It charges the configured first-registration or transfer fee directly from the player's bank account, without a workshop invoice.
local completed = exports["sky_jobs_base"]:CompleteVehicleRegistration(source, {
id = confirmed_session_id -- Bind to this player's actual ready consent session.
})
if not completed.success then
print(("Registration completion failed: %s"):format(completed.error))
return
end
-- completed.data: operationId, documentId, vehicleId, plate, kind, plateIndex
Successful replay returns the saved receipt without another charge or completion event. Persistent payment claims prevent duplicate debits. Uncertain bank/refund/database outcomes return reconciliation_required; reconcile the recorded payment before retrying, rather than deleting its claim. A confirmed failed save is refunded only to the still-bound character.
ReissueTemporaryVehicleDocument(source, { plate })
Authority-only, with current office access and ownership checks. Replaces the document ID and increments its version while retaining consumed or expired time. Success returns data = { documentId, version, vehicleId, plate, remainingSeconds, status }. Replacement is free. The authority handles inventory capacity and issuing the new metadata. Previous document versions and documents belonging to previous owners become invalid.
GetVehicleRegistrationDocument(source, documentId)
Admits the current owner or staff with the existing registry-read permission. Success returns data = { kind, documentId, version?, vehicleId, plate, model, holderName, holderIdentifier, issuedAt, status, remainingSeconds?, snapshot }. Superseded versions, revoked/deregistered certificates and registrations awaiting transfer return document_superseded.
holderIdentifier and the signed snapshot are private server-integration data. Before presenting a document, validate the held inventory item and its metadata version, choose the public display fields, and check recipient proximity. Do not forward the complete result through an unrestricted client bridge.
RecordVehiclePlateStyleChange(plate, change)
Only the authority resource may append this history. change = { previousIndex, plateIndex, actor = { identifier, name, job, source? } }; indices must be integers 0–12. Capture the actual mechanic from the trusted, confirmed post-save hook. Client-provided actor identity is not authoritative.
For a vehicle with a provisional document, including an expired one, success returns data = { recorded = true, operationId }. Equal indices return { recorded = false }; registered vehicles return not_temporary. The plate_change entry records actor, before/after type and remaining provisional time. It grants no registration, resets no time, changes no plate itself and imposes no fine. Authorized Police readers access it through GetVehicleRegistryDetail.
Enable the separate Mechanic plate-style audit explicitly from the authority integration. Capability discovery alone does not enable it.
Server events
Both events are local server notifications. Subscribe with AddEventHandler, not RegisterNetEvent. They are emitted after the database transaction succeeds and the shared write lock has been released, so listeners can immediately query the committed record. They do not issue certificates or grant consent.
sky_jobs_base:vehicleRegistry:operationCompleted
Fires once for a newly committed inspection, registration, deregistration or authority history operation. Authority kinds are temporary_registration, temporary_transfer, temporary_reissue and plate_change. A failed validation/write, repeated completion request, or invoice retry does not emit another completion event. A failed inspection still produces a completed operation with result = "failed".
AddEventHandler("sky_jobs_base:vehicleRegistry:operationCompleted", function(operation)
print(("%s: %s completed for %s"):format(
operation.operationId, operation.kind, operation.plate
))
local current = exports["sky_jobs_base"]:GetVehicleRegistryStatus(operation.plate)
if current.success then
-- Update your integration's vehicle display from current.data.
print("Registry version:", current.data.version)
end
end)
sky_jobs_base:vehicleRegistry:documentRevoked
Fires after a successful inspection or registration revocation. The existing issuer and revoke permission checks still apply.
AddEventHandler("sky_jobs_base:vehicleRegistry:documentRevoked", function(operation)
print(("Certificate %s revoked for %s: %s"):format(
operation.documentId, operation.plate, operation.reason
))
end)
Event payload
| Field | Type | Description |
|---|---|---|
operationId | string | Unique history operation ID. Use this to deduplicate external processing. |
documentId | string | Affected certificate ID. Deregistration refers to the previous registration certificate. |
vehicleId | string | Stable registry vehicle ID. |
plate | string | Current plate when the operation completed. |
kind | string | Completion: inspection, registration, deregistration, temporary_registration, temporary_transfer, temporary_reissue, plate_change. Revocation: revoke_inspection, revoke_registration. |
source | number? | Acting player's server ID at that moment, when present; not a persistent identity. |
actorJob | string | Recorded actor job; provisional authority operations can use the authority resource. |
occurredAt | number | Unix timestamp in seconds. |
result | string? | Inspection completion only: passed, advisory, or failed. |
invoiceId | number? | Completion only: created invoice reference, when present. |
billingPending | boolean? | Completion only: certificate saved but invoice creation still needs recovery. |
reason | string? | Revocation only: recorded explanation. |
plateIndex | number? | Registration display type for an authority operation, when supplied. |
These events contain no holder identifiers or signature strokes. A workshop completion confirms saved paperwork; it does not confirm payment of its invoice. Authority registration charges its bank fee before committing the certificate. Do not charge a player again from these notifications; use the operation's billing workflow and recorded payment state.
Expiry and ownership changes are calculated when a record is queried. There is no automatic expiry/sale event or replay of old events after a resource restart; query the current status when your integration needs a decision.