UnifyPort 媒體發送:檔案 URL 與送達問題排查
透過 UnifyPort 發送圖片、影片、音訊或文件時,使用 POST /v1/messages,設定渠道支援的 message.type,並在 message.url、message.file_url 或 message.file_key 中提供至少一個非空來源。URL 必須是完整 HTTP(S) 網址。本機檔名不是可存取的媒體 URL,回應中的 status: accepted 亦不代表收件人已收到檔案。
四個重點
- 先檢查訊息帳戶所屬渠道,再開放媒體類型選項。
- 優先提供一個明確、可存取的 URL,不猜測多個來源欄位的優先次序。
- 發送端
message與接收端data.message.attachments[]並非同一結構。 - 檔案存取、請求驗證、帳戶連線及最終送達須分別排查。
先確認發送接口的規格
UnifyPort 的非官方接口遵循媒體發送文件,不是 Telegram Bot API 或 LINE Messaging API。例如,Telegram 檔案發送文件描述其原生方法使用的檔案識別碼、HTTP URL 及 multipart 上傳;這不表示 UnifyPort 接受以 Telegram file_id 作為 file_key,亦不表示兩者支援相同上傳請求。
| 欄位 | UnifyPort 文件確認的意思 |
|---|---|
message.type | 包括 image、video、audio、document、file,仍須檢查渠道支援 |
message.url 或 message.file_url | 非空、完整的 HTTP(S) 來源網址 |
message.file_key | 已記載的另一種來源方式,不代表可以自行製造 key 或上傳端點 |
message.caption | 圖片、影片、文件及檔案的選填說明文字 |
provider_data.seconds | WhatsApp 音訊及影片的選填非負整數時長,單位為秒 |
WhatsApp 影片時長範圍為 0–4294967295;provider_data.waveform 仍只適用於音訊。不要將接收資料中的 duration_ms 原樣填進秒數欄位。不需要時長資料時,省略即可。
目前訊息能力矩陣未映射 TikTok 音訊及文件/檔案發送,將 X 音訊列為部分支援,並分別列出 whatsapp-protocol 與 whatsapp。統一端點不代表每個渠道有相同的媒體能力或限制。
建立請求前,先準備檔案
以下屬應用程式設計建議,不是額外的平台保證:
- 確認操作人員有權使用所選帳戶向目標對話發送內容。群組應使用對話 ID 及類型,不是訊息作者的 ID。
- 分別檢查授權狀態及
runtime_status;已授權不等於連線正常。 - 將正確檔案放在受控且可存取的來源。瀏覽器登入後能開啟的頁面,不足以證明發送服務能讀取檔案。
- 在獨立伺服器環境、不帶瀏覽器 Cookie 測試讀取,確認回傳的是媒體內容,而非 HTML 登入頁或錯誤頁。
- 若使用限時 URL,應為預期排隊及讀取時間安排有效期。發送文件未保證統一的擷取期限;預檢成功亦不代表稍後仍可存取。
建議使用 HTTPS、最小存取範圍及應用程式批准的儲存來源。不要將簽署查詢參數或憑證寫入日誌。這些是安全建議,不代表 UnifyPort 有未公開的網絡過濾機制。
建立 URL 媒體請求
以下 JavaScript 函式使用應用程式已選定的帳戶、對話及獲准媒體 URL 建立請求內容。它不會上傳檔案,亦不驗證遠端是否實際可存取。呼叫前仍須檢查渠道能力與操作權限。
function buildMediaRequest({ accountId, conversation, type, url, caption }) {
const types = new Set(['image', 'video', 'audio', 'document', 'file']);
if (!accountId || !conversation?.id || !conversation?.type) {
throw new Error('Account and conversation are required');
}
if (!types.has(type)) throw new Error('Unsupported media type');
const source = new URL(url);
if (!['http:', 'https:'].includes(source.protocol) ||
source.username || source.password) {
throw new Error('Use an approved HTTP(S) media source');
}
const message = { type, url: source.href };
if (caption !== undefined) {
if (type === 'audio' || typeof caption !== 'string') {
throw new Error('Caption is not valid for this request');
}
message.caption = caption;
}
return {
account_id: accountId,
to: { id: conversation.id, type: conversation.type },
message,
};
}
將產生的 JSON 提交至 POST /v1/messages,在伺服器端使用 X-Api-Key 驗證及 Content-Type: application/json。範例刻意只提供 message.url:文件要求至少一個來源,但沒有說明多個來源互相衝突時的優先次序。
不要直接將收到的附件物件貼進發送請求。接收媒體欄位指南中的 attachments[].type、url、mimetype 等屬於接收資料,不是完整發送請求。若原網址已過期,請先按下載連結復原指南確認相應接口的復原方式,再決定是否轉發。
按失敗環節排查
| 現象 | 下一步檢查 | 應避免 |
|---|---|---|
本機路徑、相對網址或 data: URL | 準備完整 HTTP(S) 檔案來源 | 將路徑改名為 file_key |
| 只有自己的瀏覽器能開啟 | 登入依賴、有效期、重新導向及回應內容 | 假設發送端沿用瀏覽器工作階段 |
unsupported_message_type | 渠道與媒體類型組合 | 重複相同請求 |
provider_not_ready | 授權及運行狀態 | 未診斷便重新授權 |
| 請求逾時 | 保留發送操作記錄,調查不確定結果 | 自動重發而可能產生重複訊息 |
回應為 accepted | 保存回傳識別碼,另查支援的送達證據 | 立即顯示「已送達」或「已讀」 |
按錯誤參考的機器可讀錯誤分支處理,不要猜測描述文字。保留 request_id 供排查,但不要當作去重憑證;請求追蹤指南說明了這個邊界。
處理回條時,查閱事件文件及渠道事件矩陣,驗證 webhook 簽署,並容忍重複及次序錯置。並非每個渠道都有所有回條,因此缺少確認應保留為未知,而不是觸發再次發送。
驗收檢查與常見問題
正式啟用前建議測試:可存取檔案、已過期來源、登入頁回應、不支援的渠道/類型組合、帳戶斷線及發送回應遺失。本文不聲稱已執行這些線上測試。
這個 JSON 請求可以直接上傳本機檔案嗎?
已記載的媒體請求使用 URL 或檔案 key 來源。本文不提供或承諾 multipart 上傳端點。請透過獲准儲存流程託管檔案,再提供可存取 URL。
Telegram file_id 可以當作 file_key 嗎?
文件沒有建立這種對應。請分開處理 Telegram 原生檔案識別碼與 UnifyPort 來源欄位。
accepted 表示對方收到檔案了嗎?
不是。它表示請求已接受,而不是送達或已讀回條。
下一步與參考資料
按發送圖片和檔案指南,先用一個獲准測試對話及受控檔案來源確認流程,再啟用排隊或自動發送。
參考資料核對日期:2026-10-09。
令訊息接入變成一條穩定嘅產品管線。
先用統一嘅 API 跑通發送,再用標準事件將所有入站訊息接返去業務系統。