API 文件

程式化編碼 — 限 Ultra 訂戶使用。請於 帳戶 → API 金鑰建立金鑰,將金鑰直接放在以下端點 URL 中即可。

方案上限

方案單檔上限API 存取需要 multipart 嗎?
Free 5 KB
Pro 100 MB 否(單次 PUT 即可)
Ultra 300 MB 檔案 >100 MB 必須走 multipart

Cloudflare 邊緣對單一 request body 上限為 100 MB。超過此值的檔案, 必須切成 50 MB 的 part 並走下方的 multipart 端點。

單次 PUT 上傳(≤100 MB)

POST https://webhook.binaryphp.com/upload/<api_key>
  X-BPHP-Filename: plugin.php          # 必填,副檔名 .php 或 .zip
  X-BPHP-Domain:   app.example.com,*.example.com
  X-BPHP-Mac:      aa:bb:cc:dd:ee:ff   # 選填
  X-BPHP-Expire:   2027-12-31          # 選填
  X-BPHP-Webhook:  https://you.example.com/wh    # 選填,異步通知用
  <檔案二進位內容作為 request body>

→ 200 {
    "job_id":       "...",
    "download_url": "https://...presigned R2 URL...",
    "expires_at":   "2026-05-09T...",
    "size":         12345,
    "license":      { "domains": [...], ... }
  }

curl 一行版:

curl -X POST https://webhook.binaryphp.com/upload/$API_KEY \
  -H "X-BPHP-Filename: plugin.php" \
  -H "X-BPHP-Domain: app.example.com" \
  --data-binary @plugin.php

Multipart 上傳(任意大小,>100 MB 必走)

三步驟:init → 個別 PUT 每個 part → complete

1. Init(初始化)

POST https://webhook.binaryphp.com/upload/<api_key>/init
  X-BPHP-Filename:   bigplugin.zip
  X-BPHP-Total-Size: 200000000

→ 200 {
    "job_id":     "...",
    "upload_id":  "...",                         # R2 multipart 不透明 id
    "r2_key":     "uploads/<job_id>.zip",
    "part_size":  52428800,                      # 建議 50 MB 一片
    "part_count": 4,
    "max_part_size": 104857600,                  # 100 MB 硬限
    "parts": [
      { "part_no": 1, "upload_url": "https://...presigned PUT..." },
      { "part_no": 2, "upload_url": "..." },
      { "part_no": 3, "upload_url": "..." },
      { "part_no": 4, "upload_url": "..." }
    ]
  }

2. 對每個 upload_url 做 PUT

這些預簽名 URL 直接打 R2,不耗 Worker 頻寬。 每個 PUT 回應的 ETag header 必須收集起來,complete 階段一併送上:

PUT <upload_url>
  <從檔案 (part_no-1)*part_size 偏移處讀取 part_size 個 bytes>

← 200 OK
  ETag: "abc123def456..."

每個 part 必須 ≥5 MB(最後一片可小於),且 ≤100 MB。

3. Complete(完成)

POST https://webhook.binaryphp.com/upload/<api_key>/complete
Content-Type: application/json

{
  "upload_id": "...",
  "r2_key":    "uploads/<job_id>.zip",
  "filename":  "bigplugin.zip",
  "parts": [
    { "part_no": 1, "etag": "\"abc...\"" },
    { "part_no": 2, "etag": "\"def...\"" },
    { "part_no": 3, "etag": "\"ghi...\"" },
    { "part_no": 4, "etag": "\"jkl...\"" }
  ],
  "domain":   "app.example.com",
  "mac":      "",
  "expire":   "2027-12-31",
  "plugin":   "",
  "webhook":  ""
}

→ 200 { "job_id", "download_url", "expires_at", "size", "license" }

Abort(選用)

若中途 part 上傳失敗,請呼叫 /upload/<api_key>/abort 並帶 { upload_id, r2_key },避免殘留的 part 留在 R2 計費。

Webhook 通知

設定 X-BPHP-Webhook(或 complete body 中的 "webhook") 以接收 encode.completed 事件。通知由 Cloudflare 邊緣 IP 送出, 不會洩漏我們主機 IP。Body 以你的 API key 為共享密鑰用 HMAC-SHA256 簽章:

POST <你的 webhook URL>
  X-BinaryPHP-Signature: t=1736208000,v1=<hex>
  X-BinaryPHP-Event:     encode.completed
  Content-Type:          application/json

{
  "event":        "encode.completed",
  "job_id":       "...",
  "download_url": "...",
  "expires_at":   "...",
  "size":         12345,
  "license":      { ... }
}

驗證範例(PHP):

$sig_h = $_SERVER['HTTP_X_BINARYPHP_SIGNATURE'] ?? '';
[$tpart, $vpart] = array_pad(explode(',', $sig_h, 2), 2, '');
$ts  = (int) substr($tpart, 2);
$got = substr($vpart, 3);
if (abs(time() - $ts) > 300) http_response_code(401);
$body = file_get_contents('php://input');
$expected = hash_hmac('sha256', "$ts.$body", $YOUR_API_KEY);
if (!hash_equals($expected, $got)) http_response_code(401);

現成可用的客戶端

常見錯誤

狀態碼意思處理方式
401金鑰錯誤或已撤銷 到「帳戶 → API 金鑰」重新建立
402方案不支援此操作(如 Free 上傳 zip) 升級方案
413檔案 / Content-Length 超過方案上限 改走 multipart,或升 Pro→Ultra(300 MB)
400缺欄位或格式錯(filename、parts 等) 查看回應 body,會指出缺哪個欄位
502R2 multipart 操作失敗 重試 init;R2 偶有 <1% 機率失敗