Aller au contenu
Sur cette page

Documentation / Référence

Référence de l’API d’envoi

Toutes les adresses auxquelles répond l’API d’envoi, les champs qu’elle lit et ce qu’elle renvoie. Elle parle le dialecte de Fivemanage : un script écrit pour Fivemanage marche dès que vous changez l’URL de base et la clé.

URL de base et clés

Toutes les adresses de cette page sont relatives à https://api.fivedock.dev, et les fichiers sont servis depuis https://cdn.fivedock.dev.

Deux clés, deux rôles

  • fd_live_… est un token d’API. Vous le créez dans le panel, sous Tokens, et vous choisissez ses scopes. Envoyez-le en Authorization: fd_live_… ou Authorization: Bearer fd_live_…. Il sert pour cette API, pour l’API de logs et, en paire de clés, pour l’endpoint S3.
  • fda_live_… est une clé d’agent. Elle va dans server.cfg, en set fivedock_key pour la resource fivedock, et nulle part ailleurs. L’API d’envoi ne l’accepte pas.
curl
curl "https://api.fivedock.dev/api/v3/file?limit=1" \
  -H "Authorization: Bearer fd_live_…"

Gardez le token d’API sur un serveur que vous contrôlez. Tout ce qui tourne dans le jeu ou le navigateur d’un joueur devrait envoyer par un lien à usage unique, pour ne jamais voir le token.

Scopes

Chaque adresse demande un scope, indiqué dans le tableau ci-dessous, et un token qui ne l’a pas reçoit 403. files:read liste et lit les fichiers, files:write envoie et crée des liens d’envoi, files:delete supprime, folders:write crée des dossiers, usage:read lit le stockage et le trafic, et logs:write envoie des logs.

Adresses

Les chemins et les noms de champs sont ceux de Fivemanage, plus un envoi d’archive propre à FiveDock. Les adresses média de Fivemerr répondent aussi.

Adresses de l’API d’envoi
AdresseScopeCe qu’elle fait
Fichiers
POST /api/v3/fileScope · files:writeEnvoie un fichier en multipart/form-data.
POST /api/v3/file/base64Scope · files:writeEnvoie un fichier encodé en base64 dans un corps JSON.
POST /api/v3/file/archiveScope · files:writeEnvoie un .zip ou un .rar et stocke chaque fichier qu’il contient. Propre à FiveDock.
GET /api/v3/fileScope · files:readListe vos fichiers, page par page.
GET /api/v3/file/{id}Scope · files:readLit le détail d’un fichier.
DELETE /api/v3/file/{id}Scope · files:deleteSupprime un fichier.
Liens d’envoi
GET /api/v3/file/presigned-urlScope · files:writeCrée un lien d’envoi à usage unique. Il dure 15 minutes, sauf si vous demandez moins ou plus.
POST /api/v3/file/presigned-url/{ticket}Scope · Aucun, le lien sert d’identifiantEnvoie par un lien créé ci-dessus, sans token.
GET /api/v2/presigned-urlScope · files:writeCrée un lien d’envoi à l’ancienne. Il dure 5 minutes, sauf si vous demandez moins ou plus.
POST /api/v2/presigned-url/{ticket}Scope · Aucun, le lien sert d’identifiantEnvoie par un lien créé à l’ancienne, sans token.
Fivemanage v2
POST /api/v2/imageScope · files:writeEnvoie une image. Le fichier va dans le champ image ou dans file.
POST /api/v2/videoScope · files:writeEnvoie une vidéo. Le fichier va dans le champ video ou dans file.
POST /api/v2/audioScope · files:writeEnvoie un son. Le fichier va dans le champ audio ou dans file.
Fivemerr
POST /v1/media/imagesScope · files:writeEnvoie une image. Le fichier va dans file ou data.
POST /v1/media/videosScope · files:writeEnvoie une vidéo. Le fichier va dans file ou data.
POST /v1/media/audiosScope · files:writeEnvoie un son. Le fichier va dans file ou data.
Logs
POST /api/logsScope · logs:writeEnvoie un événement de log.
POST /api/logs/batchScope · logs:writeEnvoie des événements de log par lot.
POST /api/v3/logsScope · logs:writeEnvoie des logs à l’adresse v3 de Fivemanage.

Les adresses de logs acceptent les formats qu’envoient les clients Fivemanage, y compris le logger Fivemanage d’ox_lib (set ox:logger "fivemanage"), et demandent un token avec logs:write. ox_lib garde l’adresse de Fivemanage dans imports/logger/providers/fivemanage.lua : remplacez-y la ligne endpoint par l’adresse de FiveDock, et refaites-le après chaque mise à jour d’ox_lib.

Envoyer un fichier

Envoyez POST /api/v3/file en multipart/form-data, avec le fichier dans la partie file. Tous les autres champs sont facultatifs.

Champs d’envoi
ChampCe qu’il fait
fileLe fichier lui-même. Un par requête.
filenameLe nom sous lequel le stocker. Par défaut, le nom de la partie fichier.
pathLe dossier où il arrive, par exemple /mdt/mugshots. Créé à la volée ; la racine par défaut.
keyUn nom stable comme items/burger.png. Le fichier est servi à un lien qui ne change jamais, et un nouvel envoi avec la même clé le remplace. Quand elle est fournie, elle l’emporte sur path et filename.
metadataUn objet JSON gardé avec le fichier et renvoyé quand vous le lisez, par exemple le dossier d’enquête ou le personnage auquel il appartient. Tout ce qui n’est pas un objet JSON est stocké comme un objet vide.
curl -X POST https://api.fivedock.dev/api/v3/file \
  -H "Authorization: Bearer fd_live_…" \
  -F "[email protected]" \
  -F "path=/evidence/case-1042" \
  -F 'metadata={"caseId":1042,"officer":"A. Nowak"}'

Un fichier à clé est servi sous /o/, puis le nom court de votre organisation, puis la clé. Le nom court ne change jamais, et c’est aussi le nom du bucket sur l’endpoint S3.

URL
https://cdn.fivedock.dev/o/{workspace}/{key}
https://cdn.fivedock.dev/o/my-city/items/burger.png
  • Une clé fait de 1 à 512 caractères, en segments séparés par /. Pas de segment vide, . ou .., et pas de / au début.
  • Sont permis les lettres, les chiffres et . _ - ~ @ + : ( ) , = !. Une clé qui contient autre chose, comme une espace, est refusée avec 400.
  • Envoyer sur une clé déjà prise remplace le fichier derrière le lien. L’ancien fichier est conservé 7 jours, puis purgé, comme après une suppression.
  • Les liens à clé sont servis avec Cache-Control: public, max-age=300, stale-while-revalidate=60 et un ETag : un fichier remplacé atteint les joueurs en 5 minutes environ, et un fichier inchangé est revalidé par un simple 304.
  • Dans le panel, un fichier à clé apparaît dans le dossier de sa clé : items/burger.png est sous /items.
curl
# the first upload makes the link, the next ones replace what it serves
curl -X POST https://api.fivedock.dev/api/v3/file \
  -H "Authorization: Bearer fd_live_…" \
  -F "[email protected]" \
  -F "key=items/burger.png"

Envoyer en base64

POST /api/v3/file/base64 prend un corps JSON au lieu d’un formulaire. base64 est le fichier, en data URI ou en base64 brut, et filename, path, key et metadata marchent comme dans le formulaire. C’est le chemin court depuis Lua, puisque screenshot-basic vous donne déjà une data URI.

-- server.lua: a mugshot for the MDT, sent as base64
-- your own convar with an API token (fd_live_…), not the fda_live_ agent key
local TOKEN = GetConvar('mdt_upload_token', '')

local function uploadMugshot(source, charId, cb)
  exports['screenshot-basic']:requestClientScreenshot(source, { encoding = 'webp', quality = 0.9 }, function(err, dataUri)
    if err then return cb(err) end

    PerformHttpRequest('https://api.fivedock.dev/api/v3/file/base64', function(status, body)
      if status ~= 200 then
        return cb(('upload failed (%d): %s'):format(status, body or ''))
      end
      cb(nil, json.decode(body).url)
    end, 'POST', json.encode({
      base64   = dataUri,
      filename = ('%s.webp'):format(charId),
      key      = ('mugshots/%s.webp'):format(charId),
      metadata = { charId = charId },
    }), {
      ['Authorization'] = TOKEN,
      ['Content-Type']  = 'application/json',
    })
  end)
end

Le fichier décodé peut peser jusqu’à 16 Mio. Au-delà, passez en multipart ou par un lien d’envoi.

Ce que répond un envoi

Chaque adresse d’envoi répond 200 avec la même forme. Les champs de premier niveau sont ceux qu’envoie Fivemanage ; fivedock contient nos extras.

JSON
{
  "status": "ok",
  "data": {
    "id": "8Kq2nR4t",
    "url": "https://cdn.fivedock.dev/o/my-city/mugshots/char1-abc.webp",
    "originalUrl": "https://cdn.fivedock.dev/o/my-city/mugshots/char1-abc.webp"
  },
  "id": "8Kq2nR4t",
  "uuid": "8Kq2nR4t",
  "url": "https://cdn.fivedock.dev/o/my-city/mugshots/char1-abc.webp",
  "filename": "char1-abc.webp",
  "original_filename": "char1-abc.webp",
  "mime_type": "image/webp",
  "size": 48213,
  "hash": "9f2b…",
  "metadata": { "charId": "char1-abc" },
  "created_at": "2026-09-29T12:00:00Z",
  "updated_at": "2026-09-29T12:00:00Z",
  "fivedock": {
    "publicId": "8Kq2nR4t",
    "key": "mugshots/char1-abc.webp",
    "fileUrl": "https://cdn.fivedock.dev/f/8Kq2nR4t.webp",
    "deduplicated": false,
    "status": "ready",
    "folder": "/mugshots",
    "moderation": "approved"
  }
}
  • url et data.url sont le lien à utiliser. Pour un envoi à clé, c’est le lien stable /o/.
  • fivedock.fileUrl est le lien /f/ propre au fichier. Son contenu ne change jamais, et il est mis en cache un an.
  • fivedock.key est la clé, quand le fichier en a une.
  • fivedock.deduplicated vaut true quand les mêmes octets étaient déjà stockés : rien de neuf n’a été facturé.
  • fivedock.status vaut pending pendant qu’on prépare les copies optimisées, et ready une fois qu’elles existent. Le lien marche dans les deux cas.

Envoyer une archive

POST /api/v3/file/archive prend un .zip ou un .rar dans la partie file et stocke chaque fichier qu’il contient. path est le dossier où décompresser, la racine par défaut.

curl
curl -X POST https://api.fivedock.dev/api/v3/file/archive \
  -H "Authorization: Bearer fd_live_…" \
  -F "[email protected]" \
  -F "path=items" \
  -F "keys=true"

Avec keys=true, le défaut, chaque fichier devient un fichier à clé, et sa clé est le dossier cible suivi de son chemin dans l’archive : path=items et une entrée burger.png donnent items/burger.png. Avec keys=false, les fichiers reçoivent des identifiants aléatoires et arrivent dans le dossier cible suivi de leur dossier dans l’archive.

Les dossiers de l’archive sont conservés : une archive contenant images/burger.png décompressée dans items donne items/images/burger.png. Pour laisser un dossier de côté, zippez son contenu plutôt que le dossier lui-même.

JSON
{
  "status": "ok",
  "data": {
    "uploaded": [
      {
        "id": "Qw7tL2xa",
        "key": "items/burger.png",
        "url": "https://cdn.fivedock.dev/o/my-city/items/burger.png",
        "fileUrl": "https://cdn.fivedock.dev/f/Qw7tL2xa.png",
        "filename": "burger.png",
        "path": "/items",
        "size": 18233,
        "mime_type": "image/png",
        "deduplicated": false
      }
    ],
    "skipped": [{ "name": "readme.txt", "reason": "unsupported_type" }],
    "incomplete": false
  }
}

Les dossiers, les liens, __MACOSX/, les fichiers cachés, Thumbs.db et desktop.ini sont ignorés sans un mot. Tout autre fichier qui ne peut pas être stocké est listé sous skipped avec une raison : unsupported_type, too_large, encrypted, unsafe_path, too_many_files, archive_too_large, storage_full, duplicate ou failed. La réponse est 200 dès qu’au moins un fichier a été enregistré, même si d’autres ont été ignorés ; si aucun n’a pu l’être, c’est 422, avec la même liste skipped. Si l’archive est abîmée en cours de route, incomplete vaut true : les fichiers d’avant le dommage sont enregistrés, rien n’a été lu après.

L’archive peut peser jusqu’à 512 Mio, avec au plus 5 000 fichiers et 4 Gio une fois décompressée, et chaque fichier doit être d’un type que nous acceptons. RAR 4 et RAR 5 sont lus ; les entrées protégées par mot de passe sont ignorées comme encrypted, et les archives en plusieurs volumes ne sont pas prises en charge. Ce qui n’est ni un zip ni un rar reçoit 415, et une archive illisible reçoit 422. Pour des ensembles plus gros, synchronisez via l’endpoint S3 avec rclone ou l’AWS CLI.

Liens d’envoi à usage unique

Un lien d’envoi permet à la NUI d’un joueur, ou à n’importe quel navigateur, d’envoyer un fichier sans jamais voir votre token. Votre serveur le crée avec GET /api/v3/file/presigned-url et un token doté de files:write, puis le transmet. Tous les paramètres sont facultatifs, et celui qui envoie ne peut en changer aucun.

Paramètres d’un lien d’envoi
ParamètreCe qu’il fait
expiresAtQuand le lien cesse de marcher, en secondes Unix. Par défaut, dans 15 minutes (5 sur /api/v2/presigned-url), et au plus 1 heure.
singleUsetrue fait accepter au lien exactement un envoi réussi ; ensuite il répond 410 Gone. Une tentative refusée, pour un mauvais type ou un fichier trop gros, ne l’épuise pas.
maxSizeLe plus gros fichier accepté par le lien, en octets, dans la limite par fichier. Un fichier plus gros reçoit 413.
fileTypeimage, video ou audio. Tout autre genre de fichier reçoit 415.
contentTypeUn type exact, par exemple video/webm. Tout autre type reçoit 415.
pathLe dossier où arrive le fichier.
keyStocke le fichier sous cette clé : il obtient un lien stable et remplace ce qui s’y trouvait.
curl
curl "https://api.fivedock.dev/api/v3/file/presigned-url?singleUse=true&fileType=video&contentType=video/webm&maxSize=52428800&path=/mdt/videos" \
  -H "Authorization: Bearer fd_live_…"

La réponse contient le lien et rappelle ce qu’il autorise :

JSON
{
  "status": "ok",
  "data": {
    "presignedUrl": "https://api.fivedock.dev/api/v3/file/presigned-url/eyJ…",
    "expiresAt": "2026-09-29T12:15:00Z",
    "singleUse": true,
    "maxSize": 52428800,
    "fileType": "video",
    "contentType": "video/webm",
    "path": "/mdt/videos"
  }
}

Pour envoyer, faites un POST sur presignedUrl en multipart/form-data, avec le fichier dans le champ file et, si vous voulez, filename et metadata. N’envoyez aucun en-tête Authorization. La réponse est la même que pour POST /api/v3/file.

curl
# no Authorization header: the link is the credential
curl -X POST "https://api.fivedock.dev/api/v3/file/presigned-url/eyJ…" \
  -F "[email protected]"

Enregistrer un clip dans la NUI

Un clip de caméra-piéton pour un MDT : le serveur crée un lien à usage unique pour un fichier webm, le script client le passe à la NUI, et la NUI enregistre avec MediaRecorder puis envoie le Blob directement à FiveDock.

-- server.lua: the token stays on the server
local TOKEN = GetConvar('mdt_upload_token', '') -- an API token (fd_live_…) with files:write

local function mintUploadLink(cb)
  local query = ('singleUse=true&fileType=video&contentType=video/webm&maxSize=%d&expiresAt=%d&path=/mdt/videos')
    :format(50 * 1024 * 1024, os.time() + 600)

  PerformHttpRequest('https://api.fivedock.dev/api/v3/file/presigned-url?' .. query, function(status, body)
    if status ~= 200 then return cb(nil) end
    cb(json.decode(body).data.presignedUrl)
  end, 'GET', '', { ['Authorization'] = TOKEN })
end

RegisterNetEvent('mdt:requestUploadLink', function()
  local src = source
  -- check that this player may attach evidence before minting anything
  mintUploadLink(function(url)
    TriggerClientEvent('mdt:uploadLink', src, url)
  end)
end)

RegisterNetEvent('mdt:clipUploaded', function(url)
  -- save url with the case: it plays straight away
end)

Créez un lien par envoi et donnez-lui une durée courte. Avec singleUse=true, quiconque copie le lien depuis la NUI reçoit 410 une fois votre clip arrivé.

Lister, lire et supprimer

GET /api/v3/file liste vos fichiers, 50 par page sauf si vous fixez limit, jusqu’à 100. page choisit la page, type ne garde que image, video ou audio, et path un seul dossier. Ajoutez l’identifiant d’un fichier à l’adresse pour le lire, ou envoyez DELETE à la même adresse pour le supprimer. Chaque fichier arrive avec url et fileUrl : pour un fichier à clé, url est son lien stable et key est indiquée aussi ; sinon, les deux sont le lien /f/.

# the second page of videos in /evidence
curl "https://api.fivedock.dev/api/v3/file?type=video&path=/evidence&page=2&limit=20" \
  -H "Authorization: Bearer fd_live_…"

# one file
curl https://api.fivedock.dev/api/v3/file/8Kq2nR4t \
  -H "Authorization: Bearer fd_live_…"

# delete it: the token needs files:delete
curl -X DELETE https://api.fivedock.dev/api/v3/file/8Kq2nR4t \
  -H "Authorization: Bearer fd_live_…"
JSON
{
  "status": "ok",
  "data": [
    {
      "id": "8Kq2nR4t",
      "uuid": "8Kq2nR4t",
      "filename": "evidence-3.webm",
      "url": "https://cdn.fivedock.dev/f/8Kq2nR4t.webm",
      "originalUrl": "https://cdn.fivedock.dev/f/8Kq2nR4t.webm",
      "type": "video",
      "mime_type": "video/webm",
      "size": 4821377,
      "hash": "c41d…",
      "path": "/evidence",
      "metadata": { "caseId": 1042 },
      "created_at": "2026-09-29T12:00:00Z",
      "updated_at": "2026-09-29T12:00:00Z"
    }
  ],
  "pagination": { "page": 2, "limit": 20, "total": 21, "pages": 2, "hasMore": false }
}

Un fichier supprimé cesse d’être servi aussitôt. Pendant 7 jours, il peut être restauré depuis le panel. Supprimer demande un token avec files:delete.

Limites et types de fichiers

  • Un fichier peut peser jusqu’à 512 Mio.
  • Un envoi JSON en base64 peut porter jusqu’à 16 Mio de fichier décodé.
  • Une archive peut peser jusqu’à 512 Mio, avec au plus 5 000 fichiers et 4 Gio une fois décompressée.
  • Une requête d’envoi peut durer jusqu’à 5 minutes.

Le type est lu dans les octets du fichier, pas dans son nom ni dans le Content-Type que vous envoyez. Sont acceptés :

  • Images : PNG, JPEG, WebP, AVIF et GIF.
  • Vidéo : MP4, WebM et QuickTime (.mov).
  • Audio : MP3, OGG et WAV.

video/webm est accepté : un clip MediaRecorder s’envoie tel quel. L’endpoint S3 stocke aussi d’autres types, et les sert en téléchargement.

Navigateurs et NUI (CORS)

L’API d’envoi répond aux navigateurs de toute origine, avec Access-Control-Allow-Origin: * et sans cookies. Cela inclut les pages NUI de FiveM, dont l’origine est https://cfx-nui- suivi du nom de la resource. C’est valable pour /api/v3/file…, /api/v2/… et /v1/media/…. Le preflight autorise GET, POST et OPTIONS, avec les en-têtes Authorization et Content-Type.

CORS décide quelles pages peuvent appeler, pas qui détient la clé. Tout ce qui est livré aux joueurs peut être lu : une NUI doit envoyer par un lien à usage unique et ne jamais détenir de token. Listez et supprimez depuis votre serveur.

Quand un lien marche

Tout de suite. Le lien de la réponse sert dès que l’API a répondu : les fichiers sont approuvés à l’envoi, il n’y a donc pas de file d’attente. Si la modération retire un fichier plus tard, son lien répond 404.

Une vidéo se lit telle qu’envoyée dès le départ. Les copies optimisées s’ajoutent quand le traitement se termine, et fivedock.status passe de pending à ready. Vous n’avez pas à l’attendre.

Quand vous envoyez sur une clé existante, les joueurs reçoivent le nouveau fichier en 5 minutes environ, car les liens à clé sont mis en cache 5 minutes. Le contenu des liens /f/ ne change jamais, ils sont donc mis en cache un an.

Limites de débit

L’API d’envoi n’a aujourd’hui pas de limite fixe de requêtes par token. Ce qui bloque les envois, c’est le plafond de stockage : quand une organisation dépasse sa limite de stockage avec le plafond strict activé, les envois reçoivent 429 jusqu’à ce que de l’espace soit libéré ou que le plafond soit relevé. La diffusion CDN au-delà du plafond de trafic répond 429 aussi.

Ralentissez à chaque 429, et quand Retry-After est présent, attendez au moins ce délai.

Erreurs

Une erreur revient avec son code de statut et un corps JSON ; error et message disent la même chose.

JSON
{
  "error": "that file type is not accepted",
  "message": "that file type is not accepted"
}
Codes de statut d’erreur
StatutQuand
400La requête est mal formée : pas de partie fichier, du base64 qui ne se décode pas, ou une clé qui enfreint les règles.
401Pas de token, ou un token invalide. Une clé d’agent fda_live_… reçoit aussi cette réponse.
403Le token n’a pas le scope de cette adresse, ou le lien d’envoi est invalide ou expiré.
404Aucun fichier avec cet identifiant dans votre organisation.
410Le lien à usage unique a déjà servi.
413Le fichier dépasse la limite de taille, ou le maxSize du lien.
415Le type de fichier n’est pas accepté ou ne correspond pas au fileType ou au contentType du lien, ou l’archive n’est ni un zip ni un rar.
422Le fichier a été retiré par la modération et ne peut pas être renvoyé, l’archive est illisible, ou aucun fichier de l’archive n’a pu être enregistré.
429L’organisation dépasse son plafond de stockage. Ralentissez et respectez Retry-After.
503Cette partie de l’API n’est pas configurée sur le serveur appelé.