OnDuty

Documentation

10 endpoints, et c’est tout.

Voilà l’intégralité de ce qu’il faut brancher pour que le terminal se remplisse tout seul. Chaque endpoint est documenté avec ses champs, sa réponse et un exemple Lua que vous pouvez copier tel quel. Lisible sans compte, avant même de nous parler.

Une clé, des portées

La clé voyage dans l’en-tête X-MDT-API-Key. Chaque clé porte des portées : personnages, véhicules, armes. Une clé qui n’a que la portée véhicules ne peut pas toucher aux dossiers civils, même si elle fuite.

Tout est idempotent

Un personnage est identifié par son charId, un véhicule par sa plaque, une arme par son numéro de série. Renvoyer deux fois le même appel met à jour au lieu de créer un doublon. Vous pouvez rejouer une synchronisation entière sans rien casser.

Aucune ressource imposée

Vous appelez ces endpoints depuis votre propre code, avec le framework que vous voulez. Rien à installer côté serveur, rien qui tourne en boucle chez vous, aucune dépendance à maintenir.

01

Personnages

Le dossier civil est la brique de base : sans lui, ni rapport, ni amende, ni casier. Envoyez le personnage à sa création, puis à chaque changement d’état civil ou de métier.

POST/api/fivem/charactersClé d’API · characters

Créer ou mettre à jour un personnage

Idempotent sur charId : le premier appel crée, les suivants mettent à jour. Prénom et nom sont exigés à la création seulement.

Corps de la requête

charIdrequisnumberVotre identifiant interne, la clé de rapprochement
firstnamestringObligatoire à la création
lastnamestringObligatoire à la création
discordIdstring
dateofbirthstringFormat libre, 10 caractères
nationalitystring
sexstringUn caractère
heightnumber
weightnumber
jobstring
jobGradenumber

Réponse

json
{ "ok": true, "charId": 1042, "created": true }

Exemple

lua
local API = GetConvar('onduty_url', '')
local CLE = GetConvar('onduty_api_key', '')

--- Envoie un personnage au terminal. A appeler a la creation, puis a chaque
--- changement d'etat civil ou de metier.
local function pousserPersonnage(perso)
    PerformHttpRequest(API .. '/api/fivem/characters', function(code, corps)
        if code ~= 200 then
            print(('[onduty] personnage %s refuse (%s) %s'):format(perso.charId, code, corps))
        end
    end, 'POST', json.encode(perso), {
        ['Content-Type'] = 'application/json',
        ['X-MDT-API-Key'] = CLE,
    })
end

pousserPersonnage({
    charId = 1042,
    firstname = 'Rachel',
    lastname = 'Vasquez',
    dateofbirth = '08/03/1994',
    job = 'police',
    jobGrade = 4,
})
POST/api/fivem/characters/wipeClé d’API · characters

Effacer ou restaurer un personnage

Suit un wipe de personnage côté serveur. Réversible : renvoyez le même appel avec restore à true pour rétablir.

Corps de la requête

charIdrequisnumber
reasonstringConservé dans le journal d’audit
restorebooleantrue pour annuler un effacement

Réponse

json
{ "ok": true, "charId": 1042 }

Exemple

lua
local API = GetConvar('onduty_url', '')
local CLE = GetConvar('onduty_api_key', '')

PerformHttpRequest(API .. '/api/fivem/characters/wipe', function(code)
    print('[onduty] wipe ' .. code)
end, 'POST', json.encode({
    charId = 1042,
    reason = 'Suppression demandee par le joueur',
}), {
    ['Content-Type'] = 'application/json',
    ['X-MDT-API-Key'] = CLE,
})
POST/api/fivem/bansClé d’API · characters

Bannir un compte Discord

Bloque l’accès au terminal pour tous les personnages liés à ce Discord. Durée en minutes, ou 0 pour un bannissement définitif.

Corps de la requête

discordIdrequisstring
reasonstring
durationMinutesnumber0 ou absent pour définitif

Réponse

json
{ "ok": true, "blocked": true }

Exemple

lua
local API = GetConvar('onduty_url', '')
local CLE = GetConvar('onduty_api_key', '')

PerformHttpRequest(API .. '/api/fivem/bans', function(code)
    print('[onduty] ban ' .. code)
end, 'POST', json.encode({
    discordId = '198374658374658374',
    reason = 'Sanction staff',
    durationMinutes = 10080,
}), {
    ['Content-Type'] = 'application/json',
    ['X-MDT-API-Key'] = CLE,
})
POST/api/fivem/bans/revokeClé d’API · characters

Lever un bannissement

Rend l’accès au terminal à un Discord précédemment bloqué.

Corps de la requête

discordIdrequisstring

Réponse

json
{ "ok": true, "blocked": false }

Exemple

lua
local API = GetConvar('onduty_url', '')
local CLE = GetConvar('onduty_api_key', '')

PerformHttpRequest(API .. '/api/fivem/bans/revoke', function(code)
    print('[onduty] unban ' .. code)
end, 'POST', json.encode({ discordId = '198374658374658374' }), {
    ['Content-Type'] = 'application/json',
    ['X-MDT-API-Key'] = CLE,
})

02

Véhicules

Ce qui rend un contrôle routier crédible. Le fichier des immatriculations vit chez vous, le terminal le reflète.

POST/api/fivem/vehiclesClé d’API · vehicles

Créer ou mettre à jour un véhicule

Idempotent sur la plaque. Rattachez-le à son propriétaire avec ownerCharId, ou à un métier avec ownerJob pour les véhicules de service.

Corps de la requête

platerequisstring15 caractères maximum
makestring
modelstring
colorstring
ownerCharIdnumber
ownerJobstringPour un véhicule de service
ownerJobLabelstring
categoryenumVOITURE, MOTO, POIDS_LOURD, BATEAU, AVION, AUTRE

Réponse

json
{ "ok": true, "id": 8821, "plate": "42XKQ71" }

Exemple

lua
local API = GetConvar('onduty_url', '')
local CLE = GetConvar('onduty_api_key', '')

PerformHttpRequest(API .. '/api/fivem/vehicles', function(code, corps)
    if code ~= 200 then print('[onduty] vehicule refuse ' .. corps) end
end, 'POST', json.encode({
    plate = '42XKQ71',
    make = 'Bravado',
    model = 'Buffalo STX',
    color = 'Noir',
    ownerCharId = 1042,
    category = 'VOITURE',
}), {
    ['Content-Type'] = 'application/json',
    ['X-MDT-API-Key'] = CLE,
})
POST/api/fivem/vehicles/temporaryClé d’API · vehicles

Déclarer un véhicule temporaire

Pour un véhicule de location ou de mission qui ne doit pas rester au fichier. Il disparaît tout seul au bout du délai.

Corps de la requête

platerequisstring
minutesrequisnumberDe 1 à 1440
makestring
modelstring
colorstring
ownerCharIdnumber
ownerJobstring
categoryenum

Réponse

json
{ "ok": true, "id": 8822, "plate": "LOC00412" }

Exemple

lua
local API = GetConvar('onduty_url', '')
local CLE = GetConvar('onduty_api_key', '')

PerformHttpRequest(API .. '/api/fivem/vehicles/temporary', function(code)
    print('[onduty] vehicule temporaire ' .. code)
end, 'POST', json.encode({
    plate = 'LOC00412',
    minutes = 120,
    make = 'Ubermacht',
    model = 'Sentinel',
    ownerCharId = 1042,
}), {
    ['Content-Type'] = 'application/json',
    ['X-MDT-API-Key'] = CLE,
})
POST/api/fivem/vehicles/hideClé d’API · vehicles

Masquer un véhicule temporairement

Retire une plaque du fichier pendant un temps donné, sans la supprimer. Utile pour une opération sous couverture.

Corps de la requête

platerequisstring
minutesrequisnumberDe 0 à 1440, 0 pour réafficher

Réponse

json
{ "ok": true, "plate": "42XKQ71" }

Exemple

lua
local API = GetConvar('onduty_url', '')
local CLE = GetConvar('onduty_api_key', '')

PerformHttpRequest(API .. '/api/fivem/vehicles/hide', function(code)
    print('[onduty] masquage ' .. code)
end, 'POST', json.encode({ plate = '42XKQ71', minutes = 60 }), {
    ['Content-Type'] = 'application/json',
    ['X-MDT-API-Key'] = CLE,
})

03

Armes

Une arme retrouvée sur une scène ne sert à rien si elle ne remonte à personne. Le numéro de série fait le lien.

POST/api/fivem/weaponsClé d’API · weapons

Créer ou mettre à jour une arme

Idempotent sur le numéro de série. Rattachez-la à son détenteur pour qu’elle apparaisse sur sa fiche.

Corps de la requête

serialrequisstringLe numéro de série, votre clé
itemNamerequisstringLe nom technique de l’arme
ownerCharIdnumber

Réponse

json
{ "ok": true, "id": 331, "serial": "AB-4471-KQ" }

Exemple

lua
local API = GetConvar('onduty_url', '')
local CLE = GetConvar('onduty_api_key', '')

PerformHttpRequest(API .. '/api/fivem/weapons', function(code, corps)
    if code ~= 200 then print('[onduty] arme refusee ' .. corps) end
end, 'POST', json.encode({
    serial = 'AB-4471-KQ',
    itemName = 'weapon_pistol',
    ownerCharId = 1042,
}), {
    ['Content-Type'] = 'application/json',
    ['X-MDT-API-Key'] = CLE,
})

04

Événements et session

Les deux seuls endpoints qui n’utilisent pas la clé d’API. Ils touchent à des choses plus sensibles, ils sont donc protégés autrement.

POST/api/fivem/eventsSignature HMAC

Envoyer un événement de jeu

Le canal générique : appel d’urgence, prise de service, incident. Signé, pas authentifié par clé, parce qu’il déclenche des actions dans le terminal.

Corps de la requête

typerequisstringLe type d’événement, 64 caractères maximum
payloadobjectLe contenu, libre
noncestringÉvite qu’un événement rejoué soit traité deux fois

Réponse

json
{ "ok": true, "accepted": "dispatch.call" }

Exemple

lua
-- La signature se calcule sur le corps exact envoye.
-- Horodatage en secondes, tolerance de 5 minutes.
local SECRET = GetConvar('onduty_webhook_secret', '')

local corps = json.encode({
    type = 'dispatch.call',
    payload = { code = '10-13', secteur = 'Vinewood', plainte = 'Coups de feu' },
})

local horodatage = tostring(os.time())
local signature = 'sha256=' .. Cipher.hmac('sha256', SECRET, horodatage .. '.' .. corps)

PerformHttpRequest(API .. '/api/fivem/events', function(code)
    print('[onduty] evenement ' .. code)
end, 'POST', corps, {
    ['Content-Type'] = 'application/json',
    ['X-Oren-Signature'] = signature,
    ['X-Oren-Timestamp'] = horodatage,
})
POST/api/fivem/auth/exchangeJeton de jeu

Ouvrir une session depuis le jeu

Échange un jeton émis en jeu contre une session sur le terminal. C’est ce qui permet d’ouvrir le MDT depuis le véhicule sans retaper quoi que ce soit.

Corps de la requête

tokenrequisstringLe jeton signé émis par votre serveur, 20 caractères minimum

Réponse

json
{ "ok": true }

Exemple

lua
-- Cote client, ouvre le terminal deja connecte pour ce joueur.
RegisterCommand('mdt', function()
    TriggerServerEvent('onduty:demanderJeton')
end, false)

RegisterNetEvent('onduty:jetonPret', function(jeton)
    SetNuiFocus(true, true)
    SendNUIMessage({ action = 'ouvrir', jeton = jeton })
end)

Erreurs

Ce que le terminal vous renvoie quand ça coince

Toutes les réponses portent un champ ok. En cas d’échec, error donne la cause en un mot, et le corps détaille quand c’est utile.

400invalid_bodyLe corps ne respecte pas le schéma. La réponse détaille le champ fautif.
400identity_requiredCréation d’un personnage sans prénom ni nom.
401missing_api_keyL’en-tête X-MDT-API-Key est absent.
401invalid_api_keyClé inconnue ou révoquée.
403insufficient_scopeLa clé existe mais n’a pas la portée demandée.

Une question sur votre framework maison ?

ESX, QBCore ou entièrement fait maison, on regarde votre cas avec vous. Ouvrez un ticket et décrivez votre structure, on vous dit quoi envoyer.