sfappAPI 文件

請求慣例

這一頁的規則適用於每一支端點。資源參考只寫各端點自己的欄位。

送出 JSON

請求與回應都是 JSON,字元編碼 UTF-8。有內文的請求帶 Content-Type: application/json,包含 POSTPATCH 與帶確認碼的 DELETE。圖片上傳是唯一的例外,用 multipart/form-data

請求內文不接受多餘欄位。送出規格以外的欄位回 422 validation_faileddetails 指名該欄位。

回應的欄位順序沒有保證,未來也會新增欄位。以鍵名取值,不依賴順序或欄位數量。

填寫時間

時間欄位是 ISO 8601 且含時區位移,例如 2026-09-15T09:00:00+08:00

  • 送進來的時間沒帶時區回 422 validation_failed,只送日期同樣回 422。
  • 回應的時間一律帶時區。
  • 每日配額以台北時間的日界線計算。

帶上請求標頭

標頭 何時必要 用途
Authorization 每個 /v1 端點 Bearer <金鑰>,格式見認證與金鑰
Content-Type 有 JSON 內文時 固定 application/json
Idempotency-Key 建立文章、建立分類 這次建立的識別字串,規則見下方
X-Request-Id 可選 自帶的追蹤碼,1 到 64 個字元,限英數與 _ . -,原樣回傳

讀回應標頭

標頭 出現時機 內容
X-Request-Id 每個回應 這次請求的追蹤碼,回報問題時附上
X-Sfapp-Api-Version 每個回應 目前的 API 版本,例 2026-09-14
Cache-Control 每個 API 回應 固定 no-store
X-RateLimit-Limit 通過認證後 這一類(讀或寫)每分鐘的上限
X-RateLimit-Remaining 通過認證後 這一分鐘的剩餘次數
X-RateLimit-Reset 通過認證後 額度回滿的時間,Unix 秒數
Retry-After 被擋下時 建議等待的秒數

對照版本承諾

API 版本是日期版號,目前是 2026-09-14。每個回應的 X-Sfapp-Api-Version 標頭帶著它,GET /v1/meapi_version 也看得到。

路徑上的 /v1 是主要版本。/v1 之內不做破壞性變更。

會發生 不會發生
新增端點 移除端點或改路徑
回應新增欄位 移除既有回應欄位、改變欄位型別
請求新增可選欄位 新增必填欄位
錯誤訊息文字調整 錯誤碼改名或改對應的 HTTP 狀態
列舉新增值 移除既有列舉值

解析回應時容忍沒見過的欄位與沒見過的列舉值。破壞性變更會以新的主要版本推出,舊版有重疊期。

翻頁

文章列表與稽核事件列表用游標分頁。分類列表一次回傳全部,沒有分頁參數。

查詢參數 型別 預設 用途
limit 整數 20 每頁筆數,範圍 1 到 50
cursor 字串 上一頁 meta.next_cursor 的值,第一頁不必帶

回應固定有 datameta

{
  "data": [],
  "meta": { "next_cursor": "MTAwMQ", "limit": 20 }
}

meta.next_cursornull 代表已經是最後一頁。游標是不透明字串,原樣帶回即可。自行拼造或改寫的游標回 422 validation_failed

翻頁期間其他查詢條件保持不變,例如 status 篩選。換條件就從第一頁重新開始。

帶冪等鍵

Idempotency-Key 讓同一次建立重送多次也只寫入一筆。

  • 建立文章(POST /v1/blog/posts)與建立分類(POST /v1/blog/categories)必須帶,沒帶回 422 validation_failed
  • 值是 1 到 255 個可列印的 ASCII 字元,建議用 UUID。
  • 每一次新的建立用一把新的鍵,重試沿用同一把。
  • 記錄保存 24 小時,之後同一把鍵視為全新的請求。
  • 圖片上傳不收冪等鍵,它以圖片內容去重。

同一把鍵再次送出時的結果:

情況 送出的內容 結果
回放 與第一次相同 回傳第一次的狀態碼與回應內容,不新增資料
衝突 與第一次不同 409 idempotency_key_reused,不寫入
處理中 相同,且第一次還沒結束 409 idempotency_in_progress,依 Retry-After(2 秒)再送

「相同」比對的是方法、路徑與整份內文。重試時改動內文會變成衝突。要送不同的內容就換一把新的鍵。

# 第一次
curl -sS -X POST https://api.sfec.cloud/v1/blog/posts \
  -H "Authorization: Bearer $SFAPP_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1b2c74-8b1a-4f3e-9a0d-2c5e7b4d9a11" \
  -d '{"title":"秋季新品上架","body_html":"<p>今年秋天的第一波新品已經到店。</p>"}'

# 逾時後用同一把鍵重送,結果仍是同一篇
curl -sS -X POST https://api.sfec.cloud/v1/blog/posts \
  -H "Authorization: Bearer $SFAPP_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1b2c74-8b1a-4f3e-9a0d-2c5e7b4d9a11" \
  -d '{"title":"秋季新品上架","body_html":"<p>今年秋天的第一波新品已經到店。</p>"}'

處理內容去重

內容去重比對標題與內文。十分鐘內同一家商店出現標題與內文都相同的新文章時,第二篇回 409 duplicate_contentdetails 指出既有那篇的識別碼。收到這個錯誤時沿用既有的文章。需要兩篇內容相同的文章時,等十分鐘或讓標題有實質差異。

讀限流額度

限流分讀與寫兩條獨立額度,按金鑰計算,每分鐘補滿。預設值見限制與配額,自己的值以 GET /v1/merate_limits 為準。

通過認證的回應都帶額度標頭,被擋之前就看得到剩餘次數:

X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
X-RateLimit-Reset: 1789171200

被擋時依 Retry-After 指示的秒數等待後再送。429 的三個錯誤碼處理動作各不相同,見錯誤碼

讀錯誤外殼

每個錯誤回應都是同一個外殼:

{
  "error": {
    "code": "validation_failed",
    "message": "請求欄位驗證失敗,請依 details 修正後重送。",
    "details": [{ "field": "title", "issue": "長度超過允許的上限。" }],
    "request_id": "req_k4m2p7q9r3s5t8v6w2x4y7z3",
    "retryable": false
  }
}
欄位 型別 必填或可為 null 用途
code 字串 必填 錯誤碼,程式依這個值分支
message 字串 必填 繁體中文說明,含可採取的下一步
details 陣列 可省略 逐欄位說明,每筆有 fieldissue;沒有欄位層級問題時是空陣列
request_id 字串 必填 這次請求的追蹤碼,與 X-Request-Id 相同
retryable 布林 必填 原樣重送是否可能成功

detailsfield 可能是內文欄位、查詢參數或標頭名稱,例如 Idempotency-Key

錯誤訊息文字會調整,錯誤碼不會。程式依 code 分支,先看 retryable 決定要不要重送。逐碼的處理動作見錯誤碼