> ## Documentation Index
> Fetch the complete documentation index at: https://docs.redutzu.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Media uploads

> Choose where the MDT stores images and recordings, and set up each provider.

The MDT uploads gallery images, profile pictures, evidence photos, and CCTV, bodycam, and dashcam recordings to an outside service. It saves the link it gets back.

You need two things: the provider in `config/config.lua`, and its key in `config/credentials.lua`.

## Choose a provider

```lua config/config.lua theme={null}
Config.Upload = 'fivemanage' -- 'fivemanage', 'qbox', 'discord' or 'custom'
```

| Provider | Value | Images | Videos | Best for |
| - | - | - | - | - |
| [Fivemanage](#fivemanage) | `'fivemanage'` | Yes | Yes | Every server. Built for FiveM media. |
| [Qbox CDN](#qbox-cdn) | `'qbox'` | Yes | Yes | Qbox servers. |
| [Discord](#discord) | `'discord'` | Yes | Yes | Quick tests, no account needed. |
| [Custom](#custom) | `'custom'` | Yours | Yours | Your own storage or API. |

> [!NOTE]
>
> There is no auto-detection. Always set `Config.Upload` yourself.

## Where keys go

Every key goes in `config/credentials.lua`, in the `Config.ApiKeys` table. Fill in only the provider you use.

```lua config/credentials.lua theme={null}
Config.ApiKeys = {
    Fivemanage = nil,
    Qbox = nil,
    Discord = nil,
    DiscordBotToken = nil,
    Custom = nil
}
```

> [!WARNING]
>
> Never put keys in `config/config.lua`. It's a shared script, so every player's client can read it. `credentials.lua` only loads on the server. Never share or publish it.

## Fivemanage

[Fivemanage](https://refer.fivemanage.com/redutzu) is a media and logging platform made for FiveM. It's the provider we recommend: one key covers images, video recordings, and your [MDT logs](https://docs.redutzu.com/redutzu-mdt/guides/logs.md).

> [card] [Get 20% off Fivemanage for 3 months](https://refer.fivemanage.com/redutzu)
>
> Sign up with our referral link. We earn a small commission, which helps us keep building new features.

> [card] Built for FiveM
>
> Made for game servers, with a dashboard to browse and manage every file.

> [card] Images and video
>
> Stores CCTV, bodycam, and dashcam recordings, not only screenshots.

> [card] Logs included
>
> The same key sends MDT logs to Fivemanage, searchable from the dashboard.

**1. Create an account**

Sign up at [Fivemanage](https://refer.fivemanage.com/redutzu) and open your dashboard.

**2. Create an API key**

Create an API key and copy it.

![Creating an API key in the Fivemanage dashboard](https://mintcdn.com/redutzusscripts/jYySSRvwCjpWaAJk/images/guides/media-fivemanage-api-key.webp?fit=max\&auto=format\&n=jYySSRvwCjpWaAJk\&q=85\&s=ee8888788f9c65ce4c0b2a4e9654aa7c)

**3. Add the key**

```lua config/credentials.lua theme={null}
Config.ApiKeys = {
    Fivemanage = 'your-api-key',
    ...
}
```

**4. Select Fivemanage**

```lua config/config.lua theme={null}
Config.Upload = 'fivemanage'
```

**5. Restart and test**

Restart the server, then upload an image in the MDT.

> [!TIP]
>
> Already running the [`fmsdk`](https://github.com/fivemanage/sdk) resource? The MDT uses it for images automatically. Videos still use `Config.ApiKeys.Fivemanage`, so keep the key set.

## Qbox CDN

The Qbox CDN is a hosted file service from the Qbox team. Files go from the player's client straight to the CDN, so they don't pass through your server.

**1. Get your key**

Follow the [Qbox CDN guide](https://docs.qbox.re/dashboard/cdn) to create an API key. Copy it.

**2. Add the key**

```lua config/credentials.lua theme={null}
Config.ApiKeys = {
    Qbox = 'your-cdn-key',
    ...
}
```

**3. Select Qbox**

```lua config/config.lua theme={null}
Config.Upload = 'qbox'
```

**4. Restart and test**

Restart the server, then upload an image in the MDT.

## Discord

Discord stores files posted to a channel through a webhook. It's a quick way to try the MDT without an account.

**1. Create a webhook**

In Discord, open the settings of a **private** channel and go to **Integrations**. Click **Webhooks**, then **New Webhook**, then **Copy Webhook URL**.

**2. Add the webhook**

```lua config/credentials.lua theme={null}
Config.ApiKeys = {
    Discord = 'https://discord.com/api/webhooks/...',
    ...
}
```

**3. Select Discord**

```lua config/config.lua theme={null}
Config.Upload = 'discord'
```

**4. Restart and test**

Restart the server, then upload an image in the MDT.

> [!WARNING]
>
> Discord attachment links expire after a while, so old images and recordings stop loading. Discord also limits file sizes. Use Fivemanage or Qbox for anything you want to keep, such as evidence.

## Custom

Use `'custom'` to send files to your own storage or API. Fill in the two functions in `server/custom/upload/custom.lua`.

| Function | Returns |
| - | - |
| `GetUploadTarget` | `{ mode = 'relay' }` to send the file through your server, `{ mode = 'presigned-post', url = '...' }` to let the client post it straight to your storage, or `false` if uploads aren't available. |
| `Upload` | Only for `relay`. Receives the file as base64 and returns its public URL, or `false`. |

Store your key in `Config.ApiKeys.Custom` and read it inside `Upload`.

```lua server/custom/upload/custom.lua theme={null}
Bridge.Upload.Register('custom')

function Bridge.Upload.GetUploadTarget(mediaType)
    return { mode = 'relay' }
end

function Bridge.Upload.Upload(base64Data, contentType, filename)
    local result = Utils.Http.uploadBinary('https://files.example.com/upload', 'file', filename, contentType, base64Data)

    if result.status < 200 or result.status >= 300 then
        return false
    end

    return json.decode(result.response).url
end
```

> [!TIP]
>
> For a `multipart/form-data` API, use `Utils.Http.uploadBinary` like the example. Don't build the body yourself: FiveM cuts request bodies at the first null byte, which breaks real images. `server/custom/upload/discord.lua` is a complete working example.

## Upload speed

Uploads relayed through your server are capped by `Config.UploadBandwidth`, in bytes per second.

```lua config/config.lua theme={null}
Config.UploadBandwidth = 1000000
```

Lower it if players with weak connections time out on large recordings. Raise it for faster uploads.

To keep recordings small, set the size and length limits in `Config.Cameras.Recording`:

```lua config/config.lua theme={null}
Recording = {
    MaxSize = 15,      -- MB
    MaxDuration = 120, -- seconds
}
```

## Limit image links

Officers can also paste image links. To only accept links from your provider, list its hostnames in `Config.Images.AllowedHosts`. Subdomains are included.

```lua config/config.lua theme={null}
Config.Images = {
    AllowedHosts = { 'fivemanage.com', 'cdn.discordapp.com' },
}
```

An empty list accepts any link.

## Troubleshooting

If an upload fails, check the server console. The MDT prints which provider failed and why.

| Console message | Fix |
| - | - |
| `Config.ApiKeys.<Provider> is not set` | Add the key to `config/credentials.lua` and restart. |
| `presigned-url request failed (401)` | The key is wrong or was revoked. Create a new one. |
| `upload failed (413)` | The file is too big for the provider. Lower `MaxSize`. |
| Uploads time out for some players | Lower `Config.UploadBandwidth`. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.