FiveM スマホと EdgeVault

使っているスマホの版と公開設定を確認し、メディアの保存先を接続します。各スクリプトとの接続は実機で確認してください。

EdgeVault のアップロード契約

送信先
https://nozawatec.com/api/edgeworks/vault/upload
送信形式
POST / multipart/form-data、ファイル項目名は file(NPWD のボイスメッセージが送る recording も受け付けます)
認証
X-API-Key。キーはサーバー側に保持します。
返却 URL
成功時の JSON の data.url(同じ URL を一番上の url にも返します)
上限
1 ファイル 100 MiB。画像・動画・音声の許可形式は API リファレンスを確認してください。
一時 URL
サーバーから https://nozawatec.com/api/edgeworks/vault/presign に X-API-Keyと filename・mime_type・folder を送ると、アップロード用の一時 URL(presigned URL)data.upload_url_with_token が返ります(1 時間有効)。スマホや screenshot-basic の画面(https://cfx-nui-…)からの送信も受け付けます。

スマホ側が別のフィールド名や JSON 形式を固定している場合、送信先だけの変更では接続できません。API キーをクライアントへ配らないサーバー側アダプターが必要です。

API リファレンス

LB Phone(lb-phone v2.9.0)

有料 · v2.9.0 の配布物で手順を確認 · EdgeVault 側は同じ送りかたで検査済み · FiveM の実機は要確認

写真・動画・録音を、lb-phone の「PRESIGNED_URL」の方式で EdgeVault に置きます。EdgeWorks の API キーはサーバーの中だけで使い、プレイヤーの画面には使い切りの送信先だけが渡ります。書き換えるのは 4 つのファイルで、どれも暗号化されていない(編集してよい)場所です。

  1. 1. 方式を足す — shared/upload.lua

    UploadMethods の表に EdgeVault を足します。Qbox の定義の後ろ(表を閉じる最後の } の手前)に入れてください。鍵(API_KEY)は書きません。送信先の PRESIGNED_URL は、次の手順のサーバーの処理が作った URL に置き換わります。

        EdgeVault = {
            Default = {
                url = "PRESIGNED_URL",
                field = "file",
                error = {
                    path = "success",
                    value = false
                },
                success = {
                    path = "data.url"
                },
            },
        },
  2. 2. 送信先を作る — server/custom/functions/functions.lua

    GetPresignedUrl の中の、Qbox の分岐が終わる end)) の後ろ(if を閉じる end の手前)に、EdgeVault の分岐を足します。サーバーから EdgeVault に「これから送るファイルの種類」を伝え、使い切りの送信先を受け取って返します。鍵はここでしか使いません。

        elseif uploadMethod == "EdgeVault" then
            local mime = ({ Image = Config.Image.Mime, Video = "video/webm", Audio = "audio/webm" })[uploadType]
            if mime == "image/jpg" then mime = "image/jpeg" end -- EdgeVault は正式名の image/jpeg で受ける
            local ext = ({ ["image/webp"] = "webp", ["image/png"] = "png", ["image/jpeg"] = "jpg", ["video/webm"] = "webm", ["audio/webm"] = "webm" })[mime] or "bin"
    
            return Citizen.Await(promise.new(function(p)
                PerformHttpRequest("https://nozawatec.com/api/edgeworks/vault/presign", function(status, body)
                    local okDecode, res = pcall(json.decode, body or "")
                    local url = okDecode and type(res) == "table" and res.success and res.data and res.data.upload_url_with_token
                    if status ~= 200 or not url then
                        infoprint("error", "EdgeVault: could not create an upload URL (HTTP " .. tostring(status) .. ")")
                        print("Body:", body)
                        p:resolve()
                        return
                    end
                    p:resolve(url)
                end, "POST", json.encode({
                    filename = "lb-phone." .. ext,
                    mime_type = mime,
                    folder = "/lb-phone/" .. uploadType,
                }), {
                    ["Content-Type"] = "application/json",
                    ["X-API-Key"] = apiKey,
                })
            end))
  3. 3. 鍵を置く — server/apiKeys.lua

    API_KEYS の Video・Image・Audio に、EdgeWorks のダッシュボードで発行した API キーを入れます。このファイルはサーバーだけが読み、手順 2 の処理だけが使います。同じ鍵を 3 つに入れてかまいません。

    API_KEYS = {
        Video = "EdgeWorks の API キー",
        Image = "EdgeWorks の API キー",
        Audio = "EdgeWorks の API キー",
    }
  4. 4. 方式を選び、置き場を許可する — config/config.lua

    Config.UploadMethod の 3 つを "EdgeVault" にします。あわせて Config.UploadWhitelistedDomains に nozawatec.com を足します(EdgeVault の公開 URL は https://nozawatec.com/api/edgeworks/files/… です)。足さないと、送れてもスマホの中で写真が受け付けられません。

    Config.UploadMethod.Video = "EdgeVault"
    Config.UploadMethod.Image = "EdgeVault"
    Config.UploadMethod.Audio = "EdgeVault"
    
    Config.UploadWhitelistedDomains = {
        "fivemanage.com",
        "fmfile.com",
        "amazonaws.com", -- lb-presigned (S3)
        "nozawatec.com", -- EdgeVault
    }
  5. 5. 再起動して試す

    サーバーを再起動するか、コンソールで restart lb-phone を実行します。写真・動画・ボイスメッセージを 1 つずつ試し、スマホの中で開けることと、EdgeWorks のダッシュボードの Vault の /lb-phone/Image・/lb-phone/Video・/lb-phone/Audio に現れることを確かめてください。失敗したときは、サーバーのコンソールに「EdgeVault: could not create an upload URL」と HTTP の番号が出ます。

気をつけること

  • Custom の方式(upload.lua の headers に API_KEY と書く方式)は使わないでください。lb-phone は送信をスマホの画面から行うので、鍵が全プレイヤーの手元に渡ります(以前ここで案内していた手順はこの形だったので、取り下げました)。
  • 録音は WebM(audio/webm)で保存されます。EdgeVault の受け取りは、2026-10-25 に録音の WebM を受け付けるようにしました(それより前は断っていました)。
  • 大きさ: lb-phone の既定は動画 25 MB・60 秒(Config.Video.MaxSize・Config.Video.MaxDuration)。EdgeVault は 1 ファイル 100 MiB まで、全体の容量はプランごと(無料 10 GiB・Pro 100 GiB・Enterprise 300 GiB)です。
  • アップロード用の一時 URL(presigned URL)は 1 回だけ・1 時間有効です。作る口(presign)は 1 つの API キーにつき 1 分 240 回までです。
  • スマホの中の写真は EdgeVault の URL を指しているだけです。EdgeVault で消すと、スマホの中でも見られなくなります。
  • 写真の形式(Config.Image.Mime)は、既定の image/webp か image/png を使ってください。EdgeVault 側で確かめたのはこの 2 つと WebM(動画・録音)です。
  • lb-phone のサーバーから https://nozawatec.com へ外向きに接続できる必要があります。

2026-10-25 に lb-phone v2.9.0 の配布物(暗号化されていない設定ファイル)で確かめた手順です。別の版では行の場所が違うことがあります。

提供元の資料を開く

無料のスマホ

NPWD(New Phone Who Dis)

配布の 3.16.0 で手順を確認 · EdgeVault 側は同じ送りかたで検査済み · FiveM の実機は要確認

リリース 3.16.0(2025-03-27。fxmanifest の表記は 3.15.1-beta.2) · 許諾: GPL-3.0

写真とボイスメッセージは、どちらも NPWD のサーバーが EdgeVault へ直接送ります。API キーはサーバーの設定(convar)だけに置き、プレイヤーには渡りません。書き換えるのは config.json と server.cfg です。

  1. 1. 写真の送り先 — config.json の images

    images を次のように書き換えます。type はフォームの項目名として使われるので file にします。returnedDataIndexes は応答を順にたどる鍵で、EdgeVault の応答の data.url を指します。

      "images": {
        "url": "https://nozawatec.com/api/edgeworks/vault/upload",
        "type": "file",
        "imageEncoding": "webp",
        "contentType": "multipart/form-data",
        "useContentType": false,
        "authorizationHeader": "X-API-Key",
        "authorizationPrefix": "",
        "useAuthorization": true,
        "returnedDataIndexes": ["data", "url"]
      },
  2. 2. 置き場を許可する — config.json の imageSafety

    imageSafety.safeImageUrls に nozawatec.com を足します。NPWD はホスト名の完全一致で比べるので、nozawatec.com をそのまま書いてください(EdgeVault の公開 URL は https://nozawatec.com/api/edgeworks/files/… です)。

        "safeImageUrls": [
          "nozawatec.com",
          "i.imgur.com",
          ...(元の行はそのまま)
        ]
  3. 3. ボイスメッセージ — config.json の voiceMessage

    使う場合だけ書き換えます。NPWD は録音の項目名(recording)を決め打ちにし、応答は入れ子でなく 1 段だけ読むので、returnedDataIndexes は ["url"] にします(EdgeVault は応答の一番上にも url を返します)。既定の url(https://api.fivemange/api/audio)は綴りが違っていて、そのままでは届きません。

      "voiceMessage": {
        "enabled": true,
        "authorizationHeader": "X-API-Key",
        "url": "https://nozawatec.com/api/edgeworks/vault/upload",
        "returnedDataIndexes": ["url"]
      }
  4. 4. 鍵を置く — server.cfg

    EdgeWorks のダッシュボードで発行した API キーを、NPWD が読む 2 つの convar に入れます。必ず set を使ってください(setr にするとクライアントへ配られます)。screenshot-basic を npwd より前に ensure しておきます。

    ensure screenshot-basic
    set SCREENSHOT_BASIC_TOKEN "EdgeWorks の API キー"
    set NPWD_AUDIO_TOKEN "EdgeWorks の API キー"
    ensure npwd
  5. 5. 再起動して試す

    サーバーを再起動し、カメラで撮った写真がギャラリーに出ること、EdgeWorks のダッシュボードの Vault に同じ写真が現れることを確かめます。失敗するとサーバーのコンソールに「Failed to upload photo, status code: …」が出ます。

気をつけること

  • 写真もボイスメッセージも、NPWD のサーバーから送ります(撮影はクライアント、送信はサーバー)。API キーがプレイヤーに渡ることはありません。
  • フォルダは指定できないので、EdgeVault の一番上(/)に入ります。
  • ボイスメッセージは、NPWD が audio/ogg と名乗っても、EdgeVault は中身で種類を決めます(NPWD は録音の形式を指定していないので、中身が WebM なら video/webm として残ります)。
  • ボイスメッセージを EdgeVault で受けるには、2026-10-25 以降の EdgeWorks が要ります(recording の項目名と、応答の一番上の url をこの日に足しました)。
  • 大きさ: EdgeVault は 1 ファイル 100 MiB まで、全体の容量はプランごと(無料 10 GiB・Pro 100 GiB・Enterprise 300 GiB)です。
  • 手順は配布の zip(npwd.zip)の中身で確かめています。ソースから建てた場合も config.json の形は同じです。

2026-10-25 に、公開されているソース(配布物)を取得して確かめた手順です。別の版では行の場所が違うことがあります。

提供元のリポジトリを開く

qb-phone(QBCore)

1.5.0 のソースで手順を確認 · EdgeVault 側は同じ送りかたで検査済み · FiveM の実機は要確認

1.5.0(qbcore-fivem/qb-phone のコミット 333e813・2026-08-24) · 許諾: GPL-3.0

カメラの写真を EdgeVault に置きます。qb-phone は既定で Discord の Webhook の URL をプレイヤーに渡し、プレイヤーの画面(screenshot-basic)から直接送らせます。これを、サーバーが撮影のたびに EdgeVault で使い切りの送信先を作って渡す形に書き換えます。API キーはサーバーの server.lua の中だけに置きます。

  1. 1. 送信先を作る — server.lua

    冒頭の local FivemerrApiToken = '' の下に EdgeVaultApiKey を足し、qb-phone:server:GetWebhook のコールバックを丸ごと次に置き換えます。返すのは Webhook ではなく、撮影 1 回ぶんの送信先です。

    local EdgeVaultApiKey = '' -- EdgeWorks の API キー(サーバーだけが読む)
    
    QBCore.Functions.CreateCallback('qb-phone:server:GetWebhook', function(_, cb)
        local function done(url) cb(url) end
        if EdgeVaultApiKey == '' then
            print('^1--- EdgeVault: set EdgeVaultApiKey in server.lua ---^7')
            return done(nil)
        end
        PerformHttpRequest('https://nozawatec.com/api/edgeworks/vault/presign', function(status, body)
            local okDecode, res = pcall(json.decode, body or '')
            local url = okDecode and type(res) == 'table' and res.success and res.data and res.data.upload_url_with_token
            if status ~= 200 or not url then
                print('^1--- EdgeVault: could not create an upload URL (HTTP ' .. tostring(status) .. ') ---^7')
                return done(nil)
            end
            done(url)
        end, 'POST', json.encode({ filename = 'qb-phone.jpg', mime_type = 'image/jpeg', folder = '/qb-phone' }), {
            ['Content-Type'] = 'application/json',
            ['X-API-Key'] = EdgeVaultApiKey,
        })
    end)
  2. 2. 送りかたと応答の読みかた — client.lua

    カメラの処理の中の requestScreenshotUpload の呼び出し(項目名 'files[]'・Discord の形の応答を読む部分)を次に置き換えます。項目名を file にし、応答は EdgeVault の data.url を読みます。

                    exports['screenshot-basic']:requestScreenshotUpload(tostring(hook), 'file', function(data)
                        SaveToInternalGallery()
                        local ok, image = pcall(json.decode, data or '')
                        local url = ok and type(image) == 'table' and image.data and image.data.url
                        DestroyMobilePhone()
                        CellCamActivate(false, false)
                        if not url then
                            QBCore.Functions.Notify('Upload failed', 'error')
                            cb(json.encode({ url = nil }))
                            takePhoto = false
                            return
                        end
                        TriggerServerEvent('qb-phone:server:addImageToGallery', url)
                        Wait(400)
                        TriggerServerEvent('qb-phone:server:getImageFromGallery')
                        cb(json.encode(url))
                        takePhoto = false
                    end)
  3. 3. Fivemerr は切ったまま — config.lua

    Config.Fivemerr は false のままにします(Fivemerr の送りかたは画像を JSON の base64 で送るもので、EdgeVault はその形を受けません)。

  4. 4. 再起動して試す

    screenshot-basic を qb-phone より前に ensure し、サーバーを再起動します。カメラで撮った写真がギャラリーに出ること、EdgeWorks のダッシュボードの Vault の /qb-phone に現れることを確かめます。

気をつけること

  • 既定の作りでは、Discord の Webhook の URL が撮影のたびにプレイヤーへ渡ります(qb-phone:server:GetWebhook がクライアントへ返す)。この書き換えで、渡るのは 1 回だけ・1 時間有効の送信先になります。
  • 写真は screenshot-basic の既定の JPEG です。
  • 送信はプレイヤーの画面(screenshot-basic)から行うので、EdgeVault はその画面(https://cfx-nui-screenshot-basic)からの送信を受け付けます(2026-10-25 以降の EdgeWorks)。
  • qb-phone は GPL-3.0 です。書き換えたものを配る場合は GPL-3.0 に従ってください(自分のサーバーで使うだけなら配布にはあたりません)。

2026-10-25 に、公開されているソース(配布物)を取得して確かめた手順です。別の版では行の場所が違うことがあります。

提供元のリポジトリを開く

z-phone(QBCore・Qbox)

1.0.0 のソースで手順を確認 · EdgeVault 側は同じ送りかたで検査済み · FiveM の実機は要確認

1.0.0(alfaben12/z-phone のコミット c6d1111・2025-01-15) · 許諾: 作者の独自の許諾(使用・改変は自由、販売は不可)

カメラの写真を EdgeVault に置きます。送りかたは qb-phone と同じで、既定では Discord の Webhook の URL をプレイヤーに渡しています。サーバーが撮影のたびに使い切りの送信先を作って渡す形に書き換えます。

  1. 1. 送信先を作る — server/main.lua

    1 行目の local WebHook = '…' を EdgeVaultApiKey に置き換え、z-phone:server:GetWebhook の登録を丸ごと次に置き換えます。配布物の 1 行目には作者の Discord の Webhook の URL が入ったままなので、必ず消してください。

    local EdgeVaultApiKey = '' -- EdgeWorks の API キー(サーバーだけが読む)
    
    lib.callback.register('z-phone:server:GetWebhook', function(_)
        if EdgeVaultApiKey == '' then
            print('^1--- EdgeVault: set EdgeVaultApiKey in server/main.lua ---^7')
            return nil
        end
        local p = promise.new()
        local function done(url) p:resolve(url) end
        PerformHttpRequest('https://nozawatec.com/api/edgeworks/vault/presign', function(status, body)
            local okDecode, res = pcall(json.decode, body or '')
            local url = okDecode and type(res) == 'table' and res.success and res.data and res.data.upload_url_with_token
            if status ~= 200 or not url then
                print('^1--- EdgeVault: could not create an upload URL (HTTP ' .. tostring(status) .. ') ---^7')
                return done(nil)
            end
            done(url)
        end, 'POST', json.encode({ filename = 'z-phone.jpg', mime_type = 'image/jpeg', folder = '/z-phone' }), {
            ['Content-Type'] = 'application/json',
            ['X-API-Key'] = EdgeVaultApiKey,
        })
        return Citizen.Await(p)
    end)
  2. 2. 送りかたと応答の読みかた — client/feature/camera.lua

    requestScreenshotUpload の呼び出し(項目名 'files[]'・Discord の形の応答を読む部分)を次に置き換えます。

                    exports['screenshot-basic']:requestScreenshotUpload(tostring(hook), 'file', function(data)
                        SaveToInternalGallery()
                        local ok, image = pcall(json.decode, data or '')
                        local url = ok and type(image) == 'table' and image.data and image.data.url
                        DestroyMobilePhone()
                        CellCamActivate(false, false)
                        if not url then
                            xCore.Notify('Upload failed', 'error', 3000)
                        end
                        cb(url)
                        takePhoto = false
                    end)
  3. 3. 再起動して試す

    screenshot-basic を z-phone より前に ensure し、サーバーを再起動します。カメラで撮った写真がスマホに出ること、EdgeWorks のダッシュボードの Vault の /z-phone に現れることを確かめます。

気をつけること

  • 配布物の server/main.lua には、作者のものとみられる Discord の Webhook の URL が書かれたまま公開されています。そのまま使うと、撮った写真がその Webhook(他人のチャンネル)へ送られます。
  • 写真は screenshot-basic の既定の JPEG です。送信はプレイヤーの画面(screenshot-basic)から行います(2026-10-25 以降の EdgeWorks が受け付けます)。
  • z-phone の許諾は、使用と改変を認め、販売を認めていません。

2026-10-25 に、公開されているソース(配布物)を取得して確かめた手順です。別の版では行の場所が違うことがあります。

提供元のリポジトリを開く

gcphone(N3MTV/gcphone)

無料 · つなぐ手順は載せていません

最後の更新 2020-08-21(コミット 41f9ed9)

写真の送信先と項目名が、スマホの画面の設定(クライアント側の config)に書かれる作りで、応答も files[1].url の形に決め打ちです。EdgeVault の API キーをプレイヤーに渡さずにつなぐ道が無いので、手順を載せていません。2020 年から更新が止まっています(readme の表記は GPL v3)。

提供元のリポジトリを開く

手順を確かめていないスマホ

GKSPHONE V2 / GKS Tablet

有料 · カスタム保存先の設定箇所を公開資料で確認 · 接続は要検証
  1. gksphone/config/serverconfig.lua と gks-tablet/config/serverconfig.lua の Cfg.MediaService を customMedia にします。
  2. 同ファイルの Cfg.MediaServices.customMedia にある URL・フォーム項目・レスポンスパスを EdgeVault 契約に合わせます。設定キーの形は配布版のコメントを優先してください。
  3. 写真・動画・ボイスメモをそれぞれ送信し、スマホ内の表示と EdgeVault の一覧を確認します。鍵は serverconfig.lua のみに置きます。
提供元の資料を開く

Quasar Smartphone V3

有料 · 標準メディアの保存先変更は未確認
  1. 公式資料にはカスタムアプリからギャラリー・カメラを選ぶブリッジが記載されています。標準カメラの保存先を任意の API に変える設定は、公開資料から確認できませんでした。
  2. 配布版に公開されたサーバー側のメディア橋渡しがある場合だけ、EdgeVault の契約に接続します。標準機能の連携済みとは扱わないでください。
提供元の資料を開く

YSeries V2

有料 · 配布版の公開設定を要確認
  1. 配布版の公開設定とサーバー側のアップロード処理を確認してください。現時点では標準メディアの保存先を EdgeVault に切り替える手順を検証していません。
  2. URL、認証ヘッダー、フォーム名、応答パスを変更できる場合は下の契約に合わせ、画像・動画・音声を個別に試します。
提供元の資料を開く

CodeM mPhone

有料 · 配布版の公開設定を要確認
  1. 利用中の mPhone の版を確認し、公開された設定やサーバー側のメディア処理から保存先を変更できるか調べてください。標準カメラと EdgeVault の接続は未検証です。
  2. 変更できる場合は下の EdgeVault 契約と照合します。クライアントに API キーを置かず、画像・動画・音声のそれぞれを実機で確認してください。
提供元の資料を開く

接続後の確認

テスト用の写真を撮り、スマホ内で開けることと、EdgeVault に同じファイルが現れることを確認します。動画と音声も別々に試してください。失敗時は電話スクリプトのサーバーログ、EdgeVault の応答コード、アップロード形式を確認します。

FiveM スマホのメディア連携 | EdgeWorks | EdgeWorks